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 "readUnaligned64",
55 "releasem",
56 "roundupsize",
57 "stackmapdata",
58 "stringStructOf",
59 "subtract1",
60 "subtractb",
61 "(*waitq).enqueue",
62 "funcInfo.entry",
63
64
65 "cgoInRange",
66 "gclinkptr.ptr",
67 "gcUsesSpanInlineMarkBits",
68 "guintptr.ptr",
69 "heapBitsSlice",
70 "markBits.isMarked",
71 "muintptr.ptr",
72 "puintptr.ptr",
73 "spanHeapBitsRange",
74 "spanOf",
75 "spanOfUnchecked",
76 "typePointers.nextFast",
77 "(*gcWork).putObjFast",
78 "(*gcWork).tryGetObjFast",
79 "(*guintptr).set",
80 "(*markBits).advance",
81 "(*mspan).allocBitsForIndex",
82 "(*mspan).base",
83 "(*mspan).markBitsForBase",
84 "(*mspan).markBitsForIndex",
85 "(*mspan).writeUserArenaHeapBits",
86 "(*muintptr).set",
87 "(*puintptr).set",
88 "(*wbBuf).get1",
89 "(*wbBuf).get2",
90
91
92 "traceLocker.ok",
93 "traceEnabled",
94 },
95 "bytes": {
96 "(*Buffer).Bytes",
97 "(*Buffer).Cap",
98 "(*Buffer).Len",
99 "(*Buffer).Grow",
100 "(*Buffer).Next",
101 "(*Buffer).Read",
102 "(*Buffer).ReadByte",
103 "(*Buffer).Reset",
104 "(*Buffer).String",
105 "(*Buffer).UnreadByte",
106 "(*Buffer).tryGrowByReslice",
107 },
108 "internal/abi": {
109 "(*Type).IsDirectIface",
110 "UseInterfaceSwitchCache",
111 },
112 "internal/runtime/math": {
113 "MulUintptr",
114 },
115 "internal/runtime/maps": {
116 "readUnaligned32",
117 "readUnaligned64",
118 },
119 "internal/runtime/sys": {},
120 "compress/flate": {
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 "internal/strconv": {
247 "prescale",
248 "uscale",
249 },
250 }
251
252 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
253
254
255
256
257 want["runtime"] = append(want["runtime"], "nextFreeFast")
258 }
259 if runtime.GOARCH != "386" {
260
261
262 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros64")
263 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros32")
264 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "Bswap32")
265 }
266 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" {
267
268 want["runtime"] = append(want["runtime"], "traceAcquire")
269 }
270 if bits.UintSize == 64 {
271
272 want["internal/runtime/maps"] = append(want["internal/runtime/maps"], "mix")
273
274 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
275 }
276
277 switch runtime.GOARCH {
278 case "386", "wasm", "arm":
279 default:
280
281
282
283
284 want["sync"] = []string{
285 "(*Mutex).Lock",
286 "(*Mutex).Unlock",
287 "(*RWMutex).RLock",
288 "(*RWMutex).RUnlock",
289 "(*Once).Do",
290 }
291 }
292
293 if runtime.GOARCH != "wasm" {
294
295 want["runtime"] = append(want["runtime"],
296
297 "key8",
298 "(*mLockProfile).store",
299 )
300 if bits.UintSize == 64 {
301
302 want["runtime"] = append(want["runtime"],
303
304 "mutexSampleContention",
305
306
307 "(*mLockProfile).end",
308 )
309 }
310 }
311
312
313 must := map[string]bool{}
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