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 "path": {
237 "Base",
238 "scanChunk",
239 },
240 "path/filepath": {
241 "scanChunk",
242 },
243 }
244
245 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
246
247
248
249
250 want["runtime"] = append(want["runtime"], "nextFreeFast")
251 }
252 if runtime.GOARCH != "386" {
253
254
255 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros64")
256 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros32")
257 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "Bswap32")
258 }
259 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" {
260
261 want["runtime"] = append(want["runtime"], "traceAcquire")
262 }
263 if bits.UintSize == 64 {
264
265 want["runtime"] = append(want["runtime"], "mix")
266
267 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
268 }
269
270 switch runtime.GOARCH {
271 case "386", "wasm", "arm":
272 default:
273
274
275
276
277 want["sync"] = []string{
278 "(*Mutex).Lock",
279 "(*Mutex).Unlock",
280 "(*RWMutex).RLock",
281 "(*RWMutex).RUnlock",
282 "(*Once).Do",
283 }
284 }
285
286 if runtime.GOARCH != "wasm" {
287
288 want["runtime"] = append(want["runtime"],
289
290 "key8",
291 "(*mLockProfile).store",
292 )
293 if bits.UintSize == 64 {
294
295 want["runtime"] = append(want["runtime"],
296
297 "mutexSampleContention",
298
299
300 "(*mLockProfile).end",
301 )
302 }
303 }
304
305
306 must := map[string]bool{
307 "compress/flate.byLiteral.Len": true,
308 "compress/flate.byLiteral.Less": true,
309 "compress/flate.byLiteral.Swap": true,
310 }
311
312 notInlinedReason := make(map[string]string)
313 pkgs := make([]string, 0, len(want))
314 for pname, fnames := range want {
315 pkgs = append(pkgs, pname)
316 for _, fname := range fnames {
317 fullName := pname + "." + fname
318 if _, ok := notInlinedReason[fullName]; ok {
319 t.Errorf("duplicate func: %s", fullName)
320 }
321 notInlinedReason[fullName] = "unknown reason"
322 }
323 }
324
325 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
326 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
327 pr, pw := io.Pipe()
328 cmd.Stdout = pw
329 cmd.Stderr = pw
330 cmdErr := make(chan error, 1)
331 go func() {
332 cmdErr <- cmd.Run()
333 pw.Close()
334 }()
335 scanner := bufio.NewScanner(pr)
336 curPkg := ""
337 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
338 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
339 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
340 for scanner.Scan() {
341 line := scanner.Text()
342 if strings.HasPrefix(line, "# ") {
343 curPkg = line[2:]
344 continue
345 }
346 if m := haveInlined.FindStringSubmatch(line); m != nil {
347 fname := m[1]
348 delete(notInlinedReason, curPkg+"."+fname)
349 continue
350 }
351 if m := canInline.FindStringSubmatch(line); m != nil {
352 fname := m[1]
353 fullname := curPkg + "." + fname
354
355 if _, ok := must[fullname]; !ok {
356 delete(notInlinedReason, fullname)
357 continue
358 }
359 }
360 if m := cannotInline.FindStringSubmatch(line); m != nil {
361 fname, reason := m[1], m[2]
362 fullName := curPkg + "." + fname
363 if _, ok := notInlinedReason[fullName]; ok {
364
365 notInlinedReason[fullName] = reason
366 }
367 continue
368 }
369 }
370 if err := <-cmdErr; err != nil {
371 t.Fatal(err)
372 }
373 if err := scanner.Err(); err != nil {
374 t.Fatal(err)
375 }
376 for fullName, reason := range notInlinedReason {
377 t.Errorf("%s was not inlined: %s", fullName, reason)
378 }
379 }
380
381 func collectInlCands(msgs string) map[string]struct{} {
382 rv := make(map[string]struct{})
383 lines := strings.Split(msgs, "\n")
384 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
385 for _, line := range lines {
386 m := re.FindStringSubmatch(line)
387 if m != nil {
388 rv[m[1]] = struct{}{}
389 }
390 }
391 return rv
392 }
393
394 func TestIssue56044(t *testing.T) {
395 if testing.Short() {
396 t.Skipf("skipping test: too long for short mode")
397 }
398 testenv.MustHaveGoBuild(t)
399
400 modes := []string{"-covermode=set", "-covermode=atomic"}
401
402 for _, mode := range modes {
403
404 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
405 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
406 b, err := cmd.CombinedOutput()
407 if err != nil {
408 t.Fatalf("build failed (%v): %s", err, b)
409 }
410 mbase := collectInlCands(string(b))
411
412
413 args = []string{"build", "-gcflags=runtime=-m", mode, "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 mcov := collectInlCands(string(b))
420
421
422
423 for k := range mbase {
424 if _, ok := mcov[k]; !ok {
425 t.Errorf("error: did not find %s in coverage -m output", k)
426 }
427 }
428 }
429 }
430
View as plain text