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 }
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["internal/runtime/maps"] = append(want["internal/runtime/maps"], "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
311 notInlinedReason := make(map[string]string)
312 pkgs := make([]string, 0, len(want))
313 for pname, fnames := range want {
314 pkgs = append(pkgs, pname)
315 for _, fname := range fnames {
316 fullName := pname + "." + fname
317 if _, ok := notInlinedReason[fullName]; ok {
318 t.Errorf("duplicate func: %s", fullName)
319 }
320 notInlinedReason[fullName] = "unknown reason"
321 }
322 }
323
324 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
325 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
326 pr, pw := io.Pipe()
327 cmd.Stdout = pw
328 cmd.Stderr = pw
329 cmdErr := make(chan error, 1)
330 go func() {
331 cmdErr <- cmd.Run()
332 pw.Close()
333 }()
334 scanner := bufio.NewScanner(pr)
335 curPkg := ""
336 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
337 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
338 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
339 for scanner.Scan() {
340 line := scanner.Text()
341 if strings.HasPrefix(line, "# ") {
342 curPkg = line[2:]
343 continue
344 }
345 if m := haveInlined.FindStringSubmatch(line); m != nil {
346 fname := m[1]
347 delete(notInlinedReason, curPkg+"."+fname)
348 continue
349 }
350 if m := canInline.FindStringSubmatch(line); m != nil {
351 fname := m[1]
352 fullname := curPkg + "." + fname
353
354 if _, ok := must[fullname]; !ok {
355 delete(notInlinedReason, fullname)
356 continue
357 }
358 }
359 if m := cannotInline.FindStringSubmatch(line); m != nil {
360 fname, reason := m[1], m[2]
361 fullName := curPkg + "." + fname
362 if _, ok := notInlinedReason[fullName]; ok {
363
364 notInlinedReason[fullName] = reason
365 }
366 continue
367 }
368 }
369 if err := <-cmdErr; err != nil {
370 t.Fatal(err)
371 }
372 if err := scanner.Err(); err != nil {
373 t.Fatal(err)
374 }
375 for fullName, reason := range notInlinedReason {
376 t.Errorf("%s was not inlined: %s", fullName, reason)
377 }
378 }
379
380 func collectInlCands(msgs string) map[string]struct{} {
381 rv := make(map[string]struct{})
382 lines := strings.Split(msgs, "\n")
383 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
384 for _, line := range lines {
385 m := re.FindStringSubmatch(line)
386 if m != nil {
387 rv[m[1]] = struct{}{}
388 }
389 }
390 return rv
391 }
392
393 func TestIssue56044(t *testing.T) {
394 if testing.Short() {
395 t.Skipf("skipping test: too long for short mode")
396 }
397 testenv.MustHaveGoBuild(t)
398
399 modes := []string{"-covermode=set", "-covermode=atomic"}
400
401 for _, mode := range modes {
402
403 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
404 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
405 b, err := cmd.CombinedOutput()
406 if err != nil {
407 t.Fatalf("build failed (%v): %s", err, b)
408 }
409 mbase := collectInlCands(string(b))
410
411
412 args = []string{"build", "-gcflags=runtime=-m", mode, "runtime"}
413 cmd = testenv.Command(t, testenv.GoToolPath(t), args...)
414 b, err = cmd.CombinedOutput()
415 if err != nil {
416 t.Fatalf("build failed (%v): %s", err, b)
417 }
418 mcov := collectInlCands(string(b))
419
420
421
422 for k := range mbase {
423 if _, ok := mcov[k]; !ok {
424 t.Errorf("error: did not find %s in coverage -m output", k)
425 }
426 }
427 }
428 }
429
View as plain text