use core::fmt; use crate::{Instruction, InstructionAlias, Opcode, OpcodeAlias, Operand, PixelFormat, HintStyle}; use crate::{Register, FPR, SPR, VR, VR128, GQR}; use crate::AliasOp; use crate::Power; use yaxpeax_arch::display::DisplaySink; use yaxpeax_arch::display::ArchDisplayRules; /// `yaxpeax-power`-specific additions to the more general [`yaxpeax_arch::ArchDisplayRules`] /// trait, for formatting instructions and operands in a configurable manner. /// /// all functions here return `Ok(true)` to indicate the function has handled writing the provided /// item to `S`, or `Ok(false)` to indicate the function has not printed anything and /// library-default formatting should be done. /// /// default implementations of all `emit_*` functions simply return `Ok(false)`, so library-default /// rules apply for all cases without an overridden impl. #[allow(missing_docs)] pub trait DisplayRules: ArchDisplayRules { fn emit_memory_disp(&self, reg: Register, offset: i16, s: &mut S) -> Result { let _ = reg; let _ = offset; let _ = s; Ok(false) } fn emit_memory_index(&self, base: Register, index: Register, s: &mut S) -> Result { let _ = base; let _ = index; let _ = s; Ok(false) } fn emit_register(&self, reg: Register, s: &mut S) -> Result { let _ = reg; let _ = s; Ok(false) } fn emit_immediate(&self, imm: i32, s: &mut S) -> Result { let _ = imm; let _ = s; Ok(false) } fn emit_fpr(&self, fpr: FPR, s: &mut S) -> Result { let _ = fpr; let _ = s; Ok(false) } fn emit_vr(&self, vr: VR, s: &mut S) -> Result { let _ = vr; let _ = s; Ok(false) } fn emit_vr128(&self, vr: VR128, s: &mut S) -> Result { let _ = vr; let _ = s; Ok(false) } fn emit_gqr(&self, gqr: GQR, s: &mut S) -> Result { let _ = gqr; let _ = s; Ok(false) } fn emit_cr_field(&self, cr_field: u8, s: &mut S) -> Result { let _ = cr_field; let _ = s; Ok(false) } fn emit_cr_bit(&self, cr_bit: u8, s: &mut S) -> Result { let _ = cr_bit; let _ = s; Ok(false) } fn emit_fpscr_field(&self, fpscr_field: u8, s: &mut S) -> Result { let _ = fpscr_field; let _ = s; Ok(false) } } /// the library-default rules to display an instruction. pub struct DefaultRules {} impl DefaultRules { #[allow(missing_docs)] pub fn new() -> Self { Self {} } } impl ArchDisplayRules for DefaultRules {} impl DisplayRules for DefaultRules { } /// a `DisplayRules` implementation that does not compute book-defined aliases before formatting an /// instruction. /// /// formatted instruction instead correspond literally to the opcode/operand sequences defined for /// their bytes. encoding-specific interpretations of values are still translated to a more /// human-readable manner where appropriate (for example, an immediate with value 32 that is /// encoded as "0" would still be printed as 32). pub struct NoAliasRules {} impl NoAliasRules { #[allow(missing_docs)] pub fn new() -> Self { Self {} } } impl ArchDisplayRules for NoAliasRules { fn display_aliases(&self) -> bool { false } } impl DisplayRules for NoAliasRules {} /// higher-level helpers to write `yaxpeax-power` items into a `DisplaySink`. /// /// you only need to implement this if you have a custom `DisplaySink` implementation yourself. #[allow(missing_docs)] pub trait DisplaySinkExt: DisplaySink { fn write_opcode(&mut self, instr: &Instruction) -> Result<(), core::fmt::Error> { self.span_start_opcode(); self.write_str(instr.opcode().name())?; if instr.oe() { self.write_str("o")?; } if instr.rc() { self.write_str(".")?; } // branch opcodes have the `l` and `a` bits as part of the opcode itself (bc vs bcl vc // bca), so in those cases if these bits are present we already printed them above with // write_str(). if instr.opcode().is_branch() { // except bclr and bcctr, which seem so unlikely to have an `l` bit that there is no // additional opcode for that case. then and only then we're responsible for the `l`. if instr.lk() && (instr.opcode() == Opcode::BCLR || instr.opcode() == Opcode::BCCTR) { self.write_str("l")?; } } else { if instr.lk() { self.write_str("l")?; } if instr.aa() { self.write_str("a")?; } } self.span_end_opcode(); Ok(()) } fn write_aliased_opcode(&mut self, instr: &InstructionAlias) -> Result<(), core::fmt::Error> { self.span_start_opcode(); self.write_str(instr.opcode().name())?; if instr.oe() { self.write_str("o")?; } if instr.rc() { self.write_str(".")?; } // branch opcodes have the `l` and `a` bits as part of the opcode itself (bc vs bcl vc // bca), so in those cases if these bits are present we already printed them above with // write_str(). if instr.opcode().is_branch() { // except bclr and bcctr, which seem so unlikely to have an `l` bit that there is no // additional opcode for that case. then and only then we're responsible for the `l`. if instr.lk() && ( instr.opcode() == OpcodeAlias::Hardware(Opcode::BCLR) || instr.opcode() == OpcodeAlias::Hardware(Opcode::BCCTR) ) { self.write_str("l")?; } } else { if instr.lk() { self.write_str("l")?; } if instr.aa() { self.write_str("a")?; } } self.span_end_opcode(); Ok(()) } fn write_operand>(&mut self, operand: &super::Operand, rules: &R) -> Result<(), core::fmt::Error> { match operand { Operand::Register(reg) => { self.write_reg(reg, rules) } Operand::RegisterOrZero(reg) => { if reg.reg != 0 { self.write_reg(reg, rules)?; } else { self.write_imm(0, rules)?; } Ok(()) } Operand::FPR(reg) => { if !rules.emit_fpr(*reg, self)? { self.write_fpr(reg, rules)?; } Ok(()) } Operand::SPR(SPR { reg }) => { self.span_start_register(); match reg { 1 => self.write_str("xer")?, 8 => self.write_str("lr")?, 9 => self.write_str("ctr")?, other => { write!(self, "spr{}", other)? } } self.span_end_register(); Ok(()) } Operand::VR(reg) => { if !rules.emit_vr(*reg, self)? { self.write_vr(reg)?; } Ok(()) } Operand::VR128(reg) => { if !rules.emit_vr128(*reg, self)? { self.write_vr128(reg)?; } Ok(()) } Operand::GQR(reg) => { if !rules.emit_gqr(*reg, self)? { self.write_gqr(reg)?; } Ok(()) } Operand::MemoryDisp { base, disp } => { if !rules.emit_memory_disp(*base, *disp, self)? { if base.reg == 0 { let disp = *disp; if disp < 0 { write!(self, "-{:#x}", disp.wrapping_neg())?; } else if disp < 10 { write!(self, "{}", disp)?; } else { write!(self, "{:#x}", disp)?; } } else { let disp = *disp; if disp < 0 { write!(self, "-{:#x}(", disp.wrapping_neg())?; } else if disp < 10 { write!(self, "{}(", disp)?; } else { write!(self, "{:#x}(", disp)?; } self.write_reg(&Register { reg: base.reg }, rules)?; self.write_str(")")?; } } Ok(()) } Operand::MemoryIndexed { base, index } => { if !rules.emit_memory_index(*base, *index, self)? { if base.reg != 0 { self.write_reg(&base, rules)?; } else { self.write_imm(0, rules)?; } self.write_str(", ")?; self.write_reg(&index, rules)?; } Ok(()) } Operand::BranchOffset16(offs) => { let mut printed = false; if let Some(addr) = rules.instr_addr() { let dest = addr.wrapping_add(*offs as i64 as u64); printed = rules.emit_address(dest, self)?; } if !printed { let rel = *offs; if rel < 0 { write!(self, "$-{:#x}", rel.wrapping_neg())?; } else if rel >= 10 { write!(self, "$+{:#x}", rel)?; } else { write!(self, "$+{}", rel)?; } } Ok(()) } Operand::BranchOffset(offs) => { let mut printed = false; if let Some(addr) = rules.instr_addr() { let dest = addr.wrapping_add(*offs as i64 as u64); printed = rules.emit_address(dest, self)?; } if !printed { let rel = *offs; if rel < 0 { write!(self, "$-{:#x}", -rel)?; } else if rel >= 10 { write!(self, "$+{:#x}", rel)?; } else { write!(self, "$+{}", rel)?; } } Ok(()) } Operand::BranchAbs(imm) => { if !rules.emit_address(*imm as i64 as u64, self)? { write!(self, "{:#x}", *imm as u32)?; } Ok(()) } Operand::ImmExts16(imm) => { self.write_imm(*imm as i32, rules)?; Ok(()) } Operand::Imm8(imm) => { self.write_imm(*imm as i32, rules)?; Ok(()) } Operand::ImmU16(imm) => { self.write_imm(*imm as i32, rules)?; Ok(()) } Operand::ImmU32(imm) => { self.write_imm(*imm as i32, rules)?; Ok(()) } Operand::CrField(f) => { if !rules.emit_cr_field(*f, self)? { self.span_start_register(); self.write_fixed_size("cr")?; let upper = f / 10; let lower = f % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); } Ok(()) } Operand::CrMask(m) => { self.span_start_immediate(); self.write_fixed_size("0x")?; self.write_u8(*m)?; self.span_end_immediate(); Ok(()) } Operand::FpscrField(f) => { if !rules.emit_fpscr_field(*f, self)? { self.span_start_register(); self.write_fixed_size("fpscrf")?; let upper = f / 10; let lower = f % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); } Ok(()) } Operand::CrBit(b) => { if !rules.emit_cr_bit(*b, self)? { self.span_start_register(); // TODO: objdump spells this as something like.. // 0: lt ; lt is bit 0 in a cr field // 10: 4*cr2+eq ; eq is bit 2 in a cr field // 13: 4*cr3+gt ; gt is bit 1 in a cr field self.write_fixed_size("crb")?; let upper = b / 10; let lower = b % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); } Ok(()) } Operand::LL(sync_control) => { self.write_u8(*sync_control as u8) } Operand::PixelFormat(pixel_format) => { let label = match pixel_format { PixelFormat::D3dColor => "", PixelFormat::NormShort2 => "", PixelFormat::NormPacked32 => "", PixelFormat::Float16_2 => "", PixelFormat::NormShort4 => "", PixelFormat::Float16_4 => "", PixelFormat::NormPacked64 => "", PixelFormat::Format7 => "", }; self.write_u8(*pixel_format as u8)?; self.write_str(label) } } } fn write_reg>(&mut self, reg: &Register, rules: &R) -> Result<(), core::fmt::Error> { if rules.emit_register(*reg, self)? { return Ok(()); } self.span_start_register(); self.write_char('r')?; let upper = reg.reg / 10; let lower = reg.reg % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); Ok(()) } fn write_imm>(&mut self, imm: i32, rules: &R) -> Result<(), core::fmt::Error> { if rules.emit_immediate(imm, self)? { return Ok(()); } self.span_start_immediate(); if imm < 0 { write!(self, "-{:#x}", imm.wrapping_neg())?; } else if imm < 10 { write!(self, "{}", imm)?; } else { write!(self, "{:#x}", imm)?; } self.span_end_immediate(); Ok(()) } fn write_fpr>(&mut self, fpr: &FPR, rules: &R) -> Result<(), core::fmt::Error> { if rules.emit_fpr(*fpr, self)? { return Ok(()); } self.span_start_register(); self.write_str("fr")?; let upper = fpr.reg / 10; let lower = fpr.reg % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); Ok(()) } fn write_vr(&mut self, vr: &VR) -> Result<(), core::fmt::Error> { self.span_start_register(); self.write_str("vr")?; let upper = vr.reg / 10; let lower = vr.reg % 10; if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); Ok(()) } fn write_vr128(&mut self, vr128: &VR128) -> Result<(), core::fmt::Error> { self.span_start_register(); self.write_str("vr")?; let hundreds = vr128.reg >= 100; let upper = (vr128.reg / 10) % 10; let lower = vr128.reg % 10; if hundreds { self.write_char('1')?; self.write_char((0x30 + upper) as char)?; } else if upper > 0 { self.write_char((0x30 + upper) as char)?; } self.write_char((0x30 + lower) as char)?; self.span_end_register(); Ok(()) } fn write_gqr(&mut self, gqr: &GQR) -> Result<(), core::fmt::Error> { self.span_start_register(); self.write_str("gqr")?; // reg is in [0..7] self.write_char((0x30 as u8 + gqr.reg) as char)?; self.span_end_register(); Ok(()) } fn write_branch_op(&mut self, branch_info: &BranchInfo) -> Result<(), core::fmt::Error> { self.span_start_opcode(); self.write_char('b')?; if let Some(ctr) = branch_info.ctr { if ctr { self.write_str("dz")?; } else { self.write_str("dnz")?; } } if branch_info.inline_condition { self.write_str(branch_info.bc_flags.label.expect("inline condition means conditional branch"))?; } else if branch_info.negated { // if the condition is out of line, then if it's negated we are obligated to record // that *somewhere* self.write_str("f")?; } if branch_info.to_lr { assert!(!branch_info.to_ctr); self.write_str("lr")?; } else if branch_info.to_ctr { assert!(!branch_info.to_lr); self.write_str("ctr")?; } if branch_info.lr_set { self.write_str("l")?; } if branch_info.absolute { // for absolute self.write_str("a")?; } if let Some(hint) = branch_info.hint { if hint { self.write_str("+")?; } else { self.write_str("-")?; } } self.span_end_opcode(); Ok(()) } } impl<'a, T: fmt::Write> DisplaySinkExt for yaxpeax_arch::display::FmtSink<'a, T> { } #[cfg(feature = "std")] impl DisplaySinkExt for std::string::String { } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let rules = DefaultRules::new(); let bundle = self.display_rules(&rules); fmt::Display::fmt(&bundle, f) } } /// a pair of an instruction and the potentially-custom rules by which to format it. pub struct InstructionRuleBundle<'instr, 'rules, Rules> { pub(crate) instr: &'instr Instruction, pub(crate) rules: &'rules Rules, } impl<'instr, 'rules, Rules> InstructionRuleBundle<'instr, 'rules, Rules> { /// pair up an instruction and rules for later formatting. /// /// the fields would be public because this is so trivial, but i'd like to avoid ABI breaking /// changes by adding new defaults to the formatting bundle. sorry <3 pub fn new(instr: &'instr Instruction, rules: &'rules Rules) -> Self { Self { instr, rules } } } impl<'instr, 'rules, Rules> fmt::Display for InstructionRuleBundle<'instr, 'rules, Rules> where Rules: for<'f, 'g> DisplayRules>> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut sink = yaxpeax_arch::display::FmtSink::new(fmt); sink_display(&self.instr, self.rules, &mut sink) } } impl fmt::Display for InstructionAlias { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let rules = DefaultRules::new(); let bundle = self.display_rules(&rules); fmt::Display::fmt(&bundle, f) } } /// the same concept as [`InstructionRuleBundle`], but for formatting an [`InstructionAlias`] /// instead. /// /// this exists entirely to support custom formatting of human-friendly assembler aliases as well /// as the hardware-understood instruction formats. pub struct InstructionAliasRuleBundle<'instr, 'rules, Rules> { pub(crate) instr: &'instr InstructionAlias, pub(crate) rules: &'rules Rules, } impl<'instr, 'rules, Rules> InstructionAliasRuleBundle<'instr, 'rules, Rules> { /// pair up an instruction and rules for later formatting. /// /// the fields would be public because this is so trivial, but i'd like to avoid ABI breaking /// changes by adding new defaults to the formatting bundle. sorry <3 pub fn new(instr: &'instr InstructionAlias, rules: &'rules Rules) -> Self { Self { instr, rules } } } impl<'instr, 'rules, Rules> fmt::Display for InstructionAliasRuleBundle<'instr, 'rules, Rules> where Rules: for<'f, 'g> DisplayRules>> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut sink = yaxpeax_arch::display::FmtSink::new(fmt); sink_display_aliased(&self.instr, self.rules, &mut sink) } } /// a `DisplayRules` implementation that prints relative branches as absolute with respect to the /// provided `pc`. /// /// callers must `advance()` past the displayed instruction or otherwise update the formatter to /// keep it aligned with instructions to format. #[derive(Copy, Clone)] pub struct AbsoluteAddressFormatter { pc: u64, } impl AbsoluteAddressFormatter { /// create an `AbsoluteAddressFormatter` starting at the given address. pub fn new(pc: u64) -> Self { AbsoluteAddressFormatter { pc, } } /// bundle the provided instruction with this formatter. the returned `InstructionRuleBundle` /// is suitable for formatting (or otherwise passing to a `DisplaySink`). pub fn display<'me, 'instr>(&'me self, instr: &'instr Instruction) -> InstructionRuleBundle<'instr, 'me, Self> { InstructionRuleBundle { instr, rules: self, } } /// step this `AbsoluteAddressFormatter` forward by `instr` bytes, in preparation for another /// instruction at the next word in the program. pub fn advance(&mut self, instr: &Instruction) { use yaxpeax_arch::{AddressBase, LengthedInstruction}; self.pc = self.pc.wrapping_offset(instr.len()); } } impl ArchDisplayRules for AbsoluteAddressFormatter { fn instr_addr(&self) -> Option { Some(self.pc) } fn emit_address(&self, addr: u64, s: &mut S) -> Result { s.span_start_immediate(); s.write_fixed_size("0x")?; s.write_u64(addr)?; s.span_end_immediate(); Ok(true) } } impl DisplayRules for AbsoluteAddressFormatter {} /// a collection of computed details in service of printing manual-defined aliases for branches. /// /// this is unfortunately complex, and varies across Power ISA revisions, so documentation is... /// challenging. it is only intended to be used for tests, but `DisplaySinkExt` implementations may /// find value in it as well. pub struct BranchDetails { /// the condition code for a branch to format. `None` if the branch condition was printed /// in-line. (for exmaple: `bne` instead of `bc ne`) pub cond: Option<&'static str>, /// the condition register field to test this branch condition. may often be omitted if `0`, or /// if `uses_bi` is false. pub crf: u8, /// is the branch condition negated? this often indicates an additional `f` in the printed /// mnemonic. pub negated: bool, /// is this branch actually conditional? pub uses_bi: bool, _pad: (), } /// the fully calculated branch information in preparation for printing a branch instruction mnemonic. /// /// this is unlikely to be useful unless you are implementing `DisplaySinkExt`. #[derive(Debug)] pub struct BranchInfo { /// does this instruction branch on ctr==z or ctr!=z? pub ctr: Option, /// does this instruction have a branch-taken hint, branch-not-taken hint, or no hint? pub hint: Option, /// is the branch condition to be printed in-line as part of the mnemonic, or in an operand /// position? pub inline_condition: bool, /// does this set pc to `lr`? this is mutually-exclusive with `to_ctr`. pub to_lr: bool, /// does this set pc to `ctr`? this is mutually-exclusive with `to_lr`. pub to_ctr: bool, /// does this branch set `lr` with the previous `pc`? pub lr_set: bool, /// is the branch destination relative or absolute? pub absolute: bool, /// is the branch condition negated? this often indicate an additional `f`, but may be an /// alternate condition label if inline. pub negated: bool, /// see [`BCFlags`]. pub bc_flags: BCFlags, _pad: (), } /// information for printing a branch condition. #[derive(Debug)] pub struct BCFlags { /// this branch condition's label, or `None` if this is not actually conditional. pub label: Option<&'static str>, /// the `bi` field used to compute this branch condition state. pub bi: u8, /// the condition register field to be tested in evaluating this branch. it may be ignored for /// some values of the `BO` field. pub crf: u8, _pad: (), } /// given a branch's `BO` and `BI` fields, as well as if it is absolute, sets the Link register, or /// branches to LR or CTR, compute a simple mnemonic, if any, to use for it and return information /// to print for later operands. /// /// this is only public to simplify comparing yaxpeax-power branch formatting with other /// disassemblers. pub fn compute_branch_mnemonic>( out: &mut T, _rules: &R, hint_style: Option, bo: u8, bi: u8, disp: i64, absolute: bool, lr_set: bool, to_lr: bool, to_ctr: bool ) -> Result { let t = bo & 1; let rest = bo >> 1; // interpreting `bo`. this is funky. for anything later than the oldest POWER processors, the // approach is the following (HintStyle::PowerIsa): // the top bit is "check condition register field if clear" // the next bit is "operate if condition register true if set (otherwise, if // condition register false)" // the next bit is "decrement ctr and maybe branch if clear, otherwise dont" // the next bit is "after decrementing ctr, operate if ctr nonzero if set, // otherwise if zero)" // // Book I of Power v2.01 describes the simplifications in // // > B.2.2 Simple Branch Mnemonics // // by v2.03 this gets more complicated, with the `a` and `t` bits being used to // potentially hint for branch prediction. // // however, after POWER2 and before v2.00 there is a different hinting strategy, described on // `HintStyle` above. and finally, POWER and POWER2 simply did not have hinting at all. let mut uses_bi = true; let mut negated = false; let mut branch_info = BranchInfo { ctr: None, hint: None, inline_condition: false, to_lr, to_ctr, lr_set, absolute, negated: false, bc_flags: BCFlags { label: None, bi: 0, crf: 0, _pad: (), }, _pad: (), }; branch_info.hint = match hint_style { None => None, Some(HintStyle::Legacy) => { if rest & 0b1010 != 0b1010 { // > For bcx with a negative value in the displacement operand, the branch is // > taken. // > For all other cases (bcx with a non-negative value in the displacement // > operand, bclrx, bcctrx), the branch is not taken. let taken = disp < 0 && !to_lr && !to_ctr; // what was "y" is now called "t". undo that to match the old doc let y = t != 0; Some(taken ^ y) } else { None } }, Some(HintStyle::PowerIsa) => { if rest & 0b1010 == 0b0010 { if rest & 1 != 0 { Some(t != 0) } else { None } } else if rest & 0b1010 == 0b1000 { if rest & 0b100 != 0 { Some(t != 0) } else { None } } else { None } } }; match rest { // > Decrement the CTR, then branch if the decremented CTR is not zero and // > condition false. 0b0000 => { negated = true; branch_info.ctr = Some(false); } 0b0001 => { negated = true; branch_info.ctr = Some(true); } 0b0010 | 0b0011 => { negated = true; branch_info.inline_condition = true; } 0b0100 => { branch_info.ctr = Some(false); } 0b0101 => { branch_info.ctr = Some(true); } 0b0110 | 0b0111 => { uses_bi = true; branch_info.inline_condition = true; } 0b1000 | 0b1100 => { uses_bi = false; branch_info.ctr = Some(false); } 0b1001 | 0b1101 => { uses_bi = false; branch_info.ctr = Some(true); } // bit patterns are otherwise exhaustively covered above _ => { // conditions of 0b1x1xx are "always branch" uses_bi = false; branch_info.inline_condition = false; } } // note the description of `Condition Register` under chapter 2.3.1 of (at least) Power // ISA v2.07 static CONDITIONS: [&'static str; 8] = [ "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", ]; let cond_bits = if branch_info.inline_condition { // if the condition is negated, we can invert that for the inline display (bi % 4) | ((negated as u8) << 2) } else { // otherwise it's out of line, and we don't want to negate the condition, because the // condition is more directly a selector of one of the four crf bits. bi % 4 }; branch_info.bc_flags.label = if uses_bi { Some(CONDITIONS[cond_bits as usize]) } else { None }; branch_info.bc_flags.bi = bi % 4; branch_info.bc_flags.crf = bi / 4; branch_info.negated = negated; let details = BranchDetails { crf: branch_info.bc_flags.crf, cond: if branch_info.inline_condition { None // already printed above } else { branch_info.bc_flags.label }, negated, uses_bi, _pad: (), }; out.write_branch_op(&branch_info)?; Ok(details) } fn sink_display_aliased>(aliased: &InstructionAlias, rules: &R, out: &mut T) -> fmt::Result { match aliased.opcode() { OpcodeAlias::Hardware(Opcode::BCLR) => { let bo = aliased.operand(0).unwrap().as_imm8_unwrap(); let bi = aliased.operand(1).unwrap().as_cr_bit_unwrap(); let bh = aliased.operand(2).map(|op| op.as_imm8_unwrap()).filter(|h| *h != 0); let details = compute_branch_mnemonic( out, rules, aliased.hint_style(), bo, bi, 0, aliased.aa(), aliased.lk(), true, false, )?; if let Some(cond) = details.cond { out.write_str(" ")?; if details.crf != 0 { out.write_str("4*")?; out.write_operand(&Operand::CrField(details.crf), rules)?; out.write_char('+')?; } out.write_str(cond)?; if bh.is_some() { out.write_str(",")?; } } else if details.uses_bi && (details.crf != 0 || bh.is_some()) { out.write_str(" ")?; out.write_operand(&Operand::CrField(details.crf), rules)?; if bh.is_some() { out.write_str(",")?; } } if let Some(_bh) = bh { out.write_str(" ")?; out.write_operand(aliased.operand(2).unwrap(), rules)?; } return Ok(()); } OpcodeAlias::Hardware(Opcode::BCCTR) => { let bo = aliased.operand(0).unwrap().as_imm8_unwrap(); let bi = aliased.operand(1).unwrap().as_cr_bit_unwrap(); let bh = aliased.operand(2).map(|op| op.as_imm8_unwrap()).filter(|h| *h != 0); let rest = bo >> 1; if rest & 0b0010 == 0b0010 { let details = compute_branch_mnemonic( out, rules, aliased.hint_style(), bo, bi, 0, aliased.aa(), aliased.lk(), false, true, )?; if let Some(cond) = details.cond { out.write_str(" ")?; if details.crf != 0 { out.write_str("4*")?; out.write_operand(&Operand::CrField(details.crf), rules)?; out.write_char('+')?; } out.write_str(cond)?; if bh.is_some() { out.write_str(",")?; } } else if details.uses_bi && (details.crf != 0 || bh.is_some()) { out.write_str(" ")?; out.write_operand(&Operand::CrField(details.crf), rules)?; if bh.is_some() { out.write_str(",")?; } } } else { out.write_aliased_opcode(aliased)?; out.write_str(" ")?; out.write_operand(aliased.operand(0).unwrap(), rules)?; out.write_str(", ")?; out.write_operand(aliased.operand(1).unwrap(), rules)?; if bh.is_some() { out.write_str(",")?; } } if let Some(_bh) = bh { out.write_str(" ")?; out.write_operand(aliased.operand(2).unwrap(), rules)?; } return Ok(()); } OpcodeAlias::Hardware(Opcode::BC) | OpcodeAlias::Hardware(Opcode::BCL) | OpcodeAlias::Hardware(Opcode::BCA) | OpcodeAlias::Hardware(Opcode::BCLA) => { let bo = aliased.operand(0).unwrap().as_imm8_unwrap(); let bi = aliased.operand(1).unwrap().as_cr_bit_unwrap(); let op2 = aliased.operand(2).unwrap(); let offset = match op2 { Operand::BranchOffset16(offset) => { *offset as i64 } Operand::BranchOffset(offset) => { *offset as i64 } Operand::BranchAbs(addr) => { *addr as i64 } _ => { panic!("impossible operand"); } }; let details = compute_branch_mnemonic( out, rules, aliased.hint_style(), bo, bi, offset, aliased.aa(), aliased.lk(), false, false, )?; if let Some(cond) = details.cond { out.write_str(" ")?; out.write_str(cond)?; out.write_str(",")?; } if details.crf != 0 { out.write_str(" ")?; out.write_operand(&Operand::CrField(details.crf), rules)?; out.write_str(",")?; } out.write_str(" ")?; let mut printed = false; match op2 { Operand::BranchOffset16(_) | Operand::BranchOffset(_) => { if let Some(addr) = rules.instr_addr() { let dest = addr.wrapping_add(offset as u64); printed = rules.emit_address(dest, out)?; } } Operand::BranchAbs(_) => { printed = rules.emit_address(offset as u64, out)?; } _ => {} } if !printed { out.write_operand(op2, rules)?; } return Ok(()); } _other => { out.write_aliased_opcode(aliased)?; } } let mut i = 0; let mut printed = false; while let Some(op) = aliased.operand(i) { if !printed { out.write_str(" ")?; printed = true; } else { out.write_str(", ")?; } out.write_operand(op, rules)?; i += 1; } Ok(()) } fn sink_display>(instr: &Instruction, rules: &R, out: &mut T) -> fmt::Result { if rules.display_aliases() { return sink_display_aliased(&instr.compute_alias(), rules, out); } out.write_opcode(instr)?; let mut i = 0; let mut printed = false; while let Some(op) = instr.operand(i) { if !printed { out.write_str(" ")?; printed = true; } else { out.write_str(", ")?; } out.write_operand(op, rules)?; i += 1; } Ok(()) } impl fmt::Display for Opcode { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(OPCODES[*self as usize]) } } impl fmt::Display for OpcodeAlias { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(self.name()) } } impl OpcodeAlias { fn name(&self) -> &'static str { match self { OpcodeAlias::Hardware(op) => { op.name() } OpcodeAlias::Alias(alias) => { alias.name() } } } } impl AliasOp { fn name(&self) -> &'static str { let op_id = (*self as u16) & 0xfff; ALIASES[op_id as usize] } } pub(crate) static OPCODES: &'static [&'static str] = &[ "tdi", "td", "twi", "tw", "mulld", "mulli", "mullw", "mulhwu", "mulhw", "mulhdu", "mulhd", "subfic", "subfze", "subfe", "subfme", "subfc", "subf", "cmpli", "cmpl", "cmpi", "cmp", "add", "adde", "addic", "addc", "addi", "addis", "addze", "addme", "bc", "bca", "bcl", "bcla", "sc", "svc", "b", "ba", "bl", "bla", "rlmi", "rldimi", "rlwimi", "rlwinm", "rlwnm", "or", "orc", "ori", "oris", "xor", "xori", "xoris", "andi", "andis", "and", "nand", "andc", "ld", "ldu", "lwa", "lwax", "lwaux", "lwz", "lwzu", "lwzux", "lbz", "lbzx", "lbzu", "lbzux", "std", "stdu", "stdx", "stdux", "stdcx", "stw", "stwu", "stwx", "stwcx", "stwux", "stb", "stbu", "stbx", "stbux", "lhz", "lhzu", "lhzx", "lhzux", "lha", "lhau", "lhax", "lhaux", "lhbrx", "lwbrx", "lwarx", "lwzx", "sth", "sthu", "sthx", "sthux", "lmw", "stmw", "lfs", "lfsu", "lfsx", "lfsux", "lfd", "lfdx", "lfdu", "lfdux", "lbdx", "lhdx", "lwdx", "lddx", "lfddx", "lfdp", "lfdpx", "lfq", "stfs", "stfsu", "stfd", "stfdu", "stfq", "stfdp", "stfqu", "lswi", "lswx", "lscbx", "sle", "slq", "neg", "mul", "nor", "abs", "nabs", "div", "divw", "divwu", "divs", "divd", "divdu", "extsw", "extsh", "extsb", "stfsx", "stfsux", "stfdux", "stfdpx", "stfdx", "stfiwx", "stfqux", "stfqx", "sthbrx", "stwbrx", "stswx", "stswi", "lfqu", "lfqx", "lfqux", "tlbia", "tlbie", "tlbi", "tlbre", "tlbwe", "tlbsx", "tlbivax", "sliq", "sleq", "slbia", "slbie", "slbmte", "slbmfev", "slbmfee", "eqv", "eciwx", "ecowx", "eieio", "mbar", "srw", "srd", "sre", "srea", "sreq", "sriq", "srq", "srlq", "sraw", "srawi", "srad", "sradi", "sraiq", "srliq", "sraq", "sllq", "slliq", "dcbt", "dcbt", "dcbtls", "dcbi", "dclst", "icbi", "cli", "clf", "clcs", "rac", "rrib", "doz", "dozi", "tlbsync", "sync", "isync", "mxrcr", "mcrxr", "mftb", "mfcr", "mfocrf", "mfmsr", "mfsr", "mfsri", "mfsrin", "mfspr", "mcrf", "mcrfs", "mtocrf", "mtcrf", "mtsr", "mtsrd", "mtspr", "mtmsr", "mtmsrd", "mtmsrd", "mtsrin", "mtsrdin", "mffs", "mcfrs", "mtfsfi", "mtfsf", "mtfsb0", "mtfsb1", "ldx", "ldux", "ldarx", "slw", "sld", "cntlzw", "cntlzd", "crxor", "cror", "crnor", "crand", "crnand", "crandc", "crorc", "creqv", "bcctr", "bclr", "dcba", "dcbst", "dcbtst", "dcbf", "dcbz", "dcbz128", "maskir", "maskg", "dst", "dstt", "dss", "dssall", "dstst", "dststt", "rldcl", "rldic", "rldicl", "rldcr", "rldicr", "rfi", "rfid", "rfsvc", "hrfid", "bbelr", "bblels", "attn", "msgclr", "msgsnd", "msgclrp", "msgsndp", "drsp", "dsub", "dadd", "daddq", "dmul", "dmulq", "ddiv", "fmr", "fabs", "fnabs", "fneg", "fre", "fres", "frsp", "fmul", "fmuls", "fdiv", "fdivs", "fsub", "fsubs", "fadd", "fadds", "fsqrt", "fsqrts", "fsel", "isel", "frin", "frim", "frip", "friz", "frsqrte", "frsqrtes", "flsqrt", "fmsub", "fmsubs", "fmadd", "fmadds", "fnmsub", "fnmsubs", "fnmadd", "fnmadds", "ftdiv", "ftsqrt", "fcmpo", "fcmpu", "fcpsgn", "dqua", "dquaq", "dquai", "dquaiq", "dscli", "dscliq", "dscri", "dscriq", "drdpq", "drrnd", "drrndq", "dsubq", "ddivq", "diex", "diexq", "dxex", "dxexq", "dcmpo", "dcmpu", "dcmpoq", "dcmpuq", "dcffix", "dctfix", "dcffixq", "dctfixq", "dctdp", "dctqpq", "drintn", "drintnq", "drintx", "drintxq", "dtstdc", "dtstdcq", "dtstdg", "dtstdgq", "dtstsf", "dtstsfq", "dtstex", "dtstexq", "denbcd", "denbcdq", "ddedpd", "ddedpdq", "fcfidu", "fcfidus", "fcfids", "fctiw", "fctiwz", "fctiwu", "fctiwuz", "fctidu", "fctiduz", "fcfid", "fctid", "fctidz", "vmhaddshs", "vmhraddshs", "vmladduhm", "vmsumubm", "vmsummbm", "vmsumuhm", "vmsumuhs", "vmsumshm", "vmsumshs", "vsel", "vperm", "vsldoi", "vmaddfp", "vnmsubfp", "vaddubm", "vmaxub", "vrlb", "vcmpequb", "vmuloub", "vaddfp", "vmrghb", "vpkuhum", "mulhhwu", "machhwu", "vadduhm", "vmaxuh", "vrlh", "vmulouh", "vsubfp", "vmrghh", "vpkuwum", "mulhhww", "nmachhw", "vadduwm", "vmaxuw", "vrlw", "vmrghw", "vpkuhus", "vpkuwus", "machhws", "vmaxsb", "vslb", "vmulosb", "vrefp", "vmrglb", "vpkshus", "mulchwu", "macchwu", "vmaxsh", "vslh", "vmulosh", "vrsqrtefp", "vmrglh", "vpkswus", "mulchw", "nmacchw", "vaddcuw", "vmaxsw", "vslw", "vexptefp", "vmrglw", "vpkshss", "vpkswss", "vsl", "vlogefp", "macchws", "vaddubs", "vminub", "vsrb", "vmuleub", "vrfin", "vspltb", "vupkhsb", "vadduhs", "vminuh", "vsrh", "vmuleuh", "vrfiz", "vsplth", "vupkhsh", "vadduws", "vminuw", "vsrw", "vrfip", "vspltw", "vupklsb", "vsr", "vrfim", "vupklsh", "vaddsbs", "vminsb", "vsrab", "vmulesb", "vcfux", "vspltisb", "vpkpx", "mullhwu", "maclhwu", "vaddshs", "vminsh", "vsrah", "vmulesh", "vcfsx", "vspltish", "vupkhpx", "mullhw", "nmaclhw", "vaddsws", "vminsw", "vsraw", "vctuxs", "vspltisw", "vctsxs", "vupklpx", "maclhws", "vsububm", "vavguh", "vand", "vmaxfp", "vslo", "vsubuhm", "vavguw", "vandc", "vcmpequh", "vminfp", "vsro", "vavgsh", "machhw", "vsubuwm", "vavgub", "vor", "vcmpequw", "vxor", "vcmpeqfp", "vavgsb", "vnor", "macchw", "vsubcuw", "vavgsw", "vsum4ubs", "vsum4shs", "vsum2sws", "vsum4sbs", "maclhw", "vcmpgefp", "vsububs", "mfvscr", "vcmpgtub", "vcmp4ubs", "vsubuhs", "mtvscr", "vcmpgtuh", "vsubuws", "vcmpgtuw", "vcmpgtfp", "vsubsbs", "vcmpgtsb", "vsubshs", "vcmpgtsh", "vsubsws", "vcmpgtsw", "vsumsws", "vcmpbfp", "vupkhsb128", "vupklsb128", "vcmpeqfp128", "vcmpgefp128", "vcmpgtfp128", "vcmpbfp128", "vcmpequw128", "vmaxfp128", "vminfp128", "vmrghw128", "vmrglw128", "lvsl128", "lvsr128", "lvewx128", "lvx128", "stvx128", "lvxl128", "stvewx128", "stvxl128", "lvlx128", "lvrx128", "stvlx128", "stvrx128", "lvlxl128", "lvrxl128", "stvlxl128", "stvrxl128", "vsldoi128", "vperm128", "vpkshss128", "vpkshus128", "vpkswss128", "vpkswus128", "vpkuhum128", "vpkuhus128", "vpkuwum128", "vpkuwus128", "vaddfp128", "vsubfp128", "vmulfp128", "vmaddfp128", "vmaddcfp128", "vnmsubfp128", "vmsum3fp128", "vmsum4fp128", "vand128", "vandc128", "vnor128", "vor128", "vxor128", "vsel128", "vslo128", "vsro128", "vsraw128", "vrlw128", "vslw128", "vsrw128", "vpermwi128", "vcfpsxws128", "vcfpuxws128", "vcsxwfp128", "vcuxwfp128", "vrfim128", "vrfin128", "vrfip128", "vrfiz128", "vpkd3d128", "vrefp128", "vrsqrtefp128", "vexptefp128", "vlogefp128", "vrlimi128", "vspltw128", "vspltisw128", "vupkd3d128", "wrtee", "wrteei", "lvsl", "lvsr", "stvepx", "stvepxl", "lvlx", "lvrx", "lvx", "lvxl", "lvebx", "lvehx", "lvewx", "stvx", "stvxl", "stvlx", "stvrx", "stvlxl", "stvrxl", "stvebx", "stvehx", "stvewx", "lvlxl", "lvrxl", "ldbrx", "stdbrx", "tabort", "tabortdc", "tabortdci", "tabortwc", "tabortwci", "tbegin", "tend", "tcheck", "tsr", "treclaim", "trechkpt", "dcbz_l", "ps_sum0", "ps_sum1", "ps_muls0", "ps_muls1", "ps_madds0", "ps_madds1", "ps_div", "ps_sub", "ps_add", "ps_sel", "ps_res", "ps_mul", "ps_rsqrte", "ps_msub", "ps_madd", "ps_nmsub", "ps_nmadd", "ps_neg", "ps_mr", "ps_nabs", "ps_abs", "ps_cmpu0", "ps_cmpo0", "ps_cmpu1", "ps_cmpo1", "ps_merge00", "ps_merge01", "ps_merge10", "ps_merge11", "psq_l", "psq_lu", "psq_lx", "psq_lux", "psq_st", "psq_stu", "psq_stx", "psq_stux", ]; static ALIASES: &'static [&'static str] = &[ "hwsync", "lwsync", "ptesync", "trap", "twle", "twge", "twlt", "twgt", "twne", "tweq", "twlge", "twlle", "twlgt", "twllt", "tdlt", "tdgt", "tdne", "tdeq", "tdlge", "tdlle", "mr", "nop", "not", "li", "lis", "subi", "subis", "crclr", "crset", "crnot", "crmove", "mtxer", "mtlr", "mtctr", "mfxer", "mflr", "mfctr", "mtcr", "mtmsr", "mtmsrd", "mtmsrd", "mtmsree", "mtfsf", "srdi", "clrldi", "sldi", "clrrdi", "clrrwi", "clrlwi", "rotlwi", "slwi", "srwi", "extlwi", "cmpldi", "cmplwi", "cmpdi", "cmpwi", "cmpd", "cmpw", "tdgei", "tdlei", "tdgti", "tdlti", "tdlgti", "tdllti", "tdlgei", "tdllei", "tdeqi", "tdnei", "twgti", "twlti", "twgei", "twlei", "twlgti", "twllti", "twlgei", "twllei", "tweqi", "twnei", "cmpld", "cmplw", "rotlw", "inslwi", "dcbf", "dcbfl", "dcbflp", "dcbtct", "dcbtds", "dcbtt", "dcbtstct", "dcbtstds", "dcbtstt", "tsuspend", "tresume", "tend", "tendall", ];