// checkForChunkedIndexBounds looks for index expressions of the form
// A[i+delta] where delta < K and i <= len(A)-K. That is, this is a chunked
// iteration where the index is not directly compared to the length.
-func checkForChunkedIndexBounds(ft *factsTable, b *Block, index, bound *Value) bool {
- if bound.Op != OpSliceLen {
+// if isReslice, then delta can be equal to K.
+func checkForChunkedIndexBounds(ft *factsTable, b *Block, index, bound *Value, isReslice bool) bool {
+ if bound.Op != OpSliceLen && bound.Op != OpSliceCap {
return false
}
+
+ // this is a slice bounds check against len or capacity,
+ // and refers back to a prior check against length, which
+ // will also work for the cap since that is not smaller
+ // than the length.
+
+ slice := bound.Args[0]
lim := ft.limits[index.ID]
if lim.min < 0 {
return false
}
if ow := o.w; ow.Op == OpAdd64 {
var lenOffset *Value
- if ow.Args[0] == bound {
+ if bound := ow.Args[0]; bound.Op == OpSliceLen && bound.Args[0] == slice {
lenOffset = ow.Args[1]
- } else if ow.Args[1] == bound {
+ } else if bound := ow.Args[1]; bound.Op == OpSliceLen && bound.Args[0] == slice {
lenOffset = ow.Args[0]
}
if lenOffset == nil || lenOffset.Op != OpConst64 {
}
if K := -lenOffset.AuxInt; K >= 0 {
or := o.r
+ if isReslice {
+ K++
+ }
if or == lt {
or = lt | eq
K++
- if K < 0 {
- continue
- }
+ }
+ if K < 0 { // We hate thinking about overflow
+ continue
}
if delta < K && or == lt|eq {
ft.update(b, v, v.Args[0].Args[2], signed, eq)
}
case OpIsInBounds:
- if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1]) {
+ if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1], false) {
if b.Func.pass.debug > 0 {
b.Func.Warnl(v.Pos, "Proved %s for blocked indexing", v.Op)
}
ft.booleanTrue(v)
}
+ case OpIsSliceInBounds:
+ if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1], true) {
+ if b.Func.pass.debug > 0 {
+ b.Func.Warnl(v.Pos, "Proved %s for blocked reslicing", v.Op)
+ }
+ ft.booleanTrue(v)
+ }
case OpPhi:
addLocalFactsPhi(ft, v)
}