1
2
3
4
5 package test
6
7 import (
8 "bufio"
9 "internal/goexperiment"
10 "internal/testenv"
11 "io"
12 "math/bits"
13 "regexp"
14 "runtime"
15 "strings"
16 "testing"
17 )
18
19
20
21
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
30
31
32 want := map[string][]string{
33 "runtime": {
34 "add",
35 "acquirem",
36 "add1",
37 "addb",
38 "adjustpanics",
39 "adjustpointer",
40 "alignDown",
41 "alignUp",
42 "chanbuf",
43 "fastlog2",
44 "float64bits",
45 "funcspdelta",
46 "getm",
47 "getMCache",
48 "heapSetTypeNoHeader",
49 "heapSetTypeSmallHeader",
50 "isDirectIface",
51 "itabHashFunc",
52 "nextslicecap",
53 "noescape",
54 "pcvalueCacheKey",
55 "rand32",
56 "readUnaligned32",
57 "readUnaligned64",
58 "releasem",
59 "roundupsize",
60 "stackmapdata",
61 "stringStructOf",
62 "subtract1",
63 "subtractb",
64 "(*waitq).enqueue",
65 "funcInfo.entry",
66
67
68 "cgoInRange",
69 "gclinkptr.ptr",
70 "gcUsesSpanInlineMarkBits",
71 "guintptr.ptr",
72 "heapBitsSlice",
73 "markBits.isMarked",
74 "muintptr.ptr",
75 "puintptr.ptr",
76 "spanHeapBitsRange",
77 "spanOf",
78 "spanOfUnchecked",
79 "typePointers.nextFast",
80 "(*gcWork).putObjFast",
81 "(*gcWork).tryGetObjFast",
82 "(*guintptr).set",
83 "(*markBits).advance",
84 "(*mspan).allocBitsForIndex",
85 "(*mspan).base",
86 "(*mspan).markBitsForBase",
87 "(*mspan).markBitsForIndex",
88 "(*mspan).writeUserArenaHeapBits",
89 "(*muintptr).set",
90 "(*puintptr).set",
91 "(*wbBuf).get1",
92 "(*wbBuf).get2",
93
94
95 "traceLocker.ok",
96 "traceEnabled",
97 },
98 "bytes": {
99 "(*Buffer).Bytes",
100 "(*Buffer).Cap",
101 "(*Buffer).Len",
102 "(*Buffer).Grow",
103 "(*Buffer).Next",
104 "(*Buffer).Read",
105 "(*Buffer).ReadByte",
106 "(*Buffer).Reset",
107 "(*Buffer).String",
108 "(*Buffer).UnreadByte",
109 "(*Buffer).tryGrowByReslice",
110 },
111 "internal/abi": {
112 "UseInterfaceSwitchCache",
113 },
114 "internal/runtime/math": {
115 "MulUintptr",
116 },
117 "internal/runtime/sys": {},
118 "compress/flate": {
119 "byLiteral.Len",
120 "byLiteral.Less",
121 "byLiteral.Swap",
122 "(*dictDecoder).tryWriteCopy",
123 },
124 "encoding/base64": {
125 "assemble32",
126 "assemble64",
127 },
128 "unicode/utf8": {
129 "FullRune",
130 "FullRuneInString",
131 "RuneLen",
132 "AppendRune",
133 "ValidRune",
134 },
135 "unicode/utf16": {
136 "Decode",
137 },
138 "reflect": {
139 "Value.Bool",
140 "Value.Bytes",
141 "Value.CanAddr",
142 "Value.CanComplex",
143 "Value.CanFloat",
144 "Value.CanInt",
145 "Value.CanInterface",
146 "Value.CanSet",
147 "Value.CanUint",
148 "Value.Cap",
149 "Value.Complex",
150 "Value.Float",
151 "Value.Int",
152 "Value.Interface",
153 "Value.IsNil",
154 "Value.IsValid",
155 "Value.Kind",
156 "Value.Len",
157 "Value.MapRange",
158 "Value.OverflowComplex",
159 "Value.OverflowFloat",
160 "Value.OverflowInt",
161 "Value.OverflowUint",
162 "Value.String",
163 "Value.Type",
164 "Value.Uint",
165 "Value.UnsafeAddr",
166 "Value.pointer",
167 "add",
168 "align",
169 "flag.mustBe",
170 "flag.mustBeAssignable",
171 "flag.mustBeExported",
172 "flag.kind",
173 "flag.ro",
174 },
175 "regexp": {
176 "(*bitState).push",
177 },
178 "math/big": {
179 "bigEndianWord",
180 },
181 "math/rand": {
182 "(*rngSource).Int63",
183 "(*rngSource).Uint64",
184 },
185 "net": {
186 "(*UDPConn).ReadFromUDP",
187 },
188 "sync": {
189
190
191 "OnceFunc",
192 "OnceFunc.func1",
193
194
195
196 },
197 "sync/atomic": {
198
199 "(*Bool).Load",
200 "(*Bool).Store",
201 "(*Bool).Swap",
202 "(*Int32).Add",
203 "(*Int32).CompareAndSwap",
204 "(*Int32).Load",
205 "(*Int32).Store",
206 "(*Int32).Swap",
207 "(*Int64).Add",
208 "(*Int64).CompareAndSwap",
209 "(*Int64).Load",
210 "(*Int64).Store",
211 "(*Int64).Swap",
212 "(*Uint32).Add",
213 "(*Uint32).CompareAndSwap",
214 "(*Uint32).Load",
215 "(*Uint32).Store",
216 "(*Uint32).Swap",
217 "(*Uint64).Add",
218 "(*Uint64).CompareAndSwap",
219 "(*Uint64).Load",
220 "(*Uint64).Store",
221 "(*Uint64).Swap",
222 "(*Uintptr).Add",
223 "(*Uintptr).CompareAndSwap",
224 "(*Uintptr).Load",
225 "(*Uintptr).Store",
226 "(*Uintptr).Swap",
227 "(*Pointer[go.shape.int]).CompareAndSwap",
228 "(*Pointer[go.shape.int]).Load",
229 "(*Pointer[go.shape.int]).Store",
230 "(*Pointer[go.shape.int]).Swap",
231 },
232 "testing": {
233 "(*B).Loop",
234 },
235 }
236
237 if !goexperiment.SwissMap {
238
239 want["runtime"] = append(want["runtime"], "bucketMask")
240 want["runtime"] = append(want["runtime"], "bucketShift")
241 want["runtime"] = append(want["runtime"], "evacuated")
242 want["runtime"] = append(want["runtime"], "tophash")
243 want["runtime"] = append(want["runtime"], "(*bmap).keys")
244 want["runtime"] = append(want["runtime"], "(*bmap).overflow")
245 }
246 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
247
248
249
250
251 want["runtime"] = append(want["runtime"], "nextFreeFast")
252 }
253 if runtime.GOARCH != "386" {
254
255
256 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros64")
257 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros32")
258 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "Bswap32")
259 }
260 if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" {
261
262 want["runtime"] = append(want["runtime"], "traceAcquire")
263 }
264 if bits.UintSize == 64 {
265
266 want["runtime"] = append(want["runtime"], "mix")
267
268 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
269 }
270
271 switch runtime.GOARCH {
272 case "386", "wasm", "arm":
273 default:
274
275
276
277
278 want["sync"] = []string{
279 "(*Mutex).Lock",
280 "(*Mutex).Unlock",
281 "(*RWMutex).RLock",
282 "(*RWMutex).RUnlock",
283 "(*Once).Do",
284 }
285 }
286
287 if runtime.GOARCH != "wasm" {
288
289 want["runtime"] = append(want["runtime"],
290
291 "key8",
292 "(*mLockProfile).store",
293 )
294 if bits.UintSize == 64 {
295
296 want["runtime"] = append(want["runtime"],
297
298 "mutexSampleContention",
299
300
301 "(*mLockProfile).end",
302 )
303 }
304 }
305
306
307 must := map[string]bool{
308 "compress/flate.byLiteral.Len": true,
309 "compress/flate.byLiteral.Less": true,
310 "compress/flate.byLiteral.Swap": true,
311 }
312
313 notInlinedReason := make(map[string]string)
314 pkgs := make([]string, 0, len(want))
315 for pname, fnames := range want {
316 pkgs = append(pkgs, pname)
317 for _, fname := range fnames {
318 fullName := pname + "." + fname
319 if _, ok := notInlinedReason[fullName]; ok {
320 t.Errorf("duplicate func: %s", fullName)
321 }
322 notInlinedReason[fullName] = "unknown reason"
323 }
324 }
325
326 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
327 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
328 pr, pw := io.Pipe()
329 cmd.Stdout = pw
330 cmd.Stderr = pw
331 cmdErr := make(chan error, 1)
332 go func() {
333 cmdErr <- cmd.Run()
334 pw.Close()
335 }()
336 scanner := bufio.NewScanner(pr)
337 curPkg := ""
338 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
339 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
340 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
341 for scanner.Scan() {
342 line := scanner.Text()
343 if strings.HasPrefix(line, "# ") {
344 curPkg = line[2:]
345 continue
346 }
347 if m := haveInlined.FindStringSubmatch(line); m != nil {
348 fname := m[1]
349 delete(notInlinedReason, curPkg+"."+fname)
350 continue
351 }
352 if m := canInline.FindStringSubmatch(line); m != nil {
353 fname := m[1]
354 fullname := curPkg + "." + fname
355
356 if _, ok := must[fullname]; !ok {
357 delete(notInlinedReason, fullname)
358 continue
359 }
360 }
361 if m := cannotInline.FindStringSubmatch(line); m != nil {
362 fname, reason := m[1], m[2]
363 fullName := curPkg + "." + fname
364 if _, ok := notInlinedReason[fullName]; ok {
365
366 notInlinedReason[fullName] = reason
367 }
368 continue
369 }
370 }
371 if err := <-cmdErr; err != nil {
372 t.Fatal(err)
373 }
374 if err := scanner.Err(); err != nil {
375 t.Fatal(err)
376 }
377 for fullName, reason := range notInlinedReason {
378 t.Errorf("%s was not inlined: %s", fullName, reason)
379 }
380 }
381
382 func collectInlCands(msgs string) map[string]struct{} {
383 rv := make(map[string]struct{})
384 lines := strings.Split(msgs, "\n")
385 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
386 for _, line := range lines {
387 m := re.FindStringSubmatch(line)
388 if m != nil {
389 rv[m[1]] = struct{}{}
390 }
391 }
392 return rv
393 }
394
395 func TestIssue56044(t *testing.T) {
396 if testing.Short() {
397 t.Skipf("skipping test: too long for short mode")
398 }
399 testenv.MustHaveGoBuild(t)
400
401 modes := []string{"-covermode=set", "-covermode=atomic"}
402
403 for _, mode := range modes {
404
405 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
406 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
407 b, err := cmd.CombinedOutput()
408 if err != nil {
409 t.Fatalf("build failed (%v): %s", err, b)
410 }
411 mbase := collectInlCands(string(b))
412
413
414 args = []string{"build", "-gcflags=runtime=-m", mode, "runtime"}
415 cmd = testenv.Command(t, testenv.GoToolPath(t), args...)
416 b, err = cmd.CombinedOutput()
417 if err != nil {
418 t.Fatalf("build failed (%v): %s", err, b)
419 }
420 mcov := collectInlCands(string(b))
421
422
423
424 for k := range mbase {
425 if _, ok := mcov[k]; !ok {
426 t.Errorf("error: did not find %s in coverage -m output", k)
427 }
428 }
429 }
430 }
431
View as plain text