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