in ppc64/ppc64asm/gnu.go [22:358]
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()
}