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