1
2
3
4
5 package ssa
6
7 import (
8 "fmt"
9 )
10
11 type loop struct {
12 header *Block
13 outer *loop
14
15
16 children []*loop
17 exits []*Block
18
19
20
21 nBlocks int32
22 depth int16
23 isInner bool
24
25
26 containsUnavoidableCall bool
27 }
28
29
30 func (sdom SparseTree) outerinner(outer, inner *loop) {
31
32
33
34
35
36 oldouter := inner.outer
37 for oldouter != nil && sdom.isAncestor(outer.header, oldouter.header) {
38 inner = oldouter
39 oldouter = inner.outer
40 }
41 if outer == oldouter {
42 return
43 }
44 if oldouter != nil {
45 sdom.outerinner(oldouter, outer)
46 }
47
48 inner.outer = outer
49 outer.isInner = false
50 }
51
52 func checkContainsCall(bb *Block) bool {
53 if bb.Kind == BlockDefer {
54 return true
55 }
56 for _, v := range bb.Values {
57 if opcodeTable[v.Op].call {
58 return true
59 }
60 }
61 return false
62 }
63
64 type loopnest struct {
65 f *Func
66 b2l []*loop
67 po []*Block
68 sdom SparseTree
69 loops []*loop
70 hasIrreducible bool
71
72
73 initializedChildren, initializedDepth, initializedExits bool
74 }
75
76 const (
77 blDEFAULT = 0
78 blMin = blDEFAULT
79 blCALL = 1
80 blRET = 2
81 blEXIT = 3
82 )
83
84 var bllikelies = [4]string{"default", "call", "ret", "exit"}
85
86 func describePredictionAgrees(b *Block, prediction BranchPrediction) string {
87 s := ""
88 if prediction == b.Likely {
89 s = " (agrees with previous)"
90 } else if b.Likely != BranchUnknown {
91 s = " (disagrees with previous, ignored)"
92 }
93 return s
94 }
95
96 func describeBranchPrediction(f *Func, b *Block, likely, not int8, prediction BranchPrediction) {
97 f.Warnl(b.Pos, "Branch prediction rule %s < %s%s",
98 bllikelies[likely-blMin], bllikelies[not-blMin], describePredictionAgrees(b, prediction))
99 }
100
101 func likelyadjust(f *Func) {
102
103
104
105
106 certain := f.Cache.allocInt8Slice(f.NumBlocks())
107 defer f.Cache.freeInt8Slice(certain)
108 local := f.Cache.allocInt8Slice(f.NumBlocks())
109 defer f.Cache.freeInt8Slice(local)
110
111 po := f.postorder()
112 nest := f.loopnest()
113 b2l := nest.b2l
114
115 for _, b := range po {
116 switch b.Kind {
117 case BlockExit:
118
119 local[b.ID] = blEXIT
120 certain[b.ID] = blEXIT
121
122
123 case BlockRet, BlockRetJmp:
124 local[b.ID] = blRET
125 certain[b.ID] = blRET
126
127
128
129
130 case BlockDefer:
131 local[b.ID] = blCALL
132 certain[b.ID] = max(blCALL, certain[b.Succs[0].b.ID])
133
134 default:
135 if len(b.Succs) == 1 {
136 certain[b.ID] = certain[b.Succs[0].b.ID]
137 } else if len(b.Succs) == 2 {
138
139
140
141
142
143
144 b0 := b.Succs[0].b.ID
145 b1 := b.Succs[1].b.ID
146 certain[b.ID] = min(certain[b0], certain[b1])
147
148 l := b2l[b.ID]
149 l0 := b2l[b0]
150 l1 := b2l[b1]
151
152 prediction := b.Likely
153
154
155
156 if l != nil && l0 != l1 {
157 noprediction := false
158 switch {
159
160 case l1 == nil:
161 prediction = BranchLikely
162 case l0 == nil:
163 prediction = BranchUnlikely
164
165
166 case l == l0:
167 prediction = BranchLikely
168 case l == l1:
169 prediction = BranchUnlikely
170 default:
171 noprediction = true
172 }
173 if f.pass.debug > 0 && !noprediction {
174 f.Warnl(b.Pos, "Branch prediction rule stay in loop%s",
175 describePredictionAgrees(b, prediction))
176 }
177
178 } else {
179
180 if certain[b1] > certain[b0] {
181 prediction = BranchLikely
182 if f.pass.debug > 0 {
183 describeBranchPrediction(f, b, certain[b0], certain[b1], prediction)
184 }
185 } else if certain[b0] > certain[b1] {
186 prediction = BranchUnlikely
187 if f.pass.debug > 0 {
188 describeBranchPrediction(f, b, certain[b1], certain[b0], prediction)
189 }
190 } else if local[b1] > local[b0] {
191 prediction = BranchLikely
192 if f.pass.debug > 0 {
193 describeBranchPrediction(f, b, local[b0], local[b1], prediction)
194 }
195 } else if local[b0] > local[b1] {
196 prediction = BranchUnlikely
197 if f.pass.debug > 0 {
198 describeBranchPrediction(f, b, local[b1], local[b0], prediction)
199 }
200 }
201 }
202 if b.Likely != prediction {
203 if b.Likely == BranchUnknown {
204 b.Likely = prediction
205 }
206 }
207 }
208
209 for _, v := range b.Values {
210 if opcodeTable[v.Op].call {
211 local[b.ID] = blCALL
212 certain[b.ID] = max(blCALL, certain[b.Succs[0].b.ID])
213 break
214 }
215 }
216 }
217 if f.pass.debug > 2 {
218 f.Warnl(b.Pos, "BP: Block %s, local=%s, certain=%s", b, bllikelies[local[b.ID]-blMin], bllikelies[certain[b.ID]-blMin])
219 }
220
221 }
222 }
223
224 func (l *loop) String() string {
225 return fmt.Sprintf("hdr:%s", l.header)
226 }
227
228 func (l *loop) LongString() string {
229 i := ""
230 o := ""
231 if l.isInner {
232 i = ", INNER"
233 }
234 if l.outer != nil {
235 o = ", o=" + l.outer.header.String()
236 }
237 return fmt.Sprintf("hdr:%s%s%s", l.header, i, o)
238 }
239
240 func (l *loop) isWithinOrEq(ll *loop) bool {
241 if ll == nil {
242 return true
243 }
244 for ; l != nil; l = l.outer {
245 if l == ll {
246 return true
247 }
248 }
249 return false
250 }
251
252
253
254
255
256 func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop {
257 var o *loop
258 for o = l.outer; o != nil && !sdom.IsAncestorEq(o.header, b); o = o.outer {
259 }
260 return o
261 }
262
263 func loopnestfor(f *Func) *loopnest {
264 po := f.postorder()
265 sdom := f.Sdom()
266 b2l := make([]*loop, f.NumBlocks())
267 loops := make([]*loop, 0)
268 visited := f.Cache.allocBoolSlice(f.NumBlocks())
269 defer f.Cache.freeBoolSlice(visited)
270 sawIrred := false
271
272 if f.pass.debug > 2 {
273 fmt.Printf("loop finding in %s\n", f.Name)
274 }
275
276
277 for _, b := range po {
278 if f.pass != nil && f.pass.debug > 3 {
279 fmt.Printf("loop finding at %s\n", b)
280 }
281
282 var innermost *loop
283
284
285
286
287
288
289
290
291
292
293
294 for _, e := range b.Succs {
295 bb := e.b
296 l := b2l[bb.ID]
297
298 if sdom.IsAncestorEq(bb, b) {
299 if f.pass != nil && f.pass.debug > 4 {
300 fmt.Printf("loop finding succ %s of %s is header\n", bb.String(), b.String())
301 }
302 if l == nil {
303 l = &loop{header: bb, isInner: true}
304 loops = append(loops, l)
305 b2l[bb.ID] = l
306 }
307 } else if !visited[bb.ID] {
308 sawIrred = true
309 if f.pass != nil && f.pass.debug > 4 {
310 fmt.Printf("loop finding succ %s of %s is IRRED, in %s\n", bb.String(), b.String(), f.Name)
311 }
312 } else if l != nil {
313
314
315
316
317 if !sdom.IsAncestorEq(l.header, b) {
318 l = l.nearestOuterLoop(sdom, b)
319 }
320 if f.pass != nil && f.pass.debug > 4 {
321 if l == nil {
322 fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
323 } else {
324 fmt.Printf("loop finding succ %s of %s provides loop with header %s\n", bb.String(), b.String(), l.header.String())
325 }
326 }
327 } else {
328 if f.pass != nil && f.pass.debug > 4 {
329 fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
330 }
331
332 }
333
334 if l == nil || innermost == l {
335 continue
336 }
337
338 if innermost == nil {
339 innermost = l
340 continue
341 }
342
343 if sdom.isAncestor(innermost.header, l.header) {
344 sdom.outerinner(innermost, l)
345 innermost = l
346 } else if sdom.isAncestor(l.header, innermost.header) {
347 sdom.outerinner(l, innermost)
348 }
349 }
350
351 if innermost != nil {
352 b2l[b.ID] = innermost
353 innermost.nBlocks++
354 }
355 visited[b.ID] = true
356 }
357
358 ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops, hasIrreducible: sawIrred}
359
360
361 dominatedByCall := f.Cache.allocBoolSlice(f.NumBlocks())
362 defer f.Cache.freeBoolSlice(dominatedByCall)
363 for _, b := range po {
364 if checkContainsCall(b) {
365 dominatedByCall[b.ID] = true
366 }
367 }
368
369
370
371
372
373
374
375
376
377 for _, l := range loops {
378
379 if dominatedByCall[l.header.ID] {
380 l.containsUnavoidableCall = true
381 continue
382 }
383 callfreepath := false
384 tovisit := make([]*Block, 0, len(l.header.Succs))
385
386 for _, s := range l.header.Succs {
387 nb := s.Block()
388
389 if !l.iterationEnd(nb, b2l) {
390 tovisit = append(tovisit, nb)
391 }
392 }
393 for len(tovisit) > 0 {
394 cur := tovisit[len(tovisit)-1]
395 tovisit = tovisit[:len(tovisit)-1]
396 if dominatedByCall[cur.ID] {
397 continue
398 }
399
400 dominatedByCall[cur.ID] = true
401 for _, s := range cur.Succs {
402 nb := s.Block()
403 if l.iterationEnd(nb, b2l) {
404 callfreepath = true
405 }
406 if !dominatedByCall[nb.ID] {
407 tovisit = append(tovisit, nb)
408 }
409
410 }
411 if callfreepath {
412 break
413 }
414 }
415 if !callfreepath {
416 l.containsUnavoidableCall = true
417 }
418 }
419
420
421 if f.pass != nil && f.pass.stats > 0 && len(loops) > 0 {
422 ln.assembleChildren()
423 ln.calculateDepths()
424 ln.findExits()
425
426
427
428
429 for _, l := range loops {
430 x := len(l.exits)
431 cf := 0
432 if !l.containsUnavoidableCall {
433 cf = 1
434 }
435 inner := 0
436 if l.isInner {
437 inner++
438 }
439
440 f.LogStat("loopstats:",
441 l.depth, "depth", x, "exits",
442 inner, "is_inner", cf, "always_calls", l.nBlocks, "n_blocks")
443 }
444 }
445
446 if f.pass != nil && f.pass.debug > 1 && len(loops) > 0 {
447 fmt.Printf("Loops in %s:\n", f.Name)
448 for _, l := range loops {
449 fmt.Printf("%s, b=", l.LongString())
450 for _, b := range f.Blocks {
451 if b2l[b.ID] == l {
452 fmt.Printf(" %s", b)
453 }
454 }
455 fmt.Print("\n")
456 }
457 fmt.Printf("Nonloop blocks in %s:", f.Name)
458 for _, b := range f.Blocks {
459 if b2l[b.ID] == nil {
460 fmt.Printf(" %s", b)
461 }
462 }
463 fmt.Print("\n")
464 }
465 return ln
466 }
467
468
469
470
471
472 func (ln *loopnest) assembleChildren() {
473 if ln.initializedChildren {
474 return
475 }
476 for _, l := range ln.loops {
477 if l.outer != nil {
478 l.outer.children = append(l.outer.children, l)
479 }
480 }
481 ln.initializedChildren = true
482 }
483
484
485
486
487 func (ln *loopnest) calculateDepths() {
488 if ln.initializedDepth {
489 return
490 }
491 ln.assembleChildren()
492 for _, l := range ln.loops {
493 if l.outer == nil {
494 l.setDepth(1)
495 }
496 }
497 ln.initializedDepth = true
498 }
499
500
501
502 func (ln *loopnest) findExits() {
503 if ln.initializedExits {
504 return
505 }
506 ln.calculateDepths()
507 b2l := ln.b2l
508 for _, b := range ln.po {
509 l := b2l[b.ID]
510 if l != nil && len(b.Succs) == 2 {
511 sl := b2l[b.Succs[0].b.ID]
512 if recordIfExit(l, sl, b.Succs[0].b) {
513 continue
514 }
515 sl = b2l[b.Succs[1].b.ID]
516 if recordIfExit(l, sl, b.Succs[1].b) {
517 continue
518 }
519 }
520 }
521 ln.initializedExits = true
522 }
523
524
525 func (ln *loopnest) depth(b ID) int16 {
526 if l := ln.b2l[b]; l != nil {
527 return l.depth
528 }
529 return 0
530 }
531
532
533
534
535 func recordIfExit(l, sl *loop, b *Block) bool {
536 if sl != l {
537 if sl == nil || sl.depth <= l.depth {
538 l.exits = append(l.exits, b)
539 return true
540 }
541
542
543 for sl.depth > l.depth {
544 sl = sl.outer
545 }
546 if sl != l {
547 l.exits = append(l.exits, b)
548 return true
549 }
550 }
551 return false
552 }
553
554 func (l *loop) setDepth(d int16) {
555 l.depth = d
556 for _, c := range l.children {
557 c.setDepth(d + 1)
558 }
559 }
560
561
562
563
564 func (l *loop) iterationEnd(b *Block, b2l []*loop) bool {
565 return b == l.header || b2l[b.ID] == nil || (b2l[b.ID] != l && b2l[b.ID].depth <= l.depth)
566 }
567
View as plain text