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.
4
5package ppc64asm
6
7import (
8	"fmt"
9	"strings"
10)
11
12// GoSyntax returns the Go assembler syntax for the instruction.
13// The pc is the program counter of the first instruction, used for expanding
14// PC-relative addresses into absolute ones.
15// The symname function queries the symbol table for the program
16// being disassembled. It returns the name and base address of the symbol
17// containing the target, if any; otherwise it returns "", 0.
18func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
19	if symname == nil {
20		symname = func(uint64) (string, uint64) { return "", 0 }
21	}
22	if inst.Op == 0 {
23		return "?"
24	}
25	var args []string
26	for i, a := range inst.Args[:] {
27		if a == nil {
28			break
29		}
30		if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
31			args = append(args, s)
32		}
33	}
34	var op string
35	op = plan9OpMap[inst.Op]
36	if op == "" {
37		op = strings.ToUpper(inst.Op.String())
38	}
39	// laid out the instruction
40	switch inst.Op {
41	default: // dst, sA, sB, ...
42		if len(args) == 0 {
43			return op
44		} else if len(args) == 1 {
45			return fmt.Sprintf("%s %s", op, args[0])
46		}
47		args = append(args, args[0])
48		return op + " " + strings.Join(args[1:], ", ")
49	// store instructions always have the memory operand at the end, no need to reorder
50	case STB, STBU, STBX, STBUX,
51		STH, STHU, STHX, STHUX,
52		STW, STWU, STWX, STWUX,
53		STD, STDU, STDX, STDUX,
54		STQ,
55		STHBRX, STWBRX:
56		return op + " " + strings.Join(args, ", ")
57	// branch instructions needs additional handling
58	case BCLR:
59		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
60			return "RET"
61		}
62		return op + " " + strings.Join(args, ", ")
63	case BC:
64		if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set
65			return fmt.Sprintf("B%s %s", args[1], args[2])
66		} else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set
67			return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2])
68		}
69		return op + " " + strings.Join(args, ", ")
70	case BCCTR:
71		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
72			return "BR (CTR)"
73		}
74		return op + " " + strings.Join(args, ", ")
75	case BCCTRL:
76		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
77			return "BL (CTR)"
78		}
79		return op + " " + strings.Join(args, ", ")
80	case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
81		return op + " " + strings.Join(args, ", ")
82	}
83}
84
85// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
86// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
87//       of inst, it's ok to modify inst.Args here.
88func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
89	// special cases for load/store instructions
90	if _, ok := arg.(Offset); ok {
91		if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
92			panic(fmt.Errorf("wrong table: offset not followed by register"))
93		}
94	}
95	switch arg := arg.(type) {
96	case Reg:
97		if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
98			return "0"
99		}
100		if arg == R30 {
101			return "g"
102		}
103		return strings.ToUpper(arg.String())
104	case CondReg:
105		if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
106			return "" // don't show cr0 for cmp instructions
107		} else if arg >= CR0 {
108			return fmt.Sprintf("CR%d", int(arg-CR0))
109		}
110		bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4]
111		if arg <= Cond0SO {
112			return bit
113		}
114		return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit)
115	case Imm:
116		return fmt.Sprintf("$%d", arg)
117	case SpReg:
118		switch arg {
119		case 8:
120			return "LR"
121		case 9:
122			return "CTR"
123		}
124		return fmt.Sprintf("SPR(%d)", int(arg))
125	case PCRel:
126		addr := pc + uint64(int64(arg))
127		if s, base := symname(addr); s != "" && base == addr {
128			return fmt.Sprintf("%s(SB)", s)
129		}
130		return fmt.Sprintf("%#x", addr)
131	case Label:
132		return fmt.Sprintf("%#x", int(arg))
133	case Offset:
134		reg := inst.Args[argIndex+1].(Reg)
135		removeArg(inst, argIndex+1)
136		if reg == R0 {
137			return fmt.Sprintf("%d(0)", int(arg))
138		}
139		return fmt.Sprintf("%d(R%d)", int(arg), reg-R0)
140	}
141	return fmt.Sprintf("???(%v)", arg)
142}
143
144// revCondMap maps a conditional register bit to its inverse, if possible.
145var revCondMap = map[string]string{
146	"LT": "GE", "GT": "LE", "EQ": "NE",
147}
148
149// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics.
150var plan9OpMap = map[Op]string{
151	LWARX: "LWAR", STWCX_: "STWCCC",
152	LDARX: "LDAR", STDCX_: "STDCCC",
153	LHARX: "LHAR", STHCX_: "STHCCC",
154	LBARX: "LBAR", STBCX_: "STBCCC",
155	ADDI: "ADD",
156	ADD_: "ADDCC",
157	LBZ:  "MOVBZ", STB: "MOVB",
158	LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled
159	LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH",
160	LHZU: "MOVHZU", STHU: "MOVHU",
161	LI:  "MOVD",
162	LIS: "ADDIS",
163	LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW",
164	LWZU: "MOVWZU", STWU: "MOVWU",
165	LD: "MOVD", STD: "MOVD",
166	LDU: "MOVDU", STDU: "MOVDU",
167	MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs
168	B:     "BR",
169	BL:    "CALL",
170	CMPLD: "CMPU", CMPLW: "CMPWU",
171	CMPD: "CMP", CMPW: "CMPW",
172}
173