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