ppc64: fixes for ppc64 objdump

The current output for the Go objdump on ppc64 is not correct
in many cases, generating opcodes that are not acceptable by the
Go assembler, and generating operands in the wrong order.

This adds some changes to generate an objdump for ppc64 code so that
the instructions generated are acceptable by the Go assembler,
and where some of the non-obvious operands have more meaningful names.

This includes the following changes:
- Correct some errors in ppc64map to make it work.
- Make a minor update to ppc64.csv to add the Go opcodes that have been
added to the Go assembler that were not in the table.
- Fix ppc64map to generate correct Go opcodes for ppc64 opcodes that end with '.'
indicating condition codes are set. The previous ppc64map was adding '_' at the end
for this case, but then had to be translated in plan9.go to 'CC' and many were not
being translated. This generates the correct suffix in the first step when
generating tables.go.
- Handle compare instructions correctly, since the names don't quite match
and the CR was not always handled correctly.
- Handle index loads and stores correctly, including VMX and VSX.
- Add mappings for all the names where the Go opcode does not match the ppc64 opcode.
- Handle all CR values, not just CR0 for those instructions that use them.
- Provide more information on BC instructions and generate the correct
branch mnemonic (BEQ, BNE, etc.) where possible.
- Make the output more consistent in spacing.
- Add some more testcases to testdata/decode.txt.
- If the instruction word is all 0s, the Go assembler will output WORD $0,
otherwise unrecognized instructions will be identified with '?'. For GNU,
all 0s will decode to .long 0x0 like the binutils objdump. This can happen
following a call to a panic or throw as a trap, or at the end of a function
for alignment purposes.

Note: the decoder does not handle extended opcodes correctly yet. I will add
that support in the next release and add the corresponding testcases.

We are working on an updated ppc64.csv, but I believe what is
there now covers the opcodes that are supported by the Go assembler.

Change-Id: If7fe1a2205512b476f4fb6b29a21f5459430e50c
Reviewed-on: https://go-review.googlesource.com/c/148574
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 2018-11-08 16:27:16 -05:00
Родитель b19384d3c1
Коммит 5a4828bb70
9 изменённых файлов: 973 добавлений и 765 удалений

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

@ -1198,3 +1198,23 @@
"Move From Link Register R-form","se_mflr RX","0@0|8@6|RX@12@15|",""
"Move To Link Register R-form","se_mtlr RX","0@0|9@6|RX@12@15|",""
"Instruction Synchronize C-form","se_isync","01@0@15|",""
"Add Extended using alternate carry bit Z23-form","addex RT,RA,RB,CY","31@0|RT@6|RA@11|RB@16|CY@21|170@23|/@31|",""
"Deliver A Random Number X-form","darn RT,L","31@0|RT@6|///@11|L@14|///@16|755@21|/@31|",""
"Multiply-Add High Doubleword VA-form","maddhd RT,RA,RB,RC","4@0|RT@6|RA@11|RB@16|RC@21|48@26|",""
"Multiply-Add High Doubleword Unsigned VA-form","maddhdu RT,RA,RB,RC","4@0|RT@6|RA@11|RB@16|RC@21|49@26|",""
"Multiply-Add Low Doubleword VA-form","maddld RT,RA,RB,RC","4@0|RT@6|RA@11|RB@16|RC@21|51@26|",""
"Compare Ranged Byte X-form","cmprb BF,L,RA,RB","31@0|BF@6|/@9|L@10|RA@11|RB@16|192@21|/@31|",""
"Compare Equal Byte X-form","cmpeqb BF,RA,RB","31@0|BF@6|//@9|RA@11|RB@16|224@21|/@31|",""
"Bit Permute Doubleword X-form","bpermd RA,RS,RB]","31@0|RS@6|RA@11|RB@16|252@21|/@31|",""
"Extend-Sign Word and Shift Left Immediate XS-form","extswsli RA,RS,SH ( Rc=0 )|extswsli. RA,RS,SH ( Rc=1 )","31@0|RS@6|RA@11|sh@16|445@21|sh@30|Rc@31|",""
"Move From VSR Doubleword X-form","mfvsrd RA,XS","31@0|S@6|RA@11|///@16|51@21|SX@31|",""
"Move From VSR Lower Doubleword X-form","mfvsrld RA,XS","31@0|S@6|RA@11|///@16|307@21|SX@31|",""
"Move From VSR Word and Zero X-form","mfvsrwz RA,XS","31@0|S@6|RA@11|///@16|115@21|SX@31|",""
"Move To VSR Doubleword X-form","mtvsrd XT,RA","31@0|T@6|RA@11|///@16|179@21|TX@31|",""
"Move To VSR Word Algebraic X-form","mtvsrwa XT,RA","31@0|T@6|RA@11|///@16|211@21|TX@31|",""
"Move To VSR Word and Zero X-form","mtvsrwz XT,RA","31@0|T@6|RA@11|///@16|243@21|TX@31|",""
"Move To VSR Double Doubleword X-form","mtvsrdd XT,RA,RB","31@0|T@6|RA@11|RB@16|435@21|TX@31|",""
"Move To VSR Word & Splat X-form","mtvsrws XT,RA","31@0|T@6|RA@11|///@16|403@21|TX@31|",""
"Move to CR from XER Extended X-form","mcrxrx BF","31@0|BF@6|//@9|///@11|///@16|576@21|/@31|",""
"Copy X-form","copy RA,RB","31@0|///@6|1@10|RA@11|RB@16|774@21|/@31|",""
"Paste X-form","paste. RA,RB","31@0|///@6|1@10|RA@11|RB@16|902@21|1@31|",""

Не удается отобразить этот файл, потому что он имеет неправильное количество полей в строке 3.

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

@ -172,7 +172,7 @@ func Decode(src []byte, ord binary.ByteOrder) (inst Inst, err error) {
}
break
}
if inst.Op == 0 {
if inst.Op == 0 && inst.Enc != 0 {
return inst, errUnknown
}
return inst, nil

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

@ -50,8 +50,8 @@ func TestDecode(t *testing.T) {
switch syntax {
case "gnu":
out = GNUSyntax(inst)
//case "plan9":
// out = GoSyntax(inst, 0, nil, nil)
case "plan9":
out = GoSyntax(inst, 0, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue

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

@ -14,8 +14,12 @@ import (
// This form typically matches the syntax defined in the Power ISA Reference Manual.
func GNUSyntax(inst Inst) string {
var buf bytes.Buffer
if inst.Op == 0 {
return "error: unkown instruction"
// When there are all 0s, identify them as the disassembler
// in binutils would.
if inst.Enc == 0 {
return ".long 0x0"
} else if inst.Op == 0 {
return "error: unknown instruction"
}
buf.WriteString(inst.Op.String())
sep := " "

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

@ -49,11 +49,11 @@ func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool
switch inst.Op {
case BC, BCA, BL, BLA, BCL, BCLA, TDI, TWI, TW, TD:
return true // TODO(minux): we lack the support for extended opcodes here
case RLWNM, RLWNM_, RLDICL, RLDICL_, RLWINM, RLWINM_, RLDCL, RLDCL_:
case RLWNM, RLWNMCC, RLDICL, RLDICLCC, RLWINM, RLWINMCC, RLDCL, RLDCLCC:
return true // TODO(minux): we lack the support for extended opcodes here
case DCBTST, DCBT:
return true // objdump uses the embedded argument order, we use the server argument order
case MTFSF, MTFSF_: // objdump doesn't show the last two arguments
case MTFSF, MTFSFCC: // objdump doesn't show the last two arguments
return true
case VSPLTB, VSPLTH, VSPLTW: // objdump generates unreasonable result "vspltw v6,v19,4" for 10c49a8c, the last 4 should be 0.
return true

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

@ -19,7 +19,9 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
if inst.Op == 0 {
if inst.Op == 0 && inst.Enc == 0 {
return "WORD $0"
} else if inst.Op == 0 {
return "?"
}
var args []string
@ -28,13 +30,27 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
break
}
if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
args = append(args, s)
// In the case for some BC instructions, a CondReg arg has
// both the CR and the branch condition encoded in its value.
// plan9Arg will return a string with the string representation
// of these values separated by a blank that will be treated
// as 2 args from this point on.
if strings.IndexByte(s, ' ') > 0 {
t := strings.Split(s, " ")
args = append(args, t[0])
args = append(args, t[1])
} else {
args = append(args, s)
}
}
}
var op string
op = plan9OpMap[inst.Op]
if op == "" {
op = strings.ToUpper(inst.Op.String())
if op[len(op)-1] == '.' {
op = op[:len(op)-1] + "CC"
}
}
// laid out the instruction
switch inst.Op {
@ -45,15 +61,60 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
return fmt.Sprintf("%s %s", op, args[0])
}
args = append(args, args[0])
return op + " " + strings.Join(args[1:], ", ")
return op + " " + strings.Join(args[1:], ",")
case SYNC:
if args[0] == "$1" {
return "LWSYNC"
}
return "HWSYNC"
case ISEL:
return "ISEL " + args[3] + "," + args[1] + "," + args[2] + "," + args[0]
// 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, ", ")
// indexed stores handled separately
case STB, STBU,
STH, STHU,
STW, STWU,
STD, STDU,
STQ:
return op + " " + strings.Join(args, ",")
case CMPD, CMPDI, CMPLD, CMPLDI, CMPW, CMPWI, CMPLW, CMPLWI:
if len(args) == 2 {
return op + " " + args[0] + "," + args[1]
} else if len(args) == 3 {
return op + " " + args[0] + "," + args[1] + "," + args[2]
}
return op + " " + args[0] + " ??"
case LIS:
return "ADDIS $0," + args[1] + "," + args[0]
// store instructions with index registers
case STBX, STBUX, STHX, STHUX, STWX, STWUX, STDX, STDUX,
STHBRX, STWBRX, STDBRX, STSWX, STFSX, STFSUX, STFDX, STFDUX, STFIWX, STFDPX:
return "MOV" + op[2:len(op)-1] + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
case STDCXCC, STWCXCC, STHCXCC, STBCXCC:
return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
case STXVD2X, STXVW4X:
return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
// load instructions with index registers
case LBZX, LBZUX, LHZX, LHZUX, LWZX, LWZUX, LDX, LDUX,
LHBRX, LWBRX, LDBRX, LSWX, LFSX, LFSUX, LFDX, LFDUX, LFIWAX, LFIWZX:
return "MOV" + op[1:len(op)-1] + " (" + args[2] + ")(" + args[1] + ")," + args[0]
case LDARX, LWARX, LHARX, LBARX:
return op + " (" + args[2] + ")(" + args[1] + ")," + args[0]
case LXVD2X, LXVW4X:
return op + " (" + args[2] + ")(" + args[1] + ")," + args[0]
case DCBT, DCBTST, DCBZ, DCBST:
return op + " (" + args[1] + ")"
// branch instructions needs additional handling
case BCLR:
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
@ -62,11 +123,17 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
return op + " " + strings.Join(args, ", ")
case BC:
if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set
if len(args) == 4 {
return fmt.Sprintf("B%s %s,%s", args[1], args[2], args[3])
}
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
if len(args) == 4 {
return fmt.Sprintf("B%s %s,%s", revCondMap[args[1]], args[2], args[3])
}
return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2])
}
return op + " " + strings.Join(args, ", ")
return op + " " + strings.Join(args, ",")
case BCCTR:
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
return "BR (CTR)"
@ -76,9 +143,9 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
return "BL (CTR)"
}
return op + " " + strings.Join(args, ", ")
return op + " " + strings.Join(args, ",")
case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
return op + " " + strings.Join(args, ", ")
return op + " " + strings.Join(args, ",")
}
}
@ -102,6 +169,10 @@ func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64)
}
return strings.ToUpper(arg.String())
case CondReg:
// This op is left as its numerical value, not mapped onto CR + condition
if inst.Op == ISEL {
return fmt.Sprintf("$%d", (arg - Cond0LT))
}
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
return "" // don't show cr0 for cmp instructions
} else if arg >= CR0 {
@ -111,7 +182,7 @@ func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64)
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:
@ -148,25 +219,27 @@ var revCondMap = map[string]string{
// 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
LWARX: "LWAR",
LDARX: "LDAR",
LHARX: "LHAR",
LBARX: "LBAR",
ADDI: "ADD",
SRADI: "SRAD",
SUBF: "SUB",
LI: "MOVD",
LBZ: "MOVBZ", STB: "MOVB",
LBZU: "MOVBZU", STBU: "MOVBU",
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",
CMPD: "CMP", CMPDI: "CMP",
CMPW: "CMPW", CMPWI: "CMPW",
CMPLD: "CMPU", CMPLDI: "CMPU",
CMPLW: "CMPWU", CMPLWI: "CMPWU",
MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs
B: "BR",
BL: "CALL",
CMPLD: "CMPU", CMPLW: "CMPWU",
CMPD: "CMP", CMPW: "CMPW",
B: "BR",
BL: "CALL",
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,25 +1,56 @@
6d746162| gnu xoris r20,r11,24930
6d746162| plan9 XORIS R11,$24930,R20
4c040000| gnu mcrf cr0,cr1
88000017| gnu lbz r0,23(0)
4abaa88a| gnu ba 0xfebaa888
7d8fc2a6| gnu mfspr r12,783
00000000| gnu error: unknown instruction
88a70002| gnu lbz r5,2(r7)
88a70002| plan9 MOVBZ 2(R7),R5
00000000| plan9 WORD $0
00010000| plan9 error: unknown instruction
00000000| gnu .long 0x0
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
e38d5b90| gnu lq r28,23440(r13)
84127a20| gnu lwzu r0,31264(r18)
c61bb730| gnu lfsu f16,-18640(r27)
0825f440| gnu tdi 1,r5,-3008
a9a912c1| gnu lha r13,4801(r9)
84127a20| plan9 MOVWZU 31264(R18),R0
a8630000| gnu lha r3,0(r3)
a8630000| plan9 MOVH 0(R3),R3
ebb24fd1| gnu ldu r29,20432(r18)
ebb24fd1| plan9 MOVDU 20432(R18),R29
b1ce0612| gnu sth r14,1554(r14)
f3c04322| gnu xvcvdpuxws vs30,vs40
b1ce0612| plan9 MOVH R14,1554(R14)
945c62a2| gnu stwu r2,25250(r28)
9c8156e3| gnu stbu r4,22243(r1)
f91b9c7a| gnu stq r8,-25480(r27)
2c1c81b4| gnu cmpwi r28,-32332
f87b904d| gnu stdu r3,-28596(r27)
eab3c832| gnu lwa r21,-14288(r19)
2c030001| gnu cmpwi r3,1
2c030001| plan9 CMPW R3,$1
e8610032| gnu lwa r3,48(r1)
e8610032| plan9 MOVW 48(R1),R3
4320336b| gnu bcla 25,lt,0x3368
7e40092e| gnu stwx r18,0,r1
7e40092e| plan9 MOVW R18,(R1)(0)
7c103c2c| gnu lwbrx r0,r16,r7
7c103c2c| plan9 MOVWBR (R7)(R16),R0
7c441d28| gnu stdbrx r2,r4,r3
7c441d28| plan9 MOVDBR R2,(R3)(R4)
3d220001| gnu addis r9,r2,1
3d220001| plan9 ADDIS R2,$1,R9
7ce628ae| gnu lbzx r7,r6,r5
7ce628ae| plan9 MOVBZ (R5)(R6),R7
7c0e1e99| gnu lxvd2x vs32,r14,r3
7c0e1e99| plan9 LXVD2X (R3)(R14),VS32
7c00422c| gnu dcbt r0,r8,0
7c00422c| plan9 DCBT (R8)
7fab3040| gnu cmpld cr7,r11,r6
7fab3040| plan9 CMPU CR7,R11,R6
2c030001| gnu cmpwi r3,1
2c030001| plan9 CMPW R3,$1
7c2b4840| gnu cmpld r11,r9
7c2b4840| plan9 CMPU R11,R9
7c6521ad| gnu stdcx. r3,r5,r4
7c6521ad| plan9 STDCXCC R3,(R4)(R5)
fbe1ffd1| gnu stdu r31,-48(r1)
fbe1ffd1| plan9 MOVDU R31,-48(R1)
7c941f19| gnu stxvw4x vs36,r20,r3
7c941f19| plan9 STXVW4X VS36,(R3)(R20)
7c6520a8| gnu ldarx r3,r5,r4
7c6520a8| plan9 LDAR (R4)(R5),R3

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

@ -67,7 +67,6 @@ func main() {
if err != nil {
log.Fatal(err)
}
print(p)
}
@ -214,6 +213,7 @@ func add(p *Prog, text, mnemonics, encoding, tags string) {
// And note the MSB is bit 0, not bit 31.
// Example: "31@0|RS@6|RA@11|///@16|26@21|Rc@31|"
var args Args
var err error
fields := strings.Split(encoding, "|")
for i, f := range fields {
name, off := "", -1
@ -229,7 +229,16 @@ func add(p *Prog, text, mnemonics, encoding, tags string) {
fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f)
continue
}
off, _ = strconv.Atoi(f[j+1:])
k := strings.Index(f[j+1:], " ")
if k >= 0 {
if strings.HasSuffix(f[j+1:], " 31") {
f = f[:len(f)-3]
}
}
off, err = strconv.Atoi(f[j+1:])
if err != nil {
fmt.Fprintf(os.Stderr, "err for: %s has: %s for %s\n", f[:j], err, f[j+1:])
}
name = f[:j]
}
if len(args) > 0 {
@ -355,7 +364,7 @@ func add(p *Prog, text, mnemonics, encoding, tags string) {
} else {
opr = "BD"
}
case "UI", "BO", "BH", "TH", "LEV", "NB", "L", "TO", "FXM", "U", "W", "FLM", "UIM", "SHB", "SHW", "ST", "SIX", "PS", "DCM", "DGM", "RMC", "R", "SP", "S", "DM", "CT", "EH", "E", "MO", "WC", "A", "IH", "OC", "DUI", "DUIS":
case "UI", "BO", "BH", "TH", "LEV", "NB", "L", "TO", "FXM", "FC", "U", "W", "FLM", "UIM", "IMM8", "RIC", "PRS", "SHB", "SHW", "ST", "SIX", "PS", "DCM", "DCMX", "DGM", "RMC", "R", "SP", "S", "DM", "CT", "EH", "E", "MO", "WC", "A", "IH", "OC", "DUI", "DUIS", "CY":
typ = asm.TypeImmUnsigned
if i := args.Find(opr); i < 0 {
opr = "D"
@ -397,7 +406,7 @@ func add(p *Prog, text, mnemonics, encoding, tags string) {
opr = "SI"
break
}
case "RA", "RB", "RS", "RSp", "RT", "RTp":
case "RA", "RB", "RC", "RS", "RSp", "RT", "RTp":
typ = asm.TypeReg
case "BT", "BA", "BB", "BC", "BI":
typ = asm.TypeCondRegBit
@ -414,8 +423,6 @@ func add(p *Prog, text, mnemonics, encoding, tags string) {
case "SPR", "DCRN", "BHRBE", "TBR", "SR", "TMR", "PMRN": // Note: if you add to this list and the register field needs special handling, add it to switch statement below
typ = asm.TypeSpReg
switch opr {
case "BHRBE":
opr = "bhrbe" // no special handling
case "DCRN":
opr = "DCR"
}
@ -501,7 +508,7 @@ func printText(p *Prog) {
// opName translate an opcode to a valid Go identifier all-cap op name.
func opName(op string) string {
return strings.ToUpper(strings.Replace(op, ".", "_", 1))
return strings.ToUpper(strings.Replace(op, ".", "CC", 1))
}
// argFieldName constructs a name for the argField