1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package ppc64asm 6 7import ( 8 "fmt" 9 "strings" 10) 11 12// GoSyntax returns the Go assembler syntax for the instruction. 13// The pc is the program counter of the first instruction, used for expanding 14// PC-relative addresses into absolute ones. 15// The symname function queries the symbol table for the program 16// being disassembled. It returns the name and base address of the symbol 17// containing the target, if any; otherwise it returns "", 0. 18func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string { 19 if symname == nil { 20 symname = func(uint64) (string, uint64) { return "", 0 } 21 } 22 if inst.Op == 0 { 23 return "?" 24 } 25 var args []string 26 for i, a := range inst.Args[:] { 27 if a == nil { 28 break 29 } 30 if s := plan9Arg(&inst, i, pc, a, symname); s != "" { 31 args = append(args, s) 32 } 33 } 34 var op string 35 op = plan9OpMap[inst.Op] 36 if op == "" { 37 op = strings.ToUpper(inst.Op.String()) 38 } 39 // laid out the instruction 40 switch inst.Op { 41 default: // dst, sA, sB, ... 42 if len(args) == 0 { 43 return op 44 } else if len(args) == 1 { 45 return fmt.Sprintf("%s %s", op, args[0]) 46 } 47 args = append(args, args[0]) 48 return op + " " + strings.Join(args[1:], ", ") 49 // store instructions always have the memory operand at the end, no need to reorder 50 case STB, STBU, STBX, STBUX, 51 STH, STHU, STHX, STHUX, 52 STW, STWU, STWX, STWUX, 53 STD, STDU, STDX, STDUX, 54 STQ, 55 STHBRX, STWBRX: 56 return op + " " + strings.Join(args, ", ") 57 // branch instructions needs additional handling 58 case BCLR: 59 if int(inst.Args[0].(Imm))&20 == 20 { // unconditional 60 return "RET" 61 } 62 return op + " " + strings.Join(args, ", ") 63 case BC: 64 if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set 65 return fmt.Sprintf("B%s %s", args[1], args[2]) 66 } else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set 67 return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2]) 68 } 69 return op + " " + strings.Join(args, ", ") 70 case BCCTR: 71 if int(inst.Args[0].(Imm))&20 == 20 { // unconditional 72 return "BR (CTR)" 73 } 74 return op + " " + strings.Join(args, ", ") 75 case BCCTRL: 76 if int(inst.Args[0].(Imm))&20 == 20 { // unconditional 77 return "BL (CTR)" 78 } 79 return op + " " + strings.Join(args, ", ") 80 case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL: 81 return op + " " + strings.Join(args, ", ") 82 } 83} 84 85// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules. 86// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy 87// of inst, it's ok to modify inst.Args here. 88func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string { 89 // special cases for load/store instructions 90 if _, ok := arg.(Offset); ok { 91 if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil { 92 panic(fmt.Errorf("wrong table: offset not followed by register")) 93 } 94 } 95 switch arg := arg.(type) { 96 case Reg: 97 if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 { 98 return "0" 99 } 100 if arg == R30 { 101 return "g" 102 } 103 return strings.ToUpper(arg.String()) 104 case CondReg: 105 if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") { 106 return "" // don't show cr0 for cmp instructions 107 } else if arg >= CR0 { 108 return fmt.Sprintf("CR%d", int(arg-CR0)) 109 } 110 bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4] 111 if arg <= Cond0SO { 112 return bit 113 } 114 return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit) 115 case Imm: 116 return fmt.Sprintf("$%d", arg) 117 case SpReg: 118 switch arg { 119 case 8: 120 return "LR" 121 case 9: 122 return "CTR" 123 } 124 return fmt.Sprintf("SPR(%d)", int(arg)) 125 case PCRel: 126 addr := pc + uint64(int64(arg)) 127 if s, base := symname(addr); s != "" && base == addr { 128 return fmt.Sprintf("%s(SB)", s) 129 } 130 return fmt.Sprintf("%#x", addr) 131 case Label: 132 return fmt.Sprintf("%#x", int(arg)) 133 case Offset: 134 reg := inst.Args[argIndex+1].(Reg) 135 removeArg(inst, argIndex+1) 136 if reg == R0 { 137 return fmt.Sprintf("%d(0)", int(arg)) 138 } 139 return fmt.Sprintf("%d(R%d)", int(arg), reg-R0) 140 } 141 return fmt.Sprintf("???(%v)", arg) 142} 143 144// revCondMap maps a conditional register bit to its inverse, if possible. 145var revCondMap = map[string]string{ 146 "LT": "GE", "GT": "LE", "EQ": "NE", 147} 148 149// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics. 150var plan9OpMap = map[Op]string{ 151 LWARX: "LWAR", STWCX_: "STWCCC", 152 LDARX: "LDAR", STDCX_: "STDCCC", 153 LHARX: "LHAR", STHCX_: "STHCCC", 154 LBARX: "LBAR", STBCX_: "STBCCC", 155 ADDI: "ADD", 156 ADD_: "ADDCC", 157 LBZ: "MOVBZ", STB: "MOVB", 158 LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled 159 LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH", 160 LHZU: "MOVHZU", STHU: "MOVHU", 161 LI: "MOVD", 162 LIS: "ADDIS", 163 LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW", 164 LWZU: "MOVWZU", STWU: "MOVWU", 165 LD: "MOVD", STD: "MOVD", 166 LDU: "MOVDU", STDU: "MOVDU", 167 MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs 168 B: "BR", 169 BL: "CALL", 170 CMPLD: "CMPU", CMPLW: "CMPWU", 171 CMPD: "CMP", CMPW: "CMPW", 172} 173