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