Skip to content

Commit 1caa95a

Browse files
dr2chasecherrymui
authored andcommitted
cmd/compile: enhance prove to deal with double-offset IsInBounds checks
For chunked iterations (useful for, but not exclusive to, SIMD calculations) it is common to see the combination of ``` for ; i <= len(m)-4; i += 4 { ``` and ``` r0, r1, r2, r3 := m[i], m[i+1], m[i+2], m[i+3] `` Prove did not handle the case of len-offset1 vs index+offset2 checking, but this change fixes this. There may be other similar cases yet to handle -- this worked for the chunked loops for simd, as well as a handful in std. Cherry-picked from the dev.simd branch. This CL is not necessarily SIMD specific. Apply early to reduce risk. Change-Id: I3785df83028d517e5e5763206653b34b2befd3d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/700696 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/708859 Reviewed-by: David Chase <[email protected]>
1 parent ec70d19 commit 1caa95a

File tree

2 files changed

+72
-6
lines changed

2 files changed

+72
-6
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,65 @@ func unsignedSubUnderflows(a, b uint64) bool {
21742174
return a < b
21752175
}
21762176

2177+
// checkForChunkedIndexBounds looks for index expressions of the form
2178+
// A[i+delta] where delta < K and i <= len(A)-K. That is, this is a chunked
2179+
// iteration where the index is not directly compared to the length.
2180+
func checkForChunkedIndexBounds(ft *factsTable, b *Block, index, bound *Value) bool {
2181+
if bound.Op != OpSliceLen {
2182+
return false
2183+
}
2184+
lim := ft.limits[index.ID]
2185+
if lim.min < 0 {
2186+
return false
2187+
}
2188+
i, delta := isConstDelta(index)
2189+
if i == nil {
2190+
return false
2191+
}
2192+
if delta < 0 {
2193+
return false
2194+
}
2195+
// special case for blocked iteration over a slice.
2196+
// slicelen > i + delta && <==== if clauses above
2197+
// && index >= 0 <==== if clause above
2198+
// delta >= 0 && <==== if clause above
2199+
// slicelen-K >/>= x <==== checked below
2200+
// && K >=/> delta <==== checked below
2201+
// then v > w
2202+
// example: i <=/< len - 4/3 means i+{0,1,2,3} are legal indices
2203+
for o := ft.orderings[i.ID]; o != nil; o = o.next {
2204+
if o.d != signed {
2205+
continue
2206+
}
2207+
if ow := o.w; ow.Op == OpAdd64 {
2208+
var lenOffset *Value
2209+
if ow.Args[0] == bound {
2210+
lenOffset = ow.Args[1]
2211+
} else if ow.Args[1] == bound {
2212+
lenOffset = ow.Args[0]
2213+
}
2214+
if lenOffset == nil || lenOffset.Op != OpConst64 {
2215+
continue
2216+
}
2217+
if K := -lenOffset.AuxInt; K >= 0 {
2218+
or := o.r
2219+
if or == lt {
2220+
or = lt | eq
2221+
K++
2222+
if K < 0 {
2223+
continue
2224+
}
2225+
}
2226+
2227+
if delta < K && or == lt|eq {
2228+
return true
2229+
}
2230+
}
2231+
}
2232+
}
2233+
return false
2234+
}
2235+
21772236
func addLocalFacts(ft *factsTable, b *Block) {
21782237
// Propagate constant ranges among values in this block.
21792238
// We do this before the second loop so that we have the
@@ -2285,6 +2344,13 @@ func addLocalFacts(ft *factsTable, b *Block) {
22852344
if v.Args[0].Op == OpSliceMake {
22862345
ft.update(b, v, v.Args[0].Args[2], signed, eq)
22872346
}
2347+
case OpIsInBounds:
2348+
if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1]) {
2349+
if b.Func.pass.debug > 0 {
2350+
b.Func.Warnl(v.Pos, "Proved %s for blocked indexing", v.Op)
2351+
}
2352+
ft.booleanTrue(v)
2353+
}
22882354
case OpPhi:
22892355
addLocalFactsPhi(ft, v)
22902356
}

test/prove.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -773,8 +773,8 @@ func indexGT0(b []byte, n int) {
773773
func unrollUpExcl(a []int) int {
774774
var i, x int
775775
for i = 0; i < len(a)-1; i += 2 { // ERROR "Induction variable: limits \[0,\?\), increment 2$"
776-
x += a[i] // ERROR "Proved IsInBounds$"
777-
x += a[i+1]
776+
x += a[i] // ERROR "Proved IsInBounds$"
777+
x += a[i+1] // ERROR "Proved IsInBounds( for blocked indexing)?$"
778778
}
779779
if i == len(a)-1 {
780780
x += a[i]
@@ -786,8 +786,8 @@ func unrollUpExcl(a []int) int {
786786
func unrollUpIncl(a []int) int {
787787
var i, x int
788788
for i = 0; i <= len(a)-2; i += 2 { // ERROR "Induction variable: limits \[0,\?\], increment 2$"
789-
x += a[i] // ERROR "Proved IsInBounds$"
790-
x += a[i+1]
789+
x += a[i] // ERROR "Proved IsInBounds$"
790+
x += a[i+1] // ERROR "Proved IsInBounds( for blocked indexing)?$"
791791
}
792792
if i == len(a)-1 {
793793
x += a[i]
@@ -839,7 +839,7 @@ func unrollExclStepTooLarge(a []int) int {
839839
var i, x int
840840
for i = 0; i < len(a)-1; i += 3 {
841841
x += a[i]
842-
x += a[i+1]
842+
x += a[i+1] // ERROR "Proved IsInBounds( for blocked indexing)?$"
843843
}
844844
if i == len(a)-1 {
845845
x += a[i]
@@ -852,7 +852,7 @@ func unrollInclStepTooLarge(a []int) int {
852852
var i, x int
853853
for i = 0; i <= len(a)-2; i += 3 {
854854
x += a[i]
855-
x += a[i+1]
855+
x += a[i+1] // ERROR "Proved IsInBounds( for blocked indexing)?$"
856856
}
857857
if i == len(a)-1 {
858858
x += a[i]

0 commit comments

Comments
 (0)