1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
12 "cmd/asm/internal/arch"
13 "cmd/asm/internal/lex"
17 // A simple in-out test: Do we print what we parse?
19 func setArch(goarch string) (*arch.Arch, *obj.Link) {
20 buildcfg.GOOS = "linux" // obj can handle this OS for all architectures.
21 buildcfg.GOARCH = goarch
22 architecture := arch.Set(goarch)
23 if architecture == nil {
24 panic("asm: unrecognized architecture " + goarch)
26 return architecture, obj.Linknew(architecture.LinkArch)
29 func newParser(goarch string) *Parser {
30 architecture, ctxt := setArch(goarch)
31 return NewParser(ctxt, architecture, nil, false)
34 // tryParse executes parse func in panicOnError=true context.
35 // parse is expected to call any parsing methods that may panic.
36 // Returns error gathered from recover; nil if no parse errors occurred.
38 // For unexpected panics, calls t.Fatal.
39 func tryParse(t *testing.T, parse func()) (err error) {
46 if err, ok = e.(error); e != nil && !ok {
56 func testBadOperandParser(t *testing.T, parser *Parser, tests []badOperandTest) {
57 for _, test := range tests {
58 err := tryParse(t, func() {
59 parser.start(lex.Tokenize(test.input))
66 t.Errorf("fail at %s: got no errors; expected %s\n", test.input, test.error)
67 case !strings.Contains(err.Error(), test.error):
68 t.Errorf("fail at %s: got %s; expected %s", test.input, err, test.error)
73 func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) {
74 for _, test := range tests {
75 parser.start(lex.Tokenize(test.input))
79 if parser.compilingRuntime {
80 result = obj.DconvWithABIDetail(&emptyProg, &addr)
82 result = obj.Dconv(&emptyProg, &addr)
84 if result != test.output {
85 t.Errorf("fail at %s: got %s; expected %s\n", test.input, result, test.output)
90 func TestAMD64OperandParser(t *testing.T) {
91 parser := newParser("amd64")
92 testOperandParser(t, parser, amd64OperandTests)
93 testBadOperandParser(t, parser, amd64BadOperandTests)
94 parser.compilingRuntime = true
95 testOperandParser(t, parser, amd64RuntimeOperandTests)
96 testBadOperandParser(t, parser, amd64BadOperandRuntimeTests)
99 func Test386OperandParser(t *testing.T) {
100 parser := newParser("386")
101 testOperandParser(t, parser, x86OperandTests)
104 func TestARMOperandParser(t *testing.T) {
105 parser := newParser("arm")
106 testOperandParser(t, parser, armOperandTests)
108 func TestARM64OperandParser(t *testing.T) {
109 parser := newParser("arm64")
110 testOperandParser(t, parser, arm64OperandTests)
113 func TestPPC64OperandParser(t *testing.T) {
114 parser := newParser("ppc64")
115 testOperandParser(t, parser, ppc64OperandTests)
118 func TestMIPSOperandParser(t *testing.T) {
119 parser := newParser("mips")
120 testOperandParser(t, parser, mipsOperandTests)
123 func TestMIPS64OperandParser(t *testing.T) {
124 parser := newParser("mips64")
125 testOperandParser(t, parser, mips64OperandTests)
128 func TestS390XOperandParser(t *testing.T) {
129 parser := newParser("s390x")
130 testOperandParser(t, parser, s390xOperandTests)
133 func TestFuncAddress(t *testing.T) {
134 type subtest struct {
138 for _, sub := range []subtest{
139 {"amd64", amd64OperandTests},
140 {"386", x86OperandTests},
141 {"arm", armOperandTests},
142 {"arm64", arm64OperandTests},
143 {"ppc64", ppc64OperandTests},
144 {"mips", mipsOperandTests},
145 {"mips64", mips64OperandTests},
146 {"s390x", s390xOperandTests},
148 t.Run(sub.arch, func(t *testing.T) {
149 parser := newParser(sub.arch)
150 for _, test := range sub.tests {
151 parser.start(lex.Tokenize(test.input))
152 name, _, ok := parser.funcAddress()
154 isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
155 // Ignore static symbols.
156 !strings.Contains(test.input, "<>")
160 // Strip $|* and (SB) and +Int.
161 wantName = test.output[:len(test.output)-4]
162 if strings.HasPrefix(wantName, "$") || strings.HasPrefix(wantName, "*") {
163 wantName = wantName[1:]
165 if i := strings.Index(wantName, "+"); i >= 0 {
166 wantName = wantName[:i]
169 if ok != isFuncSym || name != wantName {
170 t.Errorf("fail at %s as function address: got %s, %v; expected %s, %v", test.input, name, ok, wantName, isFuncSym)
177 type operandTest struct {
181 type badOperandTest struct {
185 // Examples collected by scanning all the assembly in the standard repo.
187 var amd64OperandTests = []operandTest{
188 {"$(-1.0)", "$(-1.0)"},
189 {"$(0.0)", "$(0.0)"},
190 {"$(0x2000000+116)", "$33554548"},
191 {"$(0x3F<<7)", "$8064"},
192 {"$(112+8)", "$120"},
193 {"$(1<<63)", "$-9223372036854775808"},
198 {"$0x000FFFFFFFFFFFFF", "$4503599627370495"},
203 {"$0x7fffffe00000", "$140737486258176"},
204 {"$0xfffffffffffff001", "$-4095"},
209 {"$1000000", "$1000000"},
210 {"$1000000000", "$1000000000"},
211 {"$__tsan_func_enter(SB)", "$__tsan_func_enter(SB)"},
212 {"$main(SB)", "$main(SB)"},
213 {"$masks<>(SB)", "$masks<>(SB)"},
214 {"$setg_gcc<>(SB)", "$setg_gcc<>(SB)"},
215 {"$shifts<>(SB)", "$shifts<>(SB)"},
216 {"$~(1<<63)", "$9223372036854775807"},
219 {"(((8)&0xf)*4)(SP)", "32(SP)"},
220 {"(((8-14)&0xf)*4)(SP)", "40(SP)"},
221 {"(6+8)(AX)", "14(AX)"},
222 {"(8*4)(BP)", "32(BP)"},
224 {"(AX)(CX*8)", "(AX)(CX*8)"},
225 {"(BP)(CX*4)", "(BP)(CX*4)"},
226 {"(BP)(DX*4)", "(BP)(DX*4)"},
227 {"(BP)(R8*4)", "(BP)(R8*4)"},
230 {"(DI)(BX*1)", "(DI)(BX*1)"},
233 {"(R9)(BX*8)", "(R9)(BX*8)"},
235 {"(SI)(BX*1)", "(SI)(BX*1)"},
236 {"(SI)(DX*1)", "(SI)(DX*1)"},
238 {"(SP)(AX*4)", "(SP)(AX*4)"},
239 {"32(SP)(BX*2)", "32(SP)(BX*2)"},
240 {"32323(SP)(R8*4)", "32323(SP)(R8*4)"},
242 {"-1(DI)(BX*1)", "-1(DI)(BX*1)"},
243 {"-3(PC)", "-3(PC)"},
244 {"-64(SI)(BX*1)", "-64(SI)(BX*1)"},
245 {"-96(SI)(BX*1)", "-96(SI)(BX*1)"},
281 {"_expand_key_128<>(SB)", "_expand_key_128<>(SB)"},
282 {"_seek<>(SB)", "_seek<>(SB)"},
283 {"a2+16(FP)", "a2+16(FP)"},
284 {"addr2+24(FP)", "addr2+24(FP)"},
285 {"asmcgocall<>(SB)", "asmcgocall<>(SB)"},
286 {"b+24(FP)", "b+24(FP)"},
287 {"b_len+32(FP)", "b_len+32(FP)"},
288 {"racecall<>(SB)", "racecall<>(SB)"},
289 {"rcv_name+20(FP)", "rcv_name+20(FP)"},
290 {"retoffset+28(FP)", "retoffset+28(FP)"},
291 {"runtime·_GetStdHandle(SB)", "runtime._GetStdHandle(SB)"},
292 {"sync\u2215atomic·AddInt64(SB)", "sync/atomic.AddInt64(SB)"},
293 {"timeout+20(FP)", "timeout+20(FP)"},
294 {"ts+16(FP)", "ts+16(FP)"},
295 {"x+24(FP)", "x+24(FP)"},
296 {"x·y(SB)", "x.y(SB)"},
297 {"x·y(SP)", "x.y(SP)"},
298 {"x·y+8(SB)", "x.y+8(SB)"},
299 {"x·y+8(SP)", "x.y+8(SP)"},
300 {"y+56(FP)", "y+56(FP)"},
301 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
302 {"·callReflect(SB)", "\"\".callReflect(SB)"},
303 {"[X0-X0]", "[X0-X0]"},
304 {"[ Z9 - Z12 ]", "[Z9-Z12]"},
305 {"[X0-AX]", "[X0-AX]"},
306 {"[AX-X0]", "[AX-X0]"},
307 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
310 var amd64RuntimeOperandTests = []operandTest{
311 {"$bar<ABI0>(SB)", "$bar<ABI0>(SB)"},
312 {"$foo<ABIInternal>(SB)", "$foo<ABIInternal>(SB)"},
315 var amd64BadOperandTests = []badOperandTest{
316 {"[", "register list: expected ']', found EOF"},
317 {"[4", "register list: bad low register in `[4`"},
318 {"[]", "register list: bad low register in `[]`"},
319 {"[f-x]", "register list: bad low register in `[f`"},
320 {"[r10-r13]", "register list: bad low register in `[r10`"},
321 {"[k3-k6]", "register list: bad low register in `[k3`"},
322 {"[X0]", "register list: expected '-' after `[X0`, found ']'"},
323 {"[X0-]", "register list: bad high register in `[X0-]`"},
324 {"[X0-x]", "register list: bad high register in `[X0-x`"},
325 {"[X0-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"},
326 {"[X0,X3]", "register list: expected '-' after `[X0`, found ','"},
327 {"[X0,X1,X2,X3]", "register list: expected '-' after `[X0`, found ','"},
328 {"$foo<ABI0>", "ABI selector only permitted when compiling runtime, reference was to \"foo\""},
331 var amd64BadOperandRuntimeTests = []badOperandTest{
332 {"$foo<bletch>", "malformed ABI selector \"bletch\" in reference to \"foo\""},
335 var x86OperandTests = []operandTest{
336 {"$(2.928932188134524e-01)", "$(0.29289321881345243)"},
339 {"$0x00000000", "$0"},
340 {"$runtime·badmcall(SB)", "$runtime.badmcall(SB)"},
341 {"$setg_gcc<>(SB)", "$setg_gcc<>(SB)"},
343 {"(-64*1024+104)(SP)", "-65432(SP)"},
344 {"(0*4)(BP)", "(BP)"},
345 {"(1*4)(DI)", "4(DI)"},
346 {"(4*4)(BP)", "16(BP)"},
348 {"(BP)(CX*4)", "(BP)(CX*4)"},
349 {"(BP*8)", "0(BP*8)"},
352 {"*AX", "AX"}, // TODO: Should make * illegal here; a simple alias for JMP AX.
353 {"*runtime·_GetStdHandle(SB)", "*runtime._GetStdHandle(SB)"},
354 {"-(4+12)(DI)", "-16(DI)"},
355 {"-1(DI)(BX*1)", "-1(DI)(BX*1)"},
356 {"-96(DI)(BX*1)", "-96(DI)(BX*1)"},
380 {"asmcgocall<>(SB)", "asmcgocall<>(SB)"},
381 {"ax+4(FP)", "ax+4(FP)"},
382 {"ptime-12(SP)", "ptime-12(SP)"},
383 {"runtime·_NtWaitForSingleObject(SB)", "runtime._NtWaitForSingleObject(SB)"},
385 {"sec+4(FP)", "sec+4(FP)"},
386 {"shifts<>(SB)(CX*8)", "shifts<>(SB)(CX*8)"},
387 {"x+4(FP)", "x+4(FP)"},
388 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
389 {"·reflectcall(SB)", "\"\".reflectcall(SB)"},
390 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
393 var armOperandTests = []operandTest{
399 {"-12(R4)", "-12(R4)"},
402 {"12(R(1))", "12(R1)"},
403 {"12(R13)", "12(R13)"},
405 {"R0->(32-1)", "R0->31"},
406 {"R0<<R1", "R0<<R1"},
407 {"R0>>R(1)", "R0>>R1"},
408 {"R0@>(32-1)", "R0@>31"},
415 {"R1<<2(R3)", "R1<<2(R3)"},
416 {"R(1)<<2(R(3))", "R1<<2(R3)"},
425 {"[R0,R1,g,R15]", "[R0,R1,g,R15]"},
426 {"[R0-R7]", "[R0,R1,R2,R3,R4,R5,R6,R7]"},
427 {"[R(0)-R(7)]", "[R0,R1,R2,R3,R4,R5,R6,R7]"},
429 {"[R1-R12]", "[R1,R2,R3,R4,R5,R6,R7,R8,R9,g,R11,R12]"},
430 {"armCAS64(SB)", "armCAS64(SB)"},
431 {"asmcgocall<>(SB)", "asmcgocall<>(SB)"},
432 {"c+28(FP)", "c+28(FP)"},
434 {"gosave<>(SB)", "gosave<>(SB)"},
435 {"retlo+12(FP)", "retlo+12(FP)"},
436 {"runtime·gogo(SB)", "runtime.gogo(SB)"},
437 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
438 {"(R1, R3)", "(R1, R3)"},
439 {"[R0,R1,g,R15", ""}, // Issue 11764 - asm hung parsing ']' missing register lists.
440 {"[):[o-FP", ""}, // Issue 12469 - there was no infinite loop for ARM; these are just sanity checks.
442 {"(", ""}, // Issue 12466 - backed up before beginning of line.
445 var ppc64OperandTests = []operandTest{
446 {"$((1<<63)-1)", "$9223372036854775807"},
447 {"$(-64*1024)", "$-65536"},
448 {"$(1024 * 8)", "$8192"},
450 {"$-24(R4)", "$-24(R4)"},
454 {"$0x7000", "$28672"},
455 {"$0x88888eef", "$2290650863"},
457 {"$_main<>(SB)", "$_main<>(SB)"},
458 {"$argframe(FP)", "$argframe(FP)"},
459 {"$runtime·tlsg(SB)", "$runtime.tlsg(SB)"},
461 {"(-288-3*8)(R1)", "-312(R1)"},
462 {"(16)(R7)", "16(R7)"},
469 {"(R5)(R6*1)", "(R5)(R6*1)"},
470 {"(R5+R6)", "(R5)(R6*1)"}, // Old syntax.
471 {"-1(R4)", "-1(R4)"},
472 {"-1(R5)", "-1(R5)"},
621 {"SPR(269)", "SPR(269)"},
624 {"ret+8(FP)", "ret+8(FP)"},
625 {"runtime·abort(SB)", "runtime.abort(SB)"},
626 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
627 {"·trunc(SB)", "\"\".trunc(SB)"},
628 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
631 var arm64OperandTests = []operandTest{
639 {"$1000000000", "$1000000000"},
640 {"$0x7fff3c000", "$34358935552"},
644 {"-16(RSP)", "-16(RSP)"},
645 {"16(RSP)", "16(RSP)"},
647 {"-1(R4)", "-1(R4)"},
648 {"18740(R5)", "18740(R5)"},
650 {"$-24(R4)", "$-24(R4)"},
651 {"-24(RSP)", "-24(RSP)"},
652 {"$24(RSP)", "$24(RSP)"},
653 {"-32(RSP)", "-32(RSP)"},
655 {"$(-64*1024)(R7)", "$-65536(R7)"},
657 {"a+0(FP)", "a(FP)"},
658 {"a1+8(FP)", "a1+8(FP)"},
659 {"·AddInt32(SB)", `"".AddInt32(SB)`},
660 {"runtime·divWVW(SB)", "runtime.divWVW(SB)"},
661 {"$argframe+0(FP)", "$argframe(FP)"},
662 {"$asmcgocall<>(SB)", "$asmcgocall<>(SB)"},
673 {"R18_PLATFORM", "R18"},
674 {"$4503601774854144.0", "$(4503601774854144.0)"},
675 {"$runtime·badsystemstack(SB)", "$runtime.badsystemstack(SB)"},
678 {"(R29, RSP)", "(R29, RSP)"},
679 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
682 var mips64OperandTests = []operandTest{
683 {"$((1<<63)-1)", "$9223372036854775807"},
684 {"$(-64*1024)", "$-65536"},
685 {"$(1024 * 8)", "$8192"},
687 {"$-24(R4)", "$-24(R4)"},
691 {"$0x7000", "$28672"},
692 {"$0x88888eef", "$2290650863"},
694 {"$_main<>(SB)", "$_main<>(SB)"},
695 {"$argframe(FP)", "$argframe(FP)"},
697 {"(-288-3*8)(R1)", "-312(R1)"},
698 {"(16)(R7)", "16(R7)"},
704 {"-1(R4)", "-1(R4)"},
705 {"-1(R5)", "-1(R5)"},
758 {"ret+8(FP)", "ret+8(FP)"},
759 {"runtime·abort(SB)", "runtime.abort(SB)"},
760 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
761 {"·trunc(SB)", "\"\".trunc(SB)"},
762 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
765 var mipsOperandTests = []operandTest{
766 {"$((1<<63)-1)", "$9223372036854775807"},
767 {"$(-64*1024)", "$-65536"},
768 {"$(1024 * 8)", "$8192"},
770 {"$-24(R4)", "$-24(R4)"},
774 {"$0x7000", "$28672"},
775 {"$0x88888eef", "$2290650863"},
777 {"$_main<>(SB)", "$_main<>(SB)"},
778 {"$argframe(FP)", "$argframe(FP)"},
780 {"(-288-3*8)(R1)", "-312(R1)"},
781 {"(16)(R7)", "16(R7)"},
787 {"-1(R4)", "-1(R4)"},
788 {"-1(R5)", "-1(R5)"},
841 {"ret+8(FP)", "ret+8(FP)"},
842 {"runtime·abort(SB)", "runtime.abort(SB)"},
843 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
844 {"·trunc(SB)", "\"\".trunc(SB)"},
845 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
848 var s390xOperandTests = []operandTest{
849 {"$((1<<63)-1)", "$9223372036854775807"},
850 {"$(-64*1024)", "$-65536"},
851 {"$(1024 * 8)", "$8192"},
853 {"$-24(R4)", "$-24(R4)"},
857 {"$0x7000", "$28672"},
858 {"$0x88888eef", "$2290650863"},
860 {"$_main<>(SB)", "$_main<>(SB)"},
861 {"$argframe(FP)", "$argframe(FP)"},
863 {"(-288-3*8)(R1)", "-312(R1)"},
864 {"(16)(R7)", "16(R7)"},
870 {"-1(R4)", "-1(R4)"},
871 {"-1(R5)", "-1(R5)"},
886 // {"R13", "R13"}, R13 is g
939 {"ret+8(FP)", "ret+8(FP)"},
940 {"runtime·abort(SB)", "runtime.abort(SB)"},
941 {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
942 {"·trunc(SB)", "\"\".trunc(SB)"},
943 {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.