Source file src/pkg/cmd/compile/internal/ssa/likelyadjust.go
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 }
226 }
227 }
228 if f.pass.debug > 2 {
229 f.Warnl(b.Pos, "BP: Block %s, local=%s, certain=%s", b, bllikelies[local[b.ID]-blMin], bllikelies[certain[b.ID]-blMin])
230 }
231
232 }
233 }
234
235 func (l *loop) String() string {
236 return fmt.Sprintf("hdr:%s", l.header)
237 }
238
239 func (l *loop) LongString() string {
240 i := ""
241 o := ""
242 if l.isInner {
243 i = ", INNER"
244 }
245 if l.outer != nil {
246 o = ", o=" + l.outer.header.String()
247 }
248 return fmt.Sprintf("hdr:%s%s%s", l.header, i, o)
249 }
250
251 func (l *loop) isWithinOrEq(ll *loop) bool {
252 if ll == nil {
253 return true
254 }
255 for ; l != nil; l = l.outer {
256 if l == ll {
257 return true
258 }
259 }
260 return false
261 }
262
263
264
265
266
267 func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop {
268 var o *loop
269 for o = l.outer; o != nil && !sdom.isAncestorEq(o.header, b); o = o.outer {
270 }
271 return o
272 }
273
274 func loopnestfor(f *Func) *loopnest {
275 po := f.postorder()
276 sdom := f.sdom()
277 b2l := make([]*loop, f.NumBlocks())
278 loops := make([]*loop, 0)
279 visited := make([]bool, f.NumBlocks())
280 sawIrred := false
281
282 if f.pass.debug > 2 {
283 fmt.Printf("loop finding in %s\n", f.Name)
284 }
285
286
287 for _, b := range po {
288 if f.pass != nil && f.pass.debug > 3 {
289 fmt.Printf("loop finding at %s\n", b)
290 }
291
292 var innermost *loop
293
294
295
296
297
298
299
300
301
302
303
304 for _, e := range b.Succs {
305 bb := e.b
306 l := b2l[bb.ID]
307
308 if sdom.isAncestorEq(bb, b) {
309 if f.pass != nil && f.pass.debug > 4 {
310 fmt.Printf("loop finding succ %s of %s is header\n", bb.String(), b.String())
311 }
312 if l == nil {
313 l = &loop{header: bb, isInner: true}
314 loops = append(loops, l)
315 b2l[bb.ID] = l
316 }
317 } else if !visited[bb.ID] {
318 sawIrred = true
319 if f.pass != nil && f.pass.debug > 4 {
320 fmt.Printf("loop finding succ %s of %s is IRRED, in %s\n", bb.String(), b.String(), f.Name)
321 }
322 } else if l != nil {
323
324
325
326
327 if !sdom.isAncestorEq(l.header, b) {
328 l = l.nearestOuterLoop(sdom, b)
329 }
330 if f.pass != nil && f.pass.debug > 4 {
331 if l == nil {
332 fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
333 } else {
334 fmt.Printf("loop finding succ %s of %s provides loop with header %s\n", bb.String(), b.String(), l.header.String())
335 }
336 }
337 } else {
338 if f.pass != nil && f.pass.debug > 4 {
339 fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
340 }
341
342 }
343
344 if l == nil || innermost == l {
345 continue
346 }
347
348 if innermost == nil {
349 innermost = l
350 continue
351 }
352
353 if sdom.isAncestor(innermost.header, l.header) {
354 sdom.outerinner(innermost, l)
355 innermost = l
356 } else if sdom.isAncestor(l.header, innermost.header) {
357 sdom.outerinner(l, innermost)
358 }
359 }
360
361 if innermost != nil {
362 b2l[b.ID] = innermost
363 innermost.nBlocks++
364 }
365 visited[b.ID] = true
366 }
367
368 ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops, hasIrreducible: sawIrred}
369
370
371 dominatedByCall := make([]bool, f.NumBlocks())
372 for _, b := range po {
373 if checkContainsCall(b) {
374 dominatedByCall[b.ID] = true
375 }
376 }
377
378
379
380
381
382
383
384
385
386 for _, l := range loops {
387
388 if dominatedByCall[l.header.ID] {
389 l.containsUnavoidableCall = true
390 continue
391 }
392 callfreepath := false
393 tovisit := make([]*Block, 0, len(l.header.Succs))
394
395 for _, s := range l.header.Succs {
396 nb := s.Block()
397
398 if !l.iterationEnd(nb, b2l) {
399 tovisit = append(tovisit, nb)
400 }
401 }
402 for len(tovisit) > 0 {
403 cur := tovisit[len(tovisit)-1]
404 tovisit = tovisit[:len(tovisit)-1]
405 if dominatedByCall[cur.ID] {
406 continue
407 }
408
409 dominatedByCall[cur.ID] = true
410 for _, s := range cur.Succs {
411 nb := s.Block()
412 if l.iterationEnd(nb, b2l) {
413 callfreepath = true
414 }
415 if !dominatedByCall[nb.ID] {
416 tovisit = append(tovisit, nb)
417 }
418
419 }
420 if callfreepath {
421 break
422 }
423 }
424 if !callfreepath {
425 l.containsUnavoidableCall = true
426 }
427 }
428
429
430 if f.pass != nil && f.pass.stats > 0 && len(loops) > 0 {
431 ln.assembleChildren()
432 ln.calculateDepths()
433 ln.findExits()
434
435
436
437
438 for _, l := range loops {
439 x := len(l.exits)
440 cf := 0
441 if !l.containsUnavoidableCall {
442 cf = 1
443 }
444 inner := 0
445 if l.isInner {
446 inner++
447 }
448
449 f.LogStat("loopstats:",
450 l.depth, "depth", x, "exits",
451 inner, "is_inner", cf, "always_calls", l.nBlocks, "n_blocks")
452 }
453 }
454
455 if f.pass != nil && f.pass.debug > 1 && len(loops) > 0 {
456 fmt.Printf("Loops in %s:\n", f.Name)
457 for _, l := range loops {
458 fmt.Printf("%s, b=", l.LongString())
459 for _, b := range f.Blocks {
460 if b2l[b.ID] == l {
461 fmt.Printf(" %s", b)
462 }
463 }
464 fmt.Print("\n")
465 }
466 fmt.Printf("Nonloop blocks in %s:", f.Name)
467 for _, b := range f.Blocks {
468 if b2l[b.ID] == nil {
469 fmt.Printf(" %s", b)
470 }
471 }
472 fmt.Print("\n")
473 }
474 return ln
475 }
476
477
478
479
480
481 func (ln *loopnest) assembleChildren() {
482 if ln.initializedChildren {
483 return
484 }
485 for _, l := range ln.loops {
486 if l.outer != nil {
487 l.outer.children = append(l.outer.children, l)
488 }
489 }
490 ln.initializedChildren = true
491 }
492
493
494
495
496 func (ln *loopnest) calculateDepths() {
497 if ln.initializedDepth {
498 return
499 }
500 ln.assembleChildren()
501 for _, l := range ln.loops {
502 if l.outer == nil {
503 l.setDepth(1)
504 }
505 }
506 ln.initializedDepth = true
507 }
508
509
510
511 func (ln *loopnest) findExits() {
512 if ln.initializedExits {
513 return
514 }
515 ln.calculateDepths()
516 b2l := ln.b2l
517 for _, b := range ln.po {
518 l := b2l[b.ID]
519 if l != nil && len(b.Succs) == 2 {
520 sl := b2l[b.Succs[0].b.ID]
521 if recordIfExit(l, sl, b.Succs[0].b) {
522 continue
523 }
524 sl = b2l[b.Succs[1].b.ID]
525 if recordIfExit(l, sl, b.Succs[1].b) {
526 continue
527 }
528 }
529 }
530 ln.initializedExits = true
531 }
532
533
534 func (ln *loopnest) depth(b ID) int16 {
535 if l := ln.b2l[b]; l != nil {
536 return l.depth
537 }
538 return 0
539 }
540
541
542
543
544 func recordIfExit(l, sl *loop, b *Block) bool {
545 if sl != l {
546 if sl == nil || sl.depth <= l.depth {
547 l.exits = append(l.exits, b)
548 return true
549 }
550
551
552 for sl.depth > l.depth {
553 sl = sl.outer
554 }
555 if sl != l {
556 l.exits = append(l.exits, b)
557 return true
558 }
559 }
560 return false
561 }
562
563 func (l *loop) setDepth(d int16) {
564 l.depth = d
565 for _, c := range l.children {
566 c.setDepth(d + 1)
567 }
568 }
569
570
571
572
573 func (l *loop) iterationEnd(b *Block, b2l []*loop) bool {
574 return b == l.header || b2l[b.ID] == nil || (b2l[b.ID] != l && b2l[b.ID].depth <= l.depth)
575 }
576
View as plain text