]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/inline/inlheur/analyze.go
cmd/compile/internal/inline: analyze function param properties
[gostls13.git] / src / cmd / compile / internal / inline / inlheur / analyze.go
1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package inlheur
6
7 import (
8         "cmd/compile/internal/base"
9         "cmd/compile/internal/ir"
10         "encoding/json"
11         "fmt"
12         "io"
13         "os"
14         "path/filepath"
15         "sort"
16         "strings"
17 )
18
19 const (
20         debugTraceFuncs = 1 << iota
21         debugTraceFuncFlags
22         debugTraceResults
23         debugTraceParams
24         debugTraceExprClassify
25 )
26
27 // propAnalyzer interface is used for defining one or more analyzer
28 // helper objects, each tasked with computing some specific subset of
29 // the properties we're interested in. The assumption is that
30 // properties are independent, so each new analyzer that implements
31 // this interface can operate entirely on its own. For a given analyzer
32 // there will be a sequence of calls to nodeVisitPre and nodeVisitPost
33 // as the nodes within a function are visited, then a followup call to
34 // setResults so that the analyzer can transfer its results into the
35 // final properties object.
36 type propAnalyzer interface {
37         nodeVisitPre(n ir.Node)
38         nodeVisitPost(n ir.Node)
39         setResults(fp *FuncProps)
40 }
41
42 // fnInlHeur contains inline heuristics state information about
43 // a specific Go function being analyzed/considered by the inliner.
44 type fnInlHeur struct {
45         fname string
46         file  string
47         line  uint
48         props *FuncProps
49 }
50
51 // computeFuncProps examines the Go function 'fn' and computes for it
52 // a function "properties" object, to be used to drive inlining
53 // heuristics. See comments on the FuncProps type for more info.
54 func computeFuncProps(fn *ir.Func, canInline func(*ir.Func)) *FuncProps {
55         enableDebugTraceIfEnv()
56         if debugTrace&debugTraceFuncs != 0 {
57                 fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n",
58                         fn.Sym().Name, fn)
59         }
60         ra := makeResultsAnalyzer(fn, canInline)
61         pa := makeParamsAnalyzer(fn)
62         ffa := makeFuncFlagsAnalyzer(fn)
63         analyzers := []propAnalyzer{ffa, ra, pa}
64         fp := new(FuncProps)
65         runAnalyzersOnFunction(fn, analyzers)
66         for _, a := range analyzers {
67                 a.setResults(fp)
68         }
69         disableDebugTrace()
70         return fp
71 }
72
73 func runAnalyzersOnFunction(fn *ir.Func, analyzers []propAnalyzer) {
74         var doNode func(ir.Node) bool
75         doNode = func(n ir.Node) bool {
76                 for _, a := range analyzers {
77                         a.nodeVisitPre(n)
78                 }
79                 ir.DoChildren(n, doNode)
80                 for _, a := range analyzers {
81                         a.nodeVisitPost(n)
82                 }
83                 return false
84         }
85         doNode(fn)
86 }
87
88 func fnFileLine(fn *ir.Func) (string, uint) {
89         p := base.Ctxt.InnermostPos(fn.Pos())
90         return filepath.Base(p.Filename()), p.Line()
91 }
92
93 func UnitTesting() bool {
94         return base.Debug.DumpInlFuncProps != ""
95 }
96
97 // DumpFuncProps computes and caches function properties for the func
98 // 'fn', or if fn is nil, writes out the cached set of properties to
99 // the file given in 'dumpfile'. Used for the "-d=dumpinlfuncprops=..."
100 // command line flag, intended for use primarily in unit testing.
101 func DumpFuncProps(fn *ir.Func, dumpfile string, canInline func(*ir.Func)) {
102         if fn != nil {
103                 captureFuncDumpEntry(fn, canInline)
104         } else {
105                 emitDumpToFile(dumpfile)
106         }
107 }
108
109 // emitDumpToFile writes out the buffer function property dump entries
110 // to a file, for unit testing. Dump entries need to be sorted by
111 // definition line, and due to generics we need to account for the
112 // possibility that several ir.Func's will have the same def line.
113 func emitDumpToFile(dumpfile string) {
114         outf, err := os.OpenFile(dumpfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
115         if err != nil {
116                 base.Fatalf("opening function props dump file %q: %v\n", dumpfile, err)
117         }
118         defer outf.Close()
119         dumpFilePreamble(outf)
120
121         atline := map[uint]uint{}
122         sl := make([]fnInlHeur, 0, len(dumpBuffer))
123         for _, e := range dumpBuffer {
124                 sl = append(sl, e)
125                 atline[e.line] = atline[e.line] + 1
126         }
127         sl = sortFnInlHeurSlice(sl)
128
129         prevline := uint(0)
130         for _, entry := range sl {
131                 idx := uint(0)
132                 if prevline == entry.line {
133                         idx++
134                 }
135                 prevline = entry.line
136                 atl := atline[entry.line]
137                 if err := dumpFnPreamble(outf, &entry, idx, atl); err != nil {
138                         base.Fatalf("function props dump: %v\n", err)
139                 }
140         }
141         dumpBuffer = nil
142 }
143
144 // captureFuncDumpEntry analyzes function 'fn' and adds a entry
145 // for it to 'dumpBuffer'. Used for unit testing.
146 func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func)) {
147         // avoid capturing compiler-generated equality funcs.
148         if strings.HasPrefix(fn.Sym().Name, ".eq.") {
149                 return
150         }
151         if dumpBuffer == nil {
152                 dumpBuffer = make(map[*ir.Func]fnInlHeur)
153         }
154         if _, ok := dumpBuffer[fn]; ok {
155                 // we can wind up seeing closures multiple times here,
156                 // so don't add them more than once.
157                 return
158         }
159         fp := computeFuncProps(fn, canInline)
160         file, line := fnFileLine(fn)
161         entry := fnInlHeur{
162                 fname: fn.Sym().Name,
163                 file:  file,
164                 line:  line,
165                 props: fp,
166         }
167         dumpBuffer[fn] = entry
168 }
169
170 // dumpFilePreamble writes out a file-level preamble for a given
171 // Go function as part of a function properties dump.
172 func dumpFilePreamble(w io.Writer) {
173         fmt.Fprintf(w, "// DO NOT EDIT (use 'go test -v -update-expected' instead.)\n")
174         fmt.Fprintf(w, "// See cmd/compile/internal/inline/inlheur/testdata/props/README.txt\n")
175         fmt.Fprintf(w, "// for more information on the format of this file.\n")
176         fmt.Fprintf(w, "// %s\n", preambleDelimiter)
177 }
178
179 // dumpFilePreamble writes out a function-level preamble for a given
180 // Go function as part of a function properties dump. See the
181 // README.txt file in testdata/props for more on the format of
182 // this preamble.
183 func dumpFnPreamble(w io.Writer, fih *fnInlHeur, idx, atl uint) error {
184         fmt.Fprintf(w, "// %s %s %d %d %d\n",
185                 fih.file, fih.fname, fih.line, idx, atl)
186         // emit props as comments, followed by delimiter
187         fmt.Fprintf(w, "%s// %s\n", fih.props.ToString("// "), comDelimiter)
188         data, err := json.Marshal(fih.props)
189         if err != nil {
190                 return fmt.Errorf("marshall error %v\n", err)
191         }
192         fmt.Fprintf(w, "// %s\n// %s\n", string(data), fnDelimiter)
193         return nil
194 }
195
196 // sortFnInlHeurSlice sorts a slice of fnInlHeur based on
197 // the starting line of the function definition, then by name.
198 func sortFnInlHeurSlice(sl []fnInlHeur) []fnInlHeur {
199         sort.SliceStable(sl, func(i, j int) bool {
200                 if sl[i].line != sl[j].line {
201                         return sl[i].line < sl[j].line
202                 }
203                 return sl[i].fname < sl[j].fname
204         })
205         return sl
206 }
207
208 // delimiters written to various preambles to make parsing of
209 // dumps easier.
210 const preambleDelimiter = "<endfilepreamble>"
211 const fnDelimiter = "<endfuncpreamble>"
212 const comDelimiter = "<endpropsdump>"
213
214 // dumpBuffer stores up function properties dumps when
215 // "-d=dumpinlfuncprops=..." is in effect.
216 var dumpBuffer map[*ir.Func]fnInlHeur