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