| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssacompile |
| |
| import ( |
| "cmd/compile/internal/ssa" |
| "cmd/compile/internal/ssa/block" |
| "cmd/compile/internal/ssa/ssaop" |
| ) |
| |
| const ( |
| blDEFAULT = 0 |
| blMin = blDEFAULT |
| blCALL = 1 |
| blRET = 2 |
| blEXIT = 3 |
| ) |
| |
| var bllikelies = [4]string{"default", "call", "ret", "exit"} |
| |
| func describePredictionAgrees(b *ssa.Block, prediction ssa.BranchPrediction) string { |
| s := "" |
| if prediction == b.Likely { |
| s = " (agrees with previous)" |
| } else if b.Likely != ssa.BranchUnknown { |
| s = " (disagrees with previous, ignored)" |
| } |
| return s |
| } |
| |
| func describeBranchPrediction(f *ssa.Func, b *ssa.Block, likely, not int8, prediction ssa.BranchPrediction) { |
| f.Warnl(b.Pos, "Branch prediction rule %s < %s%s", |
| bllikelies[likely-blMin], bllikelies[not-blMin], describePredictionAgrees(b, prediction)) |
| } |
| |
| func likelyadjust(f *ssa.Func) { |
| // The values assigned to certain and local only matter |
| // in their rank order. 0 is default, more positive |
| // is less likely. It's possible to assign a negative |
| // unlikeliness (though not currently the case). |
| certain := f.Cache.AllocInt8Slice(f.NumBlocks()) // In the long run, all outcomes are at least this bad. Mainly for Exit |
| defer f.Cache.FreeInt8Slice(certain) |
| local := f.Cache.AllocInt8Slice(f.NumBlocks()) // for our immediate predecessors. |
| defer f.Cache.FreeInt8Slice(local) |
| |
| po := f.Postorder() |
| nest := f.Loopnest() |
| b2l := nest.B2L |
| |
| for _, b := range po { |
| switch b.Kind { |
| case block.BlockExit: |
| // Very unlikely. |
| local[b.ID] = blEXIT |
| certain[b.ID] = blEXIT |
| |
| // Ret, it depends. |
| case block.BlockRet, block.BlockRetJmp: |
| local[b.ID] = blRET |
| certain[b.ID] = blRET |
| |
| // Calls. TODO not all calls are equal, names give useful clues. |
| // Any name-based heuristics are only relative to other calls, |
| // and less influential than inferences from loop structure. |
| case block.BlockDefer: |
| local[b.ID] = blCALL |
| certain[b.ID] = max(blCALL, certain[b.Succs[0].B.ID]) |
| |
| default: |
| if len(b.Succs) == 1 { |
| certain[b.ID] = certain[b.Succs[0].B.ID] |
| } else if len(b.Succs) == 2 { |
| // If successor is an unvisited backedge, it's in loop and we don't care. |
| // Its default unlikely is also zero which is consistent with favoring loop edges. |
| // Notice that this can act like a "reset" on unlikeliness at loops; the |
| // default "everything returns" unlikeliness is erased by min with the |
| // backedge likeliness; however a loop with calls on every path will be |
| // tagged with call cost. Net effect is that loop entry is favored. |
| b0 := b.Succs[0].B.ID |
| b1 := b.Succs[1].B.ID |
| certain[b.ID] = min(certain[b0], certain[b1]) |
| |
| l := b2l[b.ID] |
| l0 := b2l[b0] |
| l1 := b2l[b1] |
| |
| prediction := b.Likely |
| // Weak loop heuristic -- both source and at least one dest are in loops, |
| // and there is a difference in the destinations. |
| // TODO what is best arrangement for nested loops? |
| if l != nil && l0 != l1 { |
| noprediction := false |
| switch { |
| // prefer not to exit loops |
| case l1 == nil: |
| prediction = ssa.BranchLikely |
| case l0 == nil: |
| prediction = ssa.BranchUnlikely |
| |
| // prefer to stay in loop, not exit to outer. |
| case l == l0: |
| prediction = ssa.BranchLikely |
| case l == l1: |
| prediction = ssa.BranchUnlikely |
| default: |
| noprediction = true |
| } |
| if f.Pass.Debug > 0 && !noprediction { |
| f.Warnl(b.Pos, "Branch prediction rule stay in loop%s", |
| describePredictionAgrees(b, prediction)) |
| } |
| |
| } else { |
| // Lacking loop structure, fall back on heuristics. |
| if certain[b1] > certain[b0] { |
| prediction = ssa.BranchLikely |
| if f.Pass.Debug > 0 { |
| describeBranchPrediction(f, b, certain[b0], certain[b1], prediction) |
| } |
| } else if certain[b0] > certain[b1] { |
| prediction = ssa.BranchUnlikely |
| if f.Pass.Debug > 0 { |
| describeBranchPrediction(f, b, certain[b1], certain[b0], prediction) |
| } |
| } else if local[b1] > local[b0] { |
| prediction = ssa.BranchLikely |
| if f.Pass.Debug > 0 { |
| describeBranchPrediction(f, b, local[b0], local[b1], prediction) |
| } |
| } else if local[b0] > local[b1] { |
| prediction = ssa.BranchUnlikely |
| if f.Pass.Debug > 0 { |
| describeBranchPrediction(f, b, local[b1], local[b0], prediction) |
| } |
| } |
| } |
| if b.Likely != prediction { |
| if b.Likely == ssa.BranchUnknown { |
| b.Likely = prediction |
| } |
| } |
| } |
| // Look for calls in the block. If there is one, make this block unlikely. |
| for _, v := range b.Values { |
| if ssaop.OpcodeTable[v.Op].Call { |
| local[b.ID] = blCALL |
| certain[b.ID] = max(blCALL, certain[b.Succs[0].B.ID]) |
| break |
| } |
| } |
| } |
| if f.Pass.Debug > 2 { |
| f.Warnl(b.Pos, "BP: Block %s, local=%s, certain=%s", b, bllikelies[local[b.ID]-blMin], bllikelies[certain[b.ID]-blMin]) |
| } |
| |
| } |
| } |