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