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