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:
15 // findIndVar finds induction variables in a function.
17 // Look for variables and blocks that satisfy the following
20 // ind = (Phi min nxt),
22 // then goto enter_loop
23 // else goto exit_loop
33 // TODO: handle 32 bit operations
34 func findIndVar(f *Func) []indVar {
38 for _, b := range f.Blocks {
39 if b.Kind != BlockIf || len(b.Preds) != 2 {
43 var ind, max *Value // induction, and maximum
44 entry := -1 // which successor of b enters the loop
46 // Check thet the control if it either ind < max or max > ind.
47 // TODO: Handle Leq64, Geq64.
51 ind, max = b.Control.Args[0], b.Control.Args[1]
54 ind, max = b.Control.Args[1], b.Control.Args[0]
59 // Check that the induction variable is a phi that depends on itself.
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
71 // Not a recognized induction variable.
76 if nxt.Args[0] == ind { // nxt = ind + inc
78 } else if nxt.Args[1] == ind { // nxt = inc + ind
81 panic("unreachable") // one of the cases must be true from the above.
84 // Expect the increment to be a positive constant.
85 // TODO: handle negative increment.
86 if inc.Op != OpConst64 || inc.AuxInt <= 0 {
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).
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.
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.
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.
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.
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 {
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.
129 if min.Op == OpConst64 && max.Op == OpConst64 {
130 if max.AuxInt > min.AuxInt && max.AuxInt%inc.AuxInt == min.AuxInt%inc.AuxInt { // handle overflow
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)
143 b.Func.Config.Warnl(b.Line, "Induction variable with non-const minimum and increment %d", inc.AuxInt)
147 iv = append(iv, indVar{
153 entry: b.Succs[entry].b,
155 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
161 // loopbce performs loop based bounds check elimination.
162 func loopbce(f *Func) {
163 ivList := findIndVar(f)
165 m := make(map[*Value]indVar)
166 for _, iv := range ivList {
170 removeBoundsChecks(f, m)
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 {
183 // (IsInBounds ind max) where 0 <= const == min <= ind < max.
184 // (IsSliceInBounds ind max) where 0 <= const == min <= ind < max.
186 // for i := range a {
191 if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds {
192 ind, add := dropAdd64(v.Args[0])
196 if v.Op == OpIsInBounds && add != 0 {
199 if v.Op == OpIsSliceInBounds && (0 > add || add > 1) {
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)
215 // (IsSliceInBounds ind (SliceCap a)) where 0 <= min <= ind < max == (SliceLen a)
217 // for i := range a {
221 if v.Op == OpIsSliceInBounds {
222 ind, add := dropAdd64(v.Args[0])
226 if 0 > add || add > 1 {
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)
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])
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() {
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.
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)
275 f.Logf("removing bounds check %v at %v in %s\n", b.Control, b, f.Name)
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
285 if v.Op == OpAdd64 && v.Args[1].Op == OpConst64 {
286 return v.Args[0], v.Args[1].AuxInt
291 func isGreaterOrEqualThan(v *Value, c int64) bool {
293 return isNonNegative(v)
295 if v.isGenericIntConst() && v.AuxInt >= c {