From: Than McIntosh Date: Tue, 7 Nov 2023 15:28:56 +0000 (-0500) Subject: cmd/compile/internal/inline: revise -m=2 status messages X-Git-Tag: go1.22rc1~207 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=cfb281754ec94859e86962ee3a66b8347e3161ab;p=gostls13.git cmd/compile/internal/inline: revise -m=2 status messages This patch revises the compiler's "-m=2" status messages related to inlining. The "can inline" remarks will continue to use the same format, but the remarks when a specific call site is inlined will be changed to refer to the score used; before we had runtime/traceback.go:1131:28: inlining call to gotraceback runtime/traceback.go:1183:25: inlining call to readgstatus and with GOEXPERIMENT=newinliner the new messages will be: runtime/traceback.go:1131:28: inlining call to gotraceback with score 62 runtime/traceback.go:1183:25: inlining call to readgstatus with score 9 Change-Id: Ia86cf5351d29eda64a5426ca0a2a2ec0c2900d81 Reviewed-on: https://go-review.googlesource.com/c/go/+/540775 Reviewed-by: Matthew Dempsky LUCI-TryBot-Result: Go LUCI --- diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 74f0d341c7..b365008c76 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -29,6 +29,7 @@ package inline import ( "fmt" "go/constant" + "internal/buildcfg" "strconv" "cmd/compile/internal/base" @@ -327,14 +328,22 @@ func CanInline(fn *ir.Func, profile *pgo.Profile) { CanDelayResults: canDelayResults(fn), } + if base.Flag.LowerM != 0 || logopt.Enabled() { + noteInlinableFunc(n, fn, budget-visitor.budget) + } +} +// noteInlinableFunc issues a message to the user that the specified +// function is inlinable. +func noteInlinableFunc(n *ir.Name, fn *ir.Func, cost int32) { if base.Flag.LowerM > 1 { - fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, budget-visitor.budget, fn.Type(), ir.Nodes(fn.Body)) + fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, cost, fn.Type(), ir.Nodes(fn.Body)) } else if base.Flag.LowerM != 0 { fmt.Printf("%v: can inline %v\n", ir.Line(fn), n) } + // JSON optimization log output. if logopt.Enabled() { - logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", budget-visitor.budget)) + logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", cost)) } } @@ -558,7 +567,7 @@ opSwitch: // Check whether we'd actually inline this call. Set // log == false since we aren't actually doing inlining // yet. - if canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false) { + if ok, _ := canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false); ok { // mkinlcall would inline this call [1], so use // the cost of the inline body as the cost of // the call, as that is what will actually @@ -851,9 +860,10 @@ var InlineCall = func(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInde // inlineCostOK returns true if call n from caller to callee is cheap enough to // inline. bigCaller indicates that caller is a big function. // -// If inlineCostOK returns false, it also returns the max cost that the callee -// exceeded. -func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32) { +// In addition to the "cost OK" boolean, it also returns the "max +// cost" limit used to make the decision (which may differ depending +// on func size), and the score assigned to this specific callsite. +func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32, int32) { maxCost := int32(inlineMaxBudget) if bigCaller { // We use this to restrict inlining into very big functions. @@ -867,12 +877,11 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool if ok { metric = int32(score) } - } if metric <= maxCost { // Simple case. Function is already cheap enough. - return true, 0 + return true, 0, metric } // We'll also allow inlining of hot functions below inlineHotMaxBudget, @@ -882,7 +891,7 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool csi := pgo.CallSiteInfo{LineOffset: lineOffset, Caller: caller} if _, ok := candHotEdgeMap[csi]; !ok { // Cold - return false, maxCost + return false, maxCost, metric } // Hot @@ -891,47 +900,49 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool if base.Debug.PGODebug > 0 { fmt.Printf("hot-big check disallows inlining for call %s (cost %d) at %v in big function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller)) } - return false, maxCost + return false, maxCost, metric } if metric > inlineHotMaxBudget { - return false, inlineHotMaxBudget + return false, inlineHotMaxBudget, metric } if !base.PGOHash.MatchPosWithInfo(n.Pos(), "inline", nil) { // De-selected by PGO Hash. - return false, maxCost + return false, maxCost, metric } if base.Debug.PGODebug > 0 { fmt.Printf("hot-budget check allows inlining for call %s (cost %d) at %v in function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller)) } - return true, 0 + return true, 0, metric } -// canInlineCallsite returns true if the call n from caller to callee can be -// inlined. bigCaller indicates that caller is a big function. log indicates -// that the 'cannot inline' reason should be logged. +// canInlineCallsite returns true if the call n from caller to callee +// can be inlined, plus the score computed for the call expr in +// question. bigCaller indicates that caller is a big function. log +// indicates that the 'cannot inline' reason should be logged. // // Preconditions: CanInline(callee) has already been called. -func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) bool { +func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) (bool, int32) { if callee.Inl == nil { // callee is never inlinable. if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(callee))) } - return false + return false, 0 } - if ok, maxCost := inlineCostOK(n, callerfn, callee, bigCaller); !ok { + ok, maxCost, callSiteScore := inlineCostOK(n, callerfn, callee, bigCaller) + if !ok { // callee cost too high for this call site. if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("cost %d of %s exceeds max caller cost %d", callee.Inl.Cost, ir.PkgFuncName(callee), maxCost)) } - return false + return false, 0 } if callee == callerfn { @@ -939,7 +950,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn))) } - return false + return false, 0 } if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) { @@ -953,7 +964,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("call to runtime function %s in instrumented build", ir.PkgFuncName(callee))) } - return false + return false, 0 } if base.Flag.Race && types.IsNoRacePkg(callee.Sym().Pkg) { @@ -961,7 +972,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf(`call to into "no-race" package function %s in race build`, ir.PkgFuncName(callee))) } - return false + return false, 0 } // Check if we've already inlined this function at this particular @@ -984,11 +995,11 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa fmt.Sprintf("repeated recursive cycle to %s", ir.PkgFuncName(callee))) } } - return false + return false, 0 } } - return true + return true, callSiteScore } // mkinlcall returns an OINLCALL node that can replace OCALLFUNC n, or @@ -999,7 +1010,8 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa // // n.Left = mkinlcall(n.Left, fn, isddd) func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *ir.InlinedCallExpr { - if !canInlineCallExpr(callerfn, n, fn, bigCaller, true) { + ok, score := canInlineCallExpr(callerfn, n, fn, bigCaller, true) + if !ok { return nil } typecheck.AssertFixedCall(n) @@ -1058,7 +1070,12 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) * } if base.Flag.LowerM != 0 { - fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn) + if buildcfg.Experiment.NewInliner { + fmt.Printf("%v: inlining call to %v with score %d\n", + ir.Line(n), fn, score) + } else { + fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn) + } } if base.Flag.LowerM > 2 { fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n) diff --git a/src/cmd/compile/internal/inline/inlheur/analyze.go b/src/cmd/compile/internal/inline/inlheur/analyze.go index 93073b9851..a1b6f358e1 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze.go @@ -154,7 +154,7 @@ func revisitInlinability(fn *ir.Func, funcProps *FuncProps, budgetForFunc func(* if fn.Inl == nil { return } - maxAdj := int32(largestScoreAdjustment(fn, funcProps)) + maxAdj := int32(LargestNegativeScoreAdjustment(fn, funcProps)) budget := budgetForFunc(fn) if fn.Inl.Cost+maxAdj > budget { fn.Inl = nil diff --git a/src/cmd/compile/internal/inline/inlheur/scoring.go b/src/cmd/compile/internal/inline/inlheur/scoring.go index efbca79ae3..623ba8adf0 100644 --- a/src/cmd/compile/internal/inline/inlheur/scoring.go +++ b/src/cmd/compile/internal/inline/inlheur/scoring.go @@ -354,7 +354,7 @@ func setupFlagToAdjMaps() { } } -// largestScoreAdjustment tries to estimate the largest possible +// LargestNegativeScoreAdjustment tries to estimate the largest possible // negative score adjustment that could be applied to a call of the // function with the specified props. Example: // @@ -373,7 +373,7 @@ func setupFlagToAdjMaps() { // given call _could_ be rescored down as much as -35 points-- thus if // the size of "bar" is 100 (for example) then there is at least a // chance that scoring will enable inlining. -func largestScoreAdjustment(fn *ir.Func, props *FuncProps) int { +func LargestNegativeScoreAdjustment(fn *ir.Func, props *FuncProps) int { if resultFlagToPositiveAdj == nil { setupFlagToAdjMaps() } @@ -398,6 +398,14 @@ func largestScoreAdjustment(fn *ir.Func, props *FuncProps) int { return score } +// LargestPositiveScoreAdjustment tries to estimate the largest possible +// positive score adjustment that could be applied to a given callsite. +// At the moment we don't have very many positive score adjustments, so +// this is just hard-coded, not table-driven. +func LargestPositiveScoreAdjustment(fn *ir.Func) int { + return adjValues[panicPathAdj] + adjValues[initFuncAdj] +} + // callSiteTab contains entries for each call in the function // currently being processed by InlineCalls; this variable will either // be set to 'cstabCache' below (for non-inlinable routines) or to the