garbageCollectUnreferencedHiddenClosures()
if base.Debug.DumpInlFuncProps != "" {
- inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps, nil)
+ inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps, nil, inlineMaxBudget)
}
if goexperiment.NewInliner {
postProcessCallSites(p)
var funcProps *inlheur.FuncProps
if goexperiment.NewInliner || inlheur.UnitTesting() {
- funcProps = inlheur.AnalyzeFunc(fn,
- func(fn *ir.Func) { CanInline(fn, profile) })
+ callCanInline := func(fn *ir.Func) { CanInline(fn, profile) }
+ funcProps = inlheur.AnalyzeFunc(fn, callCanInline, inlineMaxBudget)
}
var reason string // reason, if any, that the function was not inlined
}
if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() {
inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps,
- func(fn *ir.Func) { CanInline(fn, profile) })
+ func(fn *ir.Func) { CanInline(fn, profile) }, inlineMaxBudget)
}
savefn := ir.CurFunc
ir.CurFunc = fn
// parsing a dump. This is the reason why we have file/fname/line
// fields below instead of just an *ir.Func field.
type fnInlHeur struct {
- fname string
- file string
- line uint
- props *FuncProps
- cstab CallSiteTab
+ fname string
+ file string
+ line uint
+ inlineMaxBudget int32
+ props *FuncProps
+ cstab CallSiteTab
}
var fpmap = map[*ir.Func]fnInlHeur{}
-func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func)) *FuncProps {
+func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) *FuncProps {
if fih, ok := fpmap[fn]; ok {
return fih.props
}
- fp, fcstab := computeFuncProps(fn, canInline)
+ fp, fcstab := computeFuncProps(fn, canInline, inlineMaxBudget)
file, line := fnFileLine(fn)
entry := fnInlHeur{
- fname: fn.Sym().Name,
- file: file,
- line: line,
- props: fp,
- cstab: fcstab,
+ fname: fn.Sym().Name,
+ file: file,
+ line: line,
+ inlineMaxBudget: inlineMaxBudget,
+ props: fp,
+ cstab: fcstab,
}
// Merge this functions call sites into the package level table.
if err := cstab.merge(fcstab); err != nil {
// computeFuncProps examines the Go function 'fn' and computes for it
// a function "properties" object, to be used to drive inlining
// heuristics. See comments on the FuncProps type for more info.
-func computeFuncProps(fn *ir.Func, canInline func(*ir.Func)) (*FuncProps, CallSiteTab) {
+func computeFuncProps(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) (*FuncProps, CallSiteTab) {
enableDebugTraceIfEnv()
if debugTrace&debugTraceFuncs != 0 {
fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n",
fn.Sym().Name, fn)
}
- ra := makeResultsAnalyzer(fn, canInline)
+ ra := makeResultsAnalyzer(fn, canInline, inlineMaxBudget)
pa := makeParamsAnalyzer(fn)
ffa := makeFuncFlagsAnalyzer(fn)
analyzers := []propAnalyzer{ffa, ra, pa}
// cached set of properties to the file given in 'dumpfile'. Used for
// the "-d=dumpinlfuncprops=..." command line flag, intended for use
// primarily in unit testing.
-func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func)) {
+func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func), inlineMaxBudget int32) {
if fn != nil {
enableDebugTraceIfEnv()
dmp := func(fn *ir.Func) {
if !goexperiment.NewInliner {
ScoreCalls(fn)
}
- captureFuncDumpEntry(fn, canInline)
+ captureFuncDumpEntry(fn, canInline, inlineMaxBudget)
}
- captureFuncDumpEntry(fn, canInline)
dmp(fn)
ir.Visit(fn, func(n ir.Node) {
if clo, ok := n.(*ir.ClosureExpr); ok {
// and enqueues it for later dumping. Used for the
// "-d=dumpinlfuncprops=..." command line flag, intended for use
// primarily in unit testing.
-func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func)) {
+func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) {
// avoid capturing compiler-generated equality funcs.
if strings.HasPrefix(fn.Sym().Name, ".eq.") {
return
// Props object should already be present, unless this is a
// directly recursive routine.
if !ok {
- AnalyzeFunc(fn, canInline)
+ AnalyzeFunc(fn, canInline, inlineMaxBudget)
fih = fpmap[fn]
if fn.Inl != nil && fn.Inl.Properties == "" {
fn.Inl.Properties = fih.props.SerializeToString()
// computing flags/properties for the return values of a specific Go
// function, as part of inline heuristics synthesis.
type returnsAnalyzer struct {
- fname string
- props []ResultPropBits
- values []resultVal
- canInline func(*ir.Func)
+ fname string
+ props []ResultPropBits
+ values []resultVal
+ canInline func(*ir.Func)
+ inlineMaxBudget int32
}
// resultVal captures information about a specific result returned from
derived bool // see deriveReturnFlagsFromCallee below
}
-func makeResultsAnalyzer(fn *ir.Func, canInline func(*ir.Func)) *returnsAnalyzer {
+func makeResultsAnalyzer(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) *returnsAnalyzer {
results := fn.Type().Results()
props := make([]ResultPropBits, len(results))
vals := make([]resultVal, len(results))
vals[i].top = true
}
return &returnsAnalyzer{
- props: props,
- values: vals,
- canInline: canInline,
+ props: props,
+ values: vals,
+ canInline: canInline,
+ inlineMaxBudget: inlineMaxBudget,
}
}
ra.canInline(f)
}
}
- if f.Inl != nil {
+ // HACK: in order to allow for call site score
+ // adjustments, we used a relaxed inline budget in
+ // determining inlinability. Here what we want to know is
+ // whether the func in question is likely to be inlined,
+ // as opposed to whether it might possibly be inlined if
+ // all the right score adjustments happened, so check the
+ // cost here as well.
+ if f.Inl != nil && f.Inl.Cost <= ra.inlineMaxBudget {
ra.props[i] = ResultAlwaysSameInlinableFunc
}
}