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