diff --git a/ppc64/ppc64asm/decode_test.go b/ppc64/ppc64asm/decode_test.go index 039b3ed..7e7638c 100644 --- a/ppc64/ppc64asm/decode_test.go +++ b/ppc64/ppc64asm/decode_test.go @@ -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: diff --git a/ppc64/ppc64asm/ext_test.go b/ppc64/ppc64asm/ext_test.go index cb7f319..f9242e1 100644 --- a/ppc64/ppc64asm/ext_test.go +++ b/ppc64/ppc64asm/ext_test.go @@ -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: diff --git a/ppc64/ppc64asm/gnu.go b/ppc64/ppc64asm/gnu.go index 70872bb..fc29164 100644 --- a/ppc64/ppc64asm/gnu.go +++ b/ppc64/ppc64asm/gnu.go @@ -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. @@ -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 } @@ -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 { @@ -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: diff --git a/ppc64/ppc64asm/testdata/decode.txt b/ppc64/ppc64asm/testdata/decode.txt index 2a89de0..69e6942 100644 --- a/ppc64/ppc64asm/testdata/decode.txt +++ b/ppc64/ppc64asm/testdata/decode.txt @@ -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 @@ -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