diff options
-rw-r--r-- | src/cmd/compile/internal/ssa/prove.go | 41 | ||||
-rw-r--r-- | test/prove.go | 57 |
2 files changed, 98 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index 7c69327990..697862f986 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -530,6 +530,25 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) { } } + // Look through value-preserving extensions. + // If the domain is appropriate for the pre-extension Type, + // repeat the update with the pre-extension Value. + if isCleanExt(v) { + switch { + case d == signed && v.Args[0].Type.IsSigned(): + fallthrough + case d == unsigned && !v.Args[0].Type.IsSigned(): + ft.update(parent, v.Args[0], w, d, r) + } + } + if isCleanExt(w) { + switch { + case d == signed && w.Args[0].Type.IsSigned(): + fallthrough + case d == unsigned && !w.Args[0].Type.IsSigned(): + ft.update(parent, v, w.Args[0], d, r) + } + } } var opMin = map[Op]int64{ @@ -584,6 +603,11 @@ func (ft *factsTable) isNonNegative(v *Value) bool { } } + // Check if v is a value-preserving extension of a non-negative value. + if isCleanExt(v) && ft.isNonNegative(v.Args[0]) { + return true + } + // Check if the signed poset can prove that the value is >= 0 return ft.order[0].OrderedOrEqual(ft.zero, v) } @@ -1299,3 +1323,20 @@ func isConstDelta(v *Value) (w *Value, delta int64) { } return nil, 0 } + +// isCleanExt reports whether v is the result of a value-preserving +// sign or zero extension +func isCleanExt(v *Value) bool { + switch v.Op { + case OpSignExt8to16, OpSignExt8to32, OpSignExt8to64, + OpSignExt16to32, OpSignExt16to64, OpSignExt32to64: + // signed -> signed is the only value-preserving sign extension + return v.Args[0].Type.IsSigned() && v.Type.IsSigned() + + case OpZeroExt8to16, OpZeroExt8to32, OpZeroExt8to64, + OpZeroExt16to32, OpZeroExt16to64, OpZeroExt32to64: + // unsigned -> signed/unsigned are value-preserving zero extensions + return !v.Args[0].Type.IsSigned() + } + return false +} diff --git a/test/prove.go b/test/prove.go index 6e92b9eec2..7643031c62 100644 --- a/test/prove.go +++ b/test/prove.go @@ -853,6 +853,63 @@ func unrollIncMin(a []int) int { return x } +// The 4 xxxxExtNto64 functions below test whether prove is looking +// through value-preserving sign/zero extensions of index values (issue #26292). + +// Look through all extensions +func signExtNto64(x []int, j8 int8, j16 int16, j32 int32) int { + if len(x) < 22 { + return 0 + } + if j8 >= 0 && j8 < 22 { + return x[j8] // ERROR "Proved IsInBounds$" + } + if j16 >= 0 && j16 < 22 { + return x[j16] // ERROR "Proved IsInBounds$" + } + if j32 >= 0 && j32 < 22 { + return x[j32] // ERROR "Proved IsInBounds$" + } + return 0 +} + +func zeroExtNto64(x []int, j8 uint8, j16 uint16, j32 uint32) int { + if len(x) < 22 { + return 0 + } + if j8 >= 0 && j8 < 22 { + return x[j8] // ERROR "Proved IsInBounds$" + } + if j16 >= 0 && j16 < 22 { + return x[j16] // ERROR "Proved IsInBounds$" + } + if j32 >= 0 && j32 < 22 { + return x[j32] // ERROR "Proved IsInBounds$" + } + return 0 +} + +// Process fence-post implications through 32to64 extensions (issue #29964) +func signExt32to64Fence(x []int, j int32) int { + if x[j] != 0 { + return 1 + } + if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$" + return 1 + } + return 0 +} + +func zeroExt32to64Fence(x []int, j uint32) int { + if x[j] != 0 { + return 1 + } + if j > 0 && x[j-1] != 0 { // ERROR "Proved IsInBounds$" + return 1 + } + return 0 +} + //go:noinline func useInt(a int) { } |