1
0
mirror of https://github.com/golang/go synced 2024-11-26 04:07:59 -07:00

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 <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Than McIntosh 2023-11-07 10:28:56 -05:00
parent 2e77b51df5
commit cfb281754e
3 changed files with 55 additions and 30 deletions

View File

@ -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)

View File

@ -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

View File

@ -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