]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/inline: debug flag to alter score adjustments
authorThan McIntosh <thanm@google.com>
Thu, 28 Sep 2023 18:07:29 +0000 (14:07 -0400)
committerThan McIntosh <thanm@google.com>
Thu, 16 Nov 2023 20:07:32 +0000 (20:07 +0000)
Add a debugging flag "-d=inlscoreadj" intended to support running
experiments in which the inliner uses different score adjustment
values for specific heuristics. The flag argument is a series of
clauses separated by the "/" char where each clause takes the form
"adjK:valK". For example, in this build

  go build -gcflags=-d=inlscoreadj=inLoopAdj:10/returnFeedsConstToIfAdj:-99

the "in loop" score adjustments would be reset to a value of 15 (effectively
penalizing calls in loops) adn the "return feeds constant to foldable if/switch"
score adjustment would be boosted from -15 to -99.

Change-Id: Ibd1ee334684af5992466556a69baa6dfefb246b3
Reviewed-on: https://go-review.googlesource.com/c/go/+/532116
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/base/debug.go
src/cmd/compile/internal/inline/inl.go
src/cmd/compile/internal/inline/inlheur/debugflags_test.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/scoring.go

index b9b7d5d565ce0f77b1096fe172e908cb4a4069c7..9d0dc3f4a692f42c22f8c7d4a953a177bfe6d427 100644 (file)
@@ -23,6 +23,8 @@ type DebugFlags struct {
        DisableNil            int    `help:"disable nil checks" concurrent:"ok"`
        DumpInlFuncProps      string `help:"dump function properties from inl heuristics to specified file"`
        DumpInlCallSiteScores int    `help:"dump scored callsites during inlining"`
+       InlScoreAdj           string `help:"set inliner score adjustments (ex: -d=inlscoreadj=panicPathAdj:10/passConstToNestedIfAdj:-90)"`
+       InlBudgetSlack        int    `help:"amount to expand the initial inline budget when new inliner enabled. Defaults to 80 if option not set." concurrent:"ok"`
        DumpPtrs              int    `help:"show Node pointers values in dump output"`
        DwarfInl              int    `help:"print information about DWARF inlined function creation"`
        EscapeMutationsCalls  int    `help:"print extra escape analysis diagnostics about mutations and calls" concurrent:"ok"`
index 4009b776ea6495ec8704b4a0dca570bd9d227654..e031b87dfacfdfb49e6133f8f0e39468004a060f 100644 (file)
@@ -141,6 +141,8 @@ func InlinePackage(p *pgo.Profile) {
                p = nil
        }
 
+       inlheur.SetupScoreAdjustments()
+
        InlineDecls(p, typecheck.Target.Funcs, true)
 
        // Perform a garbage collection of hidden closures functions that
@@ -268,7 +270,7 @@ func inlineBudget(fn *ir.Func, profile *pgo.Profile, relaxed bool, verbose bool)
                }
        }
        if relaxed {
-               budget += inlineMaxBudget
+               budget += inlheur.BudgetExpansion(inlineMaxBudget)
        }
        return budget
 }
diff --git a/src/cmd/compile/internal/inline/inlheur/debugflags_test.go b/src/cmd/compile/internal/inline/inlheur/debugflags_test.go
new file mode 100644 (file)
index 0000000..abf4910
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package inlheur
+
+import (
+       "testing"
+)
+
+func TestInlScoreAdjFlagParse(t *testing.T) {
+       scenarios := []struct {
+               value string
+               expok bool
+       }{
+               {
+                       value: "returnFeedsConcreteToInterfaceCallAdj:9",
+                       expok: true,
+               },
+               {
+                       value: "panicPathAdj:-1/initFuncAdj:9",
+                       expok: true,
+               },
+               {
+                       value: "",
+                       expok: false,
+               },
+               {
+                       value: "nonsenseAdj:10",
+                       expok: false,
+               },
+               {
+                       value: "inLoopAdj:",
+                       expok: false,
+               },
+               {
+                       value: "inLoopAdj:10:10",
+                       expok: false,
+               },
+               {
+                       value: "inLoopAdj:blah",
+                       expok: false,
+               },
+               {
+                       value: "/",
+                       expok: false,
+               },
+       }
+
+       for _, scenario := range scenarios {
+               err := parseScoreAdj(scenario.value)
+               t.Logf("for value=%q err is %v\n", scenario.value, err)
+               if scenario.expok {
+                       if err != nil {
+                               t.Errorf("expected parseScoreAdj(%s) ok, got err %v",
+                                       scenario.value, err)
+                       }
+               } else {
+                       if err == nil {
+                               t.Errorf("expected parseScoreAdj(%s) failure, got success",
+                                       scenario.value)
+                       }
+               }
+       }
+}
index 47f14a876a64c9ba83f008c4cc40e9ae9b43f838..9c29952edc14aa14215455a4a9cdc2a39a5808f9 100644 (file)
@@ -13,6 +13,8 @@ import (
        "fmt"
        "os"
        "sort"
+       "strconv"
+       "strings"
 )
 
 // These constants enumerate the set of possible ways/scenarios
@@ -62,6 +64,8 @@ const (
        returnFeedsFuncToIndCallAdj
        returnFeedsInlinableFuncToIndCallAdj
        returnFeedsConcreteToInterfaceCallAdj
+
+       sentinelScoreAdj // sentinel; not a real adjustment
 )
 
 // This table records the specific values we use to adjust call
@@ -88,6 +92,56 @@ var adjValues = map[scoreAdjustTyp]int{
        returnFeedsConcreteToInterfaceCallAdj: -25,
 }
 
+// SetupScoreAdjustments interprets the value of the -d=inlscoreadj
+// debugging option, if set. The value of this flag is expected to be
+// a series of "/"-separated clauses of the form adj1:value1. Example:
+// -d=inlscoreadj=inLoopAdj=0/passConstToIfAdj=-99
+func SetupScoreAdjustments() {
+       if base.Debug.InlScoreAdj == "" {
+               return
+       }
+       if err := parseScoreAdj(base.Debug.InlScoreAdj); err != nil {
+               base.Fatalf("malformed -d=inlscoreadj argument %q: %v",
+                       base.Debug.InlScoreAdj, err)
+       }
+}
+
+func adjStringToVal(s string) (scoreAdjustTyp, bool) {
+       for adj := scoreAdjustTyp(1); adj < sentinelScoreAdj; adj <<= 1 {
+               if adj.String() == s {
+                       return adj, true
+               }
+       }
+       return 0, false
+}
+
+func parseScoreAdj(val string) error {
+       clauses := strings.Split(val, "/")
+       if len(clauses) == 0 {
+               return fmt.Errorf("no clauses")
+       }
+       for _, clause := range clauses {
+               elems := strings.Split(clause, ":")
+               if len(elems) < 2 {
+                       return fmt.Errorf("clause %q: expected colon", clause)
+               }
+               if len(elems) != 2 {
+                       return fmt.Errorf("clause %q has %d elements, wanted 2", clause,
+                               len(elems))
+               }
+               adj, ok := adjStringToVal(elems[0])
+               if !ok {
+                       return fmt.Errorf("clause %q: unknown adjustment", clause)
+               }
+               val, err := strconv.Atoi(elems[1])
+               if err != nil {
+                       return fmt.Errorf("clause %q: malformed value: %v", clause, err)
+               }
+               adjValues[adj] = val
+       }
+       return nil
+}
+
 func adjValue(x scoreAdjustTyp) int {
        if val, ok := adjValues[x]; ok {
                return val
@@ -507,6 +561,27 @@ func GetCallSiteScore(fn *ir.Func, call *ir.CallExpr) (int, bool) {
        return 0, false
 }
 
+// BudgetExpansion returns the amount to relax/expand the base
+// inlining budget when the new inliner is turned on; the inliner
+// will add the returned value to the hairyness budget.
+//
+// Background: with the new inliner, the score for a given callsite
+// can be adjusted down by some amount due to heuristics, however we
+// won't know whether this is going to happen until much later after
+// the CanInline call. This function returns the amount to relax the
+// budget initially (to allow for a large score adjustment); later on
+// in RevisitInlinability we'll look at each individual function to
+// demote it if needed.
+func BudgetExpansion(maxBudget int32) int32 {
+       if base.Debug.InlBudgetSlack != 0 {
+               return int32(base.Debug.InlBudgetSlack)
+       }
+       // In the default case, return maxBudget, which will effectively
+       // double the budget from 80 to 160; this should be good enough
+       // for most cases.
+       return maxBudget
+}
+
 var allCallSites CallSiteTab
 
 // DumpInlCallSiteScores is invoked by the inliner if the debug flag