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 "bucketMask",
43 "bucketShift",
44 "chanbuf",
45 "evacuated",
46 "fastlog2",
47 "float64bits",
48 "funcspdelta",
49 "getm",
50 "getMCache",
51 "isDirectIface",
52 "itabHashFunc",
53 "nextslicecap",
54 "noescape",
55 "pcvalueCacheKey",
56 "rand32",
57 "readUnaligned32",
58 "readUnaligned64",
59 "releasem",
60 "roundupsize",
61 "stackmapdata",
62 "stringStructOf",
63 "subtract1",
64 "subtractb",
65 "tophash",
66 "(*bmap).keys",
67 "(*bmap).overflow",
68 "(*waitq).enqueue",
69 "funcInfo.entry",
70
71
72 "cgoInRange",
73 "gclinkptr.ptr",
74 "guintptr.ptr",
75 "writeHeapBitsForAddr",
76 "heapBitsSlice",
77 "markBits.isMarked",
78 "muintptr.ptr",
79 "puintptr.ptr",
80 "spanOf",
81 "spanOfUnchecked",
82 "typePointers.nextFast",
83 "(*gcWork).putFast",
84 "(*gcWork).tryGetFast",
85 "(*guintptr).set",
86 "(*markBits).advance",
87 "(*mspan).allocBitsForIndex",
88 "(*mspan).base",
89 "(*mspan).markBitsForBase",
90 "(*mspan).markBitsForIndex",
91 "(*mspan).writeUserArenaHeapBits",
92 "(*muintptr).set",
93 "(*puintptr).set",
94 "(*wbBuf).get1",
95 "(*wbBuf).get2",
96
97
98 "traceLocker.ok",
99 "traceEnabled",
100 },
101 "runtime/internal/sys": {},
102 "runtime/internal/math": {
103 "MulUintptr",
104 },
105 "bytes": {
106 "(*Buffer).Bytes",
107 "(*Buffer).Cap",
108 "(*Buffer).Len",
109 "(*Buffer).Grow",
110 "(*Buffer).Next",
111 "(*Buffer).Read",
112 "(*Buffer).ReadByte",
113 "(*Buffer).Reset",
114 "(*Buffer).String",
115 "(*Buffer).UnreadByte",
116 "(*Buffer).tryGrowByReslice",
117 },
118 "internal/abi": {
119 "UseInterfaceSwitchCache",
120 },
121 "compress/flate": {
122 "byLiteral.Len",
123 "byLiteral.Less",
124 "byLiteral.Swap",
125 "(*dictDecoder).tryWriteCopy",
126 },
127 "encoding/base64": {
128 "assemble32",
129 "assemble64",
130 },
131 "unicode/utf8": {
132 "FullRune",
133 "FullRuneInString",
134 "RuneLen",
135 "AppendRune",
136 "ValidRune",
137 },
138 "unicode/utf16": {
139 "Decode",
140 },
141 "reflect": {
142 "Value.Bool",
143 "Value.Bytes",
144 "Value.CanAddr",
145 "Value.CanComplex",
146 "Value.CanFloat",
147 "Value.CanInt",
148 "Value.CanInterface",
149 "Value.CanSet",
150 "Value.CanUint",
151 "Value.Cap",
152 "Value.Complex",
153 "Value.Float",
154 "Value.Int",
155 "Value.Interface",
156 "Value.IsNil",
157 "Value.IsValid",
158 "Value.Kind",
159 "Value.Len",
160 "Value.MapRange",
161 "Value.OverflowComplex",
162 "Value.OverflowFloat",
163 "Value.OverflowInt",
164 "Value.OverflowUint",
165 "Value.String",
166 "Value.Type",
167 "Value.Uint",
168 "Value.UnsafeAddr",
169 "Value.pointer",
170 "add",
171 "align",
172 "flag.mustBe",
173 "flag.mustBeAssignable",
174 "flag.mustBeExported",
175 "flag.kind",
176 "flag.ro",
177 },
178 "regexp": {
179 "(*bitState).push",
180 },
181 "math/big": {
182 "bigEndianWord",
183
184 "addVW",
185 "subVW",
186 },
187 "math/rand": {
188 "(*rngSource).Int63",
189 "(*rngSource).Uint64",
190 },
191 "net": {
192 "(*UDPConn).ReadFromUDP",
193 },
194 "sync": {
195
196
197 "OnceFunc",
198 "OnceFunc.func2",
199
200
201
202 },
203 "sync/atomic": {
204
205 "(*Bool).Load",
206 "(*Bool).Store",
207 "(*Bool).Swap",
208 "(*Int32).Add",
209 "(*Int32).CompareAndSwap",
210 "(*Int32).Load",
211 "(*Int32).Store",
212 "(*Int32).Swap",
213 "(*Int64).Add",
214 "(*Int64).CompareAndSwap",
215 "(*Int64).Load",
216 "(*Int64).Store",
217 "(*Int64).Swap",
218 "(*Uint32).Add",
219 "(*Uint32).CompareAndSwap",
220 "(*Uint32).Load",
221 "(*Uint32).Store",
222 "(*Uint32).Swap",
223 "(*Uint64).Add",
224 "(*Uint64).CompareAndSwap",
225 "(*Uint64).Load",
226 "(*Uint64).Store",
227 "(*Uint64).Swap",
228 "(*Uintptr).Add",
229 "(*Uintptr).CompareAndSwap",
230 "(*Uintptr).Load",
231 "(*Uintptr).Store",
232 "(*Uintptr).Swap",
233 "(*Pointer[go.shape.int]).CompareAndSwap",
234 "(*Pointer[go.shape.int]).Load",
235 "(*Pointer[go.shape.int]).Store",
236 "(*Pointer[go.shape.int]).Swap",
237 },
238 }
239
240 if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
241
242
243
244
245 want["runtime"] = append(want["runtime"], "nextFreeFast")
246
247 want["runtime"] = append(want["runtime"], "heapBits.nextFast")
248 }
249 if runtime.GOARCH != "386" {
250
251
252 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "TrailingZeros64")
253 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "TrailingZeros32")
254 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32")
255 }
256 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" {
257
258 want["runtime"] = append(want["runtime"], "traceAcquire")
259 }
260 if bits.UintSize == 64 {
261
262 want["runtime"] = append(want["runtime"], "mix")
263
264 want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap")
265 }
266
267 switch runtime.GOARCH {
268 case "386", "wasm", "arm":
269 default:
270
271
272
273
274 want["sync"] = []string{
275 "(*Mutex).Lock",
276 "(*Mutex).Unlock",
277 "(*RWMutex).RLock",
278 "(*RWMutex).RUnlock",
279 "(*Once).Do",
280 }
281 }
282
283
284 must := map[string]bool{
285 "compress/flate.byLiteral.Len": true,
286 "compress/flate.byLiteral.Less": true,
287 "compress/flate.byLiteral.Swap": true,
288 }
289
290 notInlinedReason := make(map[string]string)
291 pkgs := make([]string, 0, len(want))
292 for pname, fnames := range want {
293 pkgs = append(pkgs, pname)
294 for _, fname := range fnames {
295 fullName := pname + "." + fname
296 if _, ok := notInlinedReason[fullName]; ok {
297 t.Errorf("duplicate func: %s", fullName)
298 }
299 notInlinedReason[fullName] = "unknown reason"
300 }
301 }
302
303 args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...)
304 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), args...))
305 pr, pw := io.Pipe()
306 cmd.Stdout = pw
307 cmd.Stderr = pw
308 cmdErr := make(chan error, 1)
309 go func() {
310 cmdErr <- cmd.Run()
311 pw.Close()
312 }()
313 scanner := bufio.NewScanner(pr)
314 curPkg := ""
315 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
316 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
317 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
318 for scanner.Scan() {
319 line := scanner.Text()
320 if strings.HasPrefix(line, "# ") {
321 curPkg = line[2:]
322 continue
323 }
324 if m := haveInlined.FindStringSubmatch(line); m != nil {
325 fname := m[1]
326 delete(notInlinedReason, curPkg+"."+fname)
327 continue
328 }
329 if m := canInline.FindStringSubmatch(line); m != nil {
330 fname := m[1]
331 fullname := curPkg + "." + fname
332
333 if _, ok := must[fullname]; !ok {
334 delete(notInlinedReason, fullname)
335 continue
336 }
337 }
338 if m := cannotInline.FindStringSubmatch(line); m != nil {
339 fname, reason := m[1], m[2]
340 fullName := curPkg + "." + fname
341 if _, ok := notInlinedReason[fullName]; ok {
342
343 notInlinedReason[fullName] = reason
344 }
345 continue
346 }
347 }
348 if err := <-cmdErr; err != nil {
349 t.Fatal(err)
350 }
351 if err := scanner.Err(); err != nil {
352 t.Fatal(err)
353 }
354 for fullName, reason := range notInlinedReason {
355 t.Errorf("%s was not inlined: %s", fullName, reason)
356 }
357 }
358
359 func collectInlCands(msgs string) map[string]struct{} {
360 rv := make(map[string]struct{})
361 lines := strings.Split(msgs, "\n")
362 re := regexp.MustCompile(`^\S+\s+can\s+inline\s+(\S+)`)
363 for _, line := range lines {
364 m := re.FindStringSubmatch(line)
365 if m != nil {
366 rv[m[1]] = struct{}{}
367 }
368 }
369 return rv
370 }
371
372 func TestIssue56044(t *testing.T) {
373 if testing.Short() {
374 t.Skipf("skipping test: too long for short mode")
375 }
376 if !goexperiment.CoverageRedesign {
377 t.Skipf("skipping new coverage tests (experiment not enabled)")
378 }
379
380 testenv.MustHaveGoBuild(t)
381
382 modes := []string{"-covermode=set", "-covermode=atomic"}
383
384 for _, mode := range modes {
385
386 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
387 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
388 b, err := cmd.CombinedOutput()
389 if err != nil {
390 t.Fatalf("build failed (%v): %s", err, b)
391 }
392 mbase := collectInlCands(string(b))
393
394
395 args = []string{"build", "-gcflags=runtime=-m", mode, "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 mcov := collectInlCands(string(b))
402
403
404
405 for k := range mbase {
406 if _, ok := mcov[k]; !ok {
407 t.Errorf("error: did not find %s in coverage -m output", k)
408 }
409 }
410 }
411 }
412
View as plain text