blob: b4c9bf8df6dd9eec5e6ae412e543c9acd811603b [file] [log] [blame]
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"bytes"
"fmt"
"strings"
)
var (
// bit 3 of index is a negated check.
condBit = [8]string{
"lt", "gt", "eq", "so",
"ge", "le", "ne", "ns"}
)
// 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, pc uint64) string {
var buf bytes.Buffer
// 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"
}
PC := pc
// Special handling for some ops
startArg := 0
sep := " "
opName := inst.Op.String()
argList := inst.Args[:]
switch opName {
case "bc", "bcl", "bca", "bcla", "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
sfx := inst.Op.String()[2:]
bo := int(inst.Args[0].(Imm))
bi := inst.Args[1].(CondReg)
atsfx := [4]string{"", "?", "-", "+"}
decsfx := [2]string{"dnz", "dz"}
//BO field is... complicated (z == ignored bit, at == prediction hint)
//Paraphrased from ISA 3.1 Book I Section 2.4:
//
//0000z -> decrement ctr, b if ctr != 0 and CRbi == 0
//0001z -> decrement ctr, b if ctr == 0 and CRbi == 0
//001at -> b if CRbi == 0
//0100z -> decrement ctr, b if ctr != 0 and CRbi == 1
//0101z -> decrement ctr, b if ctr == 0 and CRbi == 1
//011at -> b if CRbi == 1
//1a00t -> decrement ctr, b if ctr != 0
//1a01t -> decrement ctr, b if ctr == 0
//1z1zz -> b always
// Decoding (in this order) we get
// BO & 0b00100 == 0b00000 -> dz if BO[1], else dnz (not simplified for bcctrl forms)
// BO & 0b10000 == 0b10000 -> (bc and bca forms not simplified), at = B[4]B[0] if B[2] != 0, done
// BO & 0b10000 == 0b00000 -> t if BO[3], else f
// BO & 0b10100 == 0b00100 -> at = B[0:1]
// BI fields rename as follows:
// less than : lt BI%4==0 && test == t
// less than or equal : le BI%4==1 && test == f
// equal : eq BI%4==2 && test == t
// greater than or equal: ge BI%4==0 && test == f
// greater than : gt BI%4==1 && test == t
// not less than : nl BI%4==0 && test == f
// not equal : ne BI%4==2 && test == f
// not greater than : ng BI%4==1 && test == f
// summary overflow : so BI%4==3 && test == t
// not summary overflow : ns BI%4==3 && test == f
// unordered : un BI%4==3 && test == t
// not unordered : nu BI%4==3 && test == f
//
// Note, there are only 8 possible tests, but quite a few more
// ways to name fields. For simplicity, we choose those in condBit.
at := 0 // 0 == no hint, 1 == reserved, 2 == not likely, 3 == likely
form := 1 // 1 == n/a, 0 == cr bit not set, 4 == cr bit set
cr := (bi - Cond0LT) / 4
bh := -1 // Only for lr/tar/ctr variants.
switch opName {
case "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
bh = int(inst.Args[2].(Imm))
}
if bo&0x14 == 0x14 {
if bo == 0x14 && bi == Cond0LT { // preferred form of unconditional branch
// Likewise, avoid printing fake b/ba/bl/bla
if opName != "bc" && opName != "bca" && opName != "bcl" && opName != "bcla" {
startArg = 2
}
}
} else if bo&0x04 == 0 { // ctr is decremented
if opName != "bcctr" && opName != "bcctrl" {
startArg = 1
tf := ""
if bo&0x10 == 0x00 {
tf = "f"
if bo&0x08 == 0x08 {
tf = "t"
}
}
sfx = decsfx[(bo>>1)&1] + tf + sfx
}
if bo&0x10 == 0x10 {
if opName != "bcctr" && opName != "bcctrl" {
startArg = 2
}
if bi != Cond0LT {
// A non-zero BI bit was encoded, but ignored by BO
startArg = 0
}
at = ((bo & 0x8) >> 2) | (bo & 0x1)
} else if bo&0x4 == 0x4 {
at = bo & 0x3
}
} else if bo&0x10 == 0x10 { // BI field is not used
if opName != "bca" && opName != "bc" {
at = ((bo & 0x8) >> 2) | (bo & 0x1)
startArg = 2
}
// If BI is encoded as a bit other than 0, no mnemonic.
if bo&0x14 == 0x14 {
startArg = 0
}
} else {
form = (bo & 0x8) >> 1
startArg = 2
if bo&0x14 == 0x04 {
at = bo & 0x3
}
}
sfx += atsfx[at]
if form != 1 {
bit := int((bi-Cond0LT)%4) | (^form)&0x4
sfx = condBit[bit] + sfx
}
if at != 1 && startArg > 0 && bh <= 0 {
str := fmt.Sprintf("b%s", sfx)
if startArg > 1 && (cr != 0 || bh > 0) {
str += fmt.Sprintf(" cr%d", cr)
sep = ","
}
buf.WriteString(str)
if startArg < 2 && bh == 0 {
str := fmt.Sprintf(" %s",
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(str)
startArg = 3
} else if bh == 0 {
startArg = 3
}
} else {
if startArg == 0 || bh > 0 || at == 1 {
buf.WriteString(inst.Op.String())
buf.WriteString(atsfx[at])
startArg = 0
} else {
buf.WriteString("b" + sfx)
}
if bh == 0 {
str := fmt.Sprintf(" %d,%s", bo, gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(str)
startArg = 3
}
}
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")
}
case "mtfsfi", "mtfsfi.":
buf.WriteString(opName)
l := inst.Args[2].(Imm)
if l == 0 {
// L == 0 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(asm)
startArg = 3
}
case "paste.":
buf.WriteString(opName)
l := inst.Args[2].(Imm)
if l == 1 {
// L == 1 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(asm)
startArg = 3
}
case "mtfsf", "mtfsf.":
buf.WriteString(opName)
l := inst.Args[3].(Imm)
if l == 0 {
// L == 0 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
buf.WriteString(asm)
startArg = 4
}
case "sync":
lsc := inst.Args[0].(Imm)<<4 | inst.Args[1].(Imm)
switch lsc {
case 0x00:
buf.WriteString("hwsync")
startArg = 2
case 0x10:
buf.WriteString("lwsync")
startArg = 2
default:
buf.WriteString(opName)
}
case "lbarx", "lharx", "lwarx", "ldarx":
// If EH == 0, omit printing EH.
eh := inst.Args[3].(Imm)
if eh == 0 {
argList = inst.Args[:3]
}
buf.WriteString(inst.Op.String())
case "paddi":
// There are several extended mnemonics. Notably, "pla" is
// the only valid mnemonic for paddi (R=1), In this case, RA must
// always be 0. Otherwise it is invalid.
r := inst.Args[3].(Imm)
ra := inst.Args[1].(Reg)
str := opName
if ra == R0 {
name := []string{"pli", "pla"}
str = fmt.Sprintf("%s %s,%s",
name[r&1],
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
startArg = 4
} else {
str = fmt.Sprintf("%s %s,%s,%s", opName,
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
startArg = 4
if r == 1 {
// This is an illegal encoding (ra != 0 && r == 1) on ISA 3.1.
v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
return fmt.Sprintf(".quad 0x%x", v)
}
}
buf.WriteString(str)
default:
// Prefixed load/stores do not print the displacement register when R==1 (they are PCrel).
// This also implies RA should be 0. Likewise, when R==0, printing of R can be omitted.
if strings.HasPrefix(opName, "pl") || strings.HasPrefix(opName, "pst") {
r := inst.Args[3].(Imm)
ra := inst.Args[2].(Reg)
d := inst.Args[1].(Offset)
if r == 1 && ra == R0 {
str := fmt.Sprintf("%s %s,%d", opName, gnuArg(&inst, 0, inst.Args[0], PC), d)
buf.WriteString(str)
startArg = 4
} else {
str := fmt.Sprintf("%s %s,%d(%s)", opName,
gnuArg(&inst, 0, inst.Args[0], PC),
d,
gnuArg(&inst, 2, inst.Args[2], PC))
if r == 1 {
// This is an invalid encoding (ra != 0 && r == 1) on ISA 3.1.
v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
return fmt.Sprintf(".quad 0x%x", v)
}
buf.WriteString(str)
startArg = 4
}
} else {
buf.WriteString(opName)
}
}
for i, arg := range argList {
if arg == nil {
break
}
if i < startArg {
continue
}
text := gnuArg(&inst, i, arg, PC)
if text == "" {
continue
}
buf.WriteString(sep)
sep = ","
buf.WriteString(text)
}
return buf.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, 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 {
panic(fmt.Errorf("wrong table: offset not followed by register"))
}
}
switch arg := arg.(type) {
case Reg:
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
return "0"
}
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 := condBit[(arg-Cond0LT)%4]
if arg <= Cond0SO {
return bit
}
return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
case Imm:
return fmt.Sprintf("%d", arg)
case SpReg:
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:
// 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:
reg := inst.Args[argIndex+1].(Reg)
removeArg(inst, argIndex+1)
if reg == R0 {
return fmt.Sprintf("%d(0)", int(arg))
}
return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
}
return fmt.Sprintf("???(%v)", arg)
}
// removeArg removes the arg in inst.Args[index].
func removeArg(inst *Inst, index int) {
for i := index; i < len(inst.Args); i++ {
if i+1 < len(inst.Args) {
inst.Args[i] = inst.Args[i+1]
} else {
inst.Args[i] = nil
}
}
}
// isLoadStoreOp returns true if op is a load or store instruction
func isLoadStoreOp(op Op) bool {
switch op {
case LBZ, LBZU, LBZX, LBZUX:
return true
case LHZ, LHZU, LHZX, LHZUX:
return true
case LHA, LHAU, LHAX, LHAUX:
return true
case LWZ, LWZU, LWZX, LWZUX:
return true
case LWA, LWAX, LWAUX:
return true
case LD, LDU, LDX, LDUX:
return true
case LQ:
return true
case STB, STBU, STBX, STBUX:
return true
case STH, STHU, STHX, STHUX:
return true
case STW, STWU, STWX, STWUX:
return true
case STD, STDU, STDX, STDUX:
return true
case STQ:
return true
case LHBRX, LWBRX, STHBRX, STWBRX:
return true
case LBARX, LWARX, LHARX, LDARX:
return true
}
return false
}