ppc64/ppc64asm: improve disassembly for ppc64

The following improvements are included in this change:
- Display common special purpose registers for mtlr,mflr,mtctr,mfctr,
mtxer,mfxer,mftb; for others use mtspr and mfspr.
- Provide branch condition information (lt, gt, eq, ne, ge, le).
- Add cr number if cr1-cr7 is used.
- Pass pc to gnuArg to generate branch targets that are not relative.

Change-Id: Ia3ef6cb248c484a3ad72545e68d1ca59e32ae645
Reviewed-on: https://go-review.googlesource.com/c/arch/+/194597
Run-TryBot: Lynn Boger <laboger@linux.vnet.ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Lynn Boger 2019-09-10 14:23:02 -04:00
Родитель 46d78d1859
Коммит 7fe50f7625
4 изменённых файлов: 169 добавлений и 11 удалений

Просмотреть файл

@ -49,7 +49,7 @@ func TestDecode(t *testing.T) {
} else { } else {
switch syntax { switch syntax {
case "gnu": case "gnu":
out = GNUSyntax(inst) out = GNUSyntax(inst, 0)
case "plan9": case "plan9":
out = GoSyntax(inst, 0, nil) out = GoSyntax(inst, 0, nil)
default: default:

Просмотреть файл

@ -245,7 +245,7 @@ func disasm(syntax string, src []byte) (inst Inst, text string) {
//case "arm": //case "arm":
// text = ARMSyntax(inst) // text = ARMSyntax(inst)
case "gnu": case "gnu":
text = GNUSyntax(inst) text = GNUSyntax(inst, 0)
//case "plan9": //case "plan9":
// text = GoSyntax(inst, 0, nil) // text = GoSyntax(inst, 0, nil)
default: default:

Просмотреть файл

@ -10,9 +10,14 @@ import (
"strings" "strings"
) )
var (
condBit = [4]string{"lt", "gt", "eq", "so"}
condBitNeg = [4]string{"ge", "le", "ne", "so"}
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This form typically matches the syntax defined in the Power ISA Reference Manual. // This form typically matches the syntax defined in the Power ISA Reference Manual.
func GNUSyntax(inst Inst) string { func GNUSyntax(inst Inst, pc uint64) string {
var buf bytes.Buffer var buf bytes.Buffer
// When there are all 0s, identify them as the disassembler // When there are all 0s, identify them as the disassembler
// in binutils would. // in binutils would.
@ -21,13 +26,125 @@ func GNUSyntax(inst Inst) string {
} else if inst.Op == 0 { } else if inst.Op == 0 {
return "error: unknown instruction" return "error: unknown instruction"
} }
buf.WriteString(inst.Op.String())
PC := pc
// Special handling for some ops
startArg := 0
sep := " " sep := " "
switch inst.Op.String() {
case "bc":
bo := gnuArg(&inst, 0, inst.Args[0], PC)
bi := inst.Args[1]
switch bi := bi.(type) {
case CondReg:
if bi >= CR0 {
if bi == CR0 && bo == "16" {
buf.WriteString("bdnz")
}
buf.WriteString(fmt.Sprintf("bc cr%d", bi-CR0))
}
cr := bi / 4
switch bo {
case "4":
bit := condBitNeg[(bi-Cond0LT)%4]
if cr == 0 {
buf.WriteString(fmt.Sprintf("b%s", bit))
} else {
buf.WriteString(fmt.Sprintf("b%s cr%d,", bit, cr))
sep = ""
}
case "12":
bit := condBit[(bi-Cond0LT)%4]
if cr == 0 {
buf.WriteString(fmt.Sprintf("b%s", bit))
} else {
buf.WriteString(fmt.Sprintf("b%s cr%d,", bit, cr))
sep = ""
}
case "8":
bit := condBit[(bi-Cond0LT)%4]
sep = ""
if cr == 0 {
buf.WriteString(fmt.Sprintf("bdnzt %s,", bit))
} else {
buf.WriteString(fmt.Sprintf("bdnzt cr%d,%s,", cr, bit))
}
case "16":
if cr == 0 && bi == Cond0LT {
buf.WriteString("bdnz")
} else {
buf.WriteString(fmt.Sprintf("bdnz cr%d,", cr))
sep = ""
}
}
startArg = 2
default:
fmt.Printf("Unexpected bi: %d for bc with bo: %s\n", bi, bo)
}
startArg = 2
case "mtspr":
opcode := inst.Op.String()
buf.WriteString(opcode[0:2])
switch spr := inst.Args[0].(type) {
case SpReg:
switch spr {
case 1:
buf.WriteString("xer")
startArg = 1
case 8:
buf.WriteString("lr")
startArg = 1
case 9:
buf.WriteString("ctr")
startArg = 1
default:
buf.WriteString("spr")
}
default:
buf.WriteString("spr")
}
case "mfspr":
opcode := inst.Op.String()
buf.WriteString(opcode[0:2])
arg := inst.Args[0]
switch spr := inst.Args[1].(type) {
case SpReg:
switch spr {
case 1:
buf.WriteString("xer ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 8:
buf.WriteString("lr ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 9:
buf.WriteString("ctr ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 268:
buf.WriteString("tb ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
default:
buf.WriteString("spr")
}
default:
buf.WriteString("spr")
}
default:
buf.WriteString(inst.Op.String())
}
for i, arg := range inst.Args[:] { for i, arg := range inst.Args[:] {
if arg == nil { if arg == nil {
break break
} }
text := gnuArg(&inst, i, arg) if i < startArg {
continue
}
text := gnuArg(&inst, i, arg, PC)
if text == "" { if text == "" {
continue continue
} }
@ -41,7 +158,7 @@ func GNUSyntax(inst Inst) string {
// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules. // gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy // NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
// of inst, it's ok to modify inst.Args here. // of inst, it's ok to modify inst.Args here.
func gnuArg(inst *Inst, argIndex int, arg Arg) string { func gnuArg(inst *Inst, argIndex int, arg Arg, pc uint64) string {
// special cases for load/store instructions // special cases for load/store instructions
if _, ok := arg.(Offset); ok { if _, ok := arg.(Offset); ok {
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil { if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
@ -55,22 +172,43 @@ func gnuArg(inst *Inst, argIndex int, arg Arg) string {
} }
return arg.String() return arg.String()
case CondReg: case CondReg:
// The CondReg can either be found in a CMP, where the
// condition register field is being set, or in an instruction
// like a branch or isel that is testing a bit in a condition
// register field.
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") { if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
return "" // don't show cr0 for cmp instructions return "" // don't show cr0 for cmp instructions
} else if arg >= CR0 { } else if arg >= CR0 {
return fmt.Sprintf("cr%d", int(arg-CR0)) return fmt.Sprintf("cr%d", int(arg-CR0))
} }
bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4] bit := condBit[(arg-Cond0LT)%4]
if arg <= Cond0SO { if arg <= Cond0SO {
return bit return bit
} }
return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit) return fmt.Sprintf("%s cr%d", bit, int(arg-Cond0LT)/4)
case Imm: case Imm:
return fmt.Sprintf("%d", arg) return fmt.Sprintf("%d", arg)
case SpReg: case SpReg:
return fmt.Sprintf("%d", int(arg)) switch int(arg) {
case 1:
return "xer"
case 8:
return "lr"
case 9:
return "ctr"
case 268:
return "tb"
default:
return fmt.Sprintf("%d", int(arg))
}
case PCRel: case PCRel:
return fmt.Sprintf(".%+#x", int(arg)) // If the arg is 0, use the relative address format.
// Otherwise the pc is meaningful, use absolute address.
if int(arg) == 0 {
return fmt.Sprintf(".%+#x", int(arg))
}
addr := pc + uint64(int64(arg))
return fmt.Sprintf("%#x", addr)
case Label: case Label:
return fmt.Sprintf("%#x", uint32(arg)) return fmt.Sprintf("%#x", uint32(arg))
case Offset: case Offset:

22
ppc64/ppc64asm/testdata/decode.txt поставляемый
Просмотреть файл

@ -9,7 +9,7 @@
00002000| gnu error: unknown instruction 00002000| gnu error: unknown instruction
a1841e80| gnu lhz r12,7808(r4) a1841e80| gnu lhz r12,7808(r4)
a1841e80| plan9 MOVHZ 7808(R4),R12 a1841e80| plan9 MOVHZ 7808(R4),R12
42093d10| gnu bc 16,4*cr2+gt,.+0x3d10 42000004| gnu bdnz 0x4
e38d5b90| gnu lq r28,23440(r13) e38d5b90| gnu lq r28,23440(r13)
84127a20| gnu lwzu r0,31264(r18) 84127a20| gnu lwzu r0,31264(r18)
84127a20| plan9 MOVWZU 31264(R18),R0 84127a20| plan9 MOVWZU 31264(R18),R0
@ -54,3 +54,23 @@ fbe1ffd1| plan9 MOVDU R31,-48(R1)
7c941f19| plan9 STXVW4X VS36,(R3)(R20) 7c941f19| plan9 STXVW4X VS36,(R3)(R20)
7c6520a8| gnu ldarx r3,r5,r4 7c6520a8| gnu ldarx r3,r5,r4
7c6520a8| plan9 LDAR (R4)(R5),R3 7c6520a8| plan9 LDAR (R4)(R5),R3
7c6803a6| plan9 MOVD R3,LR
7c6802a6| plan9 MOVD LR,R3
7c6803a6| gnu mtlr r3
7c6802a6| gnu mflr r3
7c6903a6| plan9 MOVD R3,CTR
7c6902a6| plan9 MOVD CTR,R3
7c6903a6| gnu mtctr r3
7c6902a6| gnu mfctr r3
7c6c42a6| gnu mftb r3
7c6c42a6| plan9 MOVD SPR(268),R3
7c8202a6| plan9 MOVD SPR(2),R4
7c8202a6| gnu mfspr r4,2
41820010| plan9 BEQ 0x10
41820010| gnu beq 0x10
4086000c| plan9 BNE CR1,0xc
4086000c| gnu bne cr1,0xc
41880008| plan9 BLT CR2,0x8
41880008| gnu blt cr2,0x8
418d0004| plan9 BGT CR3,0x4
418d0004| gnu bgt cr3,0x4