1
2
3
4
5 package test
6
7 import (
8 "bufio"
9 "internal/testenv"
10 "io"
11 "math/bits"
12 "os/exec"
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 "fastrand",
48 "float64bits",
49 "funcspdelta",
50 "getArgInfoFast",
51 "getm",
52 "getMCache",
53 "isDirectIface",
54 "itabHashFunc",
55 "noescape",
56 "pcvalueCacheKey",
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 "heapBits.bits",
76 "heapBits.isPointer",
77 "heapBits.morePointers",
78 "heapBits.next",
79 "heapBitsForAddr",
80 "markBits.isMarked",
81 "muintptr.ptr",
82 "puintptr.ptr",
83 "spanOf",
84 "spanOfUnchecked",
85 "(*gcWork).putFast",
86 "(*gcWork).tryGetFast",
87 "(*guintptr).set",
88 "(*markBits).advance",
89 "(*mspan).allocBitsForIndex",
90 "(*mspan).base",
91 "(*mspan).markBitsForBase",
92 "(*mspan).markBitsForIndex",
93 "(*muintptr).set",
94 "(*puintptr).set",
95 },
96 "runtime/internal/sys": {},
97 "runtime/internal/math": {
98 "MulUintptr",
99 },
100 "bytes": {
101 "(*Buffer).Bytes",
102 "(*Buffer).Cap",
103 "(*Buffer).Len",
104 "(*Buffer).Grow",
105 "(*Buffer).Next",
106 "(*Buffer).Read",
107 "(*Buffer).ReadByte",
108 "(*Buffer).Reset",
109 "(*Buffer).String",
110 "(*Buffer).UnreadByte",
111 "(*Buffer).tryGrowByReslice",
112 },
113 "compress/flate": {
114 "byLiteral.Len",
115 "byLiteral.Less",
116 "byLiteral.Swap",
117 "(*dictDecoder).tryWriteCopy",
118 },
119 "encoding/base64": {
120 "assemble32",
121 "assemble64",
122 },
123 "unicode/utf8": {
124 "FullRune",
125 "FullRuneInString",
126 "RuneLen",
127 "AppendRune",
128 "ValidRune",
129 },
130 "reflect": {
131 "Value.CanInt",
132 "Value.CanUint",
133 "Value.CanFloat",
134 "Value.CanComplex",
135 "Value.CanAddr",
136 "Value.CanSet",
137 "Value.CanInterface",
138 "Value.IsValid",
139 "Value.pointer",
140 "add",
141 "align",
142 "flag.mustBe",
143 "flag.mustBeAssignable",
144 "flag.mustBeExported",
145 "flag.kind",
146 "flag.ro",
147 },
148 "regexp": {
149 "(*bitState).push",
150 },
151 "math/big": {
152 "bigEndianWord",
153
154 "addVW",
155 "subVW",
156 },
157 "math/rand": {
158 "(*rngSource).Int63",
159 "(*rngSource).Uint64",
160 },
161 "net": {
162 "(*UDPConn).ReadFromUDP",
163 },
164 }
165
166 if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" {
167
168
169
170
171 want["runtime"] = append(want["runtime"], "nextFreeFast")
172 }
173 if runtime.GOARCH != "386" {
174
175
176 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz64")
177 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Ctz32")
178 want["runtime/internal/sys"] = append(want["runtime/internal/sys"], "Bswap32")
179 }
180 if bits.UintSize == 64 {
181
182 want["runtime"] = append(want["runtime"], "mix")
183 }
184
185 switch runtime.GOARCH {
186 case "386", "wasm", "arm":
187 default:
188
189
190
191
192 want["sync"] = []string{
193 "(*Mutex).Lock",
194 "(*Mutex).Unlock",
195 "(*RWMutex).RLock",
196 "(*RWMutex).RUnlock",
197 "(*Once).Do",
198 }
199 }
200
201
202 must := map[string]bool{
203 "compress/flate.byLiteral.Len": true,
204 "compress/flate.byLiteral.Less": true,
205 "compress/flate.byLiteral.Swap": true,
206 }
207
208 notInlinedReason := make(map[string]string)
209 pkgs := make([]string, 0, len(want))
210 for pname, fnames := range want {
211 pkgs = append(pkgs, pname)
212 for _, fname := range fnames {
213 fullName := pname + "." + fname
214 if _, ok := notInlinedReason[fullName]; ok {
215 t.Errorf("duplicate func: %s", fullName)
216 }
217 notInlinedReason[fullName] = "unknown reason"
218 }
219 }
220
221 args := append([]string{"build", "-a", "-gcflags=all=-m -m", "-tags=math_big_pure_go"}, pkgs...)
222 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...))
223 pr, pw := io.Pipe()
224 cmd.Stdout = pw
225 cmd.Stderr = pw
226 cmdErr := make(chan error, 1)
227 go func() {
228 cmdErr <- cmd.Run()
229 pw.Close()
230 }()
231 scanner := bufio.NewScanner(pr)
232 curPkg := ""
233 canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
234 haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
235 cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
236 for scanner.Scan() {
237 line := scanner.Text()
238 if strings.HasPrefix(line, "# ") {
239 curPkg = line[2:]
240 continue
241 }
242 if m := haveInlined.FindStringSubmatch(line); m != nil {
243 fname := m[1]
244 delete(notInlinedReason, curPkg+"."+fname)
245 continue
246 }
247 if m := canInline.FindStringSubmatch(line); m != nil {
248 fname := m[1]
249 fullname := curPkg + "." + fname
250
251 if _, ok := must[fullname]; !ok {
252 delete(notInlinedReason, fullname)
253 continue
254 }
255 }
256 if m := cannotInline.FindStringSubmatch(line); m != nil {
257 fname, reason := m[1], m[2]
258 fullName := curPkg + "." + fname
259 if _, ok := notInlinedReason[fullName]; ok {
260
261 notInlinedReason[fullName] = reason
262 }
263 continue
264 }
265 }
266 if err := <-cmdErr; err != nil {
267 t.Fatal(err)
268 }
269 if err := scanner.Err(); err != nil {
270 t.Fatal(err)
271 }
272 for fullName, reason := range notInlinedReason {
273 t.Errorf("%s was not inlined: %s", fullName, reason)
274 }
275 }
276
View as plain text