1// Copyright 2014 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 "bytes" 9 "fmt" 10 "strings" 11) 12 13// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. 14// This form typically matches the syntax defined in the Power ISA Reference Manual. 15func GNUSyntax(inst Inst) string { 16 var buf bytes.Buffer 17 // When there are all 0s, identify them as the disassembler 18 // in binutils would. 19 if inst.Enc == 0 { 20 return ".long 0x0" 21 } else if inst.Op == 0 { 22 return "error: unknown instruction" 23 } 24 buf.WriteString(inst.Op.String()) 25 sep := " " 26 for i, arg := range inst.Args[:] { 27 if arg == nil { 28 break 29 } 30 text := gnuArg(&inst, i, arg) 31 if text == "" { 32 continue 33 } 34 buf.WriteString(sep) 35 sep = "," 36 buf.WriteString(text) 37 } 38 return buf.String() 39} 40 41// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules. 42// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy 43// of inst, it's ok to modify inst.Args here. 44func gnuArg(inst *Inst, argIndex int, arg Arg) string { 45 // special cases for load/store instructions 46 if _, ok := arg.(Offset); ok { 47 if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil { 48 panic(fmt.Errorf("wrong table: offset not followed by register")) 49 } 50 } 51 switch arg := arg.(type) { 52 case Reg: 53 if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 { 54 return "0" 55 } 56 return arg.String() 57 case CondReg: 58 if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") { 59 return "" // don't show cr0 for cmp instructions 60 } else if arg >= CR0 { 61 return fmt.Sprintf("cr%d", int(arg-CR0)) 62 } 63 bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4] 64 if arg <= Cond0SO { 65 return bit 66 } 67 return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit) 68 case Imm: 69 return fmt.Sprintf("%d", arg) 70 case SpReg: 71 return fmt.Sprintf("%d", int(arg)) 72 case PCRel: 73 return fmt.Sprintf(".%+#x", int(arg)) 74 case Label: 75 return fmt.Sprintf("%#x", uint32(arg)) 76 case Offset: 77 reg := inst.Args[argIndex+1].(Reg) 78 removeArg(inst, argIndex+1) 79 if reg == R0 { 80 return fmt.Sprintf("%d(0)", int(arg)) 81 } 82 return fmt.Sprintf("%d(r%d)", int(arg), reg-R0) 83 } 84 return fmt.Sprintf("???(%v)", arg) 85} 86 87// removeArg removes the arg in inst.Args[index]. 88func removeArg(inst *Inst, index int) { 89 for i := index; i < len(inst.Args); i++ { 90 if i+1 < len(inst.Args) { 91 inst.Args[i] = inst.Args[i+1] 92 } else { 93 inst.Args[i] = nil 94 } 95 } 96} 97 98// isLoadStoreOp returns true if op is a load or store instruction 99func isLoadStoreOp(op Op) bool { 100 switch op { 101 case LBZ, LBZU, LBZX, LBZUX: 102 return true 103 case LHZ, LHZU, LHZX, LHZUX: 104 return true 105 case LHA, LHAU, LHAX, LHAUX: 106 return true 107 case LWZ, LWZU, LWZX, LWZUX: 108 return true 109 case LWA, LWAX, LWAUX: 110 return true 111 case LD, LDU, LDX, LDUX: 112 return true 113 case LQ: 114 return true 115 case STB, STBU, STBX, STBUX: 116 return true 117 case STH, STHU, STHX, STHUX: 118 return true 119 case STW, STWU, STWX, STWUX: 120 return true 121 case STD, STDU, STDX, STDUX: 122 return true 123 case STQ: 124 return true 125 case LHBRX, LWBRX, STHBRX, STWBRX: 126 return true 127 } 128 return false 129} 130