173 lines
5.1 KiB
Go
173 lines
5.1 KiB
Go
// 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",
|
|
}
|