mirror of
https://github.com/golang/go
synced 2024-11-18 08:14:41 -07:00
cmd/compile: refactor inlining parameters; inline panic
Inlining was refactored to perform tuning experiments, with the "knobs" now set to also inline functions/methods that include panic(), and -l=4 (inline calls) now expressed as a change to costs, rather than scattered if-thens. The -l=4 inline-calls penalty is chosen to be the best found during experiments; it makes some programs much larger and slower (notably, the compiler itself) and is believed to be risky for machine-generated code in general, which is why it is not the default. It is also not well-tested with the debugger and DWARF output. This change includes an explicit go:noinline applied to the method that is the largest cause of compiler binary growth and slowdown for midstack inlining; there are others, ideally whatever heuristic eventually appears will make this unnecessary. Change-Id: Idf7056ed2f961472cf49d2fd154ee98bef9421e2 Reviewed-on: https://go-review.googlesource.com/109918 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
7d61ad25f8
commit
87a18c6109
@ -11,7 +11,7 @@
|
||||
// making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
|
||||
// are not supported.
|
||||
// 0: disabled
|
||||
// 1: 80-nodes leaf functions, oneliners, lazy typechecking (default)
|
||||
// 1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
|
||||
// 2: (unassigned)
|
||||
// 3: (unassigned)
|
||||
// 4: allow non-leaf functions
|
||||
@ -34,6 +34,15 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Inlining budget parameters, gathered in one place
|
||||
const (
|
||||
inlineMaxBudget = 80
|
||||
inlineExtraAppendCost = 0
|
||||
inlineExtraCallCost = inlineMaxBudget // default is do not inline, -l=4 enables by using 1 instead.
|
||||
inlineExtraPanicCost = 1 // do not penalize inlining panics.
|
||||
inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
|
||||
)
|
||||
|
||||
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
|
||||
// the ->sym can be re-used in the local package, so peel it off the receiver's type.
|
||||
func fnpkg(fn *Node) *types.Pkg {
|
||||
@ -155,19 +164,23 @@ func caninl(fn *Node) {
|
||||
}
|
||||
defer n.Func.SetInlinabilityChecked(true)
|
||||
|
||||
const maxBudget = 80
|
||||
visitor := hairyVisitor{budget: maxBudget}
|
||||
cc := int32(inlineExtraCallCost)
|
||||
if Debug['l'] == 4 {
|
||||
cc = 1 // this appears to yield better performance than 0.
|
||||
}
|
||||
|
||||
visitor := hairyVisitor{budget: inlineMaxBudget, extraCallCost: cc}
|
||||
if visitor.visitList(fn.Nbody) {
|
||||
reason = visitor.reason
|
||||
return
|
||||
}
|
||||
if visitor.budget < 0 {
|
||||
reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", maxBudget-visitor.budget, maxBudget)
|
||||
reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-visitor.budget, inlineMaxBudget)
|
||||
return
|
||||
}
|
||||
|
||||
n.Func.Inl = &Inline{
|
||||
Cost: maxBudget - visitor.budget,
|
||||
Cost: inlineMaxBudget - visitor.budget,
|
||||
Dcl: inlcopylist(n.Name.Defn.Func.Dcl),
|
||||
Body: inlcopylist(fn.Nbody.Slice()),
|
||||
}
|
||||
@ -229,8 +242,9 @@ func inlFlood(n *Node) {
|
||||
// hairyVisitor visits a function body to determine its inlining
|
||||
// hairiness and whether or not it can be inlined.
|
||||
type hairyVisitor struct {
|
||||
budget int32
|
||||
reason string
|
||||
budget int32
|
||||
reason string
|
||||
extraCallCost int32
|
||||
}
|
||||
|
||||
// Look for anything we want to punt on.
|
||||
@ -257,12 +271,18 @@ func (v *hairyVisitor) visit(n *Node) bool {
|
||||
}
|
||||
// Functions that call runtime.getcaller{pc,sp} can not be inlined
|
||||
// because getcaller{pc,sp} expect a pointer to the caller's first argument.
|
||||
//
|
||||
// runtime.throw is a "cheap call" like panic in normal code.
|
||||
if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) {
|
||||
fn := n.Left.Sym.Name
|
||||
if fn == "getcallerpc" || fn == "getcallersp" {
|
||||
v.reason = "call to " + fn
|
||||
return true
|
||||
}
|
||||
if fn == "throw" {
|
||||
v.budget -= inlineExtraThrowCost
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if fn := n.Left.Func; fn != nil && fn.Inl != nil {
|
||||
@ -277,10 +297,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
|
||||
}
|
||||
// TODO(mdempsky): Budget for OCLOSURE calls if we
|
||||
// ever allow that. See #15561 and #23093.
|
||||
if Debug['l'] < 4 {
|
||||
v.reason = "non-leaf function"
|
||||
return true
|
||||
}
|
||||
|
||||
// Call cost for non-leaf inlining.
|
||||
v.budget -= v.extraCallCost
|
||||
|
||||
// Call is okay if inlinable and we have the budget for the body.
|
||||
case OCALLMETH:
|
||||
@ -310,17 +329,16 @@ func (v *hairyVisitor) visit(n *Node) bool {
|
||||
v.budget -= inlfn.Inl.Cost
|
||||
break
|
||||
}
|
||||
if Debug['l'] < 4 {
|
||||
v.reason = "non-leaf method"
|
||||
return true
|
||||
}
|
||||
// Call cost for non-leaf inlining.
|
||||
v.budget -= v.extraCallCost
|
||||
|
||||
// Things that are too hairy, irrespective of the budget
|
||||
case OCALL, OCALLINTER, OPANIC:
|
||||
if Debug['l'] < 4 {
|
||||
v.reason = "non-leaf op " + n.Op.String()
|
||||
return true
|
||||
}
|
||||
case OCALL, OCALLINTER:
|
||||
// Call cost for non-leaf inlining.
|
||||
v.budget -= v.extraCallCost
|
||||
|
||||
case OPANIC:
|
||||
v.budget -= v.extraCallCost
|
||||
|
||||
case ORECOVER:
|
||||
// recover matches the argument frame pointer to find
|
||||
@ -343,6 +361,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
|
||||
v.reason = "unhandled op " + n.Op.String()
|
||||
return true
|
||||
|
||||
case OAPPEND:
|
||||
v.budget -= inlineExtraAppendCost
|
||||
|
||||
case ODCLCONST, OEMPTY, OFALL, OLABEL:
|
||||
// These nodes don't produce code; omit from inlining budget.
|
||||
return false
|
||||
|
@ -200,6 +200,9 @@ func (v *Value) auxString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If/when midstack inlining is enabled (-l=4), the compiler gets both larger and slower.
|
||||
// Not-inlining this method is a help (*Value.reset and *Block.NewValue0 are similar).
|
||||
//go:noinline
|
||||
func (v *Value) AddArg(w *Value) {
|
||||
if v.Args == nil {
|
||||
v.resetArgs() // use argstorage
|
||||
|
Loading…
Reference in New Issue
Block a user