1
2
3
4
5 package asm
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "internal/buildcfg"
12 "io/ioutil"
13 "os"
14 "path/filepath"
15 "regexp"
16 "sort"
17 "strconv"
18 "strings"
19 "testing"
20
21 "cmd/asm/internal/lex"
22 "cmd/internal/obj"
23 )
24
25
26
27
28
29 func testEndToEnd(t *testing.T, goarch, file string) {
30 input := filepath.Join("testdata", file+".s")
31 architecture, ctxt := setArch(goarch)
32 architecture.Init(ctxt)
33 lexer := lex.NewLexer(input)
34 parser := NewParser(ctxt, architecture, lexer, false)
35 pList := new(obj.Plist)
36 var ok bool
37 testOut = new(bytes.Buffer)
38 ctxt.Bso = bufio.NewWriter(os.Stdout)
39 ctxt.IsAsm = true
40 defer ctxt.Bso.Flush()
41 failed := false
42 ctxt.DiagFunc = func(format string, args ...interface{}) {
43 failed = true
44 t.Errorf(format, args...)
45 }
46 pList.Firstpc, ok = parser.Parse()
47 if !ok || failed {
48 t.Errorf("asm: %s assembly failed", goarch)
49 return
50 }
51 output := strings.Split(testOut.String(), "\n")
52
53
54 data, err := ioutil.ReadFile(input)
55 if err != nil {
56 t.Error(err)
57 return
58 }
59 lineno := 0
60 seq := 0
61 hexByLine := map[string]string{}
62 lines := strings.SplitAfter(string(data), "\n")
63 Diff:
64 for _, line := range lines {
65 lineno++
66
67
68 if strings.HasPrefix(line, "#include ") {
69 continue
70 }
71
72
73
74
75 parts := strings.Split(line, "//")
76 printed := strings.TrimSpace(parts[0])
77 if printed == "" || strings.HasSuffix(printed, ":") {
78 continue
79 }
80 seq++
81
82 var hexes string
83 switch len(parts) {
84 default:
85 t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line)
86 case 1:
87
88 case 2:
89
90 note := strings.TrimSpace(parts[1])
91 if isHexes(note) {
92 hexes = note
93 } else {
94 printed = note
95 }
96 case 3:
97
98 printed = strings.TrimSpace(parts[1])
99 hexes = strings.TrimSpace(parts[2])
100 if !isHexes(hexes) {
101 t.Errorf("%s:%d: malformed hex instruction encoding: %s", input, lineno, line)
102 }
103 }
104
105 if hexes != "" {
106 hexByLine[fmt.Sprintf("%s:%d", input, lineno)] = hexes
107 }
108
109
110
111
112
113 var buf []byte
114 nest := 0
115 for i := 0; i < len(printed); i++ {
116 c := printed[i]
117 switch c {
118 case '{', '[':
119 nest++
120 case '}', ']':
121 nest--
122 case ',':
123 buf = append(buf, ',')
124 if nest == 0 {
125 buf = append(buf, ' ')
126 }
127 for i+1 < len(printed) && (printed[i+1] == ' ' || printed[i+1] == '\t') {
128 i++
129 }
130 continue
131 }
132 buf = append(buf, c)
133 }
134
135 f := strings.Fields(string(buf))
136
137
138
139
140 if len(f) > 0 && strings.HasSuffix(printed, "(PC)") {
141 last := f[len(f)-1]
142 n, err := strconv.Atoi(last[:len(last)-len("(PC)")])
143 if err == nil {
144 f[len(f)-1] = fmt.Sprintf("%d(PC)", seq+n)
145 }
146 }
147
148 if len(f) == 1 {
149 printed = f[0]
150 } else {
151 printed = f[0] + "\t" + strings.Join(f[1:], " ")
152 }
153
154 want := fmt.Sprintf("%05d (%s:%d)\t%s", seq, input, lineno, printed)
155 for len(output) > 0 && (output[0] < want || output[0] != want && len(output[0]) >= 5 && output[0][:5] == want[:5]) {
156 if len(output[0]) >= 5 && output[0][:5] == want[:5] {
157 t.Errorf("mismatched output:\nhave %s\nwant %s", output[0], want)
158 output = output[1:]
159 continue Diff
160 }
161 t.Errorf("unexpected output: %q", output[0])
162 output = output[1:]
163 }
164 if len(output) > 0 && output[0] == want {
165 output = output[1:]
166 } else {
167 t.Errorf("missing output: %q", want)
168 }
169 }
170 for len(output) > 0 {
171 if output[0] == "" {
172
173 output = output[1:]
174 continue
175 }
176 t.Errorf("unexpected output: %q", output[0])
177 output = output[1:]
178 }
179
180
181
182
183 top := pList.Firstpc
184 var text *obj.LSym
185 ok = true
186 ctxt.DiagFunc = func(format string, args ...interface{}) {
187 t.Errorf(format, args...)
188 ok = false
189 }
190 obj.Flushplist(ctxt, pList, nil, "")
191
192 for p := top; p != nil; p = p.Link {
193 if p.As == obj.ATEXT {
194 text = p.From.Sym
195 }
196 hexes := hexByLine[p.Line()]
197 if hexes == "" {
198 continue
199 }
200 delete(hexByLine, p.Line())
201 if text == nil {
202 t.Errorf("%s: instruction outside TEXT", p)
203 }
204 size := int64(len(text.P)) - p.Pc
205 if p.Link != nil {
206 size = p.Link.Pc - p.Pc
207 } else if p.Isize != 0 {
208 size = int64(p.Isize)
209 }
210 var code []byte
211 if p.Pc < int64(len(text.P)) {
212 code = text.P[p.Pc:]
213 if size < int64(len(code)) {
214 code = code[:size]
215 }
216 }
217 codeHex := fmt.Sprintf("%x", code)
218 if codeHex == "" {
219 codeHex = "empty"
220 }
221 ok := false
222 for _, hex := range strings.Split(hexes, " or ") {
223 if codeHex == hex {
224 ok = true
225 break
226 }
227 }
228 if !ok {
229 t.Errorf("%s: have encoding %s, want %s", p, codeHex, hexes)
230 }
231 }
232
233 if len(hexByLine) > 0 {
234 var missing []string
235 for key := range hexByLine {
236 missing = append(missing, key)
237 }
238 sort.Strings(missing)
239 for _, line := range missing {
240 t.Errorf("%s: did not find instruction encoding", line)
241 }
242 }
243
244 }
245
246 func isHexes(s string) bool {
247 if s == "" {
248 return false
249 }
250 if s == "empty" {
251 return true
252 }
253 for _, f := range strings.Split(s, " or ") {
254 if f == "" || len(f)%2 != 0 || strings.TrimLeft(f, "0123456789abcdef") != "" {
255 return false
256 }
257 }
258 return true
259 }
260
261
262
263
264
265 var fileLineRE = regexp.MustCompile(`(?:^|\()(testdata[/\\][0-9a-z]+\.s:[0-9]+)(?:$|\)|:)`)
266
267
268 var (
269 errRE = regexp.MustCompile(`// ERROR ?(.*)`)
270 errQuotesRE = regexp.MustCompile(`"([^"]*)"`)
271 )
272
273 func testErrors(t *testing.T, goarch, file string, flags ...string) {
274 input := filepath.Join("testdata", file+".s")
275 architecture, ctxt := setArch(goarch)
276 lexer := lex.NewLexer(input)
277 parser := NewParser(ctxt, architecture, lexer, false)
278 pList := new(obj.Plist)
279 var ok bool
280 testOut = new(bytes.Buffer)
281 ctxt.Bso = bufio.NewWriter(os.Stdout)
282 ctxt.IsAsm = true
283 defer ctxt.Bso.Flush()
284 failed := false
285 var errBuf bytes.Buffer
286 parser.errorWriter = &errBuf
287 ctxt.DiagFunc = func(format string, args ...interface{}) {
288 failed = true
289 s := fmt.Sprintf(format, args...)
290 if !strings.HasSuffix(s, "\n") {
291 s += "\n"
292 }
293 errBuf.WriteString(s)
294 }
295 for _, flag := range flags {
296 switch flag {
297 case "dynlink":
298 ctxt.Flag_dynlink = true
299 default:
300 t.Errorf("unknown flag %s", flag)
301 }
302 }
303 pList.Firstpc, ok = parser.Parse()
304 obj.Flushplist(ctxt, pList, nil, "")
305 if ok && !failed {
306 t.Errorf("asm: %s had no errors", file)
307 }
308
309 errors := map[string]string{}
310 for _, line := range strings.Split(errBuf.String(), "\n") {
311 if line == "" || strings.HasPrefix(line, "\t") {
312 continue
313 }
314 m := fileLineRE.FindStringSubmatch(line)
315 if m == nil {
316 t.Errorf("unexpected error: %v", line)
317 continue
318 }
319 fileline := m[1]
320 if errors[fileline] != "" && errors[fileline] != line {
321 t.Errorf("multiple errors on %s:\n\t%s\n\t%s", fileline, errors[fileline], line)
322 continue
323 }
324 errors[fileline] = line
325 }
326
327
328 data, err := ioutil.ReadFile(input)
329 if err != nil {
330 t.Error(err)
331 return
332 }
333 lineno := 0
334 lines := strings.Split(string(data), "\n")
335 for _, line := range lines {
336 lineno++
337
338 fileline := fmt.Sprintf("%s:%d", input, lineno)
339 if m := errRE.FindStringSubmatch(line); m != nil {
340 all := m[1]
341 mm := errQuotesRE.FindAllStringSubmatch(all, -1)
342 if len(mm) != 1 {
343 t.Errorf("%s: invalid errorcheck line:\n%s", fileline, line)
344 } else if err := errors[fileline]; err == "" {
345 t.Errorf("%s: missing error, want %s", fileline, all)
346 } else if !strings.Contains(err, mm[0][1]) {
347 t.Errorf("%s: wrong error for %s:\n%s", fileline, all, err)
348 }
349 } else {
350 if errors[fileline] != "" {
351 t.Errorf("unexpected error on %s: %v", fileline, errors[fileline])
352 }
353 }
354 delete(errors, fileline)
355 }
356 var extra []string
357 for key := range errors {
358 extra = append(extra, key)
359 }
360 sort.Strings(extra)
361 for _, fileline := range extra {
362 t.Errorf("unexpected error on %s: %v", fileline, errors[fileline])
363 }
364 }
365
366 func Test386EndToEnd(t *testing.T) {
367 testEndToEnd(t, "386", "386")
368 }
369
370 func TestARMEndToEnd(t *testing.T) {
371 defer func(old int) { buildcfg.GOARM = old }(buildcfg.GOARM)
372 for _, goarm := range []int{5, 6, 7} {
373 t.Logf("GOARM=%d", goarm)
374 buildcfg.GOARM = goarm
375 testEndToEnd(t, "arm", "arm")
376 if goarm == 6 {
377 testEndToEnd(t, "arm", "armv6")
378 }
379 }
380 }
381
382 func TestGoBuildErrors(t *testing.T) {
383 testErrors(t, "amd64", "buildtagerror")
384 }
385
386 func TestARMErrors(t *testing.T) {
387 testErrors(t, "arm", "armerror")
388 }
389
390 func TestARM64EndToEnd(t *testing.T) {
391 testEndToEnd(t, "arm64", "arm64")
392 }
393
394 func TestARM64Encoder(t *testing.T) {
395 testEndToEnd(t, "arm64", "arm64enc")
396 }
397
398 func TestARM64Errors(t *testing.T) {
399 testErrors(t, "arm64", "arm64error")
400 }
401
402 func TestAMD64EndToEnd(t *testing.T) {
403 testEndToEnd(t, "amd64", "amd64")
404 }
405
406 func Test386Encoder(t *testing.T) {
407 testEndToEnd(t, "386", "386enc")
408 }
409
410 func TestAMD64Encoder(t *testing.T) {
411 filenames := [...]string{
412 "amd64enc",
413 "amd64enc_extra",
414 "avx512enc/aes_avx512f",
415 "avx512enc/gfni_avx512f",
416 "avx512enc/vpclmulqdq_avx512f",
417 "avx512enc/avx512bw",
418 "avx512enc/avx512cd",
419 "avx512enc/avx512dq",
420 "avx512enc/avx512er",
421 "avx512enc/avx512f",
422 "avx512enc/avx512pf",
423 "avx512enc/avx512_4fmaps",
424 "avx512enc/avx512_4vnniw",
425 "avx512enc/avx512_bitalg",
426 "avx512enc/avx512_ifma",
427 "avx512enc/avx512_vbmi",
428 "avx512enc/avx512_vbmi2",
429 "avx512enc/avx512_vnni",
430 "avx512enc/avx512_vpopcntdq",
431 }
432 for _, name := range filenames {
433 testEndToEnd(t, "amd64", name)
434 }
435 }
436
437 func TestAMD64Errors(t *testing.T) {
438 testErrors(t, "amd64", "amd64error")
439 }
440
441 func TestAMD64DynLinkErrors(t *testing.T) {
442 testErrors(t, "amd64", "amd64dynlinkerror", "dynlink")
443 }
444
445 func TestMIPSEndToEnd(t *testing.T) {
446 testEndToEnd(t, "mips", "mips")
447 testEndToEnd(t, "mips64", "mips64")
448 }
449
450 func TestPPC64EndToEnd(t *testing.T) {
451 testEndToEnd(t, "ppc64", "ppc64")
452 }
453
454 func TestRISCVEndToEnd(t *testing.T) {
455 testEndToEnd(t, "riscv64", "riscv64")
456 }
457
458 func TestRISCVErrors(t *testing.T) {
459 testErrors(t, "riscv64", "riscv64error")
460 }
461
462 func TestS390XEndToEnd(t *testing.T) {
463 testEndToEnd(t, "s390x", "s390x")
464 }
465
View as plain text