github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/ssa/likelyadjust.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 import ( 8 "fmt" 9 ) 10 11 type loop struct { 12 header *Block // The header node of this (reducible) loop 13 outer *loop // loop containing this loop 14 15 // By default, children exits, and depth are not initialized. 16 children []*loop // loops nested directly within this loop. Initialized by assembleChildren(). 17 exits []*Block // exits records blocks reached by exits from this loop. Initialized by findExits(). 18 19 // Loops aren't that common, so rather than force regalloc to keep 20 // a map or slice for its data, just put it here. 21 spills []*Value 22 scratch int32 23 24 // Next three fields used by regalloc and/or 25 // aid in computation of inner-ness and list of blocks. 26 nBlocks int32 // Number of blocks in this loop but not within inner loops 27 depth int16 // Nesting depth of the loop; 1 is outermost. Initialized by calculateDepths(). 28 isInner bool // True if never discovered to contain a loop 29 30 // register allocation uses this. 31 containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer 32 } 33 34 // outerinner records that outer contains inner 35 func (sdom SparseTree) outerinner(outer, inner *loop) { 36 oldouter := inner.outer 37 if oldouter == nil || sdom.isAncestorEq(oldouter.header, outer.header) { 38 inner.outer = outer 39 outer.isInner = false 40 if inner.containsCall { 41 outer.setContainsCall() 42 } 43 } 44 } 45 46 func (l *loop) setContainsCall() { 47 for ; l != nil && !l.containsCall; l = l.outer { 48 l.containsCall = true 49 } 50 51 } 52 func (l *loop) checkContainsCall(bb *Block) { 53 if bb.Kind == BlockCall || bb.Kind == BlockDefer { 54 l.setContainsCall() 55 } 56 } 57 58 type loopnest struct { 59 f *Func 60 b2l []*loop 61 po []*Block 62 sdom SparseTree 63 loops []*loop 64 65 // Record which of the lazily initialized fields have actually been initialized. 66 initializedChildren, initializedDepth, initializedExits bool 67 } 68 69 func min8(a, b int8) int8 { 70 if a < b { 71 return a 72 } 73 return b 74 } 75 76 func max8(a, b int8) int8 { 77 if a > b { 78 return a 79 } 80 return b 81 } 82 83 const ( 84 blDEFAULT = 0 85 blMin = blDEFAULT 86 blCALL = 1 87 blRET = 2 88 blEXIT = 3 89 ) 90 91 var bllikelies [4]string = [4]string{"default", "call", "ret", "exit"} 92 93 func describePredictionAgrees(b *Block, prediction BranchPrediction) string { 94 s := "" 95 if prediction == b.Likely { 96 s = " (agrees with previous)" 97 } else if b.Likely != BranchUnknown { 98 s = " (disagrees with previous, ignored)" 99 } 100 return s 101 } 102 103 func describeBranchPrediction(f *Func, b *Block, likely, not int8, prediction BranchPrediction) { 104 f.Config.Warnl(b.Line, "Branch prediction rule %s < %s%s", 105 bllikelies[likely-blMin], bllikelies[not-blMin], describePredictionAgrees(b, prediction)) 106 } 107 108 func likelyadjust(f *Func) { 109 // The values assigned to certain and local only matter 110 // in their rank order. 0 is default, more positive 111 // is less likely. It's possible to assign a negative 112 // unlikeliness (though not currently the case). 113 certain := make([]int8, f.NumBlocks()) // In the long run, all outcomes are at least this bad. Mainly for Exit 114 local := make([]int8, f.NumBlocks()) // for our immediate predecessors. 115 116 nest := loopnestfor(f) 117 po := nest.po 118 b2l := nest.b2l 119 120 for _, b := range po { 121 switch b.Kind { 122 case BlockExit: 123 // Very unlikely. 124 local[b.ID] = blEXIT 125 certain[b.ID] = blEXIT 126 127 // Ret, it depends. 128 case BlockRet, BlockRetJmp: 129 local[b.ID] = blRET 130 certain[b.ID] = blRET 131 132 // Calls. TODO not all calls are equal, names give useful clues. 133 // Any name-based heuristics are only relative to other calls, 134 // and less influential than inferences from loop structure. 135 case BlockCall, BlockDefer: 136 local[b.ID] = blCALL 137 certain[b.ID] = max8(blCALL, certain[b.Succs[0].b.ID]) 138 139 default: 140 if len(b.Succs) == 1 { 141 certain[b.ID] = certain[b.Succs[0].b.ID] 142 } else if len(b.Succs) == 2 { 143 // If successor is an unvisited backedge, it's in loop and we don't care. 144 // Its default unlikely is also zero which is consistent with favoring loop edges. 145 // Notice that this can act like a "reset" on unlikeliness at loops; the 146 // default "everything returns" unlikeliness is erased by min with the 147 // backedge likeliness; however a loop with calls on every path will be 148 // tagged with call cost. Net effect is that loop entry is favored. 149 b0 := b.Succs[0].b.ID 150 b1 := b.Succs[1].b.ID 151 certain[b.ID] = min8(certain[b0], certain[b1]) 152 153 l := b2l[b.ID] 154 l0 := b2l[b0] 155 l1 := b2l[b1] 156 157 prediction := b.Likely 158 // Weak loop heuristic -- both source and at least one dest are in loops, 159 // and there is a difference in the destinations. 160 // TODO what is best arrangement for nested loops? 161 if l != nil && l0 != l1 { 162 noprediction := false 163 switch { 164 // prefer not to exit loops 165 case l1 == nil: 166 prediction = BranchLikely 167 case l0 == nil: 168 prediction = BranchUnlikely 169 170 // prefer to stay in loop, not exit to outer. 171 case l == l0: 172 prediction = BranchLikely 173 case l == l1: 174 prediction = BranchUnlikely 175 default: 176 noprediction = true 177 } 178 if f.pass.debug > 0 && !noprediction { 179 f.Config.Warnl(b.Line, "Branch prediction rule stay in loop%s", 180 describePredictionAgrees(b, prediction)) 181 } 182 183 } else { 184 // Lacking loop structure, fall back on heuristics. 185 if certain[b1] > certain[b0] { 186 prediction = BranchLikely 187 if f.pass.debug > 0 { 188 describeBranchPrediction(f, b, certain[b0], certain[b1], prediction) 189 } 190 } else if certain[b0] > certain[b1] { 191 prediction = BranchUnlikely 192 if f.pass.debug > 0 { 193 describeBranchPrediction(f, b, certain[b1], certain[b0], prediction) 194 } 195 } else if local[b1] > local[b0] { 196 prediction = BranchLikely 197 if f.pass.debug > 0 { 198 describeBranchPrediction(f, b, local[b0], local[b1], prediction) 199 } 200 } else if local[b0] > local[b1] { 201 prediction = BranchUnlikely 202 if f.pass.debug > 0 { 203 describeBranchPrediction(f, b, local[b1], local[b0], prediction) 204 } 205 } 206 } 207 if b.Likely != prediction { 208 if b.Likely == BranchUnknown { 209 b.Likely = prediction 210 } 211 } 212 } 213 } 214 if f.pass.debug > 2 { 215 f.Config.Warnl(b.Line, "BP: Block %s, local=%s, certain=%s", b, bllikelies[local[b.ID]-blMin], bllikelies[certain[b.ID]-blMin]) 216 } 217 218 } 219 } 220 221 func (l *loop) String() string { 222 return fmt.Sprintf("hdr:%s", l.header) 223 } 224 225 func (l *loop) LongString() string { 226 i := "" 227 o := "" 228 if l.isInner { 229 i = ", INNER" 230 } 231 if l.outer != nil { 232 o = ", o=" + l.outer.header.String() 233 } 234 return fmt.Sprintf("hdr:%s%s%s", l.header, i, o) 235 } 236 237 // nearestOuterLoop returns the outer loop of loop most nearly 238 // containing block b; the header must dominate b. loop itself 239 // is assumed to not be that loop. For acceptable performance, 240 // we're relying on loop nests to not be terribly deep. 241 func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop { 242 var o *loop 243 for o = l.outer; o != nil && !sdom.isAncestorEq(o.header, b); o = o.outer { 244 } 245 return o 246 } 247 248 func loopnestfor(f *Func) *loopnest { 249 po := postorder(f) 250 dom := dominators(f) 251 sdom := newSparseTree(f, dom) 252 b2l := make([]*loop, f.NumBlocks()) 253 loops := make([]*loop, 0) 254 255 // Reducible-loop-nest-finding. 256 for _, b := range po { 257 if f.pass.debug > 3 { 258 fmt.Printf("loop finding (0) at %s\n", b) 259 } 260 261 var innermost *loop // innermost header reachable from this block 262 263 // IF any successor s of b is in a loop headed by h 264 // AND h dominates b 265 // THEN b is in the loop headed by h. 266 // 267 // Choose the first/innermost such h. 268 // 269 // IF s itself dominates b, the s is a loop header; 270 // and there may be more than one such s. 271 // Since there's at most 2 successors, the inner/outer ordering 272 // between them can be established with simple comparisons. 273 for _, e := range b.Succs { 274 bb := e.b 275 l := b2l[bb.ID] 276 277 if sdom.isAncestorEq(bb, b) { // Found a loop header 278 if l == nil { 279 l = &loop{header: bb, isInner: true} 280 loops = append(loops, l) 281 b2l[bb.ID] = l 282 l.checkContainsCall(bb) 283 } 284 } else { // Perhaps a loop header is inherited. 285 // is there any loop containing our successor whose 286 // header dominates b? 287 if l != nil && !sdom.isAncestorEq(l.header, b) { 288 l = l.nearestOuterLoop(sdom, b) 289 } 290 } 291 292 if l == nil || innermost == l { 293 continue 294 } 295 296 if innermost == nil { 297 innermost = l 298 continue 299 } 300 301 if sdom.isAncestor(innermost.header, l.header) { 302 sdom.outerinner(innermost, l) 303 innermost = l 304 } else if sdom.isAncestor(l.header, innermost.header) { 305 sdom.outerinner(l, innermost) 306 } 307 } 308 309 if innermost != nil { 310 b2l[b.ID] = innermost 311 innermost.checkContainsCall(b) 312 innermost.nBlocks++ 313 } 314 } 315 316 ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops} 317 318 // Curious about the loopiness? "-d=ssa/likelyadjust/stats" 319 if f.pass.stats > 0 && len(loops) > 0 { 320 ln.assembleChildren() 321 ln.calculateDepths() 322 ln.findExits() 323 324 // Note stats for non-innermost loops are slightly flawed because 325 // they don't account for inner loop exits that span multiple levels. 326 327 for _, l := range loops { 328 x := len(l.exits) 329 cf := 0 330 if !l.containsCall { 331 cf = 1 332 } 333 inner := 0 334 if l.isInner { 335 inner++ 336 } 337 338 f.LogStat("loopstats:", 339 l.depth, "depth", x, "exits", 340 inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks") 341 } 342 } 343 344 if f.pass.debug > 1 && len(loops) > 0 { 345 fmt.Printf("Loops in %s:\n", f.Name) 346 for _, l := range loops { 347 fmt.Printf("%s, b=", l.LongString()) 348 for _, b := range f.Blocks { 349 if b2l[b.ID] == l { 350 fmt.Printf(" %s", b) 351 } 352 } 353 fmt.Print("\n") 354 } 355 fmt.Printf("Nonloop blocks in %s:", f.Name) 356 for _, b := range f.Blocks { 357 if b2l[b.ID] == nil { 358 fmt.Printf(" %s", b) 359 } 360 } 361 fmt.Print("\n") 362 } 363 return ln 364 } 365 366 // assembleChildren initializes the children field of each 367 // loop in the nest. Loop A is a child of loop B if A is 368 // directly nested within B (based on the reducible-loops 369 // detection above) 370 func (ln *loopnest) assembleChildren() { 371 if ln.initializedChildren { 372 return 373 } 374 for _, l := range ln.loops { 375 if l.outer != nil { 376 l.outer.children = append(l.outer.children, l) 377 } 378 } 379 ln.initializedChildren = true 380 } 381 382 // calculateDepths uses the children field of loops 383 // to determine the nesting depth (outer=1) of each 384 // loop. This is helpful for finding exit edges. 385 func (ln *loopnest) calculateDepths() { 386 if ln.initializedDepth { 387 return 388 } 389 ln.assembleChildren() 390 for _, l := range ln.loops { 391 if l.outer == nil { 392 l.setDepth(1) 393 } 394 } 395 ln.initializedDepth = true 396 } 397 398 // findExits uses loop depth information to find the 399 // exits from a loop. 400 func (ln *loopnest) findExits() { 401 if ln.initializedExits { 402 return 403 } 404 ln.calculateDepths() 405 b2l := ln.b2l 406 for _, b := range ln.po { 407 l := b2l[b.ID] 408 if l != nil && len(b.Succs) == 2 { 409 sl := b2l[b.Succs[0].b.ID] 410 if recordIfExit(l, sl, b.Succs[0].b) { 411 continue 412 } 413 sl = b2l[b.Succs[1].b.ID] 414 if recordIfExit(l, sl, b.Succs[1].b) { 415 continue 416 } 417 } 418 } 419 ln.initializedExits = true 420 } 421 422 // recordIfExit checks sl (the loop containing b) to see if it 423 // is outside of loop l, and if so, records b as an exit block 424 // from l and returns true. 425 func recordIfExit(l, sl *loop, b *Block) bool { 426 if sl != l { 427 if sl == nil || sl.depth <= l.depth { 428 l.exits = append(l.exits, b) 429 return true 430 } 431 // sl is not nil, and is deeper than l 432 // it's possible for this to be a goto into an irreducible loop made from gotos. 433 for sl.depth > l.depth { 434 sl = sl.outer 435 } 436 if sl != l { 437 l.exits = append(l.exits, b) 438 return true 439 } 440 } 441 return false 442 } 443 444 func (l *loop) setDepth(d int16) { 445 l.depth = d 446 for _, c := range l.children { 447 c.setDepth(d + 1) 448 } 449 }