]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/inl_test.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[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.pointer",
140                         "add",
141                         "align",
142                         "flag.mustBe",
143                         "flag.mustBeAssignable",
144                         "flag.mustBeExported",
145                         "flag.kind",
146                         "flag.ro",
147                 },
148                 "regexp": {
149                         "(*bitState).push",
150                 },
151                 "math/big": {
152                         "bigEndianWord",
153                         // The following functions require the math_big_pure_go build tag.
154                         "addVW",
155                         "subVW",
156                 },
157                 "math/rand": {
158                         "(*rngSource).Int63",
159                         "(*rngSource).Uint64",
160                 },
161                 "net": {
162                         "(*UDPConn).ReadFromUDP",
163                 },
164         }
165
166         if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
167                 // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable.
168                 // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386.
169                 // On mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive
170                 // to inline (Issue 22239).
171                 want["runtime"] = append(want["runtime"], "nextFreeFast")
172         }
173         if runtime.GOARCH != "386" {
174                 // As explained above, Ctz64 and Ctz32 are not Go code on 386.
175                 // The same applies to Bswap32.
176                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64")
177                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32")
178                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32")
179         }
180         if bits.UintSize == 64 {
181                 // mix is only defined on 64-bit architectures
182                 want["runtime"] = append(want["runtime"], "mix")
183         }
184
185         switch runtime.GOARCH {
186         case "386", "wasm", "arm":
187         default:
188                 // TODO(mvdan): As explained in /test/inline_sync.go, some
189                 // architectures don't have atomic intrinsics, so these go over
190                 // the inlining budget. Move back to the main table once that
191                 // problem is solved.
192                 want["sync"] = []string{
193                         "(*Mutex).Lock",
194                         "(*Mutex).Unlock",
195                         "(*RWMutex).RLock",
196                         "(*RWMutex).RUnlock",
197                         "(*Once).Do",
198                 }
199         }
200
201         // Functions that must actually be inlined; they must have actual callers.
202         must := map[string]bool{
203                 "compress/flate.byLiteral.Len":  true,
204                 "compress/flate.byLiteral.Less": true,
205                 "compress/flate.byLiteral.Swap": true,
206         }
207
208         notInlinedReason := make(map[string]string)
209         pkgs := make([]string, 0, len(want))
210         for pname, fnames := range want {
211                 pkgs = append(pkgs, pname)
212                 for _, fname := range fnames {
213                         fullName := pname + "." + fname
214                         if _, ok := notInlinedReason[fullName]; ok {
215                                 t.Errorf("duplicate func: %s", fullName)
216                         }
217                         notInlinedReason[fullName] = "unknown reason"
218                 }
219         }
220
221         args := append([]string{"build", "-a", "-gcflags=all=-m -m", "-tags=math_big_pure_go"}, pkgs...)
222         cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...))
223         pr, pw := io.Pipe()
224         cmd.Stdout = pw
225         cmd.Stderr = pw
226         cmdErr := make(chan error, 1)
227         go func() {
228                 cmdErr <- cmd.Run()
229                 pw.Close()
230         }()
231         scanner := bufio.NewScanner(pr)
232         curPkg := ""
233         canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
234         haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
235         cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
236         for scanner.Scan() {
237                 line := scanner.Text()
238                 if strings.HasPrefix(line, "# ") {
239                         curPkg = line[2:]
240                         continue
241                 }
242                 if m := haveInlined.FindStringSubmatch(line); m != nil {
243                         fname := m[1]
244                         delete(notInlinedReason, curPkg+"."+fname)
245                         continue
246                 }
247                 if m := canInline.FindStringSubmatch(line); m != nil {
248                         fname := m[1]
249                         fullname := curPkg + "." + fname
250                         // If function must be inlined somewhere, being inlinable is not enough
251                         if _, ok := must[fullname]; !ok {
252                                 delete(notInlinedReason, fullname)
253                                 continue
254                         }
255                 }
256                 if m := cannotInline.FindStringSubmatch(line); m != nil {
257                         fname, reason := m[1], m[2]
258                         fullName := curPkg + "." + fname
259                         if _, ok := notInlinedReason[fullName]; ok {
260                                 // cmd/compile gave us a reason why
261                                 notInlinedReason[fullName] = reason
262                         }
263                         continue
264                 }
265         }
266         if err := <-cmdErr; err != nil {
267                 t.Fatal(err)
268         }
269         if err := scanner.Err(); err != nil {
270                 t.Fatal(err)
271         }
272         for fullName, reason := range notInlinedReason {
273                 t.Errorf("%s was not inlined: %s", fullName, reason)
274         }
275 }