]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/compile/internal/inline/inlheur/analyze.go
cmd/compile/internal/inline/inlheur: remove pkg-level call site table
[gostls13.git] / src / cmd / compile / internal / inline / inlheur / analyze.go
index 8d44b37b6aac075232a788cf0b4959e33f175cd1..9af7e1207d9f1b8a3089529d61b194b869ad247b 100644 (file)
@@ -7,8 +7,10 @@ package inlheur
 import (
        "cmd/compile/internal/base"
        "cmd/compile/internal/ir"
+       "cmd/compile/internal/types"
        "encoding/json"
        "fmt"
+       "internal/goexperiment"
        "io"
        "os"
        "path/filepath"
@@ -22,6 +24,8 @@ const (
        debugTraceResults
        debugTraceParams
        debugTraceExprClassify
+       debugTraceCalls
+       debugTraceScoring
 )
 
 // propAnalyzer interface is used for defining one or more analyzer
@@ -36,38 +40,71 @@ const (
 type propAnalyzer interface {
        nodeVisitPre(n ir.Node)
        nodeVisitPost(n ir.Node)
-       setResults(fp *FuncProps)
+       setResults(funcProps *FuncProps)
 }
 
-// fnInlHeur contains inline heuristics state information about
-// a specific Go function being analyzed/considered by the inliner.
+// fnInlHeur contains inline heuristics state information about a
+// specific Go function being analyzed/considered by the inliner. Note
+// that in addition to constructing a fnInlHeur object by analyzing a
+// specific *ir.Func, there is also code in the test harness
+// (funcprops_test.go) that builds up fnInlHeur's by reading in and
+// parsing a dump. This is the reason why we have file/fname/line
+// fields below instead of just an *ir.Func field.
 type fnInlHeur struct {
-       fname string
-       file  string
-       line  uint
-       props *FuncProps
+       fname           string
+       file            string
+       line            uint
+       inlineMaxBudget int32
+       props           *FuncProps
+       cstab           CallSiteTab
+}
+
+var fpmap = map[*ir.Func]fnInlHeur{}
+
+func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) *FuncProps {
+       if funcInlHeur, ok := fpmap[fn]; ok {
+               return funcInlHeur.props
+       }
+       funcProps, fcstab := computeFuncProps(fn, canInline, inlineMaxBudget)
+       file, line := fnFileLine(fn)
+       entry := fnInlHeur{
+               fname:           fn.Sym().Name,
+               file:            file,
+               line:            line,
+               inlineMaxBudget: inlineMaxBudget,
+               props:           funcProps,
+               cstab:           fcstab,
+       }
+       fn.SetNeverReturns(entry.props.Flags&FuncPropNeverReturns != 0)
+       fpmap[fn] = entry
+       if fn.Inl != nil && fn.Inl.Properties == "" {
+               fn.Inl.Properties = entry.props.SerializeToString()
+       }
+       return funcProps
 }
 
 // computeFuncProps examines the Go function 'fn' and computes for it
 // a function "properties" object, to be used to drive inlining
 // heuristics. See comments on the FuncProps type for more info.
-func computeFuncProps(fn *ir.Func, canInline func(*ir.Func)) *FuncProps {
+func computeFuncProps(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) (*FuncProps, CallSiteTab) {
        enableDebugTraceIfEnv()
        if debugTrace&debugTraceFuncs != 0 {
                fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n",
                        fn.Sym().Name, fn)
        }
-       ra := makeResultsAnalyzer(fn, canInline)
+       ra := makeResultsAnalyzer(fn, canInline, inlineMaxBudget)
        pa := makeParamsAnalyzer(fn)
        ffa := makeFuncFlagsAnalyzer(fn)
        analyzers := []propAnalyzer{ffa, ra, pa}
-       fp := new(FuncProps)
+       funcProps := new(FuncProps)
        runAnalyzersOnFunction(fn, analyzers)
        for _, a := range analyzers {
-               a.setResults(fp)
+               a.setResults(funcProps)
        }
+       // Now build up a partial table of callsites for this func.
+       cstab := computeCallSiteTable(fn, ffa.panicPathTable())
        disableDebugTrace()
-       return fp
+       return funcProps, cstab
 }
 
 func runAnalyzersOnFunction(fn *ir.Func, analyzers []propAnalyzer) {
@@ -85,6 +122,17 @@ func runAnalyzersOnFunction(fn *ir.Func, analyzers []propAnalyzer) {
        doNode(fn)
 }
 
+func propsForFunc(fn *ir.Func) *FuncProps {
+       if funcInlHeur, ok := fpmap[fn]; ok {
+               return funcInlHeur.props
+       } else if fn.Inl != nil && fn.Inl.Properties != "" {
+               // FIXME: considering adding some sort of cache or table
+               // for deserialized properties of imported functions.
+               return DeserializeFromString(fn.Inl.Properties)
+       }
+       return nil
+}
+
 func fnFileLine(fn *ir.Func) (string, uint) {
        p := base.Ctxt.InnermostPos(fn.Pos())
        return filepath.Base(p.Filename()), p.Line()
@@ -95,12 +143,26 @@ func UnitTesting() bool {
 }
 
 // DumpFuncProps computes and caches function properties for the func
-// 'fn', or if fn is nil, writes out the cached set of properties to
-// the file given in 'dumpfile'. Used for the "-d=dumpinlfuncprops=..."
-// command line flag, intended for use primarily in unit testing.
-func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func)) {
+// 'fn' and any closures it contains, or if fn is nil, it writes out the
+// cached set of properties to the file given in 'dumpfile'. Used for
+// the "-d=dumpinlfuncprops=..." command line flag, intended for use
+// primarily in unit testing.
+func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func), inlineMaxBudget int32) {
        if fn != nil {
-               captureFuncDumpEntry(fn, canInline)
+               enableDebugTraceIfEnv()
+               dmp := func(fn *ir.Func) {
+                       if !goexperiment.NewInliner {
+                               ScoreCalls(fn)
+                       }
+                       captureFuncDumpEntry(fn, canInline, inlineMaxBudget)
+               }
+               dmp(fn)
+               ir.Visit(fn, func(n ir.Node) {
+                       if clo, ok := n.(*ir.ClosureExpr); ok {
+                               dmp(clo.Func)
+                       }
+               })
+               disableDebugTrace()
        } else {
                emitDumpToFile(dumpfile)
        }
@@ -111,7 +173,18 @@ func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func)) {
 // definition line, and due to generics we need to account for the
 // possibility that several ir.Func's will have the same def line.
 func emitDumpToFile(dumpfile string) {
-       outf, err := os.OpenFile(dumpfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+       mode := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
+       if dumpfile[0] == '+' {
+               dumpfile = dumpfile[1:]
+               mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
+       }
+       if dumpfile[0] == '%' {
+               dumpfile = dumpfile[1:]
+               d, b := filepath.Dir(dumpfile), filepath.Base(dumpfile)
+               ptag := strings.ReplaceAll(types.LocalPkg.Path, "/", ":")
+               dumpfile = d + "/" + ptag + "." + b
+       }
+       outf, err := os.OpenFile(dumpfile, mode, 0644)
        if err != nil {
                base.Fatalf("opening function props dump file %q: %v\n", dumpfile, err)
        }
@@ -134,20 +207,32 @@ func emitDumpToFile(dumpfile string) {
                }
                prevline = entry.line
                atl := atline[entry.line]
-               if err := dumpFnPreamble(outf, &entry, idx, atl); err != nil {
+               if err := dumpFnPreamble(outf, &entry, nil, idx, atl); err != nil {
                        base.Fatalf("function props dump: %v\n", err)
                }
        }
        dumpBuffer = nil
 }
 
-// captureFuncDumpEntry analyzes function 'fn' and adds a entry
-// for it to 'dumpBuffer'. Used for unit testing.
-func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func)) {
+// captureFuncDumpEntry grabs the function properties object for 'fn'
+// and enqueues it for later dumping. Used for the
+// "-d=dumpinlfuncprops=..." command line flag, intended for use
+// primarily in unit testing.
+func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func), inlineMaxBudget int32) {
        // avoid capturing compiler-generated equality funcs.
        if strings.HasPrefix(fn.Sym().Name, ".eq.") {
                return
        }
+       funcInlHeur, ok := fpmap[fn]
+       // Props object should already be present, unless this is a
+       // directly recursive routine.
+       if !ok {
+               AnalyzeFunc(fn, canInline, inlineMaxBudget)
+               funcInlHeur = fpmap[fn]
+               if fn.Inl != nil && fn.Inl.Properties == "" {
+                       fn.Inl.Properties = funcInlHeur.props.SerializeToString()
+               }
+       }
        if dumpBuffer == nil {
                dumpBuffer = make(map[*ir.Func]fnInlHeur)
        }
@@ -156,15 +241,10 @@ func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func)) {
                // so don't add them more than once.
                return
        }
-       fp := computeFuncProps(fn, canInline)
-       file, line := fnFileLine(fn)
-       entry := fnInlHeur{
-               fname: fn.Sym().Name,
-               file:  file,
-               line:  line,
-               props: fp,
+       if debugTrace&debugTraceFuncs != 0 {
+               fmt.Fprintf(os.Stderr, "=-= capturing dump for %v:\n", fn)
        }
-       dumpBuffer[fn] = entry
+       dumpBuffer[fn] = funcInlHeur
 }
 
 // dumpFilePreamble writes out a file-level preamble for a given
@@ -176,20 +256,22 @@ func dumpFilePreamble(w io.Writer) {
        fmt.Fprintf(w, "// %s\n", preambleDelimiter)
 }
 
-// dumpFilePreamble writes out a function-level preamble for a given
+// dumpFnPreamble writes out a function-level preamble for a given
 // Go function as part of a function properties dump. See the
 // README.txt file in testdata/props for more on the format of
 // this preamble.
-func dumpFnPreamble(w io.Writer, fih *fnInlHeur, idx, atl uint) error {
+func dumpFnPreamble(w io.Writer, funcInlHeur *fnInlHeur, ecst encodedCallSiteTab, idx, atl uint) error {
        fmt.Fprintf(w, "// %s %s %d %d %d\n",
-               fih.file, fih.fname, fih.line, idx, atl)
+               funcInlHeur.file, funcInlHeur.fname, funcInlHeur.line, idx, atl)
        // emit props as comments, followed by delimiter
-       fmt.Fprintf(w, "%s// %s\n", fih.props.ToString("// "), comDelimiter)
-       data, err := json.Marshal(fih.props)
+       fmt.Fprintf(w, "%s// %s\n", funcInlHeur.props.ToString("// "), comDelimiter)
+       data, err := json.Marshal(funcInlHeur.props)
        if err != nil {
                return fmt.Errorf("marshall error %v\n", err)
        }
-       fmt.Fprintf(w, "// %s\n// %s\n", string(data), fnDelimiter)
+       fmt.Fprintf(w, "// %s\n", string(data))
+       dumpCallSiteComments(w, funcInlHeur.cstab, ecst)
+       fmt.Fprintf(w, "// %s\n", fnDelimiter)
        return nil
 }
 
@@ -210,6 +292,7 @@ func sortFnInlHeurSlice(sl []fnInlHeur) []fnInlHeur {
 const preambleDelimiter = "<endfilepreamble>"
 const fnDelimiter = "<endfuncpreamble>"
 const comDelimiter = "<endpropsdump>"
+const csDelimiter = "<endcallsites>"
 
 // dumpBuffer stores up function properties dumps when
 // "-d=dumpinlfuncprops=..." is in effect.