Skip to content

Commit e5f202b

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

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package ssa
66

77
import (
8+
"cmd/compile/internal/types"
89
"cmd/internal/src"
910
"fmt"
1011
"math"
@@ -2132,6 +2133,21 @@ func addRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r rel
21322133
}
21332134
}
21342135

2136+
func unsignedAddOverflows(a, b uint64, t *types.Type) bool {
2137+
switch t.Size() {
2138+
case 8:
2139+
return a+b < a
2140+
case 4:
2141+
return a+b > math.MaxUint32
2142+
case 2:
2143+
return a+b > math.MaxUint16
2144+
case 1:
2145+
return a+b > math.MaxUint8
2146+
default:
2147+
panic("unreachable")
2148+
}
2149+
}
2150+
21352151
func addLocalFacts(ft *factsTable, b *Block) {
21362152
// Propagate constant ranges among values in this block.
21372153
// We do this before the second loop so that we have the
@@ -2151,6 +2167,21 @@ func addLocalFacts(ft *factsTable, b *Block) {
21512167
// FIXME(go.dev/issue/68857): this loop only set up limits properly when b.Values is in topological order.
21522168
// flowLimit can also depend on limits given by this loop which right now is not handled.
21532169
switch v.Op {
2170+
case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
2171+
x := ft.limits[v.Args[0].ID]
2172+
y := ft.limits[v.Args[1].ID]
2173+
if !unsignedAddOverflows(x.umax, y.umax, v.Type) {
2174+
r := gt
2175+
if !x.nonzero() {
2176+
r |= eq
2177+
}
2178+
ft.update(b, v, v.Args[1], unsigned, r)
2179+
r = gt
2180+
if !y.nonzero() {
2181+
r |= eq
2182+
}
2183+
ft.update(b, v, v.Args[0], unsigned, r)
2184+
}
21542185
case OpAnd64, OpAnd32, OpAnd16, OpAnd8:
21552186
ft.update(b, v, v.Args[0], unsigned, lt|eq)
21562187
ft.update(b, v, v.Args[1], unsigned, lt|eq)

test/prove.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,69 @@ func cvtBoolToUint8BCE(b bool, a [2]int64) int64 {
20412041
return a[c] // ERROR "Proved IsInBounds$"
20422042
}
20432043

2044+
func transitiveProofsThroughNonOverflowingUnsignedAdd(x, y, z uint64) {
2045+
x &= 1<<63 - 1
2046+
y &= 1<<63 - 1
2047+
2048+
a := x + y
2049+
if a > z {
2050+
return
2051+
}
2052+
2053+
if x > z { // ERROR "Disproved Less64U$"
2054+
return
2055+
}
2056+
if y > z { // ERROR "Disproved Less64U$"
2057+
return
2058+
}
2059+
if a == x {
2060+
return
2061+
}
2062+
if a == y {
2063+
return
2064+
}
2065+
2066+
x |= 1
2067+
y |= 1
2068+
a = x + y
2069+
if a == x { // ERROR "Disproved Eq64$"
2070+
return
2071+
}
2072+
if a == y { // ERROR "Disproved Eq64$"
2073+
return
2074+
}
2075+
}
2076+
2077+
func transitiveProofsThroughOverflowingUnsignedAdd(x, y, z uint64) {
2078+
a := x + y
2079+
if a > z {
2080+
return
2081+
}
2082+
2083+
if x > z {
2084+
return
2085+
}
2086+
if y > z {
2087+
return
2088+
}
2089+
if a == x {
2090+
return
2091+
}
2092+
if a == y {
2093+
return
2094+
}
2095+
2096+
x |= 1
2097+
y |= 1
2098+
a = x + y
2099+
if a == x {
2100+
return
2101+
}
2102+
if a == y {
2103+
return
2104+
}
2105+
}
2106+
20442107
//go:noinline
20452108
func useInt(a int) {
20462109
}

0 commit comments

Comments
 (0)