import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
"encoding/json"
"fmt"
+ "internal/goexperiment"
"io"
"os"
"path/filepath"
debugTraceResults
debugTraceParams
debugTraceExprClassify
+ debugTraceCalls
+ debugTraceScoring
)
// propAnalyzer interface is used for defining one or more analyzer
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) {
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()
}
// 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)
}
// 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)
}
}
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)
}
// 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
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
}
const preambleDelimiter = "<endfilepreamble>"
const fnDelimiter = "<endfuncpreamble>"
const comDelimiter = "<endpropsdump>"
+const csDelimiter = "<endcallsites>"
// dumpBuffer stores up function properties dumps when
// "-d=dumpinlfuncprops=..." is in effect.