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