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 "FullRune",
129 "FullRuneInString",
130 "RuneLen",
131 "AppendRune",
132 "ValidRune",
133 },
134 "unicode/utf16": {
135 "Decode",
136 },
137 "reflect": {
138 "Value.Bool",
139 "Value.Bytes",
140 "Value.CanAddr",
141 "Value.CanComplex",
142 "Value.CanFloat",
143 "Value.CanInt",
144 "Value.CanInterface",
145 "Value.CanSet",
146 "Value.CanUint",
147 "Value.Cap",
148 "Value.Complex",
149 "Value.Float",
150 "Value.Int",
151 "Value.Interface",
152 "Value.IsNil",
153 "Value.IsValid",
154 "Value.Kind",
155 "Value.Len",
156 "Value.MapRange",
157 "Value.OverflowComplex",
158 "Value.OverflowFloat",
159 "Value.OverflowInt",
160 "Value.OverflowUint",
161 "Value.String",
162 "Value.Type",
163 "Value.Uint",
164 "Value.UnsafeAddr",
165 "Value.pointer",
166 "add",
167 "align",
168 "flag.mustBe",
169 "flag.mustBeAssignable",
170 "flag.mustBeExported",
171 "flag.kind",
172 "flag.ro",
173 },
174 "regexp": {
175 "(*bitState).push",
176 },
177 "math/big": {
178 "bigEndianWord",
179 },
180 "math/rand": {
181 "(*rngSource).Int63",
182 "(*rngSource).Uint64",
183 },
184 "net": {
185 "(*UDPConn).ReadFromUDP",
186 },
187 "sync": {
188
189
190 "OnceFunc",
191 "OnceFunc.func1",
192
193
194
195 },
196 "sync/atomic": {
197
198 "(*Bool).Load",
199 "(*Bool).Store",
200 "(*Bool).Swap",
201 "(*Int32).Add",
202 "(*Int32).CompareAndSwap",
203 "(*Int32).Load",
204 "(*Int32).Store",
205 "(*Int32).Swap",
206 "(*Int64).Add",
207 "(*Int64).CompareAndSwap",
208 "(*Int64).Load",
209 "(*Int64).Store",
210 "(*Int64).Swap",
211 "(*Uint32).Add",
212 "(*Uint32).CompareAndSwap",
213 "(*Uint32).Load",
214 "(*Uint32).Store",
215 "(*Uint32).Swap",
216 "(*Uint64).Add",
217 "(*Uint64).CompareAndSwap",
218 "(*Uint64).Load",
219 "(*Uint64).Store",
220 "(*Uint64).Swap",
221 "(*Uintptr).Add",
222 "(*Uintptr).CompareAndSwap",
223 "(*Uintptr).Load",
224 "(*Uintptr).Store",
225 "(*Uintptr).Swap",
226 "(*Pointer[go.shape.int]).CompareAndSwap",
227 "(*Pointer[go.shape.int]).Load",
228 "(*Pointer[go.shape.int]).Store",
229 "(*Pointer[go.shape.int]).Swap",
230 },
231 "testing": {
232 "(*B).Loop",
233 },
234 }
235
236 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
237
238
239
240
241 want["runtime"] = append(want["runtime"], "nextFreeFast")
242 }
243 if runtime.GOARCH != "386" {
244
245
246 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros64")
247 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros32")
248 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "Bswap32")
249 }
250 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" {
251
252 want["runtime"] = append(want["runtime"], "traceAcquire")
253 }
254 if bits.UintSize == 64 {
255
256 want["runtime"] = append(want["runtime"], "mix")
257
258 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
259 }
260
261 switch runtime.GOARCH {
262 case "386", "wasm", "arm":
263 default:
264
265
266
267
268 want["sync"] = []string{
269 "(*Mutex).Lock",
270 "(*Mutex).Unlock",
271 "(*RWMutex).RLock",
272 "(*RWMutex).RUnlock",
273 "(*Once).Do",
274 }
275 }
276
277 if runtime.GOARCH != "wasm" {
278
279 want["runtime"] = append(want["runtime"],
280
281 "key8",
282 "(*mLockProfile).store",
283 )
284 if bits.UintSize == 64 {
285
286 want["runtime"] = append(want["runtime"],
287
288 "mutexSampleContention",
289
290
291 "(*mLockProfile).end",
292 )
293 }
294 }
295
296
297 must := map[string]bool{
298 "compress/flate.byLiteral.Len": true,
299 "compress/flate.byLiteral.Less": true,
300 "compress/flate.byLiteral.Swap": true,
301 }
302
303 notInlinedReason := make(map[string]string)
304 pkgs := make([]string, 0, len(want))
305 for pname, fnames := range want {
306 pkgs = append(pkgs, pname)
307 for _, fname := range fnames {
308 fullName := pname + "." + fname
309 if _, ok := notInlinedReason[fullName]; ok {
310 t.Errorf("duplicate func: %s", fullName)
311 }
312 notInlinedReason[fullName] = "unknown reason"
313 }
314 }
315
316 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
317 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
318 pr, pw := io.Pipe()
319 cmd.Stdout = pw
320 cmd.Stderr = pw
321 cmdErr := make(chan error, 1)
322 go func() {
323 cmdErr <- cmd.Run()
324 pw.Close()
325 }()
326 scanner := bufio.NewScanner(pr)
327 curPkg := ""
328 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
329 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
330 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
331 for scanner.Scan() {
332 line := scanner.Text()
333 if strings.HasPrefix(line, "# ") {
334 curPkg = line[2:]
335 continue
336 }
337 if m := haveInlined.FindStringSubmatch(line); m != nil {
338 fname := m[1]
339 delete(notInlinedReason, curPkg+"."+fname)
340 continue
341 }
342 if m := canInline.FindStringSubmatch(line); m != nil {
343 fname := m[1]
344 fullname := curPkg + "." + fname
345
346 if _, ok := must[fullname]; !ok {
347 delete(notInlinedReason, fullname)
348 continue
349 }
350 }
351 if m := cannotInline.FindStringSubmatch(line); m != nil {
352 fname, reason := m[1], m[2]
353 fullName := curPkg + "." + fname
354 if _, ok := notInlinedReason[fullName]; ok {
355
356 notInlinedReason[fullName] = reason
357 }
358 continue
359 }
360 }
361 if err := <-cmdErr; err != nil {
362 t.Fatal(err)
363 }
364 if err := scanner.Err(); err != nil {
365 t.Fatal(err)
366 }
367 for fullName, reason := range notInlinedReason {
368 t.Errorf("%s was not inlined: %s", fullName, reason)
369 }
370 }
371
372 func collectInlCands(msgs string) map[string]struct{} {
373 rv := make(map[string]struct{})
374 lines := strings.Split(msgs, "\n")
375 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
376 for _, line := range lines {
377 m := re.FindStringSubmatch(line)
378 if m != nil {
379 rv[m[1]] = struct{}{}
380 }
381 }
382 return rv
383 }
384
385 func TestIssue56044(t *testing.T) {
386 if testing.Short() {
387 t.Skipf("skipping test: too long for short mode")
388 }
389 testenv.MustHaveGoBuild(t)
390
391 modes := []string{"-covermode=set", "-covermode=atomic"}
392
393 for _, mode := range modes {
394
395 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
396 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
397 b, err := cmd.CombinedOutput()
398 if err != nil {
399 t.Fatalf("build failed (%v): %s", err, b)
400 }
401 mbase := collectInlCands(string(b))
402
403
404 args = []string{"build", "-gcflags=runtime=-m", mode, "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 mcov := collectInlCands(string(b))
411
412
413
414 for k := range mbase {
415 if _, ok := mcov[k]; !ok {
416 t.Errorf("error: did not find %s in coverage -m output", k)
417 }
418 }
419 }
420 }
421
View as plain text