1
2
3
4
5 package test
6
7 import (
8 "bufio"
9 "internal/goexperiment"
10 "internal/testenv"
11 "io"
12 "math/bits"
13 "regexp"
14 "runtime"
15 "strings"
16 "testing"
17 )
18
19
20
21
22 func TestIntendedInlining(t *testing.T) {
23 if testing.Short() && testenv.Builder() == "" {
24 t.Skip("skipping in short mode")
25 }
26 testenv.MustHaveGoRun(t)
27 t.Parallel()
28
29
30
31
32 want := map[string][]string{
33 "runtime": {
34 "add",
35 "acquirem",
36 "add1",
37 "addb",
38 "adjustpanics",
39 "adjustpointer",
40 "alignDown",
41 "alignUp",
42 "chanbuf",
43 "fastlog2",
44 "float64bits",
45 "funcspdelta",
46 "getm",
47 "getMCache",
48 "heapSetTypeNoHeader",
49 "heapSetTypeSmallHeader",
50 "isDirectIface",
51 "itabHashFunc",
52 "nextslicecap",
53 "noescape",
54 "pcvalueCacheKey",
55 "rand32",
56 "readUnaligned32",
57 "readUnaligned64",
58 "releasem",
59 "roundupsize",
60 "stackmapdata",
61 "stringStructOf",
62 "subtract1",
63 "subtractb",
64 "(*waitq).enqueue",
65 "funcInfo.entry",
66
67
68 "cgoInRange",
69 "gclinkptr.ptr",
70 "guintptr.ptr",
71 "heapBitsSlice",
72 "markBits.isMarked",
73 "muintptr.ptr",
74 "puintptr.ptr",
75 "spanOf",
76 "spanOfUnchecked",
77 "typePointers.nextFast",
78 "(*gcWork).putFast",
79 "(*gcWork).tryGetFast",
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 "UseInterfaceSwitchCache",
111 },
112 "internal/runtime/math": {
113 "MulUintptr",
114 },
115 "internal/runtime/sys": {},
116 "compress/flate": {
117 "byLiteral.Len",
118 "byLiteral.Less",
119 "byLiteral.Swap",
120 "(*dictDecoder).tryWriteCopy",
121 },
122 "encoding/base64": {
123 "assemble32",
124 "assemble64",
125 },
126 "unicode/utf8": {
127 "FullRune",
128 "FullRuneInString",
129 "RuneLen",
130 "AppendRune",
131 "ValidRune",
132 },
133 "unicode/utf16": {
134 "Decode",
135 },
136 "reflect": {
137 "Value.Bool",
138 "Value.Bytes",
139 "Value.CanAddr",
140 "Value.CanComplex",
141 "Value.CanFloat",
142 "Value.CanInt",
143 "Value.CanInterface",
144 "Value.CanSet",
145 "Value.CanUint",
146 "Value.Cap",
147 "Value.Complex",
148 "Value.Float",
149 "Value.Int",
150 "Value.Interface",
151 "Value.IsNil",
152 "Value.IsValid",
153 "Value.Kind",
154 "Value.Len",
155 "Value.MapRange",
156 "Value.OverflowComplex",
157 "Value.OverflowFloat",
158 "Value.OverflowInt",
159 "Value.OverflowUint",
160 "Value.String",
161 "Value.Type",
162 "Value.Uint",
163 "Value.UnsafeAddr",
164 "Value.pointer",
165 "add",
166 "align",
167 "flag.mustBe",
168 "flag.mustBeAssignable",
169 "flag.mustBeExported",
170 "flag.kind",
171 "flag.ro",
172 },
173 "regexp": {
174 "(*bitState).push",
175 },
176 "math/big": {
177 "bigEndianWord",
178
179 "addVW",
180 "subVW",
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.func2",
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 }
237
238 if !goexperiment.SwissMap {
239
240 want["runtime"] = append(want["runtime"], "bucketMask")
241 want["runtime"] = append(want["runtime"], "bucketShift")
242 want["runtime"] = append(want["runtime"], "evacuated")
243 want["runtime"] = append(want["runtime"], "tophash")
244 want["runtime"] = append(want["runtime"], "(*bmap).keys")
245 want["runtime"] = append(want["runtime"], "(*bmap).overflow")
246 }
247 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
248
249
250
251
252 want["runtime"] = append(want["runtime"], "nextFreeFast")
253 }
254 if runtime.GOARCH != "386" {
255
256
257 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros64")
258 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "TrailingZeros32")
259 want["internal/runtime/sys"] = append(want["internal/runtime/sys"], "Bswap32")
260 }
261 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" {
262
263 want["runtime"] = append(want["runtime"], "traceAcquire")
264 }
265 if bits.UintSize == 64 {
266
267 want["runtime"] = append(want["runtime"], "mix")
268
269 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
270 }
271
272 switch runtime.GOARCH {
273 case "386", "wasm", "arm":
274 default:
275
276
277
278
279 want["sync"] = []string{
280 "(*Mutex).Lock",
281 "(*Mutex).Unlock",
282 "(*RWMutex).RLock",
283 "(*RWMutex).RUnlock",
284 "(*Once).Do",
285 }
286 }
287
288
289 must := map[string]bool{
290 "compress/flate.byLiteral.Len": true,
291 "compress/flate.byLiteral.Less": true,
292 "compress/flate.byLiteral.Swap": true,
293 }
294
295 notInlinedReason := make(map[string]string)
296 pkgs := make([]string, 0, len(want))
297 for pname, fnames := range want {
298 pkgs = append(pkgs, pname)
299 for _, fname := range fnames {
300 fullName := pname + "." + fname
301 if _, ok := notInlinedReason[fullName]; ok {
302 t.Errorf("duplicate func: %s", fullName)
303 }
304 notInlinedReason[fullName] = "unknown reason"
305 }
306 }
307
308 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
309 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
310 pr, pw := io.Pipe()
311 cmd.Stdout = pw
312 cmd.Stderr = pw
313 cmdErr := make(chan error, 1)
314 go func() {
315 cmdErr <- cmd.Run()
316 pw.Close()
317 }()
318 scanner := bufio.NewScanner(pr)
319 curPkg := ""
320 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
321 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
322 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
323 for scanner.Scan() {
324 line := scanner.Text()
325 if strings.HasPrefix(line, "# ") {
326 curPkg = line[2:]
327 continue
328 }
329 if m := haveInlined.FindStringSubmatch(line); m != nil {
330 fname := m[1]
331 delete(notInlinedReason, curPkg+"."+fname)
332 continue
333 }
334 if m := canInline.FindStringSubmatch(line); m != nil {
335 fname := m[1]
336 fullname := curPkg + "." + fname
337
338 if _, ok := must[fullname]; !ok {
339 delete(notInlinedReason, fullname)
340 continue
341 }
342 }
343 if m := cannotInline.FindStringSubmatch(line); m != nil {
344 fname, reason := m[1], m[2]
345 fullName := curPkg + "." + fname
346 if _, ok := notInlinedReason[fullName]; ok {
347
348 notInlinedReason[fullName] = reason
349 }
350 continue
351 }
352 }
353 if err := <-cmdErr; err != nil {
354 t.Fatal(err)
355 }
356 if err := scanner.Err(); err != nil {
357 t.Fatal(err)
358 }
359 for fullName, reason := range notInlinedReason {
360 t.Errorf("%s was not inlined: %s", fullName, reason)
361 }
362 }
363
364 func collectInlCands(msgs string) map[string]struct{} {
365 rv := make(map[string]struct{})
366 lines := strings.Split(msgs, "\n")
367 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
368 for _, line := range lines {
369 m := re.FindStringSubmatch(line)
370 if m != nil {
371 rv[m[1]] = struct{}{}
372 }
373 }
374 return rv
375 }
376
377 func TestIssue56044(t *testing.T) {
378 if testing.Short() {
379 t.Skipf("skipping test: too long for short mode")
380 }
381 if !goexperiment.CoverageRedesign {
382 t.Skipf("skipping new coverage tests (experiment not enabled)")
383 }
384
385 testenv.MustHaveGoBuild(t)
386
387 modes := []string{"-covermode=set", "-covermode=atomic"}
388
389 for _, mode := range modes {
390
391 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
392 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
393 b, err := cmd.CombinedOutput()
394 if err != nil {
395 t.Fatalf("build failed (%v): %s", err, b)
396 }
397 mbase := collectInlCands(string(b))
398
399
400 args = []string{"build", "-gcflags=runtime=-m", mode, "runtime"}
401 cmd = testenv.Command(t, testenv.GoToolPath(t), args...)
402 b, err = cmd.CombinedOutput()
403 if err != nil {
404 t.Fatalf("build failed (%v): %s", err, b)
405 }
406 mcov := collectInlCands(string(b))
407
408
409
410 for k := range mbase {
411 if _, ok := mcov[k]; !ok {
412 t.Errorf("error: did not find %s in coverage -m output", k)
413 }
414 }
415 }
416 }
417
View as plain text