github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/cmd/compile/internal/pgo/irgraph.go (about) 1 // Copyright 2022 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 // A note on line numbers: when working with line numbers, we always use the 6 // binary-visible relative line number. i.e., the line number as adjusted by 7 // //line directives (ctxt.InnermostPos(ir.Node.Pos()).RelLine()). Use 8 // NodeLineOffset to compute line offsets. 9 // 10 // If you are thinking, "wait, doesn't that just make things more complex than 11 // using the real line number?", then you are 100% correct. Unfortunately, 12 // pprof profiles generated by the runtime always contain line numbers as 13 // adjusted by //line directives (because that is what we put in pclntab). Thus 14 // for the best behavior when attempting to match the source with the profile 15 // it makes sense to use the same line number space. 16 // 17 // Some of the effects of this to keep in mind: 18 // 19 // - For files without //line directives there is no impact, as RelLine() == 20 // Line(). 21 // - For functions entirely covered by the same //line directive (i.e., a 22 // directive before the function definition and no directives within the 23 // function), there should also be no impact, as line offsets within the 24 // function should be the same as the real line offsets. 25 // - Functions containing //line directives may be impacted. As fake line 26 // numbers need not be monotonic, we may compute negative line offsets. We 27 // should accept these and attempt to use them for best-effort matching, as 28 // these offsets should still match if the source is unchanged, and may 29 // continue to match with changed source depending on the impact of the 30 // changes on fake line numbers. 31 // - Functions containing //line directives may also contain duplicate lines, 32 // making it ambiguous which call the profile is referencing. This is a 33 // similar problem to multiple calls on a single real line, as we don't 34 // currently track column numbers. 35 // 36 // Long term it would be best to extend pprof profiles to include real line 37 // numbers. Until then, we have to live with these complexities. Luckily, 38 // //line directives that change line numbers in strange ways should be rare, 39 // and failing PGO matching on these files is not too big of a loss. 40 41 package pgo 42 43 import ( 44 "github.com/shogo82148/std/cmd/compile/internal/base" 45 "github.com/shogo82148/std/cmd/compile/internal/ir" 46 ) 47 48 // IRGraph is a call graph with nodes pointing to IRs of functions and edges 49 // carrying weights and callsite information. 50 // 51 // Nodes for indirect calls may have missing IR (IRNode.AST == nil) if the node 52 // is not visible from this package (e.g., not in the transitive deps). Keeping 53 // these nodes allows determining the hottest edge from a call even if that 54 // callee is not available. 55 // 56 // TODO(prattmic): Consider merging this data structure with Graph. This is 57 // effectively a copy of Graph aggregated to line number and pointing to IR. 58 type IRGraph struct { 59 // Nodes of the graph. Each node represents a function, keyed by linker 60 // symbol name. 61 IRNodes map[string]*IRNode 62 } 63 64 // IRNode represents a node (function) in the IRGraph. 65 type IRNode struct { 66 // Pointer to the IR of the Function represented by this node. 67 AST *ir.Func 68 // Linker symbol name of the Function represented by this node. 69 // Populated only if AST == nil. 70 LinkerSymbolName string 71 72 // Set of out-edges in the callgraph. The map uniquely identifies each 73 // edge based on the callsite and callee, for fast lookup. 74 OutEdges map[NamedCallEdge]*IREdge 75 } 76 77 // Name returns the symbol name of this function. 78 func (i *IRNode) Name() string 79 80 // IREdge represents a call edge in the IRGraph with source, destination, 81 // weight, callsite, and line number information. 82 type IREdge struct { 83 // Source and destination of the edge in IRNode. 84 Src, Dst *IRNode 85 Weight int64 86 CallSiteOffset int 87 } 88 89 // NamedCallEdge identifies a call edge by linker symbol names and call site 90 // offset. 91 type NamedCallEdge struct { 92 CallerName string 93 CalleeName string 94 CallSiteOffset int 95 } 96 97 // NamedEdgeMap contains all unique call edges in the profile and their 98 // edge weight. 99 type NamedEdgeMap struct { 100 Weight map[NamedCallEdge]int64 101 102 // ByWeight lists all keys in Weight, sorted by edge weight. 103 ByWeight []NamedCallEdge 104 } 105 106 // CallSiteInfo captures call-site information and its caller/callee. 107 type CallSiteInfo struct { 108 LineOffset int 109 Caller *ir.Func 110 Callee *ir.Func 111 } 112 113 // Profile contains the processed PGO profile and weighted call graph used for 114 // PGO optimizations. 115 type Profile struct { 116 // Aggregated edge weights across the profile. This helps us determine 117 // the percentage threshold for hot/cold partitioning. 118 TotalWeight int64 119 120 // NamedEdgeMap contains all unique call edges in the profile and their 121 // edge weight. 122 NamedEdgeMap NamedEdgeMap 123 124 // WeightedCG represents the IRGraph built from profile, which we will 125 // update as part of inlining. 126 WeightedCG *IRGraph 127 } 128 129 // New generates a profile-graph from the profile or pre-processed profile. 130 func New(profileFile string) (*Profile, error) 131 132 // NodeLineOffset returns the line offset of n in fn. 133 func NodeLineOffset(n ir.Node, fn *ir.Func) int 134 135 // LookupFunc looks up a function or method in export data. It is expected to 136 // be overridden by package noder, to break a dependency cycle. 137 var LookupFunc = func(fullName string) (*ir.Func, error) { 138 base.Fatalf("pgo.LookupMethodFunc not overridden") 139 panic("unreachable") 140 } 141 142 // WeightInPercentage converts profile weights to a percentage. 143 func WeightInPercentage(value int64, total int64) float64 144 145 // PrintWeightedCallGraphDOT prints IRGraph in DOT format. 146 func (p *Profile) PrintWeightedCallGraphDOT(edgeThreshold float64) 147 148 // DirectCallee takes a function-typed expression and returns the underlying 149 // function that it refers to if statically known. Otherwise, it returns nil. 150 // 151 // Equivalent to inline.inlCallee without calling CanInline on closures. 152 func DirectCallee(fn ir.Node) *ir.Func