// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ppc64asm import ( "fmt" "strings" ) // GoSyntax returns the Go assembler syntax for the instruction. // The pc is the program counter of the first instruction, used for expanding // PC-relative addresses into absolute ones. // The symname function queries the symbol table for the program // being disassembled. It returns the name and base address of the symbol // containing the target, if any; otherwise it returns "", 0. func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string { if symname == nil { symname = func(uint64) (string, uint64) { return "", 0 } } if inst.Op == 0 && inst.Enc == 0 { return "WORD $0" } else if inst.Op == 0 { return "?" } var args []string for i, a := range inst.Args[:] { if a == nil { break } if s := plan9Arg(&inst, i, pc, a, symname); s != "" { args = append(args, s) } } var op string op = plan9OpMap[inst.Op] if op == "" { op = strings.ToUpper(inst.Op.String()) if op[len(op)-1] == '.' { op = op[:len(op)-1] + "CC" } } // laid out the instruction switch inst.Op { default: // dst, sA, sB, ... switch len(args) { case 0: return op case 1: return fmt.Sprintf("%s %s", op, args[0]) case 2: if inst.Op == COPY || inst.Op == PASTECC { return op + " " + args[0] + "," + args[1] } return op + " " + args[1] + "," + args[0] case 3: if reverseOperandOrder(inst.Op) { return op + " " + args[2] + "," + args[1] + "," + args[0] } case 4: if reverseMiddleOps(inst.Op) { return op + " " + args[1] + "," + args[3] + "," + args[2] + "," + args[0] } } args = append(args, args[0]) return op + " " + strings.Join(args[1:], ",") case PASTECC: // paste. has two input registers, and an L field, unlike other 3 operand instructions. return op + " " + args[0] + "," + args[1] + "," + args[2] case SYNC: if args[0] == "$1" { return "LWSYNC" } return "HWSYNC" case ISEL: return "ISEL " + args[3] + "," + args[1] + "," + args[2] + "," + args[0] // store instructions always have the memory operand at the end, no need to reorder // indexed stores handled separately case STB, STBU, STH, STHU, STW, STWU, STD, STDU, STFD, STFDU, STFS, STFSU, STQ, HASHST, HASHSTP: return op + " " + strings.Join(args, ",") case FCMPU, FCMPO, CMPD, CMPDI, CMPLD, CMPLDI, CMPW, CMPWI, CMPLW, CMPLWI: crf := int(inst.Args[0].(CondReg) - CR0) cmpstr := op + " " + args[1] + "," + args[2] if crf != 0 { // print CRx as the final operand if not implied (i.e BF != 0) cmpstr += "," + args[0] } return cmpstr case LIS: return "ADDIS $0," + args[1] + "," + args[0] // store instructions with index registers case STBX, STBUX, STHX, STHUX, STWX, STWUX, STDX, STDUX, STHBRX, STWBRX, STDBRX, STSWX, STFIWX: return "MOV" + op[2:len(op)-1] + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")" case STDCXCC, STWCXCC, STHCXCC, STBCXCC: return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")" case STXVX, STXVD2X, STXVW4X, STXVH8X, STXVB16X, STXSDX, STVX, STVXL, STVEBX, STVEHX, STVEWX, STXSIWX, STFDX, STFDUX, STFDPX, STFSX, STFSUX: return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")" case STXV: return op + " " + args[0] + "," + args[1] case STXVL, STXVLL: return op + " " + args[0] + "," + args[1] + "," + args[2] case LWAX, LWAUX, LWZX, LHZX, LBZX, LDX, LHAX, LHAUX, LDARX, LWARX, LHARX, LBARX, LFDX, LFDUX, LFSX, LFSUX, LDBRX, LWBRX, LHBRX, LDUX, LWZUX, LHZUX, LBZUX: if args[1] == "0" { return op + " (" + args[2] + ")," + args[0] } return op + " (" + args[2] + ")(" + args[1] + ")," + args[0] case LXVX, LXVD2X, LXVW4X, LXVH8X, LXVB16X, LVX, LVXL, LVSR, LVSL, LVEBX, LVEHX, LVEWX, LXSDX, LXSIWAX: return op + " (" + args[2] + ")(" + args[1] + ")," + args[0] case LXV: return op + " " + args[1] + "," + args[0] case LXVL, LXVLL: return op + " " + args[1] + "," + args[2] + "," + args[0] case DCBT, DCBTST, DCBZ, DCBST, ICBI: if args[0] == "0" || args[0] == "R0" { return op + " (" + args[1] + ")" } return op + " (" + args[1] + ")(" + args[0] + ")" // branch instructions needs additional handling case BCLR: if int(inst.Args[0].(Imm))&20 == 20 { // unconditional return "RET" } return op + " " + strings.Join(args, ", ") case BC: bo := int(inst.Args[0].(Imm)) bi := int(inst.Args[1].(CondReg) - Cond0LT) bcname := condName[((bo&0x8)>>1)|(bi&0x3)] if bo&0x17 == 4 { // jump only a CR bit set/unset, no hints (at bits) set. if bi >= 4 { return fmt.Sprintf("B%s CR%d,%s", bcname, bi>>2, args[2]) } else { return fmt.Sprintf("B%s %s", bcname, args[2]) } } return op + " " + strings.Join(args, ",") case BCCTR: if int(inst.Args[0].(Imm))&20 == 20 { // unconditional return "BR (CTR)" } return op + " " + strings.Join(args, ", ") case BCCTRL: if int(inst.Args[0].(Imm))&20 == 20 { // unconditional return "BL (CTR)" } return op + " " + strings.Join(args, ",") case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL: return op + " " + strings.Join(args, ",") } } // plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules. // // NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy // of inst, it's ok to modify inst.Args here. func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string { // special cases for load/store instructions if _, ok := arg.(Offset); ok { if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil { panic(fmt.Errorf("wrong table: offset not followed by register")) } } switch arg := arg.(type) { case Reg: if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 { return "0" } if arg == R30 { return "g" } return strings.ToUpper(arg.String()) case CondReg: // This op is left as its numerical value, not mapped onto CR + condition if inst.Op == ISEL { return fmt.Sprintf("$%d", (arg - Cond0LT)) } bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4] if arg <= Cond0SO { return bit } else if arg > Cond0SO && arg <= Cond7SO { return fmt.Sprintf("CR%d%s", int(arg-Cond0LT)/4, bit) } else { return fmt.Sprintf("CR%d", int(arg-CR0)) } case Imm: return fmt.Sprintf("$%d", arg) case SpReg: switch arg { case 8: return "LR" case 9: return "CTR" } return fmt.Sprintf("SPR(%d)", int(arg)) case PCRel: addr := pc + uint64(int64(arg)) s, base := symname(addr) if s != "" && addr == base { return fmt.Sprintf("%s(SB)", s) } if inst.Op == BL && s != "" && (addr-base) == 8 { // When decoding an object built for PIE, a CALL targeting // a global entry point will be adjusted to the local entry // if any. For now, assume any symname+8 PC is a local call. return fmt.Sprintf("%s+%d(SB)", s, addr-base) } return fmt.Sprintf("%#x", addr) case Label: return fmt.Sprintf("%#x", int(arg)) case Offset: reg := inst.Args[argIndex+1].(Reg) removeArg(inst, argIndex+1) if reg == R0 { return fmt.Sprintf("%d(0)", int(arg)) } return fmt.Sprintf("%d(R%d)", int(arg), reg-R0) } return fmt.Sprintf("???(%v)", arg) } func reverseMiddleOps(op Op) bool { switch op { case FMADD, FMADDCC, FMADDS, FMADDSCC, FMSUB, FMSUBCC, FMSUBS, FMSUBSCC, FNMADD, FNMADDCC, FNMADDS, FNMADDSCC, FNMSUB, FNMSUBCC, FNMSUBS, FNMSUBSCC, FSEL, FSELCC: return true } return false } func reverseOperandOrder(op Op) bool { switch op { // Special case for SUBF, SUBFC: not reversed case ADD, ADDC, ADDE, ADDCC, ADDCCC: return true case MULLW, MULLWCC, MULHW, MULHWCC, MULLD, MULLDCC, MULHD, MULHDCC, MULLWO, MULLWOCC, MULHWU, MULHWUCC, MULLDO, MULLDOCC: return true case DIVD, DIVDCC, DIVDU, DIVDUCC, DIVDE, DIVDECC, DIVDEU, DIVDEUCC, DIVDO, DIVDOCC, DIVDUO, DIVDUOCC: return true case MODUD, MODSD, MODUW, MODSW: return true case FADD, FADDS, FSUB, FSUBS, FMUL, FMULS, FDIV, FDIVS, FMADD, FMADDS, FMSUB, FMSUBS, FNMADD, FNMADDS, FNMSUB, FNMSUBS, FMULSCC: return true case FADDCC, FADDSCC, FSUBCC, FMULCC, FDIVCC, FDIVSCC: return true case OR, ORCC, ORC, ORCCC, AND, ANDCC, ANDC, ANDCCC, XOR, XORCC, NAND, NANDCC, EQV, EQVCC, NOR, NORCC: return true case SLW, SLWCC, SLD, SLDCC, SRW, SRAW, SRWCC, SRAWCC, SRD, SRDCC, SRAD, SRADCC: return true } return false } // revCondMap maps a conditional register bit to its inverse, if possible. var revCondMap = map[string]string{ "LT": "GE", "GT": "LE", "EQ": "NE", } // Lookup table to map BI[0:1] and BO[3] to an extended mnemonic for CR ops. // Bits 0-1 map to a bit with a CR field, and bit 2 selects the inverted (0) // or regular (1) extended mnemonic. var condName = []string{ "GE", "LE", "NE", "NSO", "LT", "GT", "EQ", "SO", } // plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics. var plan9OpMap = map[Op]string{ LWARX: "LWAR", LDARX: "LDAR", LHARX: "LHAR", LBARX: "LBAR", LWAX: "MOVW", LHAX: "MOVH", LWAUX: "MOVWU", LHAU: "MOVHU", LHAUX: "MOVHU", LDX: "MOVD", LDUX: "MOVDU", LWZX: "MOVWZ", LWZUX: "MOVWZU", LHZX: "MOVHZ", LHZUX: "MOVHZU", LBZX: "MOVBZ", LBZUX: "MOVBZU", LDBRX: "MOVDBR", LWBRX: "MOVWBR", LHBRX: "MOVHBR", MCRF: "MOVFL", XORI: "XOR", ORI: "OR", ANDICC: "ANDCC", ANDC: "ANDN", ANDCCC: "ANDNCC", ADDEO: "ADDEV", ADDEOCC: "ADDEVCC", ADDO: "ADDV", ADDOCC: "ADDVCC", ADDMEO: "ADDMEV", ADDMEOCC: "ADDMEVCC", ADDCO: "ADDCV", ADDCOCC: "ADDCVCC", ADDZEO: "ADDZEV", ADDZEOCC: "ADDZEVCC", SUBFME: "SUBME", SUBFMECC: "SUBMECC", SUBFZE: "SUBZE", SUBFZECC: "SUBZECC", SUBFZEO: "SUBZEV", SUBFZEOCC: "SUBZEVCC", SUBF: "SUB", SUBFC: "SUBC", SUBFCC: "SUBCC", SUBFCCC: "SUBCCC", ORC: "ORN", ORCCC: "ORNCC", MULLWO: "MULLWV", MULLWOCC: "MULLWVCC", MULLDO: "MULLDV", MULLDOCC: "MULLDVCC", DIVDO: "DIVDV", DIVDOCC: "DIVDVCC", DIVDUO: "DIVDUV", DIVDUOCC: "DIVDUVCC", ADDI: "ADD", MULLI: "MULLD", SRADI: "SRAD", STBCXCC: "STBCCC", STWCXCC: "STWCCC", STDCXCC: "STDCCC", LI: "MOVD", LBZ: "MOVBZ", STB: "MOVB", LBZU: "MOVBZU", STBU: "MOVBU", LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH", LHZU: "MOVHZU", STHU: "MOVHU", LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW", LWZU: "MOVWZU", STWU: "MOVWU", LD: "MOVD", STD: "MOVD", LDU: "MOVDU", STDU: "MOVDU", LFD: "FMOVD", STFD: "FMOVD", LFS: "FMOVS", STFS: "FMOVS", LFDX: "FMOVD", STFDX: "FMOVD", LFDU: "FMOVDU", STFDU: "FMOVDU", LFDUX: "FMOVDU", STFDUX: "FMOVDU", LFSX: "FMOVS", STFSX: "FMOVS", LFSU: "FMOVSU", STFSU: "FMOVSU", LFSUX: "FMOVSU", STFSUX: "FMOVSU", CMPD: "CMP", CMPDI: "CMP", CMPW: "CMPW", CMPWI: "CMPW", CMPLD: "CMPU", CMPLDI: "CMPU", CMPLW: "CMPWU", CMPLWI: "CMPWU", MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs B: "BR", BL: "CALL", }