@@ -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
0 commit comments