]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: refactor inlining parameters; inline panic
authorDavid Chase <drchase@google.com>
Fri, 27 Apr 2018 16:13:17 +0000 (12:13 -0400)
committerDavid Chase <drchase@google.com>
Tue, 22 May 2018 02:58:04 +0000 (02:58 +0000)
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>
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/ssa/value.go

index 4029955e4d3f367079a1b693b8bb791a02efc853..9001cd2e0d6731b85552aacb4ace0ea4ec72ce92 100644 (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
index 6d5fe9caedcfba97e8387322192c56acb1d8d32f..ca7cd6bc1077893d1bea6c4961eaffc95021ecf2 100644 (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