github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/inline/inlheur/callsite.go (about) 1 // Copyright 2023 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 inlheur 6 7 import ( 8 "fmt" 9 "io" 10 "path/filepath" 11 "sort" 12 "strings" 13 14 "github.com/go-asm/go/cmd/compile/base" 15 "github.com/go-asm/go/cmd/compile/ir" 16 "github.com/go-asm/go/cmd/src" 17 ) 18 19 // CallSite records useful information about a potentially inlinable 20 // (direct) function call. "Callee" is the target of the call, "Call" 21 // is the ir node corresponding to the call itself, "Assign" is 22 // the top-level assignment statement containing the call (if the call 23 // appears in the form of a top-level statement, e.g. "x := foo()"), 24 // "Flags" contains properties of the call that might be useful for 25 // making inlining decisions, "Score" is the final score assigned to 26 // the site, and "ID" is a numeric ID for the site within its 27 // containing function. 28 type CallSite struct { 29 Callee *ir.Func 30 Call *ir.CallExpr 31 parent *CallSite 32 Assign ir.Node 33 Flags CSPropBits 34 35 ArgProps []ActualExprPropBits 36 Score int 37 ScoreMask scoreAdjustTyp 38 ID uint 39 aux uint8 40 } 41 42 // CallSiteTab is a table of call sites, keyed by call expr. 43 // Ideally it would be nice to key the table by src.XPos, but 44 // this results in collisions for calls on very long lines (the 45 // front end saturates column numbers at 255). We also wind up 46 // with many calls that share the same auto-generated pos. 47 type CallSiteTab map[*ir.CallExpr]*CallSite 48 49 // ActualExprPropBits describes a property of an actual expression (value 50 // passed to some specific func argument at a call site). 51 type ActualExprPropBits uint8 52 53 const ( 54 ActualExprConstant ActualExprPropBits = 1 << iota 55 ActualExprIsConcreteConvIface 56 ActualExprIsFunc 57 ActualExprIsInlinableFunc 58 ) 59 60 type CSPropBits uint32 61 62 const ( 63 CallSiteInLoop CSPropBits = 1 << iota 64 CallSiteOnPanicPath 65 CallSiteInInitFunc 66 ) 67 68 type csAuxBits uint8 69 70 const ( 71 csAuxInlined = 1 << iota 72 ) 73 74 // encodedCallSiteTab is a table keyed by "encoded" callsite 75 // (stringified src.XPos plus call site ID) mapping to a value of call 76 // property bits and score. 77 type encodedCallSiteTab map[string]propsAndScore 78 79 type propsAndScore struct { 80 props CSPropBits 81 score int 82 mask scoreAdjustTyp 83 } 84 85 func (pas propsAndScore) String() string { 86 return fmt.Sprintf("P=%s|S=%d|M=%s", pas.props.String(), 87 pas.score, pas.mask.String()) 88 } 89 90 func (cst CallSiteTab) merge(other CallSiteTab) error { 91 for k, v := range other { 92 if prev, ok := cst[k]; ok { 93 return fmt.Errorf("internal error: collision during call site table merge, fn=%s callsite=%s", prev.Callee.Sym().Name, fmtFullPos(prev.Call.Pos())) 94 } 95 cst[k] = v 96 } 97 return nil 98 } 99 100 func fmtFullPos(p src.XPos) string { 101 var sb strings.Builder 102 sep := "" 103 base.Ctxt.AllPos(p, func(pos src.Pos) { 104 fmt.Fprintf(&sb, sep) 105 sep = "|" 106 file := filepath.Base(pos.Filename()) 107 fmt.Fprintf(&sb, "%s:%d:%d", file, pos.Line(), pos.Col()) 108 }) 109 return sb.String() 110 } 111 112 func EncodeCallSiteKey(cs *CallSite) string { 113 var sb strings.Builder 114 // FIXME: maybe rewrite line offsets relative to function start? 115 sb.WriteString(fmtFullPos(cs.Call.Pos())) 116 fmt.Fprintf(&sb, "|%d", cs.ID) 117 return sb.String() 118 } 119 120 func buildEncodedCallSiteTab(tab CallSiteTab) encodedCallSiteTab { 121 r := make(encodedCallSiteTab) 122 for _, cs := range tab { 123 k := EncodeCallSiteKey(cs) 124 r[k] = propsAndScore{ 125 props: cs.Flags, 126 score: cs.Score, 127 mask: cs.ScoreMask, 128 } 129 } 130 return r 131 } 132 133 // dumpCallSiteComments emits comments into the dump file for the 134 // callsites in the function of interest. If "ecst" is non-nil, we use 135 // that, otherwise generated a fresh encodedCallSiteTab from "tab". 136 func dumpCallSiteComments(w io.Writer, tab CallSiteTab, ecst encodedCallSiteTab) { 137 if ecst == nil { 138 ecst = buildEncodedCallSiteTab(tab) 139 } 140 tags := make([]string, 0, len(ecst)) 141 for k := range ecst { 142 tags = append(tags, k) 143 } 144 sort.Strings(tags) 145 for _, s := range tags { 146 v := ecst[s] 147 fmt.Fprintf(w, "// callsite: %s flagstr %q flagval %d score %d mask %d maskstr %q\n", s, v.props.String(), v.props, v.score, v.mask, v.mask.String()) 148 } 149 fmt.Fprintf(w, "// %s\n", csDelimiter) 150 }