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 "math/rand": {
180 "(*rngSource).Int63",
181 "(*rngSource).Uint64",
182 },
183 "net": {
184 "(*UDPConn).ReadFromUDP",
185 },
186 "sync": {
187
188
189 "OnceFunc",
190 "OnceFunc.func1",
191
192
193
194 },
195 "sync/atomic": {
196
197 "(*Bool).Load",
198 "(*Bool).Store",
199 "(*Bool).Swap",
200 "(*Int32).Add",
201 "(*Int32).CompareAndSwap",
202 "(*Int32).Load",
203 "(*Int32).Store",
204 "(*Int32).Swap",
205 "(*Int64).Add",
206 "(*Int64).CompareAndSwap",
207 "(*Int64).Load",
208 "(*Int64).Store",
209 "(*Int64).Swap",
210 "(*Uint32).Add",
211 "(*Uint32).CompareAndSwap",
212 "(*Uint32).Load",
213 "(*Uint32).Store",
214 "(*Uint32).Swap",
215 "(*Uint64).Add",
216 "(*Uint64).CompareAndSwap",
217 "(*Uint64).Load",
218 "(*Uint64).Store",
219 "(*Uint64).Swap",
220 "(*Uintptr).Add",
221 "(*Uintptr).CompareAndSwap",
222 "(*Uintptr).Load",
223 "(*Uintptr).Store",
224 "(*Uintptr).Swap",
225 "(*Pointer[go.shape.int]).CompareAndSwap",
226 "(*Pointer[go.shape.int]).Load",
227 "(*Pointer[go.shape.int]).Store",
228 "(*Pointer[go.shape.int]).Swap",
229 },
230 "testing": {
231 "(*B).Loop",
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 testenv.MustHaveGoBuild(t)
379
380 modes := []string{"-covermode=set", "-covermode=atomic"}
381
382 for _, mode := range modes {
383
384 args := []string{"build", "-gcflags=runtime=-m", "runtime"}
385 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
386 b, err := cmd.CombinedOutput()
387 if err != nil {
388 t.Fatalf("build failed (%v): %s", err, b)
389 }
390 mbase := collectInlCands(string(b))
391
392
393 args = []string{"build", "-gcflags=runtime=-m", mode, "runtime"}
394 cmd = testenv.Command(t, testenv.GoToolPath(t), args...)
395 b, err = cmd.CombinedOutput()
396 if err != nil {
397 t.Fatalf("build failed (%v): %s", err, b)
398 }
399 mcov := collectInlCands(string(b))
400
401
402
403 for k := range mbase {
404 if _, ok := mcov[k]; !ok {
405 t.Errorf("error: did not find %s in coverage -m output", k)
406 }
407 }
408 }
409 }
410
View as plain text