From: David Chase Date: Fri, 27 Apr 2018 16:13:17 +0000 (-0400) Subject: cmd/compile: refactor inlining parameters; inline panic X-Git-Tag: go1.11beta1~379 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=87a18c61094debb31ebf4d1b80067bae302dacbe;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 4029955e4d..9001cd2e0d 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 6d5fe9caed..ca7cd6bc10 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -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