]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/inl_test.go
reflect: make Value.MapRange inlineable
[gostls13.git] / src / cmd / compile / internal / test / inl_test.go
1 // Copyright 2017 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 test
6
7 import (
8         "bufio"
9         "internal/testenv"
10         "io"
11         "math/bits"
12         "os/exec"
13         "regexp"
14         "runtime"
15         "strings"
16         "testing"
17 )
18
19 // TestIntendedInlining tests that specific functions are inlined.
20 // This allows refactoring for code clarity and re-use without fear that
21 // changes to the compiler will cause silent performance regressions.
22 func TestIntendedInlining(t *testing.T) {
23         if testing.Short() && testenv.Builder() == "" {
24                 t.Skip("skipping in short mode")
25         }
26         testenv.MustHaveGoRun(t)
27         t.Parallel()
28
29         // want is the list of function names (by package) that should
30         // be inlinable. If they have no callers in their packages, they
31         // might not actually be inlined anywhere.
32         want := map[string][]string{
33                 "runtime": {
34                         "add",
35                         "acquirem",
36                         "add1",
37                         "addb",
38                         "adjustpanics",
39                         "adjustpointer",
40                         "alignDown",
41                         "alignUp",
42                         "bucketMask",
43                         "bucketShift",
44                         "chanbuf",
45                         "evacuated",
46                         "fastlog2",
47                         "fastrand",
48                         "float64bits",
49                         "funcspdelta",
50                         "getArgInfoFast",
51                         "getm",
52                         "getMCache",
53                         "isDirectIface",
54                         "itabHashFunc",
55                         "noescape",
56                         "pcvalueCacheKey",
57                         "readUnaligned32",
58                         "readUnaligned64",
59                         "releasem",
60                         "roundupsize",
61                         "stackmapdata",
62                         "stringStructOf",
63                         "subtract1",
64                         "subtractb",
65                         "tophash",
66                         "(*bmap).keys",
67                         "(*bmap).overflow",
68                         "(*waitq).enqueue",
69                         "funcInfo.entry",
70
71                         // GC-related ones
72                         "cgoInRange",
73                         "gclinkptr.ptr",
74                         "guintptr.ptr",
75                         "heapBits.bits",
76                         "heapBits.isPointer",
77                         "heapBits.morePointers",
78                         "heapBits.next",
79                         "heapBitsForAddr",
80                         "markBits.isMarked",
81                         "muintptr.ptr",
82                         "puintptr.ptr",
83                         "spanOf",
84                         "spanOfUnchecked",
85                         "(*gcWork).putFast",
86                         "(*gcWork).tryGetFast",
87                         "(*guintptr).set",
88                         "(*markBits).advance",
89                         "(*mspan).allocBitsForIndex",
90                         "(*mspan).base",
91                         "(*mspan).markBitsForBase",
92                         "(*mspan).markBitsForIndex",
93                         "(*muintptr).set",
94                         "(*puintptr).set",
95                 },
96                 "runtime/internal/sys": {},
97                 "runtime/internal/math": {
98                         "MulUintptr",
99                 },
100                 "bytes": {
101                         "(*Buffer).Bytes",
102                         "(*Buffer).Cap",
103                         "(*Buffer).Len",
104                         "(*Buffer).Grow",
105                         "(*Buffer).Next",
106                         "(*Buffer).Read",
107                         "(*Buffer).ReadByte",
108                         "(*Buffer).Reset",
109                         "(*Buffer).String",
110                         "(*Buffer).UnreadByte",
111                         "(*Buffer).tryGrowByReslice",
112                 },
113                 "compress/flate": {
114                         "byLiteral.Len",
115                         "byLiteral.Less",
116                         "byLiteral.Swap",
117                         "(*dictDecoder).tryWriteCopy",
118                 },
119                 "encoding/base64": {
120                         "assemble32",
121                         "assemble64",
122                 },
123                 "unicode/utf8": {
124                         "FullRune",
125                         "FullRuneInString",
126                         "RuneLen",
127                         "AppendRune",
128                         "ValidRune",
129                 },
130                 "reflect": {
131                         "Value.CanInt",
132                         "Value.CanUint",
133                         "Value.CanFloat",
134                         "Value.CanComplex",
135                         "Value.CanAddr",
136                         "Value.CanSet",
137                         "Value.CanInterface",
138                         "Value.IsValid",
139                         "Value.MapRange",
140                         "Value.pointer",
141                         "add",
142                         "align",
143                         "flag.mustBe",
144                         "flag.mustBeAssignable",
145                         "flag.mustBeExported",
146                         "flag.kind",
147                         "flag.ro",
148                 },
149                 "regexp": {
150                         "(*bitState).push",
151                 },
152                 "math/big": {
153                         "bigEndianWord",
154                         // The following functions require the math_big_pure_go build tag.
155                         "addVW",
156                         "subVW",
157                 },
158                 "math/rand": {
159                         "(*rngSource).Int63",
160                         "(*rngSource).Uint64",
161                 },
162                 "net": {
163                         "(*UDPConn).ReadFromUDP",
164                 },
165         }
166
167         if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
168                 // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable.
169                 // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386.
170                 // On mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive
171                 // to inline (Issue 22239).
172                 want["runtime"] = append(want["runtime"], "nextFreeFast")
173         }
174         if runtime.GOARCH != "386" {
175                 // As explained above, Ctz64 and Ctz32 are not Go code on 386.
176                 // The same applies to Bswap32.
177                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64")
178                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32")
179                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32")
180         }
181         if bits.UintSize == 64 {
182                 // mix is only defined on 64-bit architectures
183                 want["runtime"] = append(want["runtime"], "mix")
184         }
185
186         switch runtime.GOARCH {
187         case "386", "wasm", "arm":
188         default:
189                 // TODO(mvdan): As explained in /test/inline_sync.go, some
190                 // architectures don't have atomic intrinsics, so these go over
191                 // the inlining budget. Move back to the main table once that
192                 // problem is solved.
193                 want["sync"] = []string{
194                         "(*Mutex).Lock",
195                         "(*Mutex).Unlock",
196                         "(*RWMutex).RLock",
197                         "(*RWMutex).RUnlock",
198                         "(*Once).Do",
199                 }
200         }
201
202         // Functions that must actually be inlined; they must have actual callers.
203         must := map[string]bool{
204                 "compress/flate.byLiteral.Len":  true,
205                 "compress/flate.byLiteral.Less": true,
206                 "compress/flate.byLiteral.Swap": true,
207         }
208
209         notInlinedReason := make(map[string]string)
210         pkgs := make([]string, 0, len(want))
211         for pname, fnames := range want {
212                 pkgs = append(pkgs, pname)
213                 for _, fname := range fnames {
214                         fullName := pname + "." + fname
215                         if _, ok := notInlinedReason[fullName]; ok {
216                                 t.Errorf("duplicate func: %s", fullName)
217                         }
218                         notInlinedReason[fullName] = "unknown reason"
219                 }
220         }
221
222         args := append([]string{"build", "-a", "-gcflags=all=-m -m", "-tags=math_big_pure_go"}, pkgs...)
223         cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...))
224         pr, pw := io.Pipe()
225         cmd.Stdout = pw
226         cmd.Stderr = pw
227         cmdErr := make(chan error, 1)
228         go func() {
229                 cmdErr <- cmd.Run()
230                 pw.Close()
231         }()
232         scanner := bufio.NewScanner(pr)
233         curPkg := ""
234         canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
235         haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
236         cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
237         for scanner.Scan() {
238                 line := scanner.Text()
239                 if strings.HasPrefix(line, "# ") {
240                         curPkg = line[2:]
241                         continue
242                 }
243                 if m := haveInlined.FindStringSubmatch(line); m != nil {
244                         fname := m[1]
245                         delete(notInlinedReason, curPkg+"."+fname)
246                         continue
247                 }
248                 if m := canInline.FindStringSubmatch(line); m != nil {
249                         fname := m[1]
250                         fullname := curPkg + "." + fname
251                         // If function must be inlined somewhere, being inlinable is not enough
252                         if _, ok := must[fullname]; !ok {
253                                 delete(notInlinedReason, fullname)
254                                 continue
255                         }
256                 }
257                 if m := cannotInline.FindStringSubmatch(line); m != nil {
258                         fname, reason := m[1], m[2]
259                         fullName := curPkg + "." + fname
260                         if _, ok := notInlinedReason[fullName]; ok {
261                                 // cmd/compile gave us a reason why
262                                 notInlinedReason[fullName] = reason
263                         }
264                         continue
265                 }
266         }
267         if err := <-cmdErr; err != nil {
268                 t.Fatal(err)
269         }
270         if err := scanner.Err(); err != nil {
271                 t.Fatal(err)
272         }
273         for fullName, reason := range notInlinedReason {
274                 t.Errorf("%s was not inlined: %s", fullName, reason)
275         }
276 }