-
-
Save aDotInTheVoid/f9b3d78f9d883ae77f647e4caa8f9611 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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