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