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 )
19
20 type indVar struct {
21 ind *Value
22 nxt *Value
23 min *Value
24 max *Value
25 entry *Block
26 step int64
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 func findIndVar(f *Func) []indVar {
118 var iv []indVar
119 sdom := f.Sdom()
120
121 nextblock:
122 for _, b := range f.Blocks {
123 if b.Kind != BlockIf {
124 continue
125 }
126 c := b.Controls[0]
127 for idx := range 2 {
128
129
130 inclusive := false
131 switch c.Op {
132 case OpLeq64, OpLeq32, OpLeq16, OpLeq8:
133 inclusive = true
134 case OpLess64, OpLess32, OpLess16, OpLess8:
135 default:
136 continue nextblock
137 }
138
139 less := idx == 0
140
141 ind, limit := c.Args[idx], c.Args[1-idx]
142
143 init, inc, nxt, loopReturn := parseIndVar(ind)
144 if init == nil {
145 continue
146 }
147
148
149
150 if len(ind.Block.Preds) != 2 {
151 continue
152 }
153
154
155 if !inc.isGenericIntConst() {
156 continue
157 }
158 step := inc.AuxInt
159 if step == 0 {
160 continue
161 }
162
163
164
165 if step == minSignedValue(ind.Type) {
166 continue
167 }
168
169
170 var startBody Edge
171 switch {
172 case sdom.IsAncestorEq(b.Succs[0].b, loopReturn.b):
173 startBody = b.Succs[0]
174 case sdom.IsAncestorEq(b.Succs[1].b, loopReturn.b):
175
176 startBody = b.Succs[1]
177 less = !less
178 inclusive = !inclusive
179 default:
180 continue
181 }
182
183
184
185
186
187 if step > 0 && !less {
188 continue
189 }
190 if step < 0 && less {
191 continue
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 if len(startBody.b.Preds) != 1 {
212
213 continue
214 }
215
216
217
218 if !sdom.IsAncestorEq(startBody.b, nxt.Block) {
219
220
221 continue
222 }
223
224
225
226
227
228 ok := func() bool {
229 if step > 0 {
230 if limit.isGenericIntConst() {
231
232 v := limit.AuxInt
233 if !inclusive {
234 if v == minSignedValue(limit.Type) {
235 return false
236 }
237 v--
238 }
239 if init.isGenericIntConst() {
240
241 if init.AuxInt > v {
242 return false
243 }
244
245
246 v = addU(init.AuxInt, diff(v, init.AuxInt)/uint64(step)*uint64(step))
247 }
248 if addWillOverflow(v, step, maxSignedValue(ind.Type)) {
249 return false
250 }
251 if inclusive && v != limit.AuxInt || !inclusive && v+1 != limit.AuxInt {
252
253 limit = f.constVal(limit.Op, limit.Type, v, true)
254 inclusive = true
255 }
256 return true
257 }
258 if step == 1 && !inclusive {
259
260 return true
261 }
262
263
264 knn, k := findKNN(limit)
265 if knn == nil || k < 0 {
266 return false
267 }
268
269
270 if inclusive {
271
272 return step <= k
273 }
274
275 return step <= k+1 && k != maxSignedValue(limit.Type)
276
277
278
279
280
281 } else {
282 if limit.isGenericIntConst() {
283
284 v := limit.AuxInt
285 if !inclusive {
286 if v == maxSignedValue(limit.Type) {
287 return false
288 }
289 v++
290 }
291 if init.isGenericIntConst() {
292
293 if init.AuxInt < v {
294 return false
295 }
296
297
298 v = subU(init.AuxInt, diff(init.AuxInt, v)/uint64(-step)*uint64(-step))
299 }
300 if subWillUnderflow(v, -step, minSignedValue(ind.Type)) {
301 return false
302 }
303 if inclusive && v != limit.AuxInt || !inclusive && v-1 != limit.AuxInt {
304
305 limit = f.constVal(limit.Op, limit.Type, v, true)
306 inclusive = true
307 }
308 return true
309 }
310 if step == -1 && !inclusive {
311
312 return true
313 }
314 }
315 return false
316 }
317
318 if ok() {
319 flags := indVarFlags(0)
320 var min, max *Value
321 if step > 0 {
322 min = init
323 max = limit
324 if inclusive {
325 flags |= indVarMaxInc
326 }
327 } else {
328 min = limit
329 max = init
330 flags |= indVarMaxInc
331 if !inclusive {
332 flags |= indVarMinExc
333 }
334 step = -step
335 }
336 if f.pass.debug >= 1 {
337 printIndVar(b, ind, min, max, step, flags)
338 }
339
340 iv = append(iv, indVar{
341 ind: ind,
342 nxt: nxt,
343 min: min,
344 max: max,
345
346
347
348 entry: startBody.b,
349 step: step,
350 flags: flags,
351 })
352 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
353 }
354 }
355 }
356
357 return iv
358 }
359
360
361
362 func subWillUnderflow(x, y int64, min int64) bool {
363 if y < 0 {
364 base.Fatalf("expecting positive value")
365 }
366 return x < min+y
367 }
368
369
370
371 func addWillOverflow(x, y int64, max int64) bool {
372 if y < 0 {
373 base.Fatalf("expecting positive value")
374 }
375 return x > max-y
376 }
377
378
379 func diff(x, y int64) uint64 {
380 if x < y {
381 base.Fatalf("diff %d - %d underflowed", x, y)
382 }
383 return uint64(x - y)
384 }
385
386
387 func addU(x int64, y uint64) int64 {
388 if y >= 1<<63 {
389 if x >= 0 {
390 base.Fatalf("addU overflowed %d + %d", x, y)
391 }
392 x += 1<<63 - 1
393 x += 1
394 y -= 1 << 63
395 }
396
397 if addWillOverflow(x, int64(y), maxSignedValue(types.Types[types.TINT64])) {
398 base.Fatalf("addU overflowed %d + %d", x, y)
399 }
400 return x + int64(y)
401 }
402
403
404 func subU(x int64, y uint64) int64 {
405 if y >= 1<<63 {
406 if x < 0 {
407 base.Fatalf("subU underflowed %d - %d", x, y)
408 }
409 x -= 1<<63 - 1
410 x -= 1
411 y -= 1 << 63
412 }
413
414 if subWillUnderflow(x, int64(y), minSignedValue(types.Types[types.TINT64])) {
415 base.Fatalf("subU underflowed %d - %d", x, y)
416 }
417 return x - int64(y)
418 }
419
420
421
422 func findKNN(v *Value) (*Value, int64) {
423 var x, y *Value
424 x = v
425 switch v.Op {
426 case OpSub64, OpSub32, OpSub16, OpSub8:
427 x = v.Args[0]
428 y = v.Args[1]
429
430 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
431 x = v.Args[0]
432 y = v.Args[1]
433 if x.isGenericIntConst() {
434 x, y = y, x
435 }
436 }
437 switch x.Op {
438 case OpSliceLen, OpStringLen, OpSliceCap:
439 default:
440 return nil, 0
441 }
442 if y == nil {
443 return x, 0
444 }
445 if !y.isGenericIntConst() {
446 return nil, 0
447 }
448 if v.Op == OpAdd64 || v.Op == OpAdd32 || v.Op == OpAdd16 || v.Op == OpAdd8 {
449 return x, -y.AuxInt
450 }
451 return x, y.AuxInt
452 }
453
454 func printIndVar(b *Block, i, min, max *Value, inc int64, flags indVarFlags) {
455 mb1, mb2 := "[", "]"
456 if flags&indVarMinExc != 0 {
457 mb1 = "("
458 }
459 if flags&indVarMaxInc == 0 {
460 mb2 = ")"
461 }
462
463 mlim1, mlim2 := fmt.Sprint(min.AuxInt), fmt.Sprint(max.AuxInt)
464 if !min.isGenericIntConst() {
465 if b.Func.pass.debug >= 2 {
466 mlim1 = fmt.Sprint(min)
467 } else {
468 mlim1 = "?"
469 }
470 }
471 if !max.isGenericIntConst() {
472 if b.Func.pass.debug >= 2 {
473 mlim2 = fmt.Sprint(max)
474 } else {
475 mlim2 = "?"
476 }
477 }
478 extra := ""
479 if b.Func.pass.debug >= 2 {
480 extra = fmt.Sprintf(" (%s)", i)
481 }
482 b.Func.Warnl(b.Pos, "Induction variable: limits %v%v,%v%v, increment %d%s", mb1, mlim1, mlim2, mb2, inc, extra)
483 }
484
485 func minSignedValue(t *types.Type) int64 {
486 return -1 << (t.Size()*8 - 1)
487 }
488
489 func maxSignedValue(t *types.Type) int64 {
490 return 1<<((t.Size()*8)-1) - 1
491 }
492
View as plain text