Skip to content

Commit 09ea3c0

Browse files
committed
cmd/compile: in prove, fix fence-post implications for unsigned domain
Fence-post implications of the form "x-1 >= w && x > min ⇒ x > w" were not correctly handling unsigned domain, by always checking signed limits. This bug was uncovered once we taught prove that len(x) is always >= 0 in the signed domain. In the code being miscompiled (s[len(s)-1]), prove checks whether len(s)-1 >= len(s) in the unsigned domain; if it proves that this is always false, it can remove the bound check. Notice that len(s)-1 >= len(s) can be true for len(s) = 0 because of the wrap-around, so this is something prove should not be able to deduce. But because of the bug, the gate condition for the fence-post implication was len(s) > MinInt64 instead of len(s) > 0; that condition would be good in the signed domain but not in the unsigned domain. And since in CL105635 we taught prove that len(s) >= 0, the condition incorrectly triggered (len(s) >= 0 > MinInt64) and things were going downfall. Fixes #27251 Fixes #27289 Change-Id: I3dbcb1955ac5a66a0dcbee500f41e8d219409be5 Reviewed-on: https://go-review.googlesource.com/132495 Reviewed-by: Keith Randall <[email protected]>
1 parent 8a2b5f1 commit 09ea3c0

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

src/cmd/compile/internal/ssa/prove.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,13 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
425425
//
426426
// Useful for i > 0; s[i-1].
427427
lim, ok := ft.limits[x.ID]
428-
if ok && lim.min > opMin[v.Op] {
428+
if ok && ((d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0)) {
429429
ft.update(parent, x, w, d, gt)
430430
}
431431
} else if x, delta := isConstDelta(w); x != nil && delta == 1 {
432432
// v >= x+1 && x < max ⇒ v > x
433433
lim, ok := ft.limits[x.ID]
434-
if ok && lim.max < opMax[w.Op] {
434+
if ok && ((d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op])) {
435435
ft.update(parent, v, x, d, gt)
436436
}
437437
}
@@ -527,6 +527,11 @@ var opMax = map[Op]int64{
527527
OpAdd32: math.MaxInt32, OpSub32: math.MaxInt32,
528528
}
529529

530+
var opUMax = map[Op]uint64{
531+
OpAdd64: math.MaxUint64, OpSub64: math.MaxUint64,
532+
OpAdd32: math.MaxUint32, OpSub32: math.MaxUint32,
533+
}
534+
530535
// isNonNegative reports whether v is known to be non-negative.
531536
func (ft *factsTable) isNonNegative(v *Value) bool {
532537
if isNonNegative(v) {

test/fixedbugs/issue27289.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run
2+
3+
// Copyright 2018 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Make sure we don't prove that the bounds check failure branch is unreachable.
8+
9+
package main
10+
11+
//go:noinline
12+
func f(a []int) {
13+
_ = a[len(a)-1]
14+
}
15+
16+
func main() {
17+
defer func() {
18+
if err := recover(); err != nil {
19+
return
20+
}
21+
panic("f should panic")
22+
}()
23+
f(nil)
24+
}

test/prove.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ func fence2(x, y int) {
542542
}
543543
}
544544

545-
func fence3(b []int, x, y int64) {
545+
func fence3(b, c []int, x, y int64) {
546546
if x-1 >= y {
547547
if x <= y { // Can't prove because x may have wrapped.
548548
return
@@ -555,6 +555,8 @@ func fence3(b []int, x, y int64) {
555555
}
556556
}
557557

558+
c[len(c)-1] = 0 // Can't prove because len(c) might be 0
559+
558560
if n := len(b); n > 0 {
559561
b[n-1] = 0 // ERROR "Proved IsInBounds$"
560562
}

0 commit comments

Comments
 (0)