| // 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 { |
| 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()) |
| } |
| // laid out the instruction |
| switch inst.Op { |
| default: // dst, sA, sB, ... |
| if len(args) == 0 { |
| return op |
| } else if len(args) == 1 { |
| return fmt.Sprintf("%s %s", op, args[0]) |
| } |
| args = append(args, args[0]) |
| return op + " " + strings.Join(args[1:], ", ") |
| // store instructions always have the memory operand at the end, no need to reorder |
| case STB, STBU, STBX, STBUX, |
| STH, STHU, STHX, STHUX, |
| STW, STWU, STWX, STWUX, |
| STD, STDU, STDX, STDUX, |
| STQ, |
| STHBRX, STWBRX: |
| return op + " " + strings.Join(args, ", ") |
| // 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: |
| if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set |
| return fmt.Sprintf("B%s %s", args[1], args[2]) |
| } else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set |
| return fmt.Sprintf("B%s %s", revCondMap[args[1]], 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: |
| if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") { |
| return "" // don't show cr0 for cmp instructions |
| } else if arg >= CR0 { |
| return fmt.Sprintf("CR%d", int(arg-CR0)) |
| } |
| bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4] |
| if arg <= Cond0SO { |
| return bit |
| } |
| return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit) |
| 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)) |
| if s, base := symname(addr); s != "" && base == addr { |
| return fmt.Sprintf("%s(SB)", s) |
| } |
| 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) |
| } |
| |
| // revCondMap maps a conditional register bit to its inverse, if possible. |
| var revCondMap = map[string]string{ |
| "LT": "GE", "GT": "LE", "EQ": "NE", |
| } |
| |
| // plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics. |
| var plan9OpMap = map[Op]string{ |
| LWARX: "LWAR", STWCX_: "STWCCC", |
| LDARX: "LDAR", STDCX_: "STDCCC", |
| LHARX: "LHAR", STHCX_: "STHCCC", |
| LBARX: "LBAR", STBCX_: "STBCCC", |
| ADDI: "ADD", |
| ADD_: "ADDCC", |
| LBZ: "MOVBZ", STB: "MOVB", |
| LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled |
| LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH", |
| LHZU: "MOVHZU", STHU: "MOVHU", |
| LI: "MOVD", |
| LIS: "ADDIS", |
| LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW", |
| LWZU: "MOVWZU", STWU: "MOVWU", |
| LD: "MOVD", STD: "MOVD", |
| LDU: "MOVDU", STDU: "MOVDU", |
| MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs |
| B: "BR", |
| BL: "CALL", |
| CMPLD: "CMPU", CMPLW: "CMPWU", |
| CMPD: "CMP", CMPW: "CMPW", |
| } |