Skip to content

Commit

Permalink
ppc64/ppc64asm: improve disassembly for ppc64
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
laboger committed Sep 19, 2019
1 parent 46d78d1 commit 7fe50f7
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 11 deletions.
2 changes: 1 addition & 1 deletion ppc64/ppc64asm/decode_test.go
Expand Up @@ -49,7 +49,7 @@ func TestDecode(t *testing.T) {
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst)
out = GNUSyntax(inst, 0)
case "plan9":
out = GoSyntax(inst, 0, nil)
default:
Expand Down
2 changes: 1 addition & 1 deletion ppc64/ppc64asm/ext_test.go
Expand Up @@ -245,7 +245,7 @@ func disasm(syntax string, src []byte) (inst Inst, text string) {
//case "arm":
// text = ARMSyntax(inst)
case "gnu":
text = GNUSyntax(inst)
text = GNUSyntax(inst, 0)
//case "plan9":
// text = GoSyntax(inst, 0, nil)
default:
Expand Down
154 changes: 146 additions & 8 deletions ppc64/ppc64asm/gnu.go
Expand Up @@ -10,9 +10,14 @@ import (
"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.
// 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
// When there are all 0s, identify them as the disassembler
// in binutils would.
Expand All @@ -21,13 +26,125 @@ func GNUSyntax(inst Inst) string {
} else if inst.Op == 0 {
return "error: unknown instruction"
}
buf.WriteString(inst.Op.String())

PC := pc
// Special handling for some ops
startArg := 0
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[:] {
if arg == nil {
break
}
text := gnuArg(&inst, i, arg)
if i < startArg {
continue
}
text := gnuArg(&inst, i, arg, PC)
if text == "" {
continue
}
Expand All @@ -41,7 +158,7 @@ func GNUSyntax(inst Inst) string {
// 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
// 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
if _, ok := arg.(Offset); ok {
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
Expand All @@ -55,22 +172,43 @@ func gnuArg(inst *Inst, argIndex int, arg Arg) string {
}
return arg.String()
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") {
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]
bit := condBit[(arg-Cond0LT)%4]
if arg <= Cond0SO {
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:
return fmt.Sprintf("%d", arg)
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:
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:
return fmt.Sprintf("%#x", uint32(arg))
case Offset:
Expand Down
22 changes: 21 additions & 1 deletion ppc64/ppc64asm/testdata/decode.txt
Expand Up @@ -9,7 +9,7 @@
00002000| gnu error: unknown instruction
a1841e80| gnu lhz r12,7808(r4)
a1841e80| plan9 MOVHZ 7808(R4),R12
42093d10| gnu bc 16,4*cr2+gt,.+0x3d10
42000004| gnu bdnz 0x4
e38d5b90| gnu lq r28,23440(r13)
84127a20| gnu lwzu r0,31264(r18)
84127a20| plan9 MOVWZU 31264(R18),R0
Expand Down Expand Up @@ -54,3 +54,23 @@ fbe1ffd1| plan9 MOVDU R31,-48(R1)
7c941f19| plan9 STXVW4X VS36,(R3)(R20)
7c6520a8| gnu ldarx r3,r5,r4
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

0 comments on commit 7fe50f7

Please sign in to comment.