]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/inl_test.go
sync/atomic: add typed atomic values
[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.Bool",
132                         "Value.Bytes",
133                         "Value.CanAddr",
134                         "Value.CanComplex",
135                         "Value.CanFloat",
136                         "Value.CanInt",
137                         "Value.CanInterface",
138                         "Value.CanSet",
139                         "Value.CanUint",
140                         "Value.Cap",
141                         "Value.Complex",
142                         "Value.Float",
143                         "Value.Int",
144                         "Value.Interface",
145                         "Value.IsNil",
146                         "Value.IsValid",
147                         "Value.Kind",
148                         "Value.Len",
149                         "Value.MapRange",
150                         "Value.OverflowComplex",
151                         "Value.OverflowFloat",
152                         "Value.OverflowInt",
153                         "Value.OverflowUint",
154                         "Value.String",
155                         "Value.Type",
156                         "Value.Uint",
157                         "Value.UnsafeAddr",
158                         "Value.pointer",
159                         "add",
160                         "align",
161                         "flag.mustBe",
162                         "flag.mustBeAssignable",
163                         "flag.mustBeExported",
164                         "flag.kind",
165                         "flag.ro",
166                 },
167                 "regexp": {
168                         "(*bitState).push",
169                 },
170                 "math/big": {
171                         "bigEndianWord",
172                         // The following functions require the math_big_pure_go build tag.
173                         "addVW",
174                         "subVW",
175                 },
176                 "math/rand": {
177                         "(*rngSource).Int63",
178                         "(*rngSource).Uint64",
179                 },
180                 "net": {
181                         "(*UDPConn).ReadFromUDP",
182                 },
183                 "sync/atomic": {
184                         // (*Bool).CompareAndSwap handled below.
185                         "(*Bool).Load",
186                         "(*Bool).Store",
187                         "(*Bool).Swap",
188                         "(*Int32).Add",
189                         "(*Int32).CompareAndSwap",
190                         "(*Int32).Load",
191                         "(*Int32).Store",
192                         "(*Int32).Swap",
193                         "(*Int64).Add",
194                         "(*Int64).CompareAndSwap",
195                         "(*Int64).Load",
196                         "(*Int64).Store",
197                         "(*Int64).Swap",
198                         "(*Uint32).Add",
199                         "(*Uint32).CompareAndSwap",
200                         "(*Uint32).Load",
201                         "(*Uint32).Store",
202                         "(*Uint32).Swap",
203                         "(*Uint64).Add",
204                         "(*Uint64).CompareAndSwap",
205                         "(*Uint64).Load",
206                         "(*Uint64).Store",
207                         "(*Uint64).Swap",
208                         "(*Uintptr).Add",
209                         "(*Uintptr).CompareAndSwap",
210                         "(*Uintptr).Load",
211                         "(*Uintptr).Store",
212                         "(*Uintptr).Swap",
213                         // TODO(rsc): Why are these not reported as inlined?
214                         // "(*Pointer[T]).CompareAndSwap",
215                         // "(*Pointer[T]).Load",
216                         // "(*Pointer[T]).Store",
217                         // "(*Pointer[T]).Swap",
218                 },
219         }
220
221         if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
222                 // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable.
223                 // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386.
224                 // On mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive
225                 // to inline (Issue 22239).
226                 want["runtime"] = append(want["runtime"], "nextFreeFast")
227         }
228         if runtime.GOARCH != "386" {
229                 // As explained above, Ctz64 and Ctz32 are not Go code on 386.
230                 // The same applies to Bswap32.
231                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64")
232                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32")
233                 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32")
234         }
235         if bits.UintSize == 64 {
236                 // mix is only defined on 64-bit architectures
237                 want["runtime"] = append(want["runtime"], "mix")
238                 // (*Bool).CompareAndSwap is just over budget on 32-bit systems (386, arm).
239                 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
240         }
241
242         switch runtime.GOARCH {
243         case "386", "wasm", "arm":
244         default:
245                 // TODO(mvdan): As explained in /test/inline_sync.go, some
246                 // architectures don't have atomic intrinsics, so these go over
247                 // the inlining budget. Move back to the main table once that
248                 // problem is solved.
249                 want["sync"] = []string{
250                         "(*Mutex).Lock",
251                         "(*Mutex).Unlock",
252                         "(*RWMutex).RLock",
253                         "(*RWMutex).RUnlock",
254                         "(*Once).Do",
255                 }
256         }
257
258         // Functions that must actually be inlined; they must have actual callers.
259         must := map[string]bool{
260                 "compress/flate.byLiteral.Len":  true,
261                 "compress/flate.byLiteral.Less": true,
262                 "compress/flate.byLiteral.Swap": true,
263         }
264
265         notInlinedReason := make(map[string]string)
266         pkgs := make([]string, 0, len(want))
267         for pname, fnames := range want {
268                 pkgs = append(pkgs, pname)
269                 for _, fname := range fnames {
270                         fullName := pname + "." + fname
271                         if _, ok := notInlinedReason[fullName]; ok {
272                                 t.Errorf("duplicate func: %s", fullName)
273                         }
274                         notInlinedReason[fullName] = "unknown reason"
275                 }
276         }
277
278         args := append([]string{"build", "-a", "-gcflags=all=-m -m", "-tags=math_big_pure_go"}, pkgs...)
279         cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...))
280         pr, pw := io.Pipe()
281         cmd.Stdout = pw
282         cmd.Stderr = pw
283         cmdErr := make(chan error, 1)
284         go func() {
285                 cmdErr <- cmd.Run()
286                 pw.Close()
287         }()
288         scanner := bufio.NewScanner(pr)
289         curPkg := ""
290         canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
291         haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
292         cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
293         for scanner.Scan() {
294                 line := scanner.Text()
295                 if strings.HasPrefix(line, "# ") {
296                         curPkg = line[2:]
297                         continue
298                 }
299                 if m := haveInlined.FindStringSubmatch(line); m != nil {
300                         fname := m[1]
301                         delete(notInlinedReason, curPkg+"."+fname)
302                         continue
303                 }
304                 if m := canInline.FindStringSubmatch(line); m != nil {
305                         fname := m[1]
306                         fullname := curPkg + "." + fname
307                         // If function must be inlined somewhere, being inlinable is not enough
308                         if _, ok := must[fullname]; !ok {
309                                 delete(notInlinedReason, fullname)
310                                 continue
311                         }
312                 }
313                 if m := cannotInline.FindStringSubmatch(line); m != nil {
314                         fname, reason := m[1], m[2]
315                         fullName := curPkg + "." + fname
316                         if _, ok := notInlinedReason[fullName]; ok {
317                                 // cmd/compile gave us a reason why
318                                 notInlinedReason[fullName] = reason
319                         }
320                         continue
321                 }
322         }
323         if err := <-cmdErr; err != nil {
324                 t.Fatal(err)
325         }
326         if err := scanner.Err(); err != nil {
327                 t.Fatal(err)
328         }
329         for fullName, reason := range notInlinedReason {
330                 t.Errorf("%s was not inlined: %s", fullName, reason)
331         }
332 }