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