github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/ssa/loopbce.go (about) 1 package ssa 2 3 type indVar struct { 4 ind *Value // induction variable 5 inc *Value // increment, a constant 6 nxt *Value // ind+inc variable 7 min *Value // minimum value. inclusive, 8 max *Value // maximum value. exclusive. 9 entry *Block // entry block in the loop. 10 // Invariants: for all blocks dominated by entry: 11 // min <= ind < max 12 // min <= nxt <= max 13 } 14 15 // findIndVar finds induction variables in a function. 16 // 17 // Look for variables and blocks that satisfy the following 18 // 19 // loop: 20 // ind = (Phi min nxt), 21 // if ind < max 22 // then goto enter_loop 23 // else goto exit_loop 24 // 25 // enter_loop: 26 // do something 27 // nxt = inc + ind 28 // goto loop 29 // 30 // exit_loop: 31 // 32 // 33 // TODO: handle 32 bit operations 34 func findIndVar(f *Func) []indVar { 35 var iv []indVar 36 37 nextb: 38 for _, b := range f.Blocks { 39 if b.Kind != BlockIf || len(b.Preds) != 2 { 40 continue 41 } 42 43 var ind, max *Value // induction, and maximum 44 entry := -1 // which successor of b enters the loop 45 46 // Check thet the control if it either ind < max or max > ind. 47 // TODO: Handle Leq64, Geq64. 48 switch b.Control.Op { 49 case OpLess64: 50 entry = 0 51 ind, max = b.Control.Args[0], b.Control.Args[1] 52 case OpGreater64: 53 entry = 0 54 ind, max = b.Control.Args[1], b.Control.Args[0] 55 default: 56 continue nextb 57 } 58 59 // Check that the induction variable is a phi that depends on itself. 60 if ind.Op != OpPhi { 61 continue 62 } 63 64 // Extract min and nxt knowing that nxt is an addition (e.g. Add64). 65 var min, nxt *Value // minimum, and next value 66 if n := ind.Args[0]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) { 67 min, nxt = ind.Args[1], n 68 } else if n := ind.Args[1]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) { 69 min, nxt = ind.Args[0], n 70 } else { 71 // Not a recognized induction variable. 72 continue 73 } 74 75 var inc *Value 76 if nxt.Args[0] == ind { // nxt = ind + inc 77 inc = nxt.Args[1] 78 } else if nxt.Args[1] == ind { // nxt = inc + ind 79 inc = nxt.Args[0] 80 } else { 81 panic("unreachable") // one of the cases must be true from the above. 82 } 83 84 // Expect the increment to be a positive constant. 85 // TODO: handle negative increment. 86 if inc.Op != OpConst64 || inc.AuxInt <= 0 { 87 continue 88 } 89 90 // Up to now we extracted the induction variable (ind), 91 // the increment delta (inc), the temporary sum (nxt), 92 // the mininum value (min) and the maximum value (max). 93 // 94 // We also know that ind has the form (Phi min nxt) where 95 // nxt is (Add inc nxt) which means: 1) inc dominates nxt 96 // and 2) there is a loop starting at inc and containing nxt. 97 // 98 // We need to prove that the induction variable is incremented 99 // only when it's smaller than the maximum value. 100 // Two conditions must happen listed below to accept ind 101 // as an induction variable. 102 103 // First condition: loop entry has a single predecessor, which 104 // is the header block. This implies that b.Succs[entry] is 105 // reached iff ind < max. 106 if len(b.Succs[entry].b.Preds) != 1 { 107 // b.Succs[1-entry] must exit the loop. 108 continue 109 } 110 111 // Second condition: b.Succs[entry] dominates nxt so that 112 // nxt is computed when inc < max, meaning nxt <= max. 113 if !f.sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) { 114 // inc+ind can only be reached through the branch that enters the loop. 115 continue 116 } 117 118 // If max is c + SliceLen with c <= 0 then we drop c. 119 // Makes sure c + SliceLen doesn't overflow when SliceLen == 0. 120 // TODO: save c as an offset from max. 121 if w, c := dropAdd64(max); (w.Op == OpStringLen || w.Op == OpSliceLen) && 0 >= c && -c >= 0 { 122 max = w 123 } 124 125 // We can only guarantee that the loops runs within limits of induction variable 126 // if the increment is 1 or when the limits are constants. 127 if inc.AuxInt != 1 { 128 ok := false 129 if min.Op == OpConst64 && max.Op == OpConst64 { 130 if max.AuxInt > min.AuxInt && max.AuxInt%inc.AuxInt == min.AuxInt%inc.AuxInt { // handle overflow 131 ok = true 132 } 133 } 134 if !ok { 135 continue 136 } 137 } 138 139 if f.pass.debug > 1 { 140 if min.Op == OpConst64 { 141 b.Func.Config.Warnl(b.Line, "Induction variable with minimum %d and increment %d", min.AuxInt, inc.AuxInt) 142 } else { 143 b.Func.Config.Warnl(b.Line, "Induction variable with non-const minimum and increment %d", inc.AuxInt) 144 } 145 } 146 147 iv = append(iv, indVar{ 148 ind: ind, 149 inc: inc, 150 nxt: nxt, 151 min: min, 152 max: max, 153 entry: b.Succs[entry].b, 154 }) 155 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max) 156 } 157 158 return iv 159 } 160 161 // loopbce performs loop based bounds check elimination. 162 func loopbce(f *Func) { 163 ivList := findIndVar(f) 164 165 m := make(map[*Value]indVar) 166 for _, iv := range ivList { 167 m[iv.ind] = iv 168 } 169 170 removeBoundsChecks(f, m) 171 } 172 173 // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables. 174 func removeBoundsChecks(f *Func, m map[*Value]indVar) { 175 for _, b := range f.Blocks { 176 if b.Kind != BlockIf { 177 continue 178 } 179 180 v := b.Control 181 182 // Simplify: 183 // (IsInBounds ind max) where 0 <= const == min <= ind < max. 184 // (IsSliceInBounds ind max) where 0 <= const == min <= ind < max. 185 // Found in: 186 // for i := range a { 187 // use a[i] 188 // use a[i:] 189 // use a[:i] 190 // } 191 if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds { 192 ind, add := dropAdd64(v.Args[0]) 193 if ind.Op != OpPhi { 194 goto skip1 195 } 196 if v.Op == OpIsInBounds && add != 0 { 197 goto skip1 198 } 199 if v.Op == OpIsSliceInBounds && (0 > add || add > 1) { 200 goto skip1 201 } 202 203 if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { 204 if v.Args[1] == iv.max { 205 if f.pass.debug > 0 { 206 f.Config.Warnl(b.Line, "Found redundant %s", v.Op) 207 } 208 goto simplify 209 } 210 } 211 } 212 skip1: 213 214 // Simplify: 215 // (IsSliceInBounds ind (SliceCap a)) where 0 <= min <= ind < max == (SliceLen a) 216 // Found in: 217 // for i := range a { 218 // use a[:i] 219 // use a[:i+1] 220 // } 221 if v.Op == OpIsSliceInBounds { 222 ind, add := dropAdd64(v.Args[0]) 223 if ind.Op != OpPhi { 224 goto skip2 225 } 226 if 0 > add || add > 1 { 227 goto skip2 228 } 229 230 if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { 231 if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] { 232 if f.pass.debug > 0 { 233 f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op) 234 } 235 goto simplify 236 } 237 } 238 } 239 skip2: 240 241 // Simplify 242 // (IsInBounds (Add64 ind) (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c]) 243 // (IsSliceInBounds ind (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c]) 244 if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds { 245 ind, add := dropAdd64(v.Args[0]) 246 if ind.Op != OpPhi { 247 goto skip3 248 } 249 250 // ind + add >= 0 <-> min + add >= 0 <-> min >= -add 251 if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) { 252 if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() { 253 goto skip3 254 } 255 256 limit := v.Args[1].AuxInt 257 if v.Op == OpIsSliceInBounds { 258 // If limit++ overflows signed integer then 0 <= max && max <= limit will be false. 259 limit++ 260 } 261 262 if max := iv.max.AuxInt + add; 0 <= max && max <= limit { // handle overflow 263 if f.pass.debug > 0 { 264 f.Config.Warnl(b.Line, "Found redundant (%s ind %d), ind < %d", v.Op, v.Args[1].AuxInt, iv.max.AuxInt+add) 265 } 266 goto simplify 267 } 268 } 269 } 270 skip3: 271 272 continue 273 274 simplify: 275 f.Logf("removing bounds check %v at %v in %s\n", b.Control, b, f.Name) 276 b.Kind = BlockFirst 277 b.SetControl(nil) 278 } 279 } 280 281 func dropAdd64(v *Value) (*Value, int64) { 282 if v.Op == OpAdd64 && v.Args[0].Op == OpConst64 { 283 return v.Args[1], v.Args[0].AuxInt 284 } 285 if v.Op == OpAdd64 && v.Args[1].Op == OpConst64 { 286 return v.Args[0], v.Args[1].AuxInt 287 } 288 return v, 0 289 } 290 291 func isGreaterOrEqualThan(v *Value, c int64) bool { 292 if c == 0 { 293 return isNonNegative(v) 294 } 295 if v.isGenericIntConst() && v.AuxInt >= c { 296 return true 297 } 298 return false 299 }