Skip to content

Commit 519ae51

Browse files
fengyoulingopherbot
authored andcommitted
cmd/compile: eliminate bound check for slices of the same length
If two slices start out with the same length and decrease in length by the same amount on each round of the loop (or in the if block), then we think their length are always equal. For example: if len(a) != len(b) { return } for len(a) >= 4 { a = a[4:] b = b[4:] // proved here, omit boundary check } if len(a) == len(b) { // proved here //... } Or, change 'for' to 'if': if len(a) != len(b) { return } if len(a) >= 4 { a = a[4:] b = b[4:] } if len(a) == len(b) { // proved here //... } Fixes #75144 Change-Id: I4e5902a02b5cf8fdc122715a7dbd2fb5e9a8f5dc Reviewed-on: https://go-review.googlesource.com/c/go/+/699155 Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org>
1 parent b5a29cc commit 519ae51

File tree

2 files changed

+312
-0
lines changed

2 files changed

+312
-0
lines changed

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

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,173 @@ func (ft *factsTable) cleanup(f *Func) {
12401240
f.Cache.freeBoolSlice(ft.recurseCheck)
12411241
}
12421242

1243+
// addSlicesOfSameLen finds the slices that are in the same block and whose Op
1244+
// is OpPhi and always have the same length, then add the equality relationship
1245+
// between them to ft. If two slices start out with the same length and decrease
1246+
// in length by the same amount on each round of the loop (or in the if block),
1247+
// then we think their lengths are always equal.
1248+
//
1249+
// See https://go.dev/issues/75144
1250+
//
1251+
// In fact, we are just propagating the equality
1252+
//
1253+
// if len(a) == len(b) { // from here
1254+
// for len(a) > 4 {
1255+
// a = a[4:]
1256+
// b = b[4:]
1257+
// }
1258+
// if len(a) == len(b) { // to here
1259+
// return true
1260+
// }
1261+
// }
1262+
//
1263+
// or change the for to if:
1264+
//
1265+
// if len(a) == len(b) { // from here
1266+
// if len(a) > 4 {
1267+
// a = a[4:]
1268+
// b = b[4:]
1269+
// }
1270+
// if len(a) == len(b) { // to here
1271+
// return true
1272+
// }
1273+
// }
1274+
func addSlicesOfSameLen(ft *factsTable, b *Block) {
1275+
// Let w points to the first value we're interested in, and then we
1276+
// only process those values ​​that appear to be the same length as w,
1277+
// looping only once. This should be enough in most cases. And u is
1278+
// similar to w, see comment for predIndex.
1279+
var u, w *Value
1280+
var i, j, k sliceInfo
1281+
isInterested := func(v *Value) bool {
1282+
j = getSliceInfo(v)
1283+
return j.sliceWhere != sliceUnknown
1284+
}
1285+
for _, v := range b.Values {
1286+
if v.Uses == 0 {
1287+
continue
1288+
}
1289+
if v.Op == OpPhi && len(v.Args) == 2 && ft.lens[v.ID] != nil && isInterested(v) {
1290+
if j.predIndex == 1 && ft.lens[v.Args[0].ID] != nil {
1291+
// found v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _))) or
1292+
// v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)))
1293+
if w == nil {
1294+
k = j
1295+
w = v
1296+
continue
1297+
}
1298+
// propagate the equality
1299+
if j == k && ft.orderS.Equal(ft.lens[v.Args[0].ID], ft.lens[w.Args[0].ID]) {
1300+
ft.update(b, ft.lens[v.ID], ft.lens[w.ID], signed, eq)
1301+
}
1302+
} else if j.predIndex == 0 && ft.lens[v.Args[1].ID] != nil {
1303+
// found v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _)) x) or
1304+
// v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)) x)
1305+
if u == nil {
1306+
i = j
1307+
u = v
1308+
continue
1309+
}
1310+
// propagate the equality
1311+
if j == i && ft.orderS.Equal(ft.lens[v.Args[1].ID], ft.lens[u.Args[1].ID]) {
1312+
ft.update(b, ft.lens[v.ID], ft.lens[u.ID], signed, eq)
1313+
}
1314+
}
1315+
}
1316+
}
1317+
}
1318+
1319+
type sliceWhere int
1320+
1321+
const (
1322+
sliceUnknown sliceWhere = iota
1323+
sliceInFor
1324+
sliceInIf
1325+
)
1326+
1327+
// predIndex is used to indicate the branch represented by the predecessor
1328+
// block in which the slicing operation occurs.
1329+
type predIndex int
1330+
1331+
type sliceInfo struct {
1332+
lengthDiff int64
1333+
sliceWhere
1334+
predIndex
1335+
}
1336+
1337+
// getSliceInfo returns the negative increment of the slice length in a slice
1338+
// operation by examine the Phi node at the merge block. So, we only interest
1339+
// in the slice operation if it is inside a for block or an if block.
1340+
// Otherwise it returns sliceInfo{0, sliceUnknown, 0}.
1341+
//
1342+
// For the following for block:
1343+
//
1344+
// for len(a) > 4 {
1345+
// a = a[4:]
1346+
// }
1347+
//
1348+
// vp = (Phi v3 v9)
1349+
// v5 = (SliceLen vp)
1350+
// v7 = (Add64 (Const64 [-4]) v5)
1351+
// v9 = (SliceMake _ v7 _)
1352+
//
1353+
// returns sliceInfo{-4, sliceInFor, 1}
1354+
//
1355+
// For a subsequent merge block after an if block:
1356+
//
1357+
// if len(a) > 4 {
1358+
// a = a[4:]
1359+
// }
1360+
// a // here
1361+
//
1362+
// vp = (Phi v3 v9)
1363+
// v5 = (SliceLen v3)
1364+
// v7 = (Add64 (Const64 [-4]) v5)
1365+
// v9 = (SliceMake _ v7 _)
1366+
//
1367+
// returns sliceInfo{-4, sliceInIf, 1}
1368+
//
1369+
// Returns sliceInfo{0, sliceUnknown, 0} if it is not the slice
1370+
// operation we are interested in.
1371+
func getSliceInfo(vp *Value) (inf sliceInfo) {
1372+
if vp.Op != OpPhi || len(vp.Args) != 2 {
1373+
return
1374+
}
1375+
var i predIndex
1376+
var l *Value // length for OpSliceMake
1377+
if vp.Args[0].Op != OpSliceMake && vp.Args[1].Op == OpSliceMake {
1378+
l = vp.Args[1].Args[1]
1379+
i = 1
1380+
} else if vp.Args[0].Op == OpSliceMake && vp.Args[1].Op != OpSliceMake {
1381+
l = vp.Args[0].Args[1]
1382+
i = 0
1383+
} else {
1384+
return
1385+
}
1386+
var op Op
1387+
switch l.Op {
1388+
case OpAdd64:
1389+
op = OpConst64
1390+
case OpAdd32:
1391+
op = OpConst32
1392+
default:
1393+
return
1394+
}
1395+
if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp {
1396+
return sliceInfo{l.Args[0].AuxInt, sliceInFor, i}
1397+
}
1398+
if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp {
1399+
return sliceInfo{l.Args[1].AuxInt, sliceInFor, i}
1400+
}
1401+
if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp.Args[1-i] {
1402+
return sliceInfo{l.Args[0].AuxInt, sliceInIf, i}
1403+
}
1404+
if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp.Args[1-i] {
1405+
return sliceInfo{l.Args[1].AuxInt, sliceInIf, i}
1406+
}
1407+
return
1408+
}
1409+
12431410
// prove removes redundant BlockIf branches that can be inferred
12441411
// from previous dominating comparisons.
12451412
//
@@ -1505,6 +1672,9 @@ func prove(f *Func) {
15051672
addBranchRestrictions(ft, parent, branch)
15061673
}
15071674

1675+
// Add slices of the same length start from current block.
1676+
addSlicesOfSameLen(ft, node.block)
1677+
15081678
if ft.unsat {
15091679
// node.block is unreachable.
15101680
// Remove it and don't visit

test/prove.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,6 +2342,148 @@ func setCapMaxBasedOnElementSize(x []uint64) int {
23422342
return 0
23432343
}
23442344

2345+
func issue75144for(a, b []uint64) bool {
2346+
if len(a) == len(b) {
2347+
for len(a) > 4 {
2348+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2349+
b = b[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2350+
}
2351+
if len(a) == len(b) { // ERROR "Proved Eq64$"
2352+
return true
2353+
}
2354+
}
2355+
return false
2356+
}
2357+
2358+
func issue75144if(a, b []uint64) bool {
2359+
if len(a) == len(b) {
2360+
if len(a) > 4 {
2361+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2362+
b = b[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2363+
}
2364+
if len(a) == len(b) { // ERROR "Proved Eq64$"
2365+
return true
2366+
}
2367+
}
2368+
return false
2369+
}
2370+
2371+
func issue75144if2(a, b, c, d []uint64) (r bool) {
2372+
if len(a) != len(b) || len(c) != len(d) {
2373+
return
2374+
}
2375+
if len(a) <= 4 || len(c) <= 4 {
2376+
return
2377+
}
2378+
if len(a) < len(c) {
2379+
c = c[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2380+
d = d[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2381+
} else {
2382+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2383+
b = b[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2384+
}
2385+
if len(a) == len(c) {
2386+
return
2387+
}
2388+
if len(a) == len(b) { // ERROR "Proved Eq64$"
2389+
r = true
2390+
}
2391+
if len(c) == len(d) { // ERROR "Proved Eq64$"
2392+
r = true
2393+
}
2394+
return
2395+
}
2396+
2397+
func issue75144forCannot(a, b []uint64) bool {
2398+
if len(a) == len(b) {
2399+
for len(a) > 4 {
2400+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2401+
b = b[4:]
2402+
for len(a) > 2 {
2403+
a = a[2:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2404+
b = b[2:]
2405+
}
2406+
}
2407+
if len(a) == len(b) {
2408+
return true
2409+
}
2410+
}
2411+
return false
2412+
}
2413+
2414+
func issue75144ifCannot(a, b []uint64) bool {
2415+
if len(a) == len(b) {
2416+
if len(a) > 4 {
2417+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2418+
b = b[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2419+
if len(a) > 2 {
2420+
a = a[2:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2421+
b = b[2:]
2422+
}
2423+
}
2424+
if len(a) == len(b) {
2425+
return true
2426+
}
2427+
}
2428+
return false
2429+
}
2430+
2431+
func issue75144ifCannot2(a, b []uint64) bool {
2432+
if len(a) == len(b) {
2433+
if len(a) > 4 {
2434+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2435+
b = b[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2436+
} else if len(a) > 2 {
2437+
a = a[2:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2438+
b = b[2:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2439+
}
2440+
if len(a) == len(b) {
2441+
return true
2442+
}
2443+
}
2444+
return false
2445+
}
2446+
2447+
func issue75144forNot(a, b []uint64) bool {
2448+
if len(a) == len(b) {
2449+
for len(a) > 4 {
2450+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2451+
b = b[3:]
2452+
}
2453+
if len(a) == len(b) {
2454+
return true
2455+
}
2456+
}
2457+
return false
2458+
}
2459+
2460+
func issue75144forNot2(a, b, c []uint64) bool {
2461+
if len(a) == len(b) {
2462+
for len(a) > 4 {
2463+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2464+
b = c[4:]
2465+
}
2466+
if len(a) == len(b) {
2467+
return true
2468+
}
2469+
}
2470+
return false
2471+
}
2472+
2473+
func issue75144ifNot(a, b []uint64) bool {
2474+
if len(a) == len(b) {
2475+
if len(a) > 4 {
2476+
a = a[4:] // ERROR "Proved slicemask not needed$" "Proved IsSliceInBounds$"
2477+
} else {
2478+
b = b[4:]
2479+
}
2480+
if len(a) == len(b) {
2481+
return true
2482+
}
2483+
}
2484+
return false
2485+
}
2486+
23452487
//go:noinline
23462488
func useInt(a int) {
23472489
}

0 commit comments

Comments
 (0)