Skip to content

Instantly share code, notes, and snippets.

@aDotInTheVoid
Created July 20, 2023 22:06
Show Gist options
  • Save aDotInTheVoid/f9b3d78f9d883ae77f647e4caa8f9611 to your computer and use it in GitHub Desktop.
Save aDotInTheVoid/f9b3d78f9d883ae77f647e4caa8f9611 to your computer and use it in GitHub Desktop.
use crate::{
builder::{DataLabel, Label},
operands::{Imm32, Shift},
regs, Reg, RegSet, Style,
};
use std::fmt::Write;
fn fmt_neg(neg: bool) -> &'static str {
if neg {
"-"
} else {
""
}
}
fn fmt_bang(bang: bool) -> &'static str {
if bang {
"!"
} else {
""
}
}
pub trait Instruction {
fn emit(&self, style: Style, f: &mut String);
/// This should be overwritten for everything that has a chance of using literal pool
fn uses_lit_pool(&self) -> bool {
false
}
/// Note that this method is best-effort, so this default impl is OK
fn unconditionally_branches(&self) -> bool {
false
}
}
/// Parsing Rust tokens into ARM instructions
#[macro_export]
macro_rules! emit_ins {
// Match PUSH/POP
($builder:expr; PUSH { $($reg_name:expr),* }) => {
$builder.push($crate::RegSet::new(&[$($reg_name),*]))
};
($builder:expr; POP { $($reg_name:expr),* }) => {
$builder.pop($crate::RegSet::new(&[$($reg_name),*]))
};
// Match branches
($builder:expr; $ins_name:ident $target:expr) => {
$builder.emit($crate::insns::Branch {
name: {use $crate::insns::BranchKind::*; ($ins_name)},
target: $crate::insns::BranchTarget::from($target),
})
};
// Match LDR pseudoinstruction (accepts any name for condition codes)
($builder:expr; $ins_name:ident $rd:expr, =$imm:expr) => {
#[allow(unused_parens)]
$builder.emit($crate::insns::PseudoLdr {
name: {use $crate::insns::PseudoLdrKind::*; ($ins_name)},
rd: $rd,
imm: $crate::insns::PseudoLdrOperand::from($imm),
})
};
// Match load/store instructions with zero offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr]) => {
$builder.emit($crate::insns::MemoryZeroOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
})
};
// Match load/store instructions with immediate offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, #$imm:expr]) => {
$builder.emit($crate::insns::MemoryImmOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
imm: $imm,
bang: false,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, #$imm:expr]!) => {
$builder.emit($crate::insns::MemoryImmOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
imm: $imm,
bang: true,
})
};
// Match load/store instructions with negative register offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr]) => {
$builder.emit($crate::insns::MemoryRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: false,
neg: true,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr]!) => {
$builder.emit($crate::insns::MemoryRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: true,
neg: true,
})
};
// Match load/store instructions with negative register offset operand shifted with RRX
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr, RRX]) => {
$builder.emit($crate::insns::MemoryRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: false,
neg: true,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr, RRX]!) => {
$builder.emit($crate::insns::MemoryRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: true,
neg: true,
})
};
// Match load/store instructions with negative shifted register offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr, $shift:ident #$bits:expr]) => {
$builder.emit($crate::insns::MemoryShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
bits: $bits,
bang: false,
neg: true,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, -$rm:expr, $shift:ident #$bits:expr]!) => {
$builder.emit($crate::insns::MemoryShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
bits: $bits,
bang: true,
neg: true,
})
};
// Match load/store instructions with register offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr]) => {
$builder.emit($crate::insns::MemoryRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: false,
neg: false,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr]!) => {
$builder.emit($crate::insns::MemoryRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: true,
neg: false,
})
};
// Match load/store instructions with register offset operand shifted with RRX
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr, RRX]) => {
$builder.emit($crate::insns::MemoryRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang: false
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr, RRX]!) => {
$builder.emit($crate::insns::MemoryRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bang; true,
neg: false,
})
};
// Match load/store instructions with shifted register offset operand
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr, $shift:ident #$bits:expr]) => {
$builder.emit($crate::insns::MemoryShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
bits: $bits,
bang: false,
neg: false,
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr, $rm:expr, $shift:ident #$bits:expr]!) => {
$builder.emit($crate::insns::MemoryShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
bits: $bits,
bang: true,
neg: false,
})
};
// Match load/store instruction with post-indexed addressing and immediate as offset
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], #$imm:expr) => {
$builder.emit($crate::insns::MemoryPostImmOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
imm: $imm,
})
};
// Match load/store instruction with post-indexed addressing and register as offset
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], -$rm:expr) => {
$builder.emit($crate::insns::MemoryPostRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
neg: true
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], $rm:expr) => {
$builder.emit($crate::insns::MemoryPostRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
neg: false
})
};
// Match load/store instruction with post-indexed addressing and RRXed register as offset
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], -$rm:expr, RRX) => {
$builder.emit($crate::insns::MemoryPostRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
neg: true
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], $rm:expr, RRX) => {
$builder.emit($crate::insns::MemoryPostRRXedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
neg: false
})
};
// Match load/store instruction with post-indexed addressing and shifted register as offset
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], -$rm:expr, $shift:ident #$bits:expr) => {
$builder.emit($crate::insns::MemoryPostShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bits: $bits,
shift: $crate::operands::Shift::$shift,
neg: true
})
};
($builder:expr; $ins_name:ident $rd:expr, [$rn:expr], $rm:expr, $shift:ident #$bits:expr) => {
$builder.emit($crate::insns::MemoryPostShiftedRegOffset {
name: {use $crate::insns::MemoryKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
bits: $bits,
shift: $crate::operands::Shift::$shift,
neg: false
})
};
// Match all 2op data-processing instructions with immediate as the second operand
($builder:expr; $ins_name:ident $rd:expr, #$imm:expr) => {
$builder.emit($crate::insns::DPRegAndImm {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
imm: $crate::operands::Imm32::from($imm),
})
};
// Match all 2op data-processing instructions with a register as the second operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr) => {
$builder.emit($crate::insns::DP2Regs {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
})
};
// Match all 2op data-processing instructions with a register shifted with RRX as the second operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, RRX) => {
$builder.emit($crate::insns::DP2RegsWithRRX {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
})
};
// Match all 2op data-processing instructions with a register shifted with ASR by immediate number of bits as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $shift:ident #$bits:expr) => {
$builder.emit($crate::insns::DP2RegsWithImmShift {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
shift: $crate::operands::Shift::$shift,
bits: $bits,
})
};
// Match all data-processing instructions with immediate as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, #$imm:expr) => {
$builder.emit($crate::insns::DP2RegsAndImm {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
imm: $crate::operands::Imm32::from($imm),
})
};
// Match all data-processing instructions with a register shifted by immediate number of bits as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $rm:expr, $shift:ident #$bits:expr) => {
$builder.emit($crate::insns::DP3RegsWithImmShift {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
bits: $bits,
})
};
// Match all 2op data-processing instructions with a register shifted with ASR by number of bits in rs as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $rm:expr, $shift:ident $rs:expr) => {
$builder.emit($crate::insns::D2RegsWithRegShift {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
shift: $crate::operands::Shift::$shift,
rs: $rs,
})
};
// Match all data-processing instructions with a register as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $rm:expr) => {
$builder.emit($crate::insns::DP3Regs {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
})
};
// Match all data-processing instructions with a register shifted with RRX as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $rm:expr, RRX) => {
$builder.emit($crate::insns::DP3RegsWithRRX {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
})
};
// Match all data-processing instructions with a register shifted with ASR by number of bits in rs as the third operand
($builder:expr; $ins_name:ident $rd:expr, $rn:expr, $rm:expr, $shift:ident $rs:expr) => {
$builder.emit($crate::insns::DP3RegsWithRegShift {
name: {use $crate::insns::DPKind::*; ($ins_name)},
rd: $rd,
rn: $rn,
rm: $rm,
shift: $crate::operands::Shift::$shift,
rs: $rs,
})
};
// Match all 4 register operand instructions
($builder:expr; $ins_name:ident $rdlo:expr, $rdhi:expr, $rm:expr, $rs:expr) => {
$builder.emit($crate::insns::FourRegs {
name: {use $crate::insns::FourRegsKind::*; ($ins_name)},
rdlo: $rdlo,
rdhi: $rdhi,
rm: $rm,
rs: $rs
})
}
}
macro_rules! kinds {
(
$(
enum $name:ident {
$(
$variant:ident
),*
$(,)?
}
)+
) => {
$(
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum $name {
$(
$variant
),*
}
impl $name {
#[doc(hidden)]
#[inline(always)]
pub fn _from_str(s: &str) -> Self {
match s {
$(
stringify!($variant) => $name::$variant,
)*
_ => unreachable!("Unknown variant: {s}"),
}
}
pub fn name(self) -> &'static str {
match self {
$(
$name::$variant => stringify!($variant),
)*
}
}
}
)+
};
}
kinds! {
enum BranchKind {
B, BL, BX, BVS, BCS,
BEQ, BLE, BLT, BGE, BGT, BNE
}
enum DPKind {
CMP,
MOV,
MOVEQ, MOVLE, MOVLT, MOVGE, MOVGT, MOVNE,
ADDS, SUBS, RSBS,
SUB, RSB, AND, ADD
}
enum FourRegsKind {SMULL}
enum MemoryKind {LDR, STR, LDRB, STRB}
enum PseudoLdrKind {LDR, LDREQ}
}
pub struct PseudoLdr {
pub name: PseudoLdrKind,
pub rd: Reg,
pub imm: PseudoLdrOperand,
}
pub enum PseudoLdrOperand {
Imm32(Imm32),
DataLabel(DataLabel),
Str(&'static str),
}
impl From<i32> for PseudoLdrOperand {
fn from(i: i32) -> Self {
PseudoLdrOperand::Imm32(Imm32::I32(i))
}
}
impl From<u32> for PseudoLdrOperand {
fn from(i: u32) -> Self {
PseudoLdrOperand::Imm32(Imm32::U32(i))
}
}
impl From<DataLabel> for PseudoLdrOperand {
fn from(l: DataLabel) -> Self {
PseudoLdrOperand::DataLabel(l)
}
}
impl From<&'static str> for PseudoLdrOperand {
fn from(s: &'static str) -> Self {
PseudoLdrOperand::Str(s)
}
}
impl Instruction for PseudoLdr {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, =",
style.style_ins_name(&self.name.name()),
style.style_reg(self.rd),
)
.unwrap();
match self.imm {
PseudoLdrOperand::Imm32(i) => write!(f, "{i}"),
PseudoLdrOperand::Str(s) => write!(f, "{s}"),
PseudoLdrOperand::DataLabel(l) => write!(f, "{}", style.style_data_label(l)),
}
.unwrap();
}
fn uses_lit_pool(&self) -> bool {
match self.imm {
PseudoLdrOperand::Imm32(i) => !i.fits_operand2(),
PseudoLdrOperand::DataLabel(_) => true,
PseudoLdrOperand::Str(_) => true,
}
}
}
pub struct MemoryZeroOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
}
impl Instruction for MemoryZeroOffset {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, [{}]",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn)
)
.unwrap();
}
}
pub struct MemoryImmOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub imm: i32,
pub bang: bool,
}
impl Instruction for MemoryImmOffset {
fn emit(&self, style: Style, f: &mut String) {
assert!(-4095 <= self.imm && self.imm <= 4095);
write!(
f,
"{:<6} {}, [{}, #{}]{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
self.imm,
fmt_bang(self.bang)
)
.unwrap();
}
}
pub struct MemoryPostImmOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub imm: i32,
}
impl Instruction for MemoryPostImmOffset {
fn emit(&self, style: Style, f: &mut String) {
assert!(-4095 <= self.imm && self.imm <= 4095);
write!(
f,
"{:<6} {}, [{}], #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
self.imm,
)
.unwrap();
}
}
pub struct MemoryRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub neg: bool,
pub bang: bool,
}
impl Instruction for MemoryRegOffset {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, [{}, {}{}]{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
fmt_bang(self.bang)
)
.unwrap();
}
}
pub struct MemoryPostRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub neg: bool,
}
impl Instruction for MemoryPostRegOffset {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, [{}], {}{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
)
.unwrap();
}
}
pub struct MemoryRRXedRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub neg: bool,
pub bang: bool,
}
impl Instruction for MemoryRRXedRegOffset {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, [{}, {}{}, RRX]{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
fmt_bang(self.bang)
)
.unwrap();
}
}
pub struct MemoryPostRRXedRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub neg: bool,
}
impl Instruction for MemoryPostRRXedRegOffset {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, [{}], {}{}, RRX",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
)
.unwrap();
}
}
pub struct MemoryShiftedRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub shift: Shift,
pub bits: usize,
pub neg: bool,
pub bang: bool,
}
impl Instruction for MemoryShiftedRegOffset {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.shift.imm_in_range(self.bits));
write!(
f,
"{:<6} {}, [{}, {}{}, {} #{}]{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
style.style_shift(self.shift),
self.bits,
fmt_bang(self.bang)
)
.unwrap();
}
}
pub struct MemoryPostShiftedRegOffset {
pub name: MemoryKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub shift: Shift,
pub bits: usize,
pub neg: bool,
}
impl Instruction for MemoryPostShiftedRegOffset {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.shift.imm_in_range(self.bits));
write!(
f,
"{:<6} {}, [{}], {}{}, {} #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
fmt_neg(self.neg),
style.style_reg(self.rm),
style.style_shift(self.shift),
self.bits,
)
.unwrap();
}
}
pub struct DP3Regs {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
}
impl Instruction for DP3Regs {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, {}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_reg(self.rm)
)
.unwrap();
}
}
pub struct DP2RegsAndImm {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub imm: Imm32,
}
impl Instruction for DP2RegsAndImm {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.imm.fits_operand2());
write!(
f,
"{:<6} {}, {}, #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
self.imm
)
.unwrap();
}
}
pub struct DP3RegsWithRRX {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
}
impl Instruction for DP3RegsWithRRX {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, {}, rrx",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_reg(self.rm)
)
.unwrap();
}
}
pub struct DP3RegsWithImmShift {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub shift: Shift,
pub bits: usize,
}
impl Instruction for DP3RegsWithImmShift {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.shift.imm_in_range(self.bits));
write!(
f,
"{:<6} {}, {}, {}, {} #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_reg(self.rm),
style.style_shift(self.shift),
self.bits
)
.unwrap();
}
}
pub struct DP3RegsWithRegShift {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub shift: Shift,
pub rs: Reg,
}
impl Instruction for DP3RegsWithRegShift {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, {}, {} {}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_reg(self.rm),
style.style_shift(self.shift),
style.style_reg(self.rs)
)
.unwrap();
}
}
pub struct DP2Regs {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
}
impl Instruction for DP2Regs {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn)
)
.unwrap();
}
}
pub struct DPRegAndImm {
pub name: DPKind,
pub rd: Reg,
pub imm: Imm32,
}
impl Instruction for DPRegAndImm {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.imm.fits_operand2());
write!(
f,
"{:<6} {}, #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
self.imm
)
.unwrap();
}
}
pub struct DP2RegsWithRRX {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
}
impl Instruction for DP2RegsWithRRX {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, rrx",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
)
.unwrap();
}
}
pub struct DP2RegsWithImmShift {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub shift: Shift,
pub bits: usize,
}
impl Instruction for DP2RegsWithImmShift {
fn emit(&self, style: Style, f: &mut String) {
assert!(self.shift.imm_in_range(self.bits));
write!(
f,
"{:<6} {}, {}, {} #{}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_shift(self.shift),
self.bits
)
.unwrap();
}
}
pub struct DP2RegsWithRegShift {
pub name: DPKind,
pub rd: Reg,
pub rn: Reg,
pub rm: Reg,
pub shift: Shift,
pub rs: Reg,
}
impl Instruction for DP2RegsWithRegShift {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, {} {}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rd),
style.style_reg(self.rn),
style.style_shift(self.shift),
style.style_reg(self.rs)
)
.unwrap();
}
}
pub enum BranchTarget {
Label(Label),
Reg(Reg),
String(String),
}
impl From<Label> for BranchTarget {
fn from(l: Label) -> Self {
BranchTarget::Label(l)
}
}
impl From<Reg> for BranchTarget {
fn from(r: Reg) -> Self {
BranchTarget::Reg(r)
}
}
impl From<String> for BranchTarget {
fn from(s: String) -> Self {
BranchTarget::String(s)
}
}
impl From<&'static str> for BranchTarget {
fn from(s: &'static str) -> Self {
BranchTarget::String(s.to_owned())
}
}
pub struct Branch {
pub name: BranchKind,
pub target: BranchTarget,
}
impl Instruction for Branch {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}",
style.style_ins_name(self.name.name()),
match &self.target {
BranchTarget::Label(l) => style.style_label(*l),
BranchTarget::Reg(r) => style.style_reg(*r),
BranchTarget::String(s) => s.to_owned(), // TODO: Maybe don't alloc
}
)
.unwrap();
}
fn unconditionally_branches(&self) -> bool {
match self.name {
BranchKind::B | BranchKind::BX => true,
_ => false,
}
}
}
pub struct FourRegs {
pub name: FourRegsKind,
pub rdlo: Reg,
pub rdhi: Reg,
pub rm: Reg,
pub rs: Reg,
}
impl Instruction for FourRegs {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}, {}, {}, {}",
style.style_ins_name(self.name.name()),
style.style_reg(self.rdlo),
style.style_reg(self.rdhi),
style.style_reg(self.rm),
style.style_reg(self.rs)
)
.unwrap();
}
}
#[derive(Debug, PartialEq)]
pub enum PushPopKind {
Push,
Pop,
}
pub struct PushPop {
pub kind: PushPopKind,
pub regs: RegSet,
}
impl Instruction for PushPop {
fn emit(&self, style: Style, f: &mut String) {
write!(
f,
"{:<6} {}",
style.style_ins_name(match self.kind {
PushPopKind::Push => "push",
PushPopKind::Pop => "pop",
}),
self.regs
)
.unwrap();
}
fn unconditionally_branches(&self) -> bool {
self.kind == PushPopKind::Pop && self.regs.has(regs::PC)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment