1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/types"
10 "fmt"
11 )
12
13 type indVarFlags uint8
14
15 const (
16 indVarMinExc indVarFlags = 1 << iota
17 indVarMaxInc
18 indVarCountDown
19 )
20
21 type indVar struct {
22 ind *Value
23 nxt *Value
24 min *Value
25 max *Value
26 entry *Block
27 flags indVarFlags
28
29
30
31
32
33 }
34
35
36
37
38
39
40
41
42
43
44
45 func parseIndVar(ind *Value) (min, inc, nxt *Value, loopReturn Edge) {
46 if ind.Op != OpPhi {
47 return
48 }
49
50 if n := ind.Args[0]; (n.Op == OpAdd64 || n.Op == OpAdd32 || n.Op == OpAdd16 || n.Op == OpAdd8) && (n.Args[0] == ind || n.Args[1] == ind) {
51 min, nxt, loopReturn = ind.Args[1], n, ind.Block.Preds[0]
52 } else if n := ind.Args[1]; (n.Op == OpAdd64 || n.Op == OpAdd32 || n.Op == OpAdd16 || n.Op == OpAdd8) && (n.Args[0] == ind || n.Args[1] == ind) {
53 min, nxt, loopReturn = ind.Args[0], n, ind.Block.Preds[1]
54 } else {
55
56 return
57 }
58
59 if nxt.Args[0] == ind {
60 inc = nxt.Args[1]
61 } else if nxt.Args[1] == ind {
62 inc = nxt.Args[0]
63 } else {
64 panic("unreachable")
65 }
66
67 return
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 func findIndVar(f *Func) []indVar {
87 var iv []indVar
88 sdom := f.Sdom()
89
90 for _, b := range f.Blocks {
91 if b.Kind != BlockIf || len(b.Preds) != 2 {
92 continue
93 }
94
95 var ind *Value
96 var init *Value
97 var limit *Value
98
99
100
101 c := b.Controls[0]
102 inclusive := false
103 switch c.Op {
104 case OpLeq64, OpLeq32, OpLeq16, OpLeq8:
105 inclusive = true
106 fallthrough
107 case OpLess64, OpLess32, OpLess16, OpLess8:
108 ind, limit = c.Args[0], c.Args[1]
109 default:
110 continue
111 }
112
113
114 less := true
115 init, inc, nxt, loopReturn := parseIndVar(ind)
116 if init == nil {
117
118
119
120
121 init, inc, nxt, loopReturn = parseIndVar(limit)
122 if init == nil {
123
124 continue
125 }
126
127
128
129 ind, limit = limit, ind
130 less = false
131 }
132
133 if ind.Block != b {
134
135
136
137 continue
138 }
139
140
141 if !inc.isGenericIntConst() {
142 continue
143 }
144 step := inc.AuxInt
145 if step == 0 {
146 continue
147 }
148
149
150 var startBody Edge
151 switch {
152 case sdom.IsAncestorEq(b.Succs[0].b, loopReturn.b):
153 startBody = b.Succs[0]
154 case sdom.IsAncestorEq(b.Succs[1].b, loopReturn.b):
155
156 startBody = b.Succs[1]
157 less = !less
158 inclusive = !inclusive
159 default:
160 continue
161 }
162
163
164
165
166
167 if step > 0 && !less {
168 continue
169 }
170 if step < 0 && less {
171 continue
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 if len(startBody.b.Preds) != 1 {
191
192 continue
193 }
194
195
196
197 if !sdom.IsAncestorEq(startBody.b, nxt.Block) {
198
199 continue
200 }
201
202
203
204
205
206 ok := func() bool {
207 if step > 0 {
208 if limit.isGenericIntConst() {
209
210 v := limit.AuxInt
211 if !inclusive {
212 if v == minSignedValue(limit.Type) {
213 return false
214 }
215 v--
216 }
217 if init.isGenericIntConst() {
218
219 if init.AuxInt > v {
220 return false
221 }
222 v = addU(init.AuxInt, diff(v, init.AuxInt)/uint64(step)*uint64(step))
223 }
224 if addWillOverflow(v, step) {
225 return false
226 }
227 if inclusive && v != limit.AuxInt || !inclusive && v+1 != limit.AuxInt {
228
229 limit = f.constVal(limit.Op, limit.Type, v, true)
230 inclusive = true
231 }
232 return true
233 }
234 if step == 1 && !inclusive {
235
236 return true
237 }
238
239
240 knn, k := findKNN(limit)
241 if knn == nil || k < 0 {
242 return false
243 }
244
245
246 if inclusive {
247
248 return step <= k
249 }
250
251 return step <= k+1 && k != maxSignedValue(limit.Type)
252 } else {
253 if limit.Op == OpConst64 {
254
255 v := limit.AuxInt
256 if !inclusive {
257 if v == maxSignedValue(limit.Type) {
258 return false
259 }
260 v++
261 }
262 if init.isGenericIntConst() {
263
264 if init.AuxInt < v {
265 return false
266 }
267 v = subU(init.AuxInt, diff(init.AuxInt, v)/uint64(-step)*uint64(-step))
268 }
269 if subWillUnderflow(v, -step) {
270 return false
271 }
272 if inclusive && v != limit.AuxInt || !inclusive && v-1 != limit.AuxInt {
273
274 limit = f.constVal(limit.Op, limit.Type, v, true)
275 inclusive = true
276 }
277 return true
278 }
279 if step == -1 && !inclusive {
280
281 return true
282 }
283 }
284 return false
285
286 }
287
288 if ok() {
289 flags := indVarFlags(0)
290 var min, max *Value
291 if step > 0 {
292 min = init
293 max = limit
294 if inclusive {
295 flags |= indVarMaxInc
296 }
297 } else {
298 min = limit
299 max = init
300 flags |= indVarMaxInc
301 if !inclusive {
302 flags |= indVarMinExc
303 }
304 flags |= indVarCountDown
305 step = -step
306 }
307 if f.pass.debug >= 1 {
308 printIndVar(b, ind, min, max, step, flags)
309 }
310
311 iv = append(iv, indVar{
312 ind: ind,
313 nxt: nxt,
314 min: min,
315 max: max,
316 entry: startBody.b,
317 flags: flags,
318 })
319 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
320 }
321
322
323
324
325
326 }
327
328 return iv
329 }
330
331
332 func addWillOverflow(x, y int64) bool {
333 return x+y < x
334 }
335
336
337 func subWillUnderflow(x, y int64) bool {
338 return x-y > x
339 }
340
341
342 func diff(x, y int64) uint64 {
343 if x < y {
344 base.Fatalf("diff %d - %d underflowed", x, y)
345 }
346 return uint64(x - y)
347 }
348
349
350 func addU(x int64, y uint64) int64 {
351 if y >= 1<<63 {
352 if x >= 0 {
353 base.Fatalf("addU overflowed %d + %d", x, y)
354 }
355 x += 1<<63 - 1
356 x += 1
357 y -= 1 << 63
358 }
359 if addWillOverflow(x, int64(y)) {
360 base.Fatalf("addU overflowed %d + %d", x, y)
361 }
362 return x + int64(y)
363 }
364
365
366 func subU(x int64, y uint64) int64 {
367 if y >= 1<<63 {
368 if x < 0 {
369 base.Fatalf("subU underflowed %d - %d", x, y)
370 }
371 x -= 1<<63 - 1
372 x -= 1
373 y -= 1 << 63
374 }
375 if subWillUnderflow(x, int64(y)) {
376 base.Fatalf("subU underflowed %d - %d", x, y)
377 }
378 return x - int64(y)
379 }
380
381
382
383 func findKNN(v *Value) (*Value, int64) {
384 var x, y *Value
385 x = v
386 switch v.Op {
387 case OpSub64, OpSub32, OpSub16, OpSub8:
388 x = v.Args[0]
389 y = v.Args[1]
390
391 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
392 x = v.Args[0]
393 y = v.Args[1]
394 if x.isGenericIntConst() {
395 x, y = y, x
396 }
397 }
398 switch x.Op {
399 case OpSliceLen, OpStringLen, OpSliceCap:
400 default:
401 return nil, 0
402 }
403 if y == nil {
404 return x, 0
405 }
406 if !y.isGenericIntConst() {
407 return nil, 0
408 }
409 if v.Op == OpAdd64 || v.Op == OpAdd32 || v.Op == OpAdd16 || v.Op == OpAdd8 {
410 return x, -y.AuxInt
411 }
412 return x, y.AuxInt
413 }
414
415 func printIndVar(b *Block, i, min, max *Value, inc int64, flags indVarFlags) {
416 mb1, mb2 := "[", "]"
417 if flags&indVarMinExc != 0 {
418 mb1 = "("
419 }
420 if flags&indVarMaxInc == 0 {
421 mb2 = ")"
422 }
423
424 mlim1, mlim2 := fmt.Sprint(min.AuxInt), fmt.Sprint(max.AuxInt)
425 if !min.isGenericIntConst() {
426 if b.Func.pass.debug >= 2 {
427 mlim1 = fmt.Sprint(min)
428 } else {
429 mlim1 = "?"
430 }
431 }
432 if !max.isGenericIntConst() {
433 if b.Func.pass.debug >= 2 {
434 mlim2 = fmt.Sprint(max)
435 } else {
436 mlim2 = "?"
437 }
438 }
439 extra := ""
440 if b.Func.pass.debug >= 2 {
441 extra = fmt.Sprintf(" (%s)", i)
442 }
443 b.Func.Warnl(b.Pos, "Induction variable: limits %v%v,%v%v, increment %d%s", mb1, mlim1, mlim2, mb2, inc, extra)
444 }
445
446 func minSignedValue(t *types.Type) int64 {
447 return -1 << (t.Size()*8 - 1)
448 }
449
450 func maxSignedValue(t *types.Type) int64 {
451 return 1<<((t.Size()*8)-1) - 1
452 }
453
View as plain text