1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
8 "cmd/compile/internal/base"
13 type indVarFlags uint8
16 indVarMinExc indVarFlags = 1 << iota // minimum value is exclusive (default: inclusive)
17 indVarMaxInc // maximum value is inclusive (default: exclusive)
21 ind *Value // induction variable
22 min *Value // minimum value, inclusive/exclusive depends on flags
23 max *Value // maximum value, inclusive/exclusive depends on flags
24 entry *Block // entry block in the loop.
26 // Invariant: for all blocks strictly dominated by entry:
27 // min <= ind < max [if flags == 0]
28 // min < ind < max [if flags == indVarMinExc]
29 // min <= ind <= max [if flags == indVarMaxInc]
30 // min < ind <= max [if flags == indVarMinExc|indVarMaxInc]
33 // parseIndVar checks whether the SSA value passed as argument is a valid induction
34 // variable, and, if so, extracts:
35 // - the minimum bound
36 // - the increment value
37 // - the "next" value (SSA value that is Phi'd into the induction variable every loop)
39 // Currently, we detect induction variables that match (Phi min nxt),
40 // with nxt being (Add inc ind).
41 // If it can't parse the induction variable correctly, it returns (nil, nil, nil).
42 func parseIndVar(ind *Value) (min, inc, nxt *Value) {
47 if n := ind.Args[0]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) {
48 min, nxt = ind.Args[1], n
49 } else if n := ind.Args[1]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) {
50 min, nxt = ind.Args[0], n
52 // Not a recognized induction variable.
56 if nxt.Args[0] == ind { // nxt = ind + inc
58 } else if nxt.Args[1] == ind { // nxt = inc + ind
61 panic("unreachable") // one of the cases must be true from the above.
67 // findIndVar finds induction variables in a function.
69 // Look for variables and blocks that satisfy the following
72 // ind = (Phi min nxt),
74 // then goto enter_loop
75 // else goto exit_loop
84 // TODO: handle 32 bit operations
85 func findIndVar(f *Func) []indVar {
89 for _, b := range f.Blocks {
90 if b.Kind != BlockIf || len(b.Preds) != 2 {
94 var ind *Value // induction variable
95 var init *Value // starting value
96 var limit *Value // ending value
98 // Check thet the control if it either ind </<= limit or limit </<= ind.
99 // TODO: Handle 32-bit comparisons.
100 // TODO: Handle unsigned comparisons?
108 ind, limit = c.Args[0], c.Args[1]
113 // See if this is really an induction variable
115 init, inc, nxt := parseIndVar(ind)
117 // We failed to parse the induction variable. Before punting, we want to check
118 // whether the control op was written with the induction variable on the RHS
119 // instead of the LHS. This happens for the downwards case, like:
120 // for i := len(n)-1; i >= 0; i--
121 init, inc, nxt = parseIndVar(limit)
123 // No recognied induction variable on either operand
127 // Ok, the arguments were reversed. Swap them, and remember that we're
128 // looking at a ind >/>= loop (so the induction must be decrementing).
129 ind, limit = limit, ind
133 // Expect the increment to be a nonzero constant.
134 if inc.Op != OpConst64 {
142 // Increment sign must match comparison direction.
143 // When incrementing, the termination comparison must be ind </<= limit.
144 // When decrementing, the termination comparison must be ind >/>= limit.
146 if step > 0 && !less {
149 if step < 0 && less {
153 // Up to now we extracted the induction variable (ind),
154 // the increment delta (inc), the temporary sum (nxt),
155 // the initial value (init) and the limiting value (limit).
157 // We also know that ind has the form (Phi init nxt) where
158 // nxt is (Add inc nxt) which means: 1) inc dominates nxt
159 // and 2) there is a loop starting at inc and containing nxt.
161 // We need to prove that the induction variable is incremented
162 // only when it's smaller than the limiting value.
163 // Two conditions must happen listed below to accept ind
164 // as an induction variable.
166 // First condition: loop entry has a single predecessor, which
167 // is the header block. This implies that b.Succs[0] is
168 // reached iff ind < limit.
169 if len(b.Succs[0].b.Preds) != 1 {
170 // b.Succs[1] must exit the loop.
174 // Second condition: b.Succs[0] dominates nxt so that
175 // nxt is computed when inc < limit.
176 if !sdom.IsAncestorEq(b.Succs[0].b, nxt.Block) {
177 // inc+ind can only be reached through the branch that enters the loop.
181 // Check for overflow/underflow. We need to make sure that inc never causes
182 // the induction variable to wrap around.
183 // We use a function wrapper here for easy return true / return false / keep going logic.
184 // This function returns true if the increment will never overflow/underflow.
187 if limit.Op == OpConst64 {
188 // Figure out the actual largest value.
191 if v == math.MinInt64 {
192 return false // < minint is never satisfiable.
196 if init.Op == OpConst64 {
197 // Use stride to compute a better lower limit.
201 v = addU(init.AuxInt, diff(v, init.AuxInt)/uint64(step)*uint64(step))
203 // It is ok if we can't overflow when incrementing from the largest value.
204 return !addWillOverflow(v, step)
206 if step == 1 && !inclusive {
207 // Can't overflow because maxint is never a possible value.
210 // If the limit is not a constant, check to see if it is a
211 // negative offset from a known non-negative value.
212 knn, k := findKNN(limit)
213 if knn == nil || k < 0 {
216 // limit == (something nonnegative) - k. That subtraction can't underflow, so
219 // ind <= knn - k cannot overflow if step is at most k
222 // ind < knn - k cannot overflow if step is at most k+1
223 return step <= k+1 && k != math.MaxInt64
225 if limit.Op == OpConst64 {
226 // Figure out the actual smallest value.
229 if v == math.MaxInt64 {
230 return false // > maxint is never satisfiable.
234 if init.Op == OpConst64 {
235 // Use stride to compute a better lower limit.
239 v = subU(init.AuxInt, diff(init.AuxInt, v)/uint64(-step)*uint64(-step))
241 // It is ok if we can't underflow when decrementing from the smallest value.
242 return !subWillUnderflow(v, -step)
244 if step == -1 && !inclusive {
245 // Can't underflow because minint is never a possible value.
254 flags := indVarFlags(0)
260 flags |= indVarMaxInc
265 flags |= indVarMaxInc
267 flags |= indVarMinExc
271 if f.pass.debug >= 1 {
272 printIndVar(b, ind, min, max, step, flags)
275 iv = append(iv, indVar{
282 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
285 // TODO: other unrolling idioms
286 // for i := 0; i < KNN - KNN % k ; i += k
287 // for i := 0; i < KNN&^(k-1) ; i += k // k a power of 2
288 // for i := 0; i < KNN&(-k) ; i += k // k a power of 2
294 // addWillOverflow reports whether x+y would result in a value more than maxint.
295 func addWillOverflow(x, y int64) bool {
299 // subWillUnderflow reports whether x-y would result in a value less than minint.
300 func subWillUnderflow(x, y int64) bool {
304 // diff returns x-y as a uint64. Requires x>=y.
305 func diff(x, y int64) uint64 {
307 base.Fatalf("diff %d - %d underflowed", x, y)
312 // addU returns x+y. Requires that x+y does not overflow an int64.
313 func addU(x int64, y uint64) int64 {
316 base.Fatalf("addU overflowed %d + %d", x, y)
322 if addWillOverflow(x, int64(y)) {
323 base.Fatalf("addU overflowed %d + %d", x, y)
328 // subU returns x-y. Requires that x-y does not underflow an int64.
329 func subU(x int64, y uint64) int64 {
332 base.Fatalf("subU underflowed %d - %d", x, y)
338 if subWillUnderflow(x, int64(y)) {
339 base.Fatalf("subU underflowed %d - %d", x, y)
344 // if v is known to be x - c, where x is known to be nonnegative and c is a
345 // constant, return x, c. Otherwise return nil, 0.
346 func findKNN(v *Value) (*Value, int64) {
357 if x.Op == OpConst64 {
362 case OpSliceLen, OpStringLen, OpSliceCap:
369 if y.Op != OpConst64 {
378 func printIndVar(b *Block, i, min, max *Value, inc int64, flags indVarFlags) {
380 if flags&indVarMinExc != 0 {
383 if flags&indVarMaxInc == 0 {
387 mlim1, mlim2 := fmt.Sprint(min.AuxInt), fmt.Sprint(max.AuxInt)
388 if !min.isGenericIntConst() {
389 if b.Func.pass.debug >= 2 {
390 mlim1 = fmt.Sprint(min)
395 if !max.isGenericIntConst() {
396 if b.Func.pass.debug >= 2 {
397 mlim2 = fmt.Sprint(max)
403 if b.Func.pass.debug >= 2 {
404 extra = fmt.Sprintf(" (%s)", i)
406 b.Func.Warnl(b.Pos, "Induction variable: limits %v%v,%v%v, increment %d%s", mb1, mlim1, mlim2, mb2, inc, extra)