Skip to content

Commit 1a72920

Browse files
Jorropogopherbot
authored andcommitted
cmd/compile: learn transitive proofs for safe positive signed adds
I've split this into it's own CL to make git bisect more effective. Change-Id: I3fbb42ec7d29169a29f7f55ef2c188317512f532 Reviewed-on: https://go-review.googlesource.com/c/go/+/685819 Auto-Submit: Michael Knyszek <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent e5f202b commit 1a72920

File tree

3 files changed

+111
-14
lines changed

3 files changed

+111
-14
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,22 @@ func unsignedAddOverflows(a, b uint64, t *types.Type) bool {
21482148
}
21492149
}
21502150

2151+
func signedAddOverflowsOrUnderflows(a, b int64, t *types.Type) bool {
2152+
r := a + b
2153+
switch t.Size() {
2154+
case 8:
2155+
return (a >= 0 && b >= 0 && r < 0) || (a < 0 && b < 0 && r >= 0)
2156+
case 4:
2157+
return r < math.MinInt32 || math.MaxInt32 < r
2158+
case 2:
2159+
return r < math.MinInt16 || math.MaxInt16 < r
2160+
case 1:
2161+
return r < math.MinInt8 || math.MaxInt8 < r
2162+
default:
2163+
panic("unreachable")
2164+
}
2165+
}
2166+
21512167
func addLocalFacts(ft *factsTable, b *Block) {
21522168
// Propagate constant ranges among values in this block.
21532169
// We do this before the second loop so that we have the
@@ -2182,6 +2198,20 @@ func addLocalFacts(ft *factsTable, b *Block) {
21822198
}
21832199
ft.update(b, v, v.Args[0], unsigned, r)
21842200
}
2201+
if x.min >= 0 && !signedAddOverflowsOrUnderflows(x.max, y.max, v.Type) {
2202+
r := gt
2203+
if !x.nonzero() {
2204+
r |= eq
2205+
}
2206+
ft.update(b, v, v.Args[1], signed, r)
2207+
}
2208+
if y.min >= 0 && !signedAddOverflowsOrUnderflows(x.max, y.max, v.Type) {
2209+
r := gt
2210+
if !y.nonzero() {
2211+
r |= eq
2212+
}
2213+
ft.update(b, v, v.Args[0], signed, r)
2214+
}
21852215
case OpAnd64, OpAnd32, OpAnd16, OpAnd8:
21862216
ft.update(b, v, v.Args[0], unsigned, lt|eq)
21872217
ft.update(b, v, v.Args[1], unsigned, lt|eq)

test/prove.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,6 +2104,73 @@ func transitiveProofsThroughOverflowingUnsignedAdd(x, y, z uint64) {
21042104
}
21052105
}
21062106

2107+
func transitiveProofsThroughNonOverflowingSignedAddPositive(x, y, z int64) {
2108+
x &= 1<<62 - 1
2109+
y &= 1<<62 - 1
2110+
2111+
a := x + y
2112+
if a > z {
2113+
return
2114+
}
2115+
2116+
if x > z { // ERROR "Disproved Less64$"
2117+
return
2118+
}
2119+
if y > z { // ERROR "Disproved Less64$"
2120+
return
2121+
}
2122+
if a == x {
2123+
return
2124+
}
2125+
if a == y {
2126+
return
2127+
}
2128+
2129+
x |= 1
2130+
y |= 1
2131+
a = x + y
2132+
if a == x { // ERROR "Disproved Eq64$"
2133+
return
2134+
}
2135+
if a == y { // ERROR "Disproved Eq64$"
2136+
return
2137+
}
2138+
}
2139+
2140+
func transitiveProofsThroughOverflowingSignedAddPositive(x, y, z int64) {
2141+
if x < 0 || y < 0 {
2142+
return
2143+
}
2144+
2145+
a := x + y
2146+
if a > z {
2147+
return
2148+
}
2149+
2150+
if x > z {
2151+
return
2152+
}
2153+
if y > z {
2154+
return
2155+
}
2156+
if a == x {
2157+
return
2158+
}
2159+
if a == y {
2160+
return
2161+
}
2162+
2163+
x |= 1
2164+
y |= 1
2165+
a = x + y
2166+
if a == x { // ERROR "Disproved Eq64$"
2167+
return
2168+
}
2169+
if a == y { // ERROR "Disproved Eq64$"
2170+
return
2171+
}
2172+
}
2173+
21072174
//go:noinline
21082175
func useInt(a int) {
21092176
}

test/prove_constant_folding.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@
99
package main
1010

1111
func f0i(x int) int {
12-
if x == 20 {
13-
return x // ERROR "Proved.+is constant 20$"
14-
}
12+
if x == 20 {
13+
return x // ERROR "Proved.+is constant 20$"
14+
}
1515

16-
if (x + 20) == 20 {
17-
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$"
18-
}
16+
if (x + 20) == 20 {
17+
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$" "x\+d >=? w"
18+
}
1919

20-
return x / 2
20+
return x / 2
2121
}
2222

2323
func f0u(x uint) uint {
24-
if x == 20 {
25-
return x // ERROR "Proved.+is constant 20$"
26-
}
24+
if x == 20 {
25+
return x // ERROR "Proved.+is constant 20$"
26+
}
2727

28-
if (x + 20) == 20 {
29-
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$"
30-
}
28+
if (x + 20) == 20 {
29+
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$" "x\+d >=? w"
30+
}
3131

32-
return x / 2
32+
return x / 2
3333
}

0 commit comments

Comments
 (0)