1
0
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:
David Chase 2018-04-27 12:13:17 -04:00
parent 7d61ad25f8
commit 87a18c6109
2 changed files with 44 additions and 20 deletions

View File

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

View File

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