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