1// Copyright 2017 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.
4
5package arm64asm
6
7import (
8	"fmt"
9	"io"
10	"sort"
11	"strings"
12)
13
14// GoSyntax returns the Go assembler syntax for the instruction.
15// The syntax was originally defined by Plan 9.
16// The pc is the program counter of the instruction, used for
17// expanding PC-relative addresses into absolute ones.
18// The symname function queries the symbol table for the program
19// being disassembled. Given a target address it returns the name
20// and base address of the symbol containing the target, if any;
21// otherwise it returns "", 0.
22// The reader text should read from the text segment using text addresses
23// as offsets; it is used to display pc-relative loads as constant loads.
24func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
25	if symname == nil {
26		symname = func(uint64) (string, uint64) { return "", 0 }
27	}
28
29	var args []string
30	for _, a := range inst.Args {
31		if a == nil {
32			break
33		}
34		args = append(args, plan9Arg(&inst, pc, symname, a))
35	}
36
37	op := inst.Op.String()
38
39	switch inst.Op {
40	case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW:
41		// Check for PC-relative load.
42		if offset, ok := inst.Args[1].(PCRel); ok {
43			addr := pc + uint64(offset)
44			if _, ok := inst.Args[0].(Reg); !ok {
45				break
46			}
47			if s, base := symname(addr); s != "" && addr == base {
48				args[1] = fmt.Sprintf("$%s(SB)", s)
49			}
50		}
51	}
52
53	// Move addressing mode into opcode suffix.
54	suffix := ""
55	switch inst.Op {
56	case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW, STR, STRB, STRH, STUR, STURB, STURH, LD1, ST1:
57		switch mem := inst.Args[1].(type) {
58		case MemImmediate:
59			switch mem.Mode {
60			case AddrOffset:
61				// no suffix
62			case AddrPreIndex:
63				suffix = ".W"
64			case AddrPostIndex, AddrPostReg:
65				suffix = ".P"
66			}
67		}
68
69	case STP, LDP:
70		switch mem := inst.Args[2].(type) {
71		case MemImmediate:
72			switch mem.Mode {
73			case AddrOffset:
74				// no suffix
75			case AddrPreIndex:
76				suffix = ".W"
77			case AddrPostIndex:
78				suffix = ".P"
79			}
80		}
81	}
82
83	switch inst.Op {
84	case BL:
85		return "CALL " + args[0]
86
87	case BLR:
88		r := inst.Args[0].(Reg)
89		regno := uint16(r) & 31
90		return fmt.Sprintf("CALL (R%d)", regno)
91
92	case RET:
93		if r, ok := inst.Args[0].(Reg); ok && r == X30 {
94			return "RET"
95		}
96
97	case B:
98		if cond, ok := inst.Args[0].(Cond); ok {
99			return "B" + cond.String() + " " + args[1]
100		}
101		return "JMP" + " " + args[0]
102
103	case BR:
104		r := inst.Args[0].(Reg)
105		regno := uint16(r) & 31
106		return fmt.Sprintf("JMP (R%d)", regno)
107
108	case MOV:
109		rno := -1
110		switch a := inst.Args[0].(type) {
111		case Reg:
112			rno = int(a)
113		case RegSP:
114			rno = int(a)
115		case RegisterWithArrangementAndIndex:
116			op = "VMOV"
117		case RegisterWithArrangement:
118			op = "VMOV"
119		}
120		if rno >= 0 && rno <= int(WZR) {
121			op = "MOVW"
122		} else if rno >= int(X0) && rno <= int(XZR) {
123			op = "MOVD"
124		}
125		if _, ok := inst.Args[1].(RegisterWithArrangementAndIndex); ok {
126			op = "VMOV"
127		}
128
129	case LDR:
130		var rno uint16
131		if r, ok := inst.Args[0].(Reg); ok {
132			rno = uint16(r)
133		} else {
134			rno = uint16(inst.Args[0].(RegSP))
135		}
136		if rno <= uint16(WZR) {
137			op = "MOVWU" + suffix
138		} else if rno >= uint16(S0) && rno <= uint16(S31) {
139			op = "FMOVS" + suffix
140			args[0] = fmt.Sprintf("F%d", rno&31)
141		} else if rno >= uint16(D0) && rno <= uint16(D31) {
142			op = "FMOVD" + suffix
143			args[0] = fmt.Sprintf("F%d", rno&31)
144		} else {
145			op = "MOVD" + suffix
146		}
147
148	case LDRB:
149		op = "MOVBU" + suffix
150
151	case LDRH:
152		op = "MOVHU" + suffix
153
154	case LDRSW:
155		op = "MOVW" + suffix
156
157	case LDRSB:
158		if r, ok := inst.Args[0].(Reg); ok {
159			rno := uint16(r)
160			if rno <= uint16(WZR) {
161				op = "MOVBW" + suffix
162			} else {
163				op = "MOVB" + suffix
164			}
165		}
166	case LDRSH:
167		if r, ok := inst.Args[0].(Reg); ok {
168			rno := uint16(r)
169			if rno <= uint16(WZR) {
170				op = "MOVHW" + suffix
171			} else {
172				op = "MOVH" + suffix
173			}
174		}
175	case STR, STUR:
176		var rno uint16
177		if r, ok := inst.Args[0].(Reg); ok {
178			rno = uint16(r)
179		} else {
180			rno = uint16(inst.Args[0].(RegSP))
181		}
182		if rno <= uint16(WZR) {
183			op = "MOVW" + suffix
184		} else if rno >= uint16(S0) && rno <= uint16(S31) {
185			op = "FMOVS" + suffix
186			args[0] = fmt.Sprintf("F%d", rno&31)
187		} else if rno >= uint16(D0) && rno <= uint16(D31) {
188			op = "FMOVD" + suffix
189			args[0] = fmt.Sprintf("F%d", rno&31)
190		} else {
191			op = "MOVD" + suffix
192		}
193		args[0], args[1] = args[1], args[0]
194
195	case STRB, STURB:
196		op = "MOVB" + suffix
197		args[0], args[1] = args[1], args[0]
198
199	case STRH, STURH:
200		op = "MOVH" + suffix
201		args[0], args[1] = args[1], args[0]
202
203	case TBNZ, TBZ:
204		args[0], args[1], args[2] = args[2], args[0], args[1]
205
206	case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL:
207		if r, ok := inst.Args[0].(Reg); ok {
208			rno := uint16(r)
209			if rno <= uint16(WZR) {
210				op += "W"
211			}
212		}
213		args[2], args[3] = args[3], args[2]
214	case STLR:
215		if r, ok := inst.Args[0].(Reg); ok {
216			rno := uint16(r)
217			if rno <= uint16(WZR) {
218				op += "W"
219			}
220		}
221		args[0], args[1] = args[1], args[0]
222
223	case STLRB, STLRH:
224		args[0], args[1] = args[1], args[0]
225
226	case STLXR, STXR:
227		if r, ok := inst.Args[1].(Reg); ok {
228			rno := uint16(r)
229			if rno <= uint16(WZR) {
230				op += "W"
231			}
232		}
233		args[1], args[2] = args[2], args[1]
234
235	case STLXRB, STLXRH, STXRB, STXRH:
236		args[1], args[2] = args[2], args[1]
237
238	case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX:
239		if r, ok := inst.Args[0].(Reg); ok {
240			rno := uint16(r)
241			if rno <= uint16(WZR) {
242				op += "W"
243			}
244		}
245		args[1], args[2], args[3] = args[3], args[1], args[2]
246
247	case LDAXP, LDXP:
248		if r, ok := inst.Args[0].(Reg); ok {
249			rno := uint16(r)
250			if rno <= uint16(WZR) {
251				op += "W"
252			}
253		}
254		fallthrough
255
256	case STP, LDP:
257		args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
258		args[1] = args[2]
259		if op == "STP" {
260			op = op + suffix
261			return op + " " + args[0] + ", " + args[1]
262		} else if op == "LDP" {
263			op = op + suffix
264			return op + " " + args[1] + ", " + args[0]
265		} else if op == "LDAXP" || op == "LDXP" || op == "LDAXPW" || op == "LDXPW" {
266			return op + " " + args[1] + ", " + args[0]
267		}
268
269	case STLXP, STXP:
270		if r, ok := inst.Args[1].(Reg); ok {
271			rno := uint16(r)
272			if rno <= uint16(WZR) {
273				op += "W"
274			}
275		}
276		args[1] = fmt.Sprintf("(%s, %s)", args[1], args[2])
277		args[2] = args[3]
278		return op + " " + args[1] + ", " + args[2] + ", " + args[0]
279
280	case FCCMP, FCCMPE:
281		args[0], args[1] = args[1], args[0]
282		fallthrough
283
284	case FCMP, FCMPE:
285		if _, ok := inst.Args[1].(Imm); ok {
286			args[1] = "$(0.0)"
287		}
288		fallthrough
289
290	case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL, FMADD, FMSUB, FNMADD, FNMSUB:
291		if strings.HasSuffix(op, "MADD") || strings.HasSuffix(op, "MSUB") {
292			args[2], args[3] = args[3], args[2]
293		}
294		if r, ok := inst.Args[0].(Reg); ok {
295			rno := uint16(r)
296			if rno >= uint16(S0) && rno <= uint16(S31) {
297				op = fmt.Sprintf("%sS", op)
298			} else if rno >= uint16(D0) && rno <= uint16(D31) {
299				op = fmt.Sprintf("%sD", op)
300			}
301		}
302
303	case FCVT:
304		for i := 1; i >= 0; i-- {
305			if r, ok := inst.Args[i].(Reg); ok {
306				rno := uint16(r)
307				if rno >= uint16(H0) && rno <= uint16(H31) {
308					op = fmt.Sprintf("%sH", op)
309				} else if rno >= uint16(S0) && rno <= uint16(S31) {
310					op = fmt.Sprintf("%sS", op)
311				} else if rno >= uint16(D0) && rno <= uint16(D31) {
312					op = fmt.Sprintf("%sD", op)
313				}
314			}
315		}
316
317	case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI:
318		if r, ok := inst.Args[1].(Reg); ok {
319			rno := uint16(r)
320			if rno >= uint16(S0) && rno <= uint16(S31) {
321				op = fmt.Sprintf("%sS", op)
322			} else if rno >= uint16(D0) && rno <= uint16(D31) {
323				op = fmt.Sprintf("%sD", op)
324			}
325		}
326
327	case FCVTZS, FCVTZU, SCVTF, UCVTF:
328		if _, ok := inst.Args[2].(Imm); !ok {
329			for i := 1; i >= 0; i-- {
330				if r, ok := inst.Args[i].(Reg); ok {
331					rno := uint16(r)
332					if rno >= uint16(S0) && rno <= uint16(S31) {
333						op = fmt.Sprintf("%sS", op)
334					} else if rno >= uint16(D0) && rno <= uint16(D31) {
335						op = fmt.Sprintf("%sD", op)
336					} else if rno <= uint16(WZR) {
337						op += "W"
338					}
339				}
340			}
341		}
342
343	case FMOV:
344		for i := 0; i <= 1; i++ {
345			if r, ok := inst.Args[i].(Reg); ok {
346				rno := uint16(r)
347				if rno >= uint16(S0) && rno <= uint16(S31) {
348					op = fmt.Sprintf("%sS", op)
349					break
350				} else if rno >= uint16(D0) && rno <= uint16(D31) {
351					op = fmt.Sprintf("%sD", op)
352					break
353				}
354			}
355		}
356
357	case SYSL:
358		op1 := int(inst.Args[1].(Imm).Imm)
359		cn := int(inst.Args[2].(Imm_c))
360		cm := int(inst.Args[3].(Imm_c))
361		op2 := int(inst.Args[4].(Imm).Imm)
362		sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5)
363		args[1] = fmt.Sprintf("$%d", sysregno)
364		return op + " " + args[1] + ", " + args[0]
365
366	case CBNZ, CBZ:
367		if r, ok := inst.Args[0].(Reg); ok {
368			rno := uint16(r)
369			if rno <= uint16(WZR) {
370				op += "W"
371			}
372		}
373		args[0], args[1] = args[1], args[0]
374
375	case ADR, ADRP:
376		addr := int64(inst.Args[1].(PCRel))
377		args[1] = fmt.Sprintf("%d(PC)", addr)
378
379	case MSR:
380		args[0] = inst.Args[0].String()
381
382	case ST1:
383		op = fmt.Sprintf("V%s", op) + suffix
384		args[0], args[1] = args[1], args[0]
385
386	case LD1:
387		op = fmt.Sprintf("V%s", op) + suffix
388
389	case UMOV:
390		op = "VMOV"
391
392	default:
393		index := sort.SearchStrings(noSuffixOpSet, op)
394		if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) {
395			rno := -1
396			switch a := inst.Args[0].(type) {
397			case Reg:
398				rno = int(a)
399			case RegSP:
400				rno = int(a)
401			case RegisterWithArrangement:
402				op = fmt.Sprintf("V%s", op)
403			}
404
405			if rno >= int(B0) && rno <= int(Q31) && !strings.HasPrefix(op, "F") {
406				op = fmt.Sprintf("V%s", op)
407			}
408			if rno >= 0 && rno <= int(WZR) {
409				// Add "w" to opcode suffix.
410				op += "W"
411			}
412		}
413		op = op + suffix
414	}
415
416	// conditional instructions, replace args.
417	if _, ok := inst.Args[3].(Cond); ok {
418		if _, ok := inst.Args[2].(Reg); ok {
419			args[1], args[2] = args[2], args[1]
420		} else {
421			args[0], args[2] = args[2], args[0]
422		}
423	}
424	// Reverse args, placing dest last.
425	for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
426		args[i], args[j] = args[j], args[i]
427	}
428
429	if args != nil {
430		op += " " + strings.Join(args, ", ")
431	}
432
433	return op
434}
435
436// No need add "W" to opcode suffix.
437// Opcode must be inserted in ascending order.
438var noSuffixOpSet = strings.Fields(`
439AESD
440AESE
441AESIMC
442AESMC
443CRC32B
444CRC32CB
445CRC32CH
446CRC32CW
447CRC32CX
448CRC32H
449CRC32W
450CRC32X
451LDARB
452LDARH
453LDAXRB
454LDAXRH
455LDTRH
456LDXRB
457LDXRH
458SHA1C
459SHA1H
460SHA1M
461SHA1P
462SHA1SU0
463SHA1SU1
464SHA256H
465SHA256H2
466SHA256SU0
467SHA256SU1
468`)
469
470func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
471	switch a := arg.(type) {
472	case Imm:
473		return fmt.Sprintf("$%d", uint32(a.Imm))
474
475	case Imm64:
476		return fmt.Sprintf("$%d", int64(a.Imm))
477
478	case ImmShift:
479		if a.shift == 0 {
480			return fmt.Sprintf("$%d", a.imm)
481		}
482		return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift)
483
484	case PCRel:
485		addr := int64(pc) + int64(a)
486		if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
487			return fmt.Sprintf("%s(SB)", s)
488		}
489		return fmt.Sprintf("%d(PC)", a/4)
490
491	case Reg:
492		regenum := uint16(a)
493		regno := uint16(a) & 31
494
495		if regenum >= uint16(B0) && regenum <= uint16(D31) {
496			if strings.HasPrefix(inst.Op.String(), "F") || strings.HasSuffix(inst.Op.String(), "CVTF") {
497				// FP registers are the same ones as SIMD registers
498				// Print Fn for scalar variant to align with assembler (e.g., FCVT, SCVTF, UCVTF, etc.)
499				return fmt.Sprintf("F%d", regno)
500			} else {
501				return fmt.Sprintf("V%d", regno)
502			}
503
504		} else if regenum >= uint16(Q0) && regenum <= uint16(Q31) {
505			// Print Vn to align with assembler (e.g., SHA256H)
506			return fmt.Sprintf("V%d", regno)
507		}
508
509		if regno == 31 {
510			return "ZR"
511		}
512		return fmt.Sprintf("R%d", regno)
513
514	case RegSP:
515		regno := uint16(a) & 31
516		if regno == 31 {
517			return "RSP"
518		}
519		return fmt.Sprintf("R%d", regno)
520
521	case RegExtshiftAmount:
522		reg := ""
523		regno := uint16(a.reg) & 31
524		if regno == 31 {
525			reg = "ZR"
526		} else {
527			reg = fmt.Sprintf("R%d", uint16(a.reg)&31)
528		}
529		extshift := ""
530		amount := ""
531		if a.extShift != ExtShift(0) {
532			switch a.extShift {
533			default:
534				extshift = "." + a.extShift.String()
535
536			case lsl:
537				extshift = "<<"
538				amount = fmt.Sprintf("%d", a.amount)
539				return reg + extshift + amount
540
541			case lsr:
542				extshift = ">>"
543				amount = fmt.Sprintf("%d", a.amount)
544				return reg + extshift + amount
545
546			case asr:
547				extshift = "->"
548				amount = fmt.Sprintf("%d", a.amount)
549				return reg + extshift + amount
550			case ror:
551				extshift = "@>"
552				amount = fmt.Sprintf("%d", a.amount)
553				return reg + extshift + amount
554			}
555			if a.amount != 0 {
556				amount = fmt.Sprintf("<<%d", a.amount)
557			}
558		}
559		return reg + extshift + amount
560
561	case MemImmediate:
562		off := ""
563		base := ""
564		regno := uint16(a.Base) & 31
565		if regno == 31 {
566			base = "(RSP)"
567		} else {
568			base = fmt.Sprintf("(R%d)", regno)
569		}
570		if a.imm != 0 && a.Mode != AddrPostReg {
571			off = fmt.Sprintf("%d", a.imm)
572		} else if a.Mode == AddrPostReg {
573			postR := fmt.Sprintf("(R%d)", a.imm)
574			return base + postR
575		}
576		return off + base
577
578	case MemExtend:
579		base := ""
580		index := ""
581		indexreg := ""
582		regno := uint16(a.Base) & 31
583		if regno == 31 {
584			base = "(RSP)"
585		} else {
586			base = fmt.Sprintf("(R%d)", regno)
587		}
588		regno = uint16(a.Index) & 31
589		if regno == 31 {
590			indexreg = "ZR"
591		} else {
592			indexreg = fmt.Sprintf("R%d", regno)
593		}
594
595		if a.Extend == lsl {
596			// a.Amount indicates the index shift amount, encoded in "S" field.
597			// a.ShiftMustBeZero is set true when the index shift amount must be 0,
598			// even if the a.Amount field is not 0.
599			// When a.ShiftMustBeZero is ture, GNU syntax prints #0 shift amount if
600			// "S" equals to 1, or does not print #0 shift amount if "S" equals to 0.
601			// Go syntax should never print a zero index shift amount.
602			if a.Amount != 0 && !a.ShiftMustBeZero {
603				index = fmt.Sprintf("(%s<<%d)", indexreg, a.Amount)
604			} else {
605				index = fmt.Sprintf("(%s)", indexreg)
606			}
607		} else {
608			if a.Amount != 0 && !a.ShiftMustBeZero {
609				index = fmt.Sprintf("(%s.%s<<%d)", indexreg, a.Extend.String(), a.Amount)
610			} else {
611				index = fmt.Sprintf("(%s.%s)", indexreg, a.Extend.String())
612			}
613		}
614
615		return base + index
616
617	case Cond:
618		switch arg.String() {
619		case "CS":
620			return "HS"
621		case "CC":
622			return "LO"
623		}
624
625	case Imm_clrex:
626		return fmt.Sprintf("$%d", uint32(a))
627
628	case Imm_dcps:
629		return fmt.Sprintf("$%d", uint32(a))
630
631	case Imm_option:
632		return fmt.Sprintf("$%d", uint8(a))
633
634	case Imm_hint:
635		return fmt.Sprintf("$%d", uint8(a))
636
637	case Imm_fp:
638		var s, pre, numerator, denominator int16
639		var result float64
640		if a.s == 0 {
641			s = 1
642		} else {
643			s = -1
644		}
645		pre = s * int16(16+a.pre)
646		if a.exp > 0 {
647			numerator = (pre << uint8(a.exp))
648			denominator = 16
649		} else {
650			numerator = pre
651			denominator = (16 << uint8(-1*a.exp))
652		}
653		result = float64(numerator) / float64(denominator)
654		return strings.TrimRight(fmt.Sprintf("$%f", result), "0")
655
656	case RegisterWithArrangement:
657		result := a.r.String()
658		arrange := a.a.String()
659		c := []rune(arrange)
660		switch len(c) {
661		case 3:
662			c[1], c[2] = c[2], c[1] // .8B -> .B8
663		case 4:
664			c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16
665		}
666		arrange = string(c)
667		result += arrange
668		if a.cnt > 0 {
669			result = "[" + result
670			for i := 1; i < int(a.cnt); i++ {
671				cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
672				result += ", " + cur.String() + arrange
673			}
674			result += "]"
675		}
676		return result
677
678	case RegisterWithArrangementAndIndex:
679		result := a.r.String()
680		arrange := a.a.String()
681		result += arrange
682		if a.cnt > 1 {
683			result = "[" + result
684			for i := 1; i < int(a.cnt); i++ {
685				cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
686				result += ", " + cur.String() + arrange
687			}
688			result += "]"
689		}
690		return fmt.Sprintf("%s[%d]", result, a.index)
691
692	case Systemreg:
693		return fmt.Sprintf("$%d", uint32(a.op0&1)<<14|uint32(a.op1&7)<<11|uint32(a.cn&15)<<7|uint32(a.cm&15)<<3|uint32(a.op2)&7)
694
695	case Imm_prfop:
696		if strings.Contains(a.String(), "#") {
697			return fmt.Sprintf("$%d", a)
698		}
699	}
700
701	return strings.ToUpper(arg.String())
702}
703