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 var startBody Edge
165 switch {
166 case sdom.IsAncestorEq(b.Succs[0].b, loopReturn.b):
167 startBody = b.Succs[0]
168 case sdom.IsAncestorEq(b.Succs[1].b, loopReturn.b):
169
170 startBody = b.Succs[1]
171 less = !less
172 inclusive = !inclusive
173 default:
174 continue
175 }
176
177
178
179
180
181 if step > 0 && !less {
182 continue
183 }
184 if step < 0 && less {
185 continue
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 if len(startBody.b.Preds) != 1 {
206
207 continue
208 }
209
210
211
212 if !sdom.IsAncestorEq(startBody.b, nxt.Block) {
213
214
215 continue
216 }
217
218
219
220
221
222 ok := func() bool {
223 if step > 0 {
224 if limit.isGenericIntConst() {
225
226 v := limit.AuxInt
227 if !inclusive {
228 if v == minSignedValue(limit.Type) {
229 return false
230 }
231 v--
232 }
233 if init.isGenericIntConst() {
234
235 if init.AuxInt > v {
236 return false
237 }
238 v = addU(init.AuxInt, diff(v, init.AuxInt)/uint64(step)*uint64(step))
239 }
240 if addWillOverflow(v, step) {
241 return false
242 }
243 if inclusive && v != limit.AuxInt || !inclusive && v+1 != limit.AuxInt {
244
245 limit = f.constVal(limit.Op, limit.Type, v, true)
246 inclusive = true
247 }
248 return true
249 }
250 if step == 1 && !inclusive {
251
252 return true
253 }
254
255
256 knn, k := findKNN(limit)
257 if knn == nil || k < 0 {
258 return false
259 }
260
261
262 if inclusive {
263
264 return step <= k
265 }
266
267 return step <= k+1 && k != maxSignedValue(limit.Type)
268
269
270
271
272
273 } else {
274 if limit.Op == OpConst64 {
275
276 v := limit.AuxInt
277 if !inclusive {
278 if v == maxSignedValue(limit.Type) {
279 return false
280 }
281 v++
282 }
283 if init.isGenericIntConst() {
284
285 if init.AuxInt < v {
286 return false
287 }
288 v = subU(init.AuxInt, diff(init.AuxInt, v)/uint64(-step)*uint64(-step))
289 }
290 if subWillUnderflow(v, -step) {
291 return false
292 }
293 if inclusive && v != limit.AuxInt || !inclusive && v-1 != limit.AuxInt {
294
295 limit = f.constVal(limit.Op, limit.Type, v, true)
296 inclusive = true
297 }
298 return true
299 }
300 if step == -1 && !inclusive {
301
302 return true
303 }
304 }
305 return false
306 }
307
308 if ok() {
309 flags := indVarFlags(0)
310 var min, max *Value
311 if step > 0 {
312 min = init
313 max = limit
314 if inclusive {
315 flags |= indVarMaxInc
316 }
317 } else {
318 min = limit
319 max = init
320 flags |= indVarMaxInc
321 if !inclusive {
322 flags |= indVarMinExc
323 }
324 step = -step
325 }
326 if f.pass.debug >= 1 {
327 printIndVar(b, ind, min, max, step, flags)
328 }
329
330 iv = append(iv, indVar{
331 ind: ind,
332 nxt: nxt,
333 min: min,
334 max: max,
335
336
337
338 entry: startBody.b,
339 step: step,
340 flags: flags,
341 })
342 b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
343 }
344 }
345 }
346
347 return iv
348 }
349
350
351 func addWillOverflow(x, y int64) bool {
352 return x+y < x
353 }
354
355
356 func subWillUnderflow(x, y int64) bool {
357 return x-y > x
358 }
359
360
361 func diff(x, y int64) uint64 {
362 if x < y {
363 base.Fatalf("diff %d - %d underflowed", x, y)
364 }
365 return uint64(x - y)
366 }
367
368
369 func addU(x int64, y uint64) int64 {
370 if y >= 1<<63 {
371 if x >= 0 {
372 base.Fatalf("addU overflowed %d + %d", x, y)
373 }
374 x += 1<<63 - 1
375 x += 1
376 y -= 1 << 63
377 }
378 if addWillOverflow(x, int64(y)) {
379 base.Fatalf("addU overflowed %d + %d", x, y)
380 }
381 return x + int64(y)
382 }
383
384
385 func subU(x int64, y uint64) int64 {
386 if y >= 1<<63 {
387 if x < 0 {
388 base.Fatalf("subU underflowed %d - %d", x, y)
389 }
390 x -= 1<<63 - 1
391 x -= 1
392 y -= 1 << 63
393 }
394 if subWillUnderflow(x, int64(y)) {
395 base.Fatalf("subU underflowed %d - %d", x, y)
396 }
397 return x - int64(y)
398 }
399
400
401
402 func findKNN(v *Value) (*Value, int64) {
403 var x, y *Value
404 x = v
405 switch v.Op {
406 case OpSub64, OpSub32, OpSub16, OpSub8:
407 x = v.Args[0]
408 y = v.Args[1]
409
410 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
411 x = v.Args[0]
412 y = v.Args[1]
413 if x.isGenericIntConst() {
414 x, y = y, x
415 }
416 }
417 switch x.Op {
418 case OpSliceLen, OpStringLen, OpSliceCap:
419 default:
420 return nil, 0
421 }
422 if y == nil {
423 return x, 0
424 }
425 if !y.isGenericIntConst() {
426 return nil, 0
427 }
428 if v.Op == OpAdd64 || v.Op == OpAdd32 || v.Op == OpAdd16 || v.Op == OpAdd8 {
429 return x, -y.AuxInt
430 }
431 return x, y.AuxInt
432 }
433
434 func printIndVar(b *Block, i, min, max *Value, inc int64, flags indVarFlags) {
435 mb1, mb2 := "[", "]"
436 if flags&indVarMinExc != 0 {
437 mb1 = "("
438 }
439 if flags&indVarMaxInc == 0 {
440 mb2 = ")"
441 }
442
443 mlim1, mlim2 := fmt.Sprint(min.AuxInt), fmt.Sprint(max.AuxInt)
444 if !min.isGenericIntConst() {
445 if b.Func.pass.debug >= 2 {
446 mlim1 = fmt.Sprint(min)
447 } else {
448 mlim1 = "?"
449 }
450 }
451 if !max.isGenericIntConst() {
452 if b.Func.pass.debug >= 2 {
453 mlim2 = fmt.Sprint(max)
454 } else {
455 mlim2 = "?"
456 }
457 }
458 extra := ""
459 if b.Func.pass.debug >= 2 {
460 extra = fmt.Sprintf(" (%s)", i)
461 }
462 b.Func.Warnl(b.Pos, "Induction variable: limits %v%v,%v%v, increment %d%s", mb1, mlim1, mlim2, mb2, inc, extra)
463 }
464
465 func minSignedValue(t *types.Type) int64 {
466 return -1 << (t.Size()*8 - 1)
467 }
468
469 func maxSignedValue(t *types.Type) int64 {
470 return 1<<((t.Size()*8)-1) - 1
471 }
472
View as plain text