use crate::arm::arm_calling_convention::ArmCallingConvention;
use crate::arm::arm_register_info::*;
use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ARMDeepConditionCode {
EQ = 0,
NE = 1,
CS = 2,
HS = 2, CC = 3,
LO = 3, MI = 4,
PL = 5,
VS = 6,
VC = 7,
HI = 8,
LS = 9,
GE = 10,
LT = 11,
GT = 12,
LE = 13,
AL = 14,
NV = 15,
}
impl ARMDeepConditionCode {
pub fn suffix(&self) -> &'static str {
match self {
ARMDeepConditionCode::EQ => "eq",
ARMDeepConditionCode::NE => "ne",
ARMDeepConditionCode::CS | ARMDeepConditionCode::HS => "cs",
ARMDeepConditionCode::CC | ARMDeepConditionCode::LO => "cc",
ARMDeepConditionCode::MI => "mi",
ARMDeepConditionCode::PL => "pl",
ARMDeepConditionCode::VS => "vs",
ARMDeepConditionCode::VC => "vc",
ARMDeepConditionCode::HI => "hi",
ARMDeepConditionCode::LS => "ls",
ARMDeepConditionCode::GE => "ge",
ARMDeepConditionCode::LT => "lt",
ARMDeepConditionCode::GT => "gt",
ARMDeepConditionCode::LE => "le",
ARMDeepConditionCode::AL => "", ARMDeepConditionCode::NV => "nv",
}
}
pub fn description(&self) -> &'static str {
match self {
ARMDeepConditionCode::EQ => "Equal",
ARMDeepConditionCode::NE => "Not Equal",
ARMDeepConditionCode::CS => "Carry Set (unsigned higher or same)",
ARMDeepConditionCode::CC => "Carry Clear (unsigned lower)",
ARMDeepConditionCode::MI => "Minus / Negative",
ARMDeepConditionCode::PL => "Plus / Positive or Zero",
ARMDeepConditionCode::VS => "Overflow Set",
ARMDeepConditionCode::VC => "Overflow Clear",
ARMDeepConditionCode::HI => "Unsigned Higher",
ARMDeepConditionCode::LS => "Unsigned Lower or Same",
ARMDeepConditionCode::GE => "Signed Greater or Equal",
ARMDeepConditionCode::LT => "Signed Less Than",
ARMDeepConditionCode::GT => "Signed Greater Than",
ARMDeepConditionCode::LE => "Signed Less or Equal",
ARMDeepConditionCode::AL => "Always (unconditional)",
ARMDeepConditionCode::NV => "Never (deprecated)",
}
}
pub fn invert(&self) -> ARMDeepConditionCode {
match self {
ARMDeepConditionCode::EQ => ARMDeepConditionCode::NE,
ARMDeepConditionCode::NE => ARMDeepConditionCode::EQ,
ARMDeepConditionCode::CS => ARMDeepConditionCode::CC,
ARMDeepConditionCode::CC => ARMDeepConditionCode::CS,
ARMDeepConditionCode::MI => ARMDeepConditionCode::PL,
ARMDeepConditionCode::PL => ARMDeepConditionCode::MI,
ARMDeepConditionCode::VS => ARMDeepConditionCode::VC,
ARMDeepConditionCode::VC => ARMDeepConditionCode::VS,
ARMDeepConditionCode::HI => ARMDeepConditionCode::LS,
ARMDeepConditionCode::LS => ARMDeepConditionCode::HI,
ARMDeepConditionCode::GE => ARMDeepConditionCode::LT,
ARMDeepConditionCode::LT => ARMDeepConditionCode::GE,
ARMDeepConditionCode::GT => ARMDeepConditionCode::LE,
ARMDeepConditionCode::LE => ARMDeepConditionCode::GT,
ARMDeepConditionCode::AL => ARMDeepConditionCode::NV,
ARMDeepConditionCode::NV => ARMDeepConditionCode::AL,
}
}
pub fn from_bits(bits: u8) -> Option<ARMDeepConditionCode> {
match bits {
0 => Some(ARMDeepConditionCode::EQ),
1 => Some(ARMDeepConditionCode::NE),
2 => Some(ARMDeepConditionCode::CS),
3 => Some(ARMDeepConditionCode::CC),
4 => Some(ARMDeepConditionCode::MI),
5 => Some(ARMDeepConditionCode::PL),
6 => Some(ARMDeepConditionCode::VS),
7 => Some(ARMDeepConditionCode::VC),
8 => Some(ARMDeepConditionCode::HI),
9 => Some(ARMDeepConditionCode::LS),
10 => Some(ARMDeepConditionCode::GE),
11 => Some(ARMDeepConditionCode::LT),
12 => Some(ARMDeepConditionCode::GT),
13 => Some(ARMDeepConditionCode::LE),
14 => Some(ARMDeepConditionCode::AL),
15 => Some(ARMDeepConditionCode::NV),
_ => None,
}
}
pub const ALL: [ARMDeepConditionCode; 16] = [
ARMDeepConditionCode::EQ,
ARMDeepConditionCode::NE,
ARMDeepConditionCode::CS,
ARMDeepConditionCode::CC,
ARMDeepConditionCode::MI,
ARMDeepConditionCode::PL,
ARMDeepConditionCode::VS,
ARMDeepConditionCode::VC,
ARMDeepConditionCode::HI,
ARMDeepConditionCode::LS,
ARMDeepConditionCode::GE,
ARMDeepConditionCode::LT,
ARMDeepConditionCode::GT,
ARMDeepConditionCode::LE,
ARMDeepConditionCode::AL,
ARMDeepConditionCode::NV,
];
}
impl fmt::Display for ARMDeepConditionCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.suffix())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum ARMDeepOpcode {
ADD = 0,
ADC,
SUB,
SBC,
RSB,
RSC,
AND,
ORR,
EOR,
BIC,
ORN,
CMP,
CMN,
TST,
TEQ,
MOV,
MVN,
MUL,
MLA,
MLS,
UMULL,
UMLAL,
SMULL,
SMLAL,
SMMLA,
SMMUL,
SMMLS,
SDIV,
UDIV,
LSL,
LSR,
ASR,
ROR,
RRX,
B,
BL,
BX,
BLX,
BXJ,
CBZ,
CBNZ,
LDR,
STR,
LDRB,
STRB,
LDRSB,
LDRH,
STRH,
LDRSH,
LDRD,
STRD,
LDR_PRE,
LDR_POST,
STR_PRE,
STR_POST,
LDMIA,
LDMIB,
LDMDA,
LDMDB,
STMIA,
STMIB,
STMDA,
STMDB,
LDM,
STM,
PUSH,
POP,
MOVW,
MOVT,
BFC,
BFI,
SBFX,
UBFX,
UXTB,
UXTH,
SXTB,
SXTH,
SXTB16,
CLZ,
RBIT,
REV,
REV16,
REVSH,
QADD,
QSUB,
QDADD,
QDSUB,
SSAT,
USAT,
SSAT16,
USAT16,
SEL,
SADD16,
UADD16,
SSUB16,
USUB16,
SADD8,
UADD8,
SSUB8,
USUB8,
SHADD16,
UHADD16,
SHSUB16,
UHSUB16,
SHADD8,
UHADD8,
SHSUB8,
UHSUB8,
QADD16,
UQADD16,
QSUB16,
UQSUB16,
QADD8,
UQADD8,
QSUB8,
UQSUB8,
USAD8,
USADA8,
SWP,
SWPB,
LDREX,
STREX,
LDREXB,
STREXB,
LDREXH,
STREXH,
LDREXD,
STREXD,
CLREX,
DMB,
DSB,
ISB,
NOP,
YIELD,
WFE,
WFI,
SEV,
SVC,
SMC,
HVC,
BKPT,
MSR,
MRS,
CPS,
SETEND,
MCR,
MRC,
MRRC,
MCRR,
CDP,
LDC,
STC,
PLD,
PLI,
VADD_F32,
VSUB_F32,
VMUL_F32,
VDIV_F32,
VNEG_F32,
VABS_F32,
VSQRT_F32,
VCMP_F32,
VCMPZ_F32,
VCVT_F32_F64,
VCVT_F64_F32,
VCVT_S32_F32,
VCVT_U32_F32,
VCVT_F32_S32,
VCVT_F32_U32,
VMLA_F32,
VMLS_F32,
VNMUL_F32,
VNMLA_F32,
VNMLS_F32,
VMOV_F32,
VMOV_S_R,
VMOV_R_S,
VMOV_2S,
VMOV_IMM_F32,
VADD_F64,
VSUB_F64,
VMUL_F64,
VDIV_F64,
VNEG_F64,
VABS_F64,
VSQRT_F64,
VCMP_F64,
VCMPZ_F64,
VMLA_F64,
VMLS_F64,
VMOV_F64,
VNMUL_F64,
VNMLA_F64,
VNMLS_F64,
VADD_I8,
VADD_I16,
VADD_I32,
VADD_I64,
VSUB_I8,
VSUB_I16,
VSUB_I32,
VSUB_I64,
VMUL_I8,
VMUL_I16,
VMUL_I32,
VMLA_I8,
VMLA_I16,
VMLA_I32,
VMLS_I8,
VMLS_I16,
VMLS_I32,
VPADD_I8,
VPADD_I16,
VPADD_I32,
VPMAX_S8,
VPMAX_S16,
VPMAX_S32,
VPMIN_S8,
VPMIN_S16,
VPMIN_S32,
VPMAX_U8,
VPMAX_U16,
VPMAX_U32,
VPMIN_U8,
VPMIN_U16,
VPMIN_U32,
VSHL_I8,
VSHL_I16,
VSHL_I32,
VSHL_I64,
VSHR_S8,
VSHR_S16,
VSHR_S32,
VSHR_S64,
VSHR_U8,
VSHR_U16,
VSHR_U32,
VSHR_U64,
VRSHR_S8,
VRSHR_S16,
VRSHR_S32,
VRSHR_S64,
VRSHR_U8,
VRSHR_U16,
VRSHR_U32,
VRSHR_U64,
VSLI_I8,
VSLI_I16,
VSLI_I32,
VSLI_I64,
VSRI_I8,
VSRI_I16,
VSRI_I32,
VSRI_I64,
VEXT_8,
VEXT_16,
VEXT_32,
VEXT_64,
VREV64_8,
VREV64_16,
VREV64_32,
VREV32_8,
VREV32_16,
VREV16_8,
VTRN_8,
VTRN_16,
VTRN_32,
VUZP_8,
VUZP_16,
VUZP_32,
VZIP_8,
VZIP_16,
VZIP_32,
VTBL_1,
VTBL_2,
VTBL_3,
VTBL_4,
VTBX_1,
VTBX_2,
VTBX_3,
VTBX_4,
VMOVL_S8,
VMOVL_S16,
VMOVL_S32,
VMOVL_U8,
VMOVL_U16,
VMOVL_U32,
VMOVN_I16,
VMOVN_I32,
VMOVN_I64,
VABS_S8,
VABS_S16,
VABS_S32,
VNEG_S8,
VNEG_S16,
VNEG_S32,
VMAX_S8,
VMAX_S16,
VMAX_S32,
VMIN_S8,
VMIN_S16,
VMIN_S32,
VMAX_U8,
VMAX_U16,
VMAX_U32,
VMIN_U8,
VMIN_U16,
VMIN_U32,
VCEQ_I8,
VCEQ_I16,
VCEQ_I32,
VCGT_S8,
VCGT_S16,
VCGT_S32,
VCGT_U8,
VCGT_U16,
VCGT_U32,
VAND,
VORR,
VEOR,
VBIC,
VORN,
VBSL,
VBIT,
VBIF,
VCLS_S8,
VCLS_S16,
VCLS_S32,
VCLZ_I8,
VCLZ_I16,
VCLZ_I32,
VCNT,
VLD1_8,
VLD1_16,
VLD1_32,
VLD1_64,
VLD2_8,
VLD2_16,
VLD2_32,
VLD3_8,
VLD3_16,
VLD3_32,
VLD4_8,
VLD4_16,
VLD4_32,
VST1_8,
VST1_16,
VST1_32,
VST1_64,
VST2_8,
VST2_16,
VST2_32,
VST3_8,
VST3_16,
VST3_32,
VST4_8,
VST4_16,
VST4_32,
VLDR,
VSTR,
VPUSH,
VPOP,
T_ADD_LOW,
T_ADD_HIGH,
T_ADD_I3,
T_ADD_I8,
T_SUB_LOW,
T_SUB_I3,
T_SUB_I8,
T_MOV_LOW,
T_MOV_HIGH,
T_MOV_I8,
T_CMP_LOW,
T_CMP_HIGH,
T_CMP_I8,
T_ADD_SP,
T_SUB_SP,
T_B,
T_BL,
T_BX,
T_BLX,
T_LDR,
T_LDR_IMM,
T_LDR_SP,
T_LDR_PC,
T_STR,
T_STR_IMM,
T_STR_SP,
T_PUSH,
T_POP,
T_LDRB,
T_STRB,
T_LDRH,
T_STRH,
T_LDRSB,
T_LDRSH,
T_ASR,
T_LSL,
T_LSR,
T_MUL,
T_MVN,
T_NEG,
T_ORR,
T_AND,
T_EOR,
T_BIC,
T_SXTB,
T_SXTH,
T_UXTB,
T_UXTH,
T_REV,
T_REV16,
T_REVSH,
T_TST,
T_ADC,
T_SBC,
T_RSB,
T2_ADD,
T2_ADC,
T2_SUB,
T2_SBC,
T2_RSB,
T2_AND,
T2_ORR,
T2_EOR,
T2_BIC,
T2_ORN,
T2_MUL,
T2_MLA,
T2_MLS,
T2_SDIV,
T2_UDIV,
T2_MOVW,
T2_MOVT,
T2_MVN,
T2_TST,
T2_CMP,
T2_CMN,
T2_TEQ,
T2_LDR,
T2_STR,
T2_LDRB,
T2_STRB,
T2_LDRH,
T2_STRH,
T2_LDRSB,
T2_LDRSH,
T2_LDRD,
T2_STRD,
T2_LDMIA,
T2_STMIA,
T2_PUSH,
T2_POP,
T2_B,
T2_BL,
T2_BX,
T2_BLX,
T2_CBZ,
T2_CBNZ,
T2_TBB,
T2_TBH,
T2_IT,
T2_NOP,
T2_SVC,
T2_BKPT,
T2_UXTB,
T2_UXTH,
T2_SXTB,
T2_SXTH,
T2_BFC,
T2_BFI,
T2_SBFX,
T2_UBFX,
T2_RBIT,
T2_REV,
T2_REV16,
T2_REVSH,
T2_CLZ,
T2_LSL,
T2_LSR,
T2_ASR,
T2_ROR,
INVALID,
}
impl ARMDeepOpcode {
pub const fn count() -> usize {
ARMDeepOpcode::INVALID as usize
}
pub fn mnemonic(&self) -> &'static str {
match self {
ARMDeepOpcode::ADD => "add",
ARMDeepOpcode::ADC => "adc",
ARMDeepOpcode::SUB => "sub",
ARMDeepOpcode::SBC => "sbc",
ARMDeepOpcode::RSB => "rsb",
ARMDeepOpcode::RSC => "rsc",
ARMDeepOpcode::AND => "and",
ARMDeepOpcode::ORR => "orr",
ARMDeepOpcode::EOR => "eor",
ARMDeepOpcode::BIC => "bic",
ARMDeepOpcode::ORN => "orn",
ARMDeepOpcode::CMP => "cmp",
ARMDeepOpcode::CMN => "cmn",
ARMDeepOpcode::TST => "tst",
ARMDeepOpcode::TEQ => "teq",
ARMDeepOpcode::MOV => "mov",
ARMDeepOpcode::MVN => "mvn",
ARMDeepOpcode::MUL => "mul",
ARMDeepOpcode::MLA => "mla",
ARMDeepOpcode::MLS => "mls",
ARMDeepOpcode::UMULL => "umull",
ARMDeepOpcode::UMLAL => "umlal",
ARMDeepOpcode::SMULL => "smull",
ARMDeepOpcode::SMLAL => "smlal",
ARMDeepOpcode::SMMLA => "smmla",
ARMDeepOpcode::SMMUL => "smmul",
ARMDeepOpcode::SMMLS => "smmls",
ARMDeepOpcode::SDIV => "sdiv",
ARMDeepOpcode::UDIV => "udiv",
ARMDeepOpcode::LSL => "lsl",
ARMDeepOpcode::LSR => "lsr",
ARMDeepOpcode::ASR => "asr",
ARMDeepOpcode::ROR => "ror",
ARMDeepOpcode::RRX => "rrx",
ARMDeepOpcode::B => "b",
ARMDeepOpcode::BL => "bl",
ARMDeepOpcode::BX => "bx",
ARMDeepOpcode::BLX => "blx",
ARMDeepOpcode::BXJ => "bxj",
ARMDeepOpcode::CBZ => "cbz",
ARMDeepOpcode::CBNZ => "cbnz",
ARMDeepOpcode::LDR => "ldr",
ARMDeepOpcode::STR => "str",
ARMDeepOpcode::LDRB => "ldrb",
ARMDeepOpcode::STRB => "strb",
ARMDeepOpcode::LDRSB => "ldrsb",
ARMDeepOpcode::LDRH => "ldrh",
ARMDeepOpcode::STRH => "strh",
ARMDeepOpcode::LDRSH => "ldrsh",
ARMDeepOpcode::LDRD => "ldrd",
ARMDeepOpcode::STRD => "strd",
ARMDeepOpcode::LDR_PRE => "ldr",
ARMDeepOpcode::LDR_POST => "ldr",
ARMDeepOpcode::STR_PRE => "str",
ARMDeepOpcode::STR_POST => "str",
ARMDeepOpcode::LDMIA => "ldmia",
ARMDeepOpcode::LDMIB => "ldmib",
ARMDeepOpcode::LDMDA => "ldmda",
ARMDeepOpcode::LDMDB => "ldmdb",
ARMDeepOpcode::STMIA => "stmia",
ARMDeepOpcode::STMIB => "stmib",
ARMDeepOpcode::STMDA => "stmda",
ARMDeepOpcode::STMDB => "stmdb",
ARMDeepOpcode::LDM => "ldm",
ARMDeepOpcode::STM => "stm",
ARMDeepOpcode::PUSH => "push",
ARMDeepOpcode::POP => "pop",
ARMDeepOpcode::MOVW => "movw",
ARMDeepOpcode::MOVT => "movt",
ARMDeepOpcode::BFC => "bfc",
ARMDeepOpcode::BFI => "bfi",
ARMDeepOpcode::SBFX => "sbfx",
ARMDeepOpcode::UBFX => "ubfx",
ARMDeepOpcode::UXTB => "uxtb",
ARMDeepOpcode::UXTH => "uxth",
ARMDeepOpcode::SXTB => "sxtb",
ARMDeepOpcode::SXTH => "sxth",
ARMDeepOpcode::SXTB16 => "sxtb16",
ARMDeepOpcode::CLZ => "clz",
ARMDeepOpcode::RBIT => "rbit",
ARMDeepOpcode::REV => "rev",
ARMDeepOpcode::REV16 => "rev16",
ARMDeepOpcode::REVSH => "revsh",
ARMDeepOpcode::QADD => "qadd",
ARMDeepOpcode::QSUB => "qsub",
ARMDeepOpcode::QDADD => "qdadd",
ARMDeepOpcode::QDSUB => "qdsub",
ARMDeepOpcode::SSAT => "ssat",
ARMDeepOpcode::USAT => "usat",
ARMDeepOpcode::SSAT16 => "ssat16",
ARMDeepOpcode::USAT16 => "usat16",
ARMDeepOpcode::SEL => "sel",
ARMDeepOpcode::SADD16 => "sadd16",
ARMDeepOpcode::UADD16 => "uadd16",
ARMDeepOpcode::SSUB16 => "ssub16",
ARMDeepOpcode::USUB16 => "usub16",
ARMDeepOpcode::SADD8 => "sadd8",
ARMDeepOpcode::UADD8 => "uadd8",
ARMDeepOpcode::SSUB8 => "ssub8",
ARMDeepOpcode::USUB8 => "usub8",
ARMDeepOpcode::SHADD16 => "shadd16",
ARMDeepOpcode::UHADD16 => "uhadd16",
ARMDeepOpcode::SHSUB16 => "shsub16",
ARMDeepOpcode::UHSUB16 => "uhsub16",
ARMDeepOpcode::SHADD8 => "shadd8",
ARMDeepOpcode::UHADD8 => "uhadd8",
ARMDeepOpcode::SHSUB8 => "shsub8",
ARMDeepOpcode::UHSUB8 => "uhsub8",
ARMDeepOpcode::QADD16 => "qadd16",
ARMDeepOpcode::UQADD16 => "uqadd16",
ARMDeepOpcode::QSUB16 => "qsub16",
ARMDeepOpcode::UQSUB16 => "uqsub16",
ARMDeepOpcode::QADD8 => "qadd8",
ARMDeepOpcode::UQADD8 => "uqadd8",
ARMDeepOpcode::QSUB8 => "qsub8",
ARMDeepOpcode::UQSUB8 => "uqsub8",
ARMDeepOpcode::USAD8 => "usad8",
ARMDeepOpcode::USADA8 => "usada8",
ARMDeepOpcode::SWP => "swp",
ARMDeepOpcode::SWPB => "swpb",
ARMDeepOpcode::LDREX => "ldrex",
ARMDeepOpcode::STREX => "strex",
ARMDeepOpcode::LDREXB => "ldrexb",
ARMDeepOpcode::STREXB => "strexb",
ARMDeepOpcode::LDREXH => "ldrexh",
ARMDeepOpcode::STREXH => "strexh",
ARMDeepOpcode::LDREXD => "ldrexd",
ARMDeepOpcode::STREXD => "strexd",
ARMDeepOpcode::CLREX => "clrex",
ARMDeepOpcode::DMB => "dmb",
ARMDeepOpcode::DSB => "dsb",
ARMDeepOpcode::ISB => "isb",
ARMDeepOpcode::NOP => "nop",
ARMDeepOpcode::YIELD => "yield",
ARMDeepOpcode::WFE => "wfe",
ARMDeepOpcode::WFI => "wfi",
ARMDeepOpcode::SEV => "sev",
ARMDeepOpcode::SVC => "svc",
ARMDeepOpcode::SMC => "smc",
ARMDeepOpcode::HVC => "hvc",
ARMDeepOpcode::BKPT => "bkpt",
ARMDeepOpcode::MSR => "msr",
ARMDeepOpcode::MRS => "mrs",
ARMDeepOpcode::CPS => "cps",
ARMDeepOpcode::SETEND => "setend",
ARMDeepOpcode::MCR => "mcr",
ARMDeepOpcode::MRC => "mrc",
ARMDeepOpcode::MRRC => "mrrc",
ARMDeepOpcode::MCRR => "mcrr",
ARMDeepOpcode::CDP => "cdp",
ARMDeepOpcode::LDC => "ldc",
ARMDeepOpcode::STC => "stc",
ARMDeepOpcode::PLD => "pld",
ARMDeepOpcode::PLI => "pli",
ARMDeepOpcode::VADD_F32 => "vadd.f32",
ARMDeepOpcode::VSUB_F32 => "vsub.f32",
ARMDeepOpcode::VMUL_F32 => "vmul.f32",
ARMDeepOpcode::VDIV_F32 => "vdiv.f32",
ARMDeepOpcode::VNEG_F32 => "vneg.f32",
ARMDeepOpcode::VABS_F32 => "vabs.f32",
ARMDeepOpcode::VSQRT_F32 => "vsqrt.f32",
ARMDeepOpcode::VCMP_F32 => "vcmp.f32",
ARMDeepOpcode::VCMPZ_F32 => "vcmp.f32",
ARMDeepOpcode::VCVT_F32_F64 => "vcvt.f32.f64",
ARMDeepOpcode::VCVT_F64_F32 => "vcvt.f64.f32",
ARMDeepOpcode::VCVT_S32_F32 => "vcvt.s32.f32",
ARMDeepOpcode::VCVT_U32_F32 => "vcvt.u32.f32",
ARMDeepOpcode::VCVT_F32_S32 => "vcvt.f32.s32",
ARMDeepOpcode::VCVT_F32_U32 => "vcvt.f32.u32",
ARMDeepOpcode::VMLA_F32 => "vmla.f32",
ARMDeepOpcode::VMLS_F32 => "vmls.f32",
ARMDeepOpcode::VNMUL_F32 => "vnmul.f32",
ARMDeepOpcode::VNMLA_F32 => "vnmla.f32",
ARMDeepOpcode::VNMLS_F32 => "vnmls.f32",
ARMDeepOpcode::VMOV_F32 => "vmov.f32",
ARMDeepOpcode::VMOV_S_R => "vmov",
ARMDeepOpcode::VMOV_R_S => "vmov",
ARMDeepOpcode::VMOV_2S => "vmov",
ARMDeepOpcode::VMOV_IMM_F32 => "vmov.f32",
ARMDeepOpcode::VADD_F64 => "vadd.f64",
ARMDeepOpcode::VSUB_F64 => "vsub.f64",
ARMDeepOpcode::VMUL_F64 => "vmul.f64",
ARMDeepOpcode::VDIV_F64 => "vdiv.f64",
ARMDeepOpcode::VNEG_F64 => "vneg.f64",
ARMDeepOpcode::VABS_F64 => "vabs.f64",
ARMDeepOpcode::VSQRT_F64 => "vsqrt.f64",
ARMDeepOpcode::VCMP_F64 => "vcmp.f64",
ARMDeepOpcode::VCMPZ_F64 => "vcmp.f64",
ARMDeepOpcode::VMLA_F64 => "vmla.f64",
ARMDeepOpcode::VMLS_F64 => "vmls.f64",
ARMDeepOpcode::VMOV_F64 => "vmov.f64",
ARMDeepOpcode::VNMUL_F64 => "vnmul.f64",
ARMDeepOpcode::VNMLA_F64 => "vnmla.f64",
ARMDeepOpcode::VNMLS_F64 => "vnmls.f64",
ARMDeepOpcode::VADD_I8 => "vadd.i8",
ARMDeepOpcode::VADD_I16 => "vadd.i16",
ARMDeepOpcode::VADD_I32 => "vadd.i32",
ARMDeepOpcode::VADD_I64 => "vadd.i64",
ARMDeepOpcode::VSUB_I8 => "vsub.i8",
ARMDeepOpcode::VSUB_I16 => "vsub.i16",
ARMDeepOpcode::VSUB_I32 => "vsub.i32",
ARMDeepOpcode::VSUB_I64 => "vsub.i64",
ARMDeepOpcode::VMUL_I8 => "vmul.i8",
ARMDeepOpcode::VMUL_I16 => "vmul.i16",
ARMDeepOpcode::VMUL_I32 => "vmul.i32",
ARMDeepOpcode::VMLA_I8 => "vmla.i8",
ARMDeepOpcode::VMLA_I16 => "vmla.i16",
ARMDeepOpcode::VMLA_I32 => "vmla.i32",
ARMDeepOpcode::VMLS_I8 => "vmls.i8",
ARMDeepOpcode::VMLS_I16 => "vmls.i16",
ARMDeepOpcode::VMLS_I32 => "vmls.i32",
ARMDeepOpcode::VPADD_I8 => "vpadd.i8",
ARMDeepOpcode::VPADD_I16 => "vpadd.i16",
ARMDeepOpcode::VPADD_I32 => "vpadd.i32",
ARMDeepOpcode::VPMAX_S8 => "vpmax.s8",
ARMDeepOpcode::VPMAX_S16 => "vpmax.s16",
ARMDeepOpcode::VPMAX_S32 => "vpmax.s32",
ARMDeepOpcode::VPMIN_S8 => "vpmin.s8",
ARMDeepOpcode::VPMIN_S16 => "vpmin.s16",
ARMDeepOpcode::VPMIN_S32 => "vpmin.s32",
ARMDeepOpcode::VPMAX_U8 => "vpmax.u8",
ARMDeepOpcode::VPMAX_U16 => "vpmax.u16",
ARMDeepOpcode::VPMAX_U32 => "vpmax.u32",
ARMDeepOpcode::VPMIN_U8 => "vpmin.u8",
ARMDeepOpcode::VPMIN_U16 => "vpmin.u16",
ARMDeepOpcode::VPMIN_U32 => "vpmin.u32",
ARMDeepOpcode::VSHL_I8 => "vshl.i8",
ARMDeepOpcode::VSHL_I16 => "vshl.i16",
ARMDeepOpcode::VSHL_I32 => "vshl.i32",
ARMDeepOpcode::VSHL_I64 => "vshl.i64",
ARMDeepOpcode::VSHR_S8 => "vshr.s8",
ARMDeepOpcode::VSHR_S16 => "vshr.s16",
ARMDeepOpcode::VSHR_S32 => "vshr.s32",
ARMDeepOpcode::VSHR_S64 => "vshr.s64",
ARMDeepOpcode::VSHR_U8 => "vshr.u8",
ARMDeepOpcode::VSHR_U16 => "vshr.u16",
ARMDeepOpcode::VSHR_U32 => "vshr.u32",
ARMDeepOpcode::VSHR_U64 => "vshr.u64",
ARMDeepOpcode::VRSHR_S8 => "vrshr.s8",
ARMDeepOpcode::VRSHR_S16 => "vrshr.s16",
ARMDeepOpcode::VRSHR_S32 => "vrshr.s32",
ARMDeepOpcode::VRSHR_S64 => "vrshr.s64",
ARMDeepOpcode::VRSHR_U8 => "vrshr.u8",
ARMDeepOpcode::VRSHR_U16 => "vrshr.u16",
ARMDeepOpcode::VRSHR_U32 => "vrshr.u32",
ARMDeepOpcode::VRSHR_U64 => "vrshr.u64",
ARMDeepOpcode::VSLI_I8 => "vsli.i8",
ARMDeepOpcode::VSLI_I16 => "vsli.i16",
ARMDeepOpcode::VSLI_I32 => "vsli.i32",
ARMDeepOpcode::VSLI_I64 => "vsli.i64",
ARMDeepOpcode::VSRI_I8 => "vsri.i8",
ARMDeepOpcode::VSRI_I16 => "vsri.i16",
ARMDeepOpcode::VSRI_I32 => "vsri.i32",
ARMDeepOpcode::VSRI_I64 => "vsri.i64",
ARMDeepOpcode::VEXT_8 => "vext.8",
ARMDeepOpcode::VEXT_16 => "vext.16",
ARMDeepOpcode::VEXT_32 => "vext.32",
ARMDeepOpcode::VEXT_64 => "vext.64",
ARMDeepOpcode::VREV64_8 => "vrev64.8",
ARMDeepOpcode::VREV64_16 => "vrev64.16",
ARMDeepOpcode::VREV64_32 => "vrev64.32",
ARMDeepOpcode::VREV32_8 => "vrev32.8",
ARMDeepOpcode::VREV32_16 => "vrev32.16",
ARMDeepOpcode::VREV16_8 => "vrev16.8",
ARMDeepOpcode::VTRN_8 => "vtrn.8",
ARMDeepOpcode::VTRN_16 => "vtrn.16",
ARMDeepOpcode::VTRN_32 => "vtrn.32",
ARMDeepOpcode::VUZP_8 => "vuzp.8",
ARMDeepOpcode::VUZP_16 => "vuzp.16",
ARMDeepOpcode::VUZP_32 => "vuzp.32",
ARMDeepOpcode::VZIP_8 => "vzip.8",
ARMDeepOpcode::VZIP_16 => "vzip.16",
ARMDeepOpcode::VZIP_32 => "vzip.32",
ARMDeepOpcode::VTBL_1 => "vtbl.8",
ARMDeepOpcode::VTBL_2 => "vtbl.8",
ARMDeepOpcode::VTBL_3 => "vtbl.8",
ARMDeepOpcode::VTBL_4 => "vtbl.8",
ARMDeepOpcode::VTBX_1 => "vtbx.8",
ARMDeepOpcode::VTBX_2 => "vtbx.8",
ARMDeepOpcode::VTBX_3 => "vtbx.8",
ARMDeepOpcode::VTBX_4 => "vtbx.8",
ARMDeepOpcode::VMOVL_S8 => "vmovl.s8",
ARMDeepOpcode::VMOVL_S16 => "vmovl.s16",
ARMDeepOpcode::VMOVL_S32 => "vmovl.s32",
ARMDeepOpcode::VMOVL_U8 => "vmovl.u8",
ARMDeepOpcode::VMOVL_U16 => "vmovl.u16",
ARMDeepOpcode::VMOVL_U32 => "vmovl.u32",
ARMDeepOpcode::VMOVN_I16 => "vmovn.i16",
ARMDeepOpcode::VMOVN_I32 => "vmovn.i32",
ARMDeepOpcode::VMOVN_I64 => "vmovn.i64",
ARMDeepOpcode::VABS_S8 => "vabs.s8",
ARMDeepOpcode::VABS_S16 => "vabs.s16",
ARMDeepOpcode::VABS_S32 => "vabs.s32",
ARMDeepOpcode::VNEG_S8 => "vneg.s8",
ARMDeepOpcode::VNEG_S16 => "vneg.s16",
ARMDeepOpcode::VNEG_S32 => "vneg.s32",
ARMDeepOpcode::VMAX_S8 => "vmax.s8",
ARMDeepOpcode::VMAX_S16 => "vmax.s16",
ARMDeepOpcode::VMAX_S32 => "vmax.s32",
ARMDeepOpcode::VMIN_S8 => "vmin.s8",
ARMDeepOpcode::VMIN_S16 => "vmin.s16",
ARMDeepOpcode::VMIN_S32 => "vmin.s32",
ARMDeepOpcode::VMAX_U8 => "vmax.u8",
ARMDeepOpcode::VMAX_U16 => "vmax.u16",
ARMDeepOpcode::VMAX_U32 => "vmax.u32",
ARMDeepOpcode::VMIN_U8 => "vmin.u8",
ARMDeepOpcode::VMIN_U16 => "vmin.u16",
ARMDeepOpcode::VMIN_U32 => "vmin.u32",
ARMDeepOpcode::VCEQ_I8 => "vceq.i8",
ARMDeepOpcode::VCEQ_I16 => "vceq.i16",
ARMDeepOpcode::VCEQ_I32 => "vceq.i32",
ARMDeepOpcode::VCGT_S8 => "vcgt.s8",
ARMDeepOpcode::VCGT_S16 => "vcgt.s16",
ARMDeepOpcode::VCGT_S32 => "vcgt.s32",
ARMDeepOpcode::VCGT_U8 => "vcgt.u8",
ARMDeepOpcode::VCGT_U16 => "vcgt.u16",
ARMDeepOpcode::VCGT_U32 => "vcgt.u32",
ARMDeepOpcode::VAND => "vand",
ARMDeepOpcode::VORR => "vorr",
ARMDeepOpcode::VEOR => "veor",
ARMDeepOpcode::VBIC => "vbic",
ARMDeepOpcode::VORN => "vorn",
ARMDeepOpcode::VBSL => "vbsl",
ARMDeepOpcode::VBIT => "vbit",
ARMDeepOpcode::VBIF => "vbif",
ARMDeepOpcode::VCLS_S8 => "vcls.s8",
ARMDeepOpcode::VCLS_S16 => "vcls.s16",
ARMDeepOpcode::VCLS_S32 => "vcls.s32",
ARMDeepOpcode::VCLZ_I8 => "vclz.i8",
ARMDeepOpcode::VCLZ_I16 => "vclz.i16",
ARMDeepOpcode::VCLZ_I32 => "vclz.i32",
ARMDeepOpcode::VCNT => "vcnt.8",
ARMDeepOpcode::VLD1_8 => "vld1.8",
ARMDeepOpcode::VLD1_16 => "vld1.16",
ARMDeepOpcode::VLD1_32 => "vld1.32",
ARMDeepOpcode::VLD1_64 => "vld1.64",
ARMDeepOpcode::VLD2_8 => "vld2.8",
ARMDeepOpcode::VLD2_16 => "vld2.16",
ARMDeepOpcode::VLD2_32 => "vld2.32",
ARMDeepOpcode::VLD3_8 => "vld3.8",
ARMDeepOpcode::VLD3_16 => "vld3.16",
ARMDeepOpcode::VLD3_32 => "vld3.32",
ARMDeepOpcode::VLD4_8 => "vld4.8",
ARMDeepOpcode::VLD4_16 => "vld4.16",
ARMDeepOpcode::VLD4_32 => "vld4.32",
ARMDeepOpcode::VST1_8 => "vst1.8",
ARMDeepOpcode::VST1_16 => "vst1.16",
ARMDeepOpcode::VST1_32 => "vst1.32",
ARMDeepOpcode::VST1_64 => "vst1.64",
ARMDeepOpcode::VST2_8 => "vst2.8",
ARMDeepOpcode::VST2_16 => "vst2.16",
ARMDeepOpcode::VST2_32 => "vst2.32",
ARMDeepOpcode::VST3_8 => "vst3.8",
ARMDeepOpcode::VST3_16 => "vst3.16",
ARMDeepOpcode::VST3_32 => "vst3.32",
ARMDeepOpcode::VST4_8 => "vst4.8",
ARMDeepOpcode::VST4_16 => "vst4.16",
ARMDeepOpcode::VST4_32 => "vst4.32",
ARMDeepOpcode::VLDR => "vldr",
ARMDeepOpcode::VSTR => "vstr",
ARMDeepOpcode::VPUSH => "vpush",
ARMDeepOpcode::VPOP => "vpop",
ARMDeepOpcode::T_ADD_LOW => "add",
ARMDeepOpcode::T_ADD_HIGH => "add",
ARMDeepOpcode::T_ADD_I3 => "adds",
ARMDeepOpcode::T_ADD_I8 => "adds",
ARMDeepOpcode::T_SUB_LOW => "sub",
ARMDeepOpcode::T_SUB_I3 => "subs",
ARMDeepOpcode::T_SUB_I8 => "subs",
ARMDeepOpcode::T_MOV_LOW => "mov",
ARMDeepOpcode::T_MOV_HIGH => "mov",
ARMDeepOpcode::T_MOV_I8 => "movs",
ARMDeepOpcode::T_CMP_LOW => "cmp",
ARMDeepOpcode::T_CMP_HIGH => "cmp",
ARMDeepOpcode::T_CMP_I8 => "cmp",
ARMDeepOpcode::T_ADD_SP => "add",
ARMDeepOpcode::T_SUB_SP => "sub",
ARMDeepOpcode::T_B => "b",
ARMDeepOpcode::T_BL => "bl",
ARMDeepOpcode::T_BX => "bx",
ARMDeepOpcode::T_BLX => "blx",
ARMDeepOpcode::T_LDR => "ldr",
ARMDeepOpcode::T_LDR_IMM => "ldr",
ARMDeepOpcode::T_LDR_SP => "ldr",
ARMDeepOpcode::T_LDR_PC => "ldr",
ARMDeepOpcode::T_STR => "str",
ARMDeepOpcode::T_STR_IMM => "str",
ARMDeepOpcode::T_STR_SP => "str",
ARMDeepOpcode::T_PUSH => "push",
ARMDeepOpcode::T_POP => "pop",
ARMDeepOpcode::T_LDRB => "ldrb",
ARMDeepOpcode::T_STRB => "strb",
ARMDeepOpcode::T_LDRH => "ldrh",
ARMDeepOpcode::T_STRH => "strh",
ARMDeepOpcode::T_LDRSB => "ldrsb",
ARMDeepOpcode::T_LDRSH => "ldrsh",
ARMDeepOpcode::T_ASR => "asrs",
ARMDeepOpcode::T_LSL => "lsls",
ARMDeepOpcode::T_LSR => "lsrs",
ARMDeepOpcode::T_MUL => "muls",
ARMDeepOpcode::T_MVN => "mvns",
ARMDeepOpcode::T_NEG => "negs",
ARMDeepOpcode::T_ORR => "orrs",
ARMDeepOpcode::T_AND => "ands",
ARMDeepOpcode::T_EOR => "eors",
ARMDeepOpcode::T_BIC => "bics",
ARMDeepOpcode::T_SXTB => "sxtb",
ARMDeepOpcode::T_SXTH => "sxth",
ARMDeepOpcode::T_UXTB => "uxtb",
ARMDeepOpcode::T_UXTH => "uxth",
ARMDeepOpcode::T_REV => "rev",
ARMDeepOpcode::T_REV16 => "rev16",
ARMDeepOpcode::T_REVSH => "revsh",
ARMDeepOpcode::T_TST => "tst",
ARMDeepOpcode::T_ADC => "adcs",
ARMDeepOpcode::T_SBC => "sbcs",
ARMDeepOpcode::T_RSB => "rsbs",
ARMDeepOpcode::T2_ADD => "add.w",
ARMDeepOpcode::T2_ADC => "adc.w",
ARMDeepOpcode::T2_SUB => "sub.w",
ARMDeepOpcode::T2_SBC => "sbc.w",
ARMDeepOpcode::T2_RSB => "rsb.w",
ARMDeepOpcode::T2_AND => "and.w",
ARMDeepOpcode::T2_ORR => "orr.w",
ARMDeepOpcode::T2_EOR => "eor.w",
ARMDeepOpcode::T2_BIC => "bic.w",
ARMDeepOpcode::T2_ORN => "orn.w",
ARMDeepOpcode::T2_MUL => "mul.w",
ARMDeepOpcode::T2_MLA => "mla.w",
ARMDeepOpcode::T2_MLS => "mls.w",
ARMDeepOpcode::T2_SDIV => "sdiv.w",
ARMDeepOpcode::T2_UDIV => "udiv.w",
ARMDeepOpcode::T2_MOVW => "movw",
ARMDeepOpcode::T2_MOVT => "movt",
ARMDeepOpcode::T2_MVN => "mvn.w",
ARMDeepOpcode::T2_TST => "tst.w",
ARMDeepOpcode::T2_CMP => "cmp.w",
ARMDeepOpcode::T2_CMN => "cmn.w",
ARMDeepOpcode::T2_TEQ => "teq.w",
ARMDeepOpcode::T2_LDR => "ldr.w",
ARMDeepOpcode::T2_STR => "str.w",
ARMDeepOpcode::T2_LDRB => "ldrb.w",
ARMDeepOpcode::T2_STRB => "strb.w",
ARMDeepOpcode::T2_LDRH => "ldrh.w",
ARMDeepOpcode::T2_STRH => "strh.w",
ARMDeepOpcode::T2_LDRSB => "ldrsb.w",
ARMDeepOpcode::T2_LDRSH => "ldrsh.w",
ARMDeepOpcode::T2_LDRD => "ldrd.w",
ARMDeepOpcode::T2_STRD => "strd.w",
ARMDeepOpcode::T2_LDMIA => "ldmia.w",
ARMDeepOpcode::T2_STMIA => "stmia.w",
ARMDeepOpcode::T2_PUSH => "push.w",
ARMDeepOpcode::T2_POP => "pop.w",
ARMDeepOpcode::T2_B => "b.w",
ARMDeepOpcode::T2_BL => "bl",
ARMDeepOpcode::T2_BX => "bx",
ARMDeepOpcode::T2_BLX => "blx",
ARMDeepOpcode::T2_CBZ => "cbz",
ARMDeepOpcode::T2_CBNZ => "cbnz",
ARMDeepOpcode::T2_TBB => "tbb",
ARMDeepOpcode::T2_TBH => "tbh",
ARMDeepOpcode::T2_IT => "it",
ARMDeepOpcode::T2_NOP => "nop.w",
ARMDeepOpcode::T2_SVC => "svc",
ARMDeepOpcode::T2_BKPT => "bkpt",
ARMDeepOpcode::T2_UXTB => "uxtb.w",
ARMDeepOpcode::T2_UXTH => "uxth.w",
ARMDeepOpcode::T2_SXTB => "sxtb.w",
ARMDeepOpcode::T2_SXTH => "sxth.w",
ARMDeepOpcode::T2_BFC => "bfc.w",
ARMDeepOpcode::T2_BFI => "bfi.w",
ARMDeepOpcode::T2_SBFX => "sbfx.w",
ARMDeepOpcode::T2_UBFX => "ubfx.w",
ARMDeepOpcode::T2_RBIT => "rbit.w",
ARMDeepOpcode::T2_REV => "rev.w",
ARMDeepOpcode::T2_REV16 => "rev16.w",
ARMDeepOpcode::T2_REVSH => "revsh.w",
ARMDeepOpcode::T2_CLZ => "clz.w",
ARMDeepOpcode::T2_LSL => "lsl.w",
ARMDeepOpcode::T2_LSR => "lsr.w",
ARMDeepOpcode::T2_ASR => "asr.w",
ARMDeepOpcode::T2_ROR => "ror.w",
ARMDeepOpcode::INVALID => "???",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ArmDeepOperandType {
GPR,
DPR,
SPR,
Imm3,
Imm5,
Imm8Rotate,
Imm12,
Imm16,
Imm24,
Cond,
RegList,
Shift,
MemAddr,
MemPreIndex,
MemPostIndex,
Label,
SysReg,
Coproc,
NeonReg,
SPOffset,
PCOffset,
}
#[derive(Debug, Clone)]
pub struct ArmDeepInstrDesc {
pub opcode: ARMDeepOpcode,
pub mnemonic: &'static str,
pub num_operands: u8,
pub is_terminator: bool,
pub is_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub is_compare: bool,
pub is_move_imm: bool,
pub has_side_effects: bool,
pub may_load: bool,
pub may_store: bool,
pub is_commutative: bool,
pub operand_types: Vec<ArmDeepOperandType>,
pub implicit_defs: Vec<u16>,
pub implicit_uses: Vec<u16>,
pub is_arm: bool,
pub is_thumb: bool,
pub is_thumb2: bool,
pub is_vfp: bool,
pub is_neon: bool,
}
impl ArmDeepInstrDesc {
fn new(opcode: ARMDeepOpcode, mnemonic: &'static str) -> Self {
ArmDeepInstrDesc {
opcode,
mnemonic,
num_operands: 0,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_compare: false,
is_move_imm: false,
has_side_effects: false,
may_load: false,
may_store: false,
is_commutative: false,
operand_types: Vec::new(),
implicit_defs: Vec::new(),
implicit_uses: Vec::new(),
is_arm: false,
is_thumb: false,
is_thumb2: false,
is_vfp: false,
is_neon: false,
}
}
fn with_operands(mut self, ops: Vec<ArmDeepOperandType>) -> Self {
self.num_operands = ops.len() as u8;
self.operand_types = ops;
self
}
fn with_flags(
mut self,
term: bool,
branch: bool,
call: bool,
ret: bool,
cmp: bool,
mov_imm: bool,
side: bool,
load: bool,
store: bool,
comm: bool,
arm: bool,
thumb: bool,
thumb2: bool,
vfp: bool,
neon: bool,
) -> Self {
self.is_terminator = term;
self.is_branch = branch;
self.is_call = call;
self.is_return = ret;
self.is_compare = cmp;
self.is_move_imm = mov_imm;
self.has_side_effects = side;
self.may_load = load;
self.may_store = store;
self.is_commutative = comm;
self.is_arm = arm;
self.is_thumb = thumb;
self.is_thumb2 = thumb2;
self.is_vfp = vfp;
self.is_neon = neon;
self
}
fn with_implicit_defs(mut self, defs: Vec<u16>) -> Self {
self.implicit_defs = defs;
self
}
fn with_implicit_uses(mut self, uses: Vec<u16>) -> Self {
self.implicit_uses = uses;
self
}
}
pub struct ARMInstrInfoDeep {
descriptors: Vec<Option<ArmDeepInstrDesc>>,
max_opcode: usize,
mnemonic_map: HashMap<String, ARMDeepOpcode>,
}
impl ARMInstrInfoDeep {
pub fn new() -> Self {
let max_opcode = ARMDeepOpcode::INVALID as usize;
let mut descriptors: Vec<Option<ArmDeepInstrDesc>> = vec![None; max_opcode];
let mut mnemonic_map: HashMap<String, ARMDeepOpcode> = HashMap::new();
let mut register = |desc: ArmDeepInstrDesc| {
let idx = desc.opcode as usize;
mnemonic_map.insert(desc.mnemonic.to_string(), desc.opcode);
if idx < descriptors.len() {
descriptors[idx] = Some(desc);
}
};
for (opcode, mnem, ops, comm) in &[
(
ARMDeepOpcode::ADD,
"add",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
true,
),
(
ARMDeepOpcode::ADC,
"adc",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::SUB,
"sub",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::SBC,
"sbc",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::RSB,
"rsb",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::RSC,
"rsc",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::AND,
"and",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
true,
),
(
ARMDeepOpcode::ORR,
"orr",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
true,
),
(
ARMDeepOpcode::EOR,
"eor",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
true,
),
(
ARMDeepOpcode::BIC,
"bic",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
(
ARMDeepOpcode::ORN,
"orn",
vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
],
false,
),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(ops.clone())
.with_flags(
false, false, false, false, false, false, false, false, false, *comm, true,
false, false, false, false,
),
);
}
for (opcode, mnem, ops) in &[
(
ARMDeepOpcode::CMP,
"cmp",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
),
(
ARMDeepOpcode::CMN,
"cmn",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
),
(
ARMDeepOpcode::TST,
"tst",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
),
(
ARMDeepOpcode::TEQ,
"teq",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(ops.clone())
.with_flags(
false, false, false, false, true, false, false, false, false, true, true,
false, false, false, false,
)
.with_implicit_defs(vec![CPSR]),
);
}
for (opcode, mnem, ops, mov_imm) in &[
(
ARMDeepOpcode::MOV,
"mov",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
true,
),
(
ARMDeepOpcode::MVN,
"mvn",
vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR],
false,
),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(ops.clone())
.with_flags(
false, false, false, false, false, *mov_imm, false, false, false, false,
true, false, false, false, false,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MUL, "mul")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MLA, "mla")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MLS, "mls")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::UMULL, "umull")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::UMLAL, "umlal")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::SMULL, "smull")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::SMLAL, "smlal")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
for (opcode, mnem, signed) in &[
(ARMDeepOpcode::SDIV, "sdiv", true),
(ARMDeepOpcode::UDIV, "udiv", false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::LSL, "lsl"),
(ARMDeepOpcode::LSR, "lsr"),
(ARMDeepOpcode::ASR, "asr"),
(ARMDeepOpcode::ROR, "ror"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::RRX, "rrx")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::B, "b")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, false, false, false, false, false, false, false, false, true, true,
false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::BL, "bl")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, true, false, false, false, false, false, false, false, true, false,
false, false, false,
)
.with_implicit_defs(vec![LR_ARM32])
.with_implicit_uses(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::BX, "bx")
.with_operands(vec![ArmDeepOperandType::GPR])
.with_flags(
true, true, false, true, false, false, false, false, false, false, true, true,
false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::BLX, "blx")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, true, false, false, false, false, false, false, false, true, false,
false, false, false,
)
.with_implicit_defs(vec![LR_ARM32]),
);
for (opcode, mnem, load, store) in &[
(ARMDeepOpcode::LDR, "ldr", true, false),
(ARMDeepOpcode::STR, "str", false, true),
(ARMDeepOpcode::LDRB, "ldrb", true, false),
(ARMDeepOpcode::STRB, "strb", false, true),
(ARMDeepOpcode::LDRH, "ldrh", true, false),
(ARMDeepOpcode::STRH, "strh", false, true),
(ARMDeepOpcode::LDRSB, "ldrsb", true, false),
(ARMDeepOpcode::LDRSH, "ldrsh", true, false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::MemAddr])
.with_flags(
false, false, false, false, false, false, false, *load, *store, false,
true, false, false, false, false,
),
);
}
for (opcode, mnem, load, store) in &[
(ARMDeepOpcode::LDRD, "ldrd", true, false),
(ARMDeepOpcode::STRD, "strd", false, true),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::MemAddr,
])
.with_flags(
false, false, false, false, false, false, false, *load, *store, false,
true, false, false, false, false,
),
);
}
for (opcode, mnem, load, store) in &[
(ARMDeepOpcode::LDMIA, "ldmia", true, false),
(ARMDeepOpcode::LDMIB, "ldmib", true, false),
(ARMDeepOpcode::LDMDA, "ldmda", true, false),
(ARMDeepOpcode::LDMDB, "ldmdb", true, false),
(ARMDeepOpcode::LDM, "ldm", true, false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, *load, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem, load, store) in &[
(ARMDeepOpcode::STMIA, "stmia", false, true),
(ARMDeepOpcode::STMIB, "stmib", false, true),
(ARMDeepOpcode::STMDA, "stmda", false, true),
(ARMDeepOpcode::STMDB, "stmdb", false, true),
(ARMDeepOpcode::STM, "stm", false, true),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, false, true, false, true,
false, false, false, false,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::PUSH, "push")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, false, true, false, true,
true, false, false, false,
)
.with_implicit_defs(vec![SP_ARM32])
.with_implicit_uses(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::POP, "pop")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, true, false, false, true,
true, false, false, false,
)
.with_implicit_defs(vec![SP_ARM32])
.with_implicit_uses(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MOVW, "movw")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::Imm16])
.with_flags(
false, false, false, false, false, true, false, false, false, false, true,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MOVT, "movt")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::Imm16])
.with_flags(
false, false, false, false, false, true, false, false, false, false, true,
false, true, false, false,
),
);
for (opcode, mnem) in &[(ARMDeepOpcode::BFC, "bfc"), (ARMDeepOpcode::BFI, "bfi")] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::Imm5,
ArmDeepOperandType::Imm5,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem) in &[(ARMDeepOpcode::SBFX, "sbfx"), (ARMDeepOpcode::UBFX, "ubfx")] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::Imm5,
ArmDeepOperandType::Imm5,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::UXTB, "uxtb"),
(ARMDeepOpcode::UXTH, "uxth"),
(ARMDeepOpcode::SXTB, "sxtb"),
(ARMDeepOpcode::SXTH, "sxth"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::CLZ, "clz"),
(ARMDeepOpcode::RBIT, "rbit"),
(ARMDeepOpcode::REV, "rev"),
(ARMDeepOpcode::REV16, "rev16"),
(ARMDeepOpcode::REVSH, "revsh"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::GPR])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::QADD, "qadd"),
(ARMDeepOpcode::QSUB, "qsub"),
(ARMDeepOpcode::QDADD, "qdadd"),
(ARMDeepOpcode::QDSUB, "qdsub"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, false,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::DMB, "dmb")
.with_operands(vec![ArmDeepOperandType::Imm5])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::DSB, "dsb")
.with_operands(vec![ArmDeepOperandType::Imm5])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::ISB, "isb")
.with_operands(vec![])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::NOP, "nop")
.with_operands(vec![])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
true, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::SVC, "svc")
.with_operands(vec![ArmDeepOperandType::Imm12])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::BKPT, "bkpt")
.with_operands(vec![ArmDeepOperandType::Imm16])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
true, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MRS, "mrs")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::SysReg])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::MSR, "msr")
.with_operands(vec![ArmDeepOperandType::SysReg, ArmDeepOperandType::GPR])
.with_flags(
false, false, false, false, false, false, true, false, false, false, true,
false, false, false, false,
),
);
for (opcode, mnem, commutative) in &[
(ARMDeepOpcode::VADD_F32, "vadd.f32", true),
(ARMDeepOpcode::VSUB_F32, "vsub.f32", false),
(ARMDeepOpcode::VMUL_F32, "vmul.f32", true),
(ARMDeepOpcode::VDIV_F32, "vdiv.f32", false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
])
.with_flags(
false,
false,
false,
false,
false,
false,
false,
false,
false,
*commutative,
true,
false,
false,
true,
false,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::VNEG_F32, "vneg.f32"),
(ARMDeepOpcode::VABS_F32, "vabs.f32"),
(ARMDeepOpcode::VSQRT_F32, "vsqrt.f32"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::SPR, ArmDeepOperandType::SPR])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, true, false,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::VCMP_F32, "vcmp.f32")
.with_operands(vec![ArmDeepOperandType::SPR, ArmDeepOperandType::SPR])
.with_flags(
false, false, false, false, true, false, false, false, false, false, true,
false, false, true, false,
)
.with_implicit_defs(vec![CPSR]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::VMLA_F32, "vmla.f32")
.with_operands(vec![
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, true, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::VMLS_F32, "vmls.f32")
.with_operands(vec![
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
ArmDeepOperandType::SPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, true, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::VMOV_F32, "vmov.f32")
.with_operands(vec![ArmDeepOperandType::SPR, ArmDeepOperandType::SPR])
.with_flags(
false, false, false, false, false, true, false, false, false, false, true,
false, false, true, false,
),
);
for (opcode, mnem, commutative) in &[
(ARMDeepOpcode::VADD_F64, "vadd.f64", true),
(ARMDeepOpcode::VSUB_F64, "vsub.f64", false),
(ARMDeepOpcode::VMUL_F64, "vmul.f64", true),
(ARMDeepOpcode::VDIV_F64, "vdiv.f64", false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
])
.with_flags(
false,
false,
false,
false,
false,
false,
false,
false,
false,
*commutative,
true,
false,
false,
true,
false,
),
);
}
let neon_typelist: &[(ARMDeepOpcode, &str, &[ArmDeepOperandType], bool)] = &[
(
ARMDeepOpcode::VADD_I8,
"vadd.i8",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VADD_I16,
"vadd.i16",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VADD_I32,
"vadd.i32",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VADD_I64,
"vadd.i64",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VSUB_I8,
"vsub.i8",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VSUB_I16,
"vsub.i16",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VSUB_I32,
"vsub.i32",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VSUB_I64,
"vsub.i64",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMUL_I8,
"vmul.i8",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VMUL_I16,
"vmul.i16",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VMUL_I32,
"vmul.i32",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
true,
),
(
ARMDeepOpcode::VMLA_I8,
"vmla.i8",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMLA_I16,
"vmla.i16",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMLA_I32,
"vmla.i32",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMLS_I8,
"vmls.i8",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMLS_I16,
"vmls.i16",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
(
ARMDeepOpcode::VMLS_I32,
"vmls.i32",
&[
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
],
false,
),
];
for (opcode, mnem, ops, comm) in neon_typelist {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(ops.to_vec())
.with_flags(
false, false, false, false, false, false, false, false, false, *comm, true,
false, false, false, true,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::VPADD_I8, "vpadd.i8"),
(ARMDeepOpcode::VPADD_I16, "vpadd.i16"),
(ARMDeepOpcode::VPADD_I32, "vpadd.i32"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, true,
false, false, false, true,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::VSHL_I8, "vshl.i8"),
(ARMDeepOpcode::VSHL_I16, "vshl.i16"),
(ARMDeepOpcode::VSHL_I32, "vshl.i32"),
(ARMDeepOpcode::VSHL_I64, "vshl.i64"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::Imm5,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, true,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::VTRN_8, "vtrn.8"),
(ARMDeepOpcode::VTRN_16, "vtrn.16"),
(ARMDeepOpcode::VTRN_32, "vtrn.32"),
(ARMDeepOpcode::VUZP_8, "vuzp.8"),
(ARMDeepOpcode::VUZP_16, "vuzp.16"),
(ARMDeepOpcode::VUZP_32, "vuzp.32"),
(ARMDeepOpcode::VZIP_8, "vzip.8"),
(ARMDeepOpcode::VZIP_16, "vzip.16"),
(ARMDeepOpcode::VZIP_32, "vzip.32"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, true,
),
);
}
for (opcode, mnem) in &[
(ARMDeepOpcode::VEXT_8, "vext.8"),
(ARMDeepOpcode::VEXT_16, "vext.16"),
(ARMDeepOpcode::VEXT_32, "vext.32"),
(ARMDeepOpcode::VEXT_64, "vext.64"),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::Imm5,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, true,
false, false, false, true,
),
);
}
for (opcode, mnem, comm) in &[
(ARMDeepOpcode::VAND, "vand", true),
(ARMDeepOpcode::VORR, "vorr", true),
(ARMDeepOpcode::VEOR, "veor", true),
(ARMDeepOpcode::VBIC, "vbic", false),
(ARMDeepOpcode::VORN, "vorn", false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::DPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, *comm, true,
false, false, false, true,
),
);
}
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_ADD_LOW, "add")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, false,
true, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_PUSH, "push")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, false, true, false, false,
true, false, false, false,
)
.with_implicit_defs(vec![SP_ARM32])
.with_implicit_uses(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_POP, "pop")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, true, false, false, false,
true, false, false, false,
)
.with_implicit_defs(vec![SP_ARM32])
.with_implicit_uses(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_B, "b")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, false, false, false, false, false, false, false, false, false,
true, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_BX, "bx")
.with_operands(vec![ArmDeepOperandType::GPR])
.with_flags(
true, true, false, true, false, false, false, false, false, false, false, true,
false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_BL, "bl")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, true, false, false, false, false, false, false, false, false, true,
false, false, false,
)
.with_implicit_defs(vec![LR_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_LDR, "ldr")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::MemAddr])
.with_flags(
false, false, false, false, false, false, false, true, false, false, false,
true, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T_STR, "str")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::MemAddr])
.with_flags(
false, false, false, false, false, false, false, false, true, false, false,
true, false, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_ADD, "add.w")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_SUB, "sub.w")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, false, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_MUL, "mul.w")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_LDR, "ldr.w")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::MemAddr])
.with_flags(
false, false, false, false, false, false, false, true, false, false, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_STR, "str.w")
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::MemAddr])
.with_flags(
false, false, false, false, false, false, false, false, true, false, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_B, "b.w")
.with_operands(vec![ArmDeepOperandType::Label])
.with_flags(
true, true, false, false, false, false, false, false, false, false, false,
false, true, false, false,
),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_PUSH, "push.w")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, false, true, false, false,
false, true, false, false,
)
.with_implicit_defs(vec![SP_ARM32]),
);
register(
ArmDeepInstrDesc::new(ARMDeepOpcode::T2_POP, "pop.w")
.with_operands(vec![ArmDeepOperandType::RegList])
.with_flags(
false, false, false, false, false, false, false, true, false, false, false,
false, true, false, false,
)
.with_implicit_defs(vec![SP_ARM32]),
);
for (opcode, mnem, is_arm_variant) in &[
(ARMDeepOpcode::CBZ, "cbz", false),
(ARMDeepOpcode::CBNZ, "cbnz", false),
] {
register(
ArmDeepInstrDesc::new(*opcode, mnem)
.with_operands(vec![ArmDeepOperandType::GPR, ArmDeepOperandType::Label])
.with_flags(
true,
true,
false,
false,
false,
false,
false,
false,
false,
false,
*is_arm_variant,
false,
false,
false,
false,
),
);
}
ARMInstrInfoDeep {
descriptors,
max_opcode,
mnemonic_map,
}
}
pub fn get(&self, opcode: ARMDeepOpcode) -> Option<&ArmDeepInstrDesc> {
let idx = opcode as usize;
if idx < self.descriptors.len() {
self.descriptors[idx].as_ref()
} else {
None
}
}
pub fn find_by_mnemonic(&self, mnemonic: &str) -> Option<ARMDeepOpcode> {
self.mnemonic_map.get(mnemonic).copied()
}
pub fn is_terminator(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.is_terminator).unwrap_or(false)
}
pub fn is_branch(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.is_branch).unwrap_or(false)
}
pub fn is_call(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.is_call).unwrap_or(false)
}
pub fn is_return(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.is_return).unwrap_or(false)
}
pub fn may_load(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.may_load).unwrap_or(false)
}
pub fn may_store(&self, opcode: ARMDeepOpcode) -> bool {
self.get(opcode).map(|d| d.may_store).unwrap_or(false)
}
pub fn get_arm_opcodes(&self) -> Vec<ARMDeepOpcode> {
self.descriptors
.iter()
.filter_map(|d| d.as_ref())
.filter(|d| d.is_arm)
.map(|d| d.opcode)
.collect()
}
pub fn get_thumb_opcodes(&self) -> Vec<ARMDeepOpcode> {
self.descriptors
.iter()
.filter_map(|d| d.as_ref())
.filter(|d| d.is_thumb && !d.is_thumb2)
.map(|d| d.opcode)
.collect()
}
pub fn get_thumb2_opcodes(&self) -> Vec<ARMDeepOpcode> {
self.descriptors
.iter()
.filter_map(|d| d.as_ref())
.filter(|d| d.is_thumb2)
.map(|d| d.opcode)
.collect()
}
pub fn get_vfp_opcodes(&self) -> Vec<ARMDeepOpcode> {
self.descriptors
.iter()
.filter_map(|d| d.as_ref())
.filter(|d| d.is_vfp)
.map(|d| d.opcode)
.collect()
}
pub fn get_neon_opcodes(&self) -> Vec<ARMDeepOpcode> {
self.descriptors
.iter()
.filter_map(|d| d.as_ref())
.filter(|d| d.is_neon)
.map(|d| d.opcode)
.collect()
}
pub fn len(&self) -> usize {
self.descriptors.iter().filter(|d| d.is_some()).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for ARMInstrInfoDeep {
fn default() -> Self {
Self::new()
}
}
pub struct ARMRegisterInfoDeep;
impl ARMRegisterInfoDeep {
pub fn get_asm_name(reg_id: u16) -> &'static str {
match reg_id {
R0..=R15 => {
let idx = (reg_id - R0) as usize;
const NAMES: [&str; 16] = [
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11",
"r12", "sp", "lr", "pc",
];
if idx < 16 {
NAMES[idx]
} else {
"??"
}
}
CPSR => "cpsr",
SP_ARM32 => "sp",
LR_ARM32 => "lr",
PC_ARM32 => "pc",
D0_ARM32..=D31_ARM32 => {
let idx = (reg_id - D0_ARM32) as usize;
const D_NAMES: [&str; 32] = [
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11",
"d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22",
"d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
];
if idx < 32 {
D_NAMES[idx]
} else {
"??"
}
}
S0_ARM32..=S31_ARM32 => {
let idx = (reg_id - S0_ARM32) as usize;
const S_NAMES: [&str; 32] = [
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
"s12", "s13", "s14", "s15", "s16", "s17", "s18", "s19", "s20", "s21", "s22",
"s23", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
];
if idx < 32 {
S_NAMES[idx]
} else {
"??"
}
}
_ => {
ArmRegisterInfo::get_asm_name(reg_id)
}
}
}
pub fn gpr_name(reg: u8) -> &'static str {
const NAMES: [&str; 16] = [
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp",
"lr", "pc",
];
if (reg as usize) < 16 {
NAMES[reg as usize]
} else {
"??"
}
}
pub fn dpr_name(reg: u8) -> &'static str {
const NAMES: [&str; 32] = [
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13",
"d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25",
"d26", "d27", "d28", "d29", "d30", "d31",
];
if (reg as usize) < 32 {
NAMES[reg as usize]
} else {
"??"
}
}
pub fn spr_name(reg: u8) -> &'static str {
const NAMES: [&str; 32] = [
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s13",
"s14", "s15", "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24", "s25",
"s26", "s27", "s28", "s29", "s30", "s31",
];
if (reg as usize) < 32 {
NAMES[reg as usize]
} else {
"??"
}
}
pub fn is_callee_saved(reg_id: u16) -> bool {
matches!(
reg_id,
R4 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | SP_ARM32 | LR_ARM32
)
}
pub fn is_caller_saved(reg_id: u16) -> bool {
matches!(reg_id, R0 | R1 | R2 | R3 | R12)
}
pub fn get_callee_saved_gprs() -> Vec<u16> {
vec![R4, R5, R6, R7, R8, R9, R10, R11]
}
pub fn get_caller_saved_gprs() -> Vec<u16> {
vec![R0, R1, R2, R3, R12]
}
pub fn get_arg_regs() -> Vec<u16> {
vec![R0, R1, R2, R3]
}
pub fn get_return_regs() -> Vec<u16> {
vec![R0, R1]
}
pub fn get_allocatable_gprs() -> Vec<u16> {
(R0..=R12).collect()
}
pub fn is_reserved(reg_id: u16) -> bool {
matches!(reg_id, SP_ARM32 | LR_ARM32 | PC_ARM32 | CPSR)
}
pub fn get_reg_width(reg_id: u16) -> u16 {
match reg_id {
R0..=R15 | CPSR | SP_ARM32 | LR_ARM32 | PC_ARM32 => 32,
D0_ARM32..=D31_ARM32 => 64,
S0_ARM32..=S31_ARM32 => 32,
_ => 0,
}
}
pub fn get_dwarf_num(reg_id: u16) -> Option<u16> {
match reg_id {
R0..=R15 => Some(reg_id - R0),
_ => None,
}
}
pub fn get_gpr_id(idx: u8) -> u16 {
if idx <= 15 {
R0 + idx as u16
} else {
R0
}
}
pub fn get_dpr_id(idx: u8) -> u16 {
if idx <= 31 {
D0_ARM32 + idx as u16
} else {
D0_ARM32
}
}
pub fn get_spr_id(idx: u8) -> u16 {
if idx <= 31 {
S0_ARM32 + idx as u16
} else {
S0_ARM32
}
}
pub fn is_gpr(reg_id: u16) -> bool {
matches!(reg_id, R0..=R15 | SP_ARM32 | LR_ARM32 | PC_ARM32)
}
pub fn is_dpr(reg_id: u16) -> bool {
matches!(reg_id, D0_ARM32..=D31_ARM32)
}
pub fn is_spr(reg_id: u16) -> bool {
matches!(reg_id, S0_ARM32..=S31_ARM32)
}
pub fn spr_to_dpr(spr: u16) -> u16 {
let idx = if spr >= S0_ARM32 { spr - S0_ARM32 } else { 0 };
D0_ARM32 + (idx / 2)
}
}
pub const ARM_DEEP_STACK_ALIGNMENT: i64 = 8;
pub const ARM_DEEP_PUSH_SIZE: i64 = 4;
pub const ARM_DEEP_FP: u16 = R11;
pub const ARM_DEEP_LR: u16 = R14;
pub const ARM_DEEP_SP: u16 = R13;
pub const ARM_DEEP_PC: u16 = R15;
pub const ARM_DEEP_CALLEE_SAVED_GPRS: &[u16] = &[R4, R5, R6, R7, R8, R9, R10, R11];
#[derive(Debug, Clone)]
pub struct ARMDeepFrameInfo {
pub frame_size: i64,
pub saved_regs: Vec<u16>,
pub has_frame_pointer: bool,
pub has_calls: bool,
pub local_area_offset: i64,
pub callee_saved_size: i64,
pub saved_fp_offset: i64,
pub saved_lr_offset: i64,
pub fixed_frame_size: i64,
}
impl ARMDeepFrameInfo {
pub fn new() -> Self {
let push_size = ARM_DEEP_PUSH_SIZE;
ARMDeepFrameInfo {
frame_size: 0,
saved_regs: Vec::new(),
has_frame_pointer: true,
has_calls: false,
local_area_offset: 0,
callee_saved_size: 0,
saved_fp_offset: -(push_size * 2), saved_lr_offset: -push_size, fixed_frame_size: push_size * 2, }
}
pub fn total_frame_size(&self) -> i64 {
self.frame_size
}
pub fn local_start_offset(&self) -> i64 {
-(self.fixed_frame_size + self.callee_saved_size)
}
}
impl Default for ARMDeepFrameInfo {
fn default() -> Self {
Self::new()
}
}
pub struct ARMFrameLoweringDeep {
pub call_conv: ArmCallingConvention,
}
impl ARMFrameLoweringDeep {
pub fn new(conv: ArmCallingConvention) -> Self {
ARMFrameLoweringDeep { call_conv: conv }
}
pub fn emit_prologue(&self, info: &ARMDeepFrameInfo) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let mut push_fp_lr = MachineInstr::new(ARMDeepOpcode::PUSH as u32);
push_fp_lr
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_FP as u32));
push_fp_lr
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_LR as u32));
instrs.push(push_fp_lr);
let mut mov_fp = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mov_fp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_FP as u32));
mov_fp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
instrs.push(mov_fp);
let local_size = info.frame_size;
if local_size > 0 {
let mut sub_sp = MachineInstr::new(ARMDeepOpcode::SUB as u32);
sub_sp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub_sp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub_sp.push_imm(local_size);
instrs.push(sub_sp);
}
if !info.saved_regs.is_empty() {
let mut push_callee = MachineInstr::new(ARMDeepOpcode::PUSH as u32);
for ® in &info.saved_regs {
push_callee
.operands
.push(MachineOperand::PhysReg(reg as u32));
}
instrs.push(push_callee);
}
instrs
}
pub fn emit_epilogue(&self, info: &ARMDeepFrameInfo) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if !info.saved_regs.is_empty() {
let mut pop_callee = MachineInstr::new(ARMDeepOpcode::POP as u32);
for ® in &info.saved_regs {
pop_callee
.operands
.push(MachineOperand::PhysReg(reg as u32));
}
instrs.push(pop_callee);
}
let mut mov_sp = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mov_sp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
mov_sp
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_FP as u32));
instrs.push(mov_sp);
let mut pop_fp_pc = MachineInstr::new(ARMDeepOpcode::POP as u32);
pop_fp_pc
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_FP as u32));
pop_fp_pc
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_PC as u32));
instrs.push(pop_fp_pc);
instrs
}
pub fn build_frame_info(&self, mf: &MachineFunction) -> ARMDeepFrameInfo {
let mut info = ARMDeepFrameInfo::new();
for bb in &mf.blocks {
for mi in &bb.instructions {
let opcode = mi.opcode;
let is_call = opcode == ARMDeepOpcode::BL as u32
|| opcode == ARMDeepOpcode::BLX as u32
|| opcode == ARMDeepOpcode::T_BL as u32
|| opcode == ARMDeepOpcode::T2_BL as u32;
if is_call {
info.has_calls = true;
}
}
}
let mut used_callee_saved = Vec::new();
for ® in ARM_DEEP_CALLEE_SAVED_GPRS {
if reg == ARM_DEEP_FP || reg == ARM_DEEP_LR {
continue;
}
let mut is_used = false;
for bb in &mf.blocks {
for mi in &bb.instructions {
for op in &mi.operands {
if let MachineOperand::PhysReg(r) = op {
if *r == reg as u32 {
is_used = true;
break;
}
}
}
if is_used {
break;
}
}
if is_used {
break;
}
}
if is_used {
used_callee_saved.push(reg);
}
}
info.saved_regs = used_callee_saved;
info.callee_saved_size = info.saved_regs.len() as i64 * ARM_DEEP_PUSH_SIZE;
let mut max_sub = 0i64;
for bb in &mf.blocks {
for mi in &bb.instructions {
if mi.opcode == ARMDeepOpcode::SUB as u32 {
let is_sp_op = mi.operands.len() >= 2
&& mi.operands[0] == MachineOperand::PhysReg(ARM_DEEP_SP as u32)
&& mi.operands[1] == MachineOperand::PhysReg(ARM_DEEP_SP as u32);
if is_sp_op {
if let Some(MachineOperand::Imm(v)) = mi.operands.get(2) {
if *v > max_sub {
max_sub = *v;
}
}
}
}
}
}
info.frame_size = max_sub;
info
}
}
pub struct ARMCallingConventionDeep;
impl ARMCallingConventionDeep {
pub const ARG_REGS: [u16; 4] = [R0, R1, R2, R3];
pub const RETURN_REGS: [u16; 2] = [R0, R1];
pub const CALLEE_SAVED: [u16; 8] = [R4, R5, R6, R7, R8, R9, R10, R11];
pub const CALLER_SAVED: [u16; 5] = [R0, R1, R2, R3, R12];
pub fn get_arg_regs() -> Vec<u16> {
Self::ARG_REGS.to_vec()
}
pub fn get_return_regs() -> Vec<u16> {
Self::RETURN_REGS.to_vec()
}
pub fn get_num_int_param_regs() -> usize {
4
}
pub fn get_stack_alignment() -> u32 {
8
}
pub fn get_frame_pointer_reg() -> u16 {
ARM_DEEP_FP }
pub fn get_link_register_reg() -> u16 {
ARM_DEEP_LR }
pub fn get_stack_pointer_reg() -> u16 {
ARM_DEEP_SP }
pub fn get_implicit_uses_at_call() -> Vec<u16> {
vec![ARM_DEEP_SP, ARM_DEEP_LR]
}
pub fn get_call_clobbered_regs() -> Vec<u16> {
let mut clobbered = vec![R0, R1, R2, R3, R12];
clobbered.push(CPSR);
clobbered
}
pub fn get_callee_saved_regs() -> Vec<u16> {
Self::CALLEE_SAVED.to_vec()
}
pub fn get_caller_saved_regs() -> Vec<u16> {
Self::CALLER_SAVED.to_vec()
}
pub fn get_return_regs_for_size(size_bytes: u32) -> Vec<u16> {
match size_bytes {
0 => vec![],
1..=4 => vec![R0],
_ => vec![R0, R1],
}
}
pub fn needs_indirect_return(size_bytes: u32) -> bool {
size_bytes > 8
}
pub fn get_indirect_result_reg() -> u16 {
R0
}
}
pub struct ARMDeepRegisterAllocator {
pub assignments: HashMap<u32, u32>, available: Vec<u32>,
}
impl ARMDeepRegisterAllocator {
pub fn new() -> Self {
let available: Vec<u32> = (0..=12).collect();
ARMDeepRegisterAllocator {
assignments: HashMap::new(),
available,
}
}
pub fn allocate(&mut self, mf: &mut MachineFunction) {
let mut next_phys = 0usize;
for bb in &mut mf.blocks {
for mi in &mut bb.instructions {
if let Some(def) = mi.def {
let phys = if let Some(&p) = self.assignments.get(&def) {
p
} else {
let p = self.available[next_phys % self.available.len()];
self.assignments.insert(def, p);
next_phys += 1;
p
};
mi.operands.insert(0, MachineOperand::PhysReg(phys));
}
for op in &mut mi.operands {
if let MachineOperand::Reg(vr) = *op {
if let Some(&phys) = self.assignments.get(&vr) {
*op = MachineOperand::PhysReg(phys);
}
}
}
}
}
}
pub fn reset(&mut self) {
self.assignments.clear();
}
}
impl Default for ARMDeepRegisterAllocator {
fn default() -> Self {
Self::new()
}
}
pub struct ARMDeepAsmPrinter {
pub output: String,
}
impl ARMDeepAsmPrinter {
pub fn new() -> Self {
ARMDeepAsmPrinter {
output: String::new(),
}
}
pub fn print_function(&mut self, mf: &MachineFunction) {
self.output.push_str(&format!(".globl {}\n", mf.name));
self.output
.push_str(&format!(".type {}, %function\n", mf.name));
self.output.push_str(&format!("{}:\n", mf.name));
self.output.push_str(" .fnstart\n");
for bb in &mf.blocks {
if !bb.name.is_empty() && bb.name != "entry" {
self.output.push_str(&format!(".L{}:\n", bb.name));
}
for mi in &bb.instructions {
self.print_instr(mi);
}
}
self.output.push_str(" .fnend\n");
self.output.push('\n');
}
fn print_instr(&mut self, mi: &MachineInstr) {
let mnemonic = self.mnemonic(mi.opcode);
self.output.push_str(&format!(" {}", mnemonic));
let mut first = true;
for op in &mi.operands {
if first {
self.output.push(' ');
first = false;
} else {
self.output.push_str(", ");
}
match op {
MachineOperand::PhysReg(r) => {
let name = ARMRegisterInfoDeep::get_asm_name(*r as u16);
self.output.push_str(name);
}
MachineOperand::Reg(r) => {
self.output.push_str(&format!("v{}", r));
}
MachineOperand::Imm(i) => {
self.output.push_str(&format!("#{}", i));
}
MachineOperand::Label(l) => {
self.output.push_str(&format!(".L{}", l));
}
MachineOperand::Global(g) => {
self.output.push_str(g);
}
}
}
self.output.push('\n');
}
fn mnemonic(&self, opcode: u32) -> &'static str {
let count = ARMDeepOpcode::INVALID as u32;
if opcode < count {
let op = unsafe { std::mem::transmute::<u32, ARMDeepOpcode>(opcode) };
op.mnemonic()
} else {
"???"
}
}
pub fn format_reg_list(&self, regs: &[u16]) -> String {
let names: Vec<&str> = regs
.iter()
.map(|r| ARMRegisterInfoDeep::get_asm_name(*r))
.collect();
format!("{{{}}}", names.join(", "))
}
pub fn format_cond_instr(&self, mnem: &str, cond: ARMDeepConditionCode) -> String {
if cond == ARMDeepConditionCode::AL {
mnem.to_string()
} else {
format!("{}{}", mnem, cond.suffix())
}
}
}
impl Default for ARMDeepAsmPrinter {
fn default() -> Self {
Self::new()
}
}
pub struct ARMDeepPeepholeOptimizer;
impl ARMDeepPeepholeOptimizer {
pub fn new() -> Self {
ARMDeepPeepholeOptimizer
}
pub fn optimize_block(&self, block: &mut MachineBasicBlock) -> usize {
let mut removed = 0usize;
let mut i = 0;
while i < block.instructions.len() {
let should_remove = if i + 1 < block.instructions.len() {
self.try_peephole(&block.instructions[i], &block.instructions[i + 1])
} else {
None
};
match should_remove {
Some(PeepholeAction::RemoveCurrent) => {
block.instructions.remove(i);
removed += 1;
}
Some(PeepholeAction::RemovePair) => {
block.instructions.remove(i); block.instructions.remove(i); removed += 2;
}
Some(PeepholeAction::ReplaceCurrent(new_instr)) => {
block.instructions[i] = new_instr;
removed += 1;
i += 1;
}
None => {
i += 1;
}
}
}
removed
}
fn try_peephole(&self, a: &MachineInstr, b: &MachineInstr) -> Option<PeepholeAction> {
if a.opcode == ARMDeepOpcode::MOV as u32 && a.operands.len() == 2 {
if a.operands[0] == a.operands[1] {
return Some(PeepholeAction::RemoveCurrent);
}
}
if a.opcode == ARMDeepOpcode::MOV as u32
&& b.opcode == ARMDeepOpcode::MOV as u32
&& a.operands.len() >= 1
&& b.operands.len() >= 1
&& a.operands[0] == b.operands[0]
{
return Some(PeepholeAction::RemoveCurrent);
}
if a.opcode == ARMDeepOpcode::PUSH as u32
&& b.opcode == ARMDeepOpcode::POP as u32
&& a.operands == b.operands
{
return Some(PeepholeAction::RemovePair);
}
None
}
pub fn optimize_function(&self, mf: &mut MachineFunction) -> usize {
let mut total_removed = 0;
for bb in &mut mf.blocks {
total_removed += self.optimize_block(bb);
}
total_removed
}
}
enum PeepholeAction {
RemoveCurrent,
RemovePair,
ReplaceCurrent(MachineInstr),
}
impl Default for ARMDeepPeepholeOptimizer {
fn default() -> Self {
Self::new()
}
}
pub struct ARMDeepInstructionSelector {
pub instr_info: ARMInstrInfoDeep,
}
impl ARMDeepInstructionSelector {
pub fn new() -> Self {
ARMDeepInstructionSelector {
instr_info: ARMInstrInfoDeep::new(),
}
}
fn map_binary_op(&self, op_name: &str) -> ARMDeepOpcode {
match op_name {
"add" => ARMDeepOpcode::ADD,
"sub" => ARMDeepOpcode::SUB,
"mul" => ARMDeepOpcode::MUL,
"sdiv" => ARMDeepOpcode::SDIV,
"udiv" => ARMDeepOpcode::UDIV,
"and" => ARMDeepOpcode::AND,
"or" => ARMDeepOpcode::ORR,
"xor" => ARMDeepOpcode::EOR,
"shl" => ARMDeepOpcode::LSL,
"lshr" => ARMDeepOpcode::LSR,
"ashr" => ARMDeepOpcode::ASR,
_ => ARMDeepOpcode::ADD,
}
}
pub fn select_simple(&self, mf: &mut MachineFunction, ops: &[(u32, Vec<MachineOperand>)]) {
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: Vec::new(),
successors: Vec::new(),
};
for (opcode, operands) in ops {
let mut mi = MachineInstr::new(*opcode);
mi.operands = operands.clone();
bb.instructions.push(mi);
}
mf.push_block(bb);
}
}
impl Default for ARMDeepInstructionSelector {
fn default() -> Self {
Self::new()
}
}
pub struct ARMDeep {
pub instr_info: ARMInstrInfoDeep,
pub reg_alloc: ARMDeepRegisterAllocator,
pub frame_lowering: ARMFrameLoweringDeep,
pub call_conv: ArmCallingConvention,
pub peephole: ARMDeepPeepholeOptimizer,
pub is_thumb: bool,
pub is_thumb2: bool,
}
impl ARMDeep {
pub fn new() -> Self {
let conv = ArmCallingConvention::AAPCS;
ARMDeep {
instr_info: ARMInstrInfoDeep::new(),
reg_alloc: ARMDeepRegisterAllocator::new(),
frame_lowering: ARMFrameLoweringDeep::new(conv),
call_conv: conv,
peephole: ARMDeepPeepholeOptimizer::new(),
is_thumb: false,
is_thumb2: false,
}
}
pub fn new_thumb() -> Self {
let mut backend = ARMDeep::new();
backend.is_thumb = true;
backend
}
pub fn new_thumb2() -> Self {
let mut backend = ARMDeep::new();
backend.is_thumb = true;
backend.is_thumb2 = true;
backend
}
pub fn new_vfp() -> Self {
let conv = ArmCallingConvention::AAPCS_VFP;
ARMDeep {
instr_info: ARMInstrInfoDeep::new(),
reg_alloc: ARMDeepRegisterAllocator::new(),
frame_lowering: ARMFrameLoweringDeep::new(conv),
call_conv: conv,
peephole: ARMDeepPeepholeOptimizer::new(),
is_thumb: false,
is_thumb2: false,
}
}
pub fn get_target_triple(&self) -> &'static str {
if self.is_thumb {
if self.is_thumb2 {
"thumbv7m-none-eabi"
} else {
"thumbv6m-none-eabi"
}
} else {
"armv7a-none-eabi"
}
}
pub fn get_data_layout(&self) -> &'static str {
"e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
}
pub fn compile_function(&mut self, mf: &mut MachineFunction) -> String {
self.reg_alloc.allocate(mf);
self.peephole.optimize_function(mf);
let mut printer = ARMDeepAsmPrinter::new();
printer.print_function(mf);
printer.output
}
pub fn compile_with_frame(&mut self, mf: &mut MachineFunction) -> String {
let frame_info = self.frame_lowering.build_frame_info(mf);
let prologue = self.frame_lowering.emit_prologue(&frame_info);
if let Some(first_bb) = mf.blocks.first_mut() {
let mut new_instrs = prologue;
new_instrs.append(&mut first_bb.instructions);
first_bb.instructions = new_instrs;
}
self.reg_alloc.allocate(mf);
self.peephole.optimize_function(mf);
let mut printer = ARMDeepAsmPrinter::new();
printer.print_function(mf);
printer.output
}
pub fn instruction_count(&self) -> usize {
self.instr_info.len()
}
pub fn get_opcode(&self, mnemonic: &str) -> Option<ARMDeepOpcode> {
self.instr_info.find_by_mnemonic(mnemonic)
}
pub fn is_terminator(&self, opcode: ARMDeepOpcode) -> bool {
self.instr_info.is_terminator(opcode)
}
pub fn validate(&self, mf: &MachineFunction) -> Vec<String> {
let mut errors = Vec::new();
for bb in &mf.blocks {
for (i, mi) in bb.instructions.iter().enumerate() {
let count = ARMDeepOpcode::count() as u32;
if mi.opcode >= count {
errors.push(format!(
"Unknown opcode {} in block '{}', instruction {}",
mi.opcode, bb.name, i
));
}
}
}
errors
}
}
impl Default for ARMDeep {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for ARMDeep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ARMDeep(target={}, thumb={}, thumb2={}, instructions={}, call_conv={})",
if self.is_thumb { "Thumb" } else { "ARM32" },
self.is_thumb,
self.is_thumb2,
self.instruction_count(),
self.call_conv.name(),
)
}
}
impl fmt::Debug for ARMDeep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ARMDeep")
.field("is_thumb", &self.is_thumb)
.field("is_thumb2", &self.is_thumb2)
.field("instruction_count", &self.instruction_count())
.field("call_conv", &self.call_conv.name())
.finish()
}
}
pub fn parse_reglist_mask(mask: u16, include_sp_pc: bool) -> Vec<u16> {
let mut regs = Vec::new();
for i in 0..16u16 {
if (mask >> i) & 1 != 0 {
let reg_id = R0 + i;
if !include_sp_pc && (reg_id == SP_ARM32 || reg_id == PC_ARM32) {
continue;
}
regs.push(reg_id);
}
}
regs
}
pub fn encode_reglist_mask(regs: &[u16]) -> u16 {
let mut mask = 0u16;
for &r in regs {
if (R0..=R15).contains(&r) || r == SP_ARM32 || r == LR_ARM32 || r == PC_ARM32 {
let idx = if r <= R15 { r - R0 } else { 0 };
mask |= 1 << idx;
}
}
mask
}
pub fn format_reglist(regs: &[u16]) -> String {
let names: Vec<&str> = regs
.iter()
.map(|r| ARMRegisterInfoDeep::get_asm_name(*r))
.collect();
format!("{{{}}}", names.join(", "))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ARMEncodingFormat {
DataProcessing,
LoadStore,
LoadStoreMultiple,
Branch,
Coprocessor,
CoprocessorData,
SoftwareInterrupt,
Unconditional,
Thumb16,
Thumb2_32,
VFPNEON,
}
impl ARMEncodingFormat {
pub fn has_condition_field(&self) -> bool {
match self {
ARMEncodingFormat::DataProcessing
| ARMEncodingFormat::LoadStore
| ARMEncodingFormat::LoadStoreMultiple
| ARMEncodingFormat::Branch
| ARMEncodingFormat::Coprocessor
| ARMEncodingFormat::CoprocessorData
| ARMEncodingFormat::SoftwareInterrupt
| ARMEncodingFormat::VFPNEON => true,
ARMEncodingFormat::Unconditional
| ARMEncodingFormat::Thumb16
| ARMEncodingFormat::Thumb2_32 => false,
}
}
pub fn is_16bit(&self) -> bool {
matches!(self, ARMEncodingFormat::Thumb16)
}
pub fn is_32bit(&self) -> bool {
!self.is_16bit()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ARMShifterOperand {
Immediate {
imm8: u8,
rotate: u8,
},
Register(u16),
ImmediateShift {
rm: u16,
shift: ARMShiftType,
amount: u8,
},
RegisterShift {
rm: u16,
shift: ARMShiftType,
rs: u16,
},
RRX(u16),
}
impl ARMShifterOperand {
pub fn immediate(value: u32) -> Option<Self> {
if value == 0 {
return Some(ARMShifterOperand::Immediate { imm8: 0, rotate: 0 });
}
for rotate in 0..16u8 {
let rotated = value.rotate_right((rotate as u32) * 2);
if rotated <= 0xFF {
return Some(ARMShifterOperand::Immediate {
imm8: rotated as u8,
rotate,
});
}
}
None }
pub fn reg(rm: u16) -> Self {
ARMShifterOperand::Register(rm)
}
pub fn reg_shift(rm: u16, shift: ARMShiftType, amount: u8) -> Self {
ARMShifterOperand::ImmediateShift { rm, shift, amount }
}
pub fn rrx(rm: u16) -> Self {
ARMShifterOperand::RRX(rm)
}
pub fn is_immediate(&self) -> bool {
matches!(self, ARMShifterOperand::Immediate { .. })
}
pub fn is_register(&self) -> bool {
matches!(self, ARMShifterOperand::Register(_))
}
pub fn format(&self) -> String {
match self {
ARMShifterOperand::Immediate { imm8, rotate } => {
let value = (*imm8 as u32).rotate_right((*rotate as u32) * 2);
format!("#{}", value)
}
ARMShifterOperand::Register(rm) => ARMRegisterInfoDeep::get_asm_name(*rm).to_string(),
ARMShifterOperand::ImmediateShift { rm, shift, amount } => {
format!(
"{}, {} #{}",
ARMRegisterInfoDeep::get_asm_name(*rm),
shift.mnemonic(),
amount
)
}
ARMShifterOperand::RegisterShift { rm, shift, rs } => {
format!(
"{}, {} {}",
ARMRegisterInfoDeep::get_asm_name(*rm),
shift.mnemonic(),
ARMRegisterInfoDeep::get_asm_name(*rs)
)
}
ARMShifterOperand::RRX(rm) => {
format!("{}, rrx", ARMRegisterInfoDeep::get_asm_name(*rm))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ARMShiftType {
LSL,
LSR,
ASR,
ROR,
RRX,
}
impl ARMShiftType {
pub fn mnemonic(&self) -> &'static str {
match self {
ARMShiftType::LSL => "lsl",
ARMShiftType::LSR => "lsr",
ARMShiftType::ASR => "asr",
ARMShiftType::ROR => "ror",
ARMShiftType::RRX => "rrx",
}
}
pub fn encode(&self) -> u8 {
match self {
ARMShiftType::LSL => 0b00,
ARMShiftType::LSR => 0b01,
ARMShiftType::ASR => 0b10,
ARMShiftType::ROR => 0b11,
ARMShiftType::RRX => 0b11, }
}
pub fn decode(bits: u8) -> Option<Self> {
match bits {
0b00 => Some(ARMShiftType::LSL),
0b01 => Some(ARMShiftType::LSR),
0b10 => Some(ARMShiftType::ASR),
0b11 => Some(ARMShiftType::ROR),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ITBlockState {
pub cond: ARMDeepConditionCode,
pub mask: u8,
pub num_insts: u8,
}
impl ITBlockState {
pub fn new_single(cond: ARMDeepConditionCode) -> Self {
ITBlockState {
cond,
mask: 0b1000, num_insts: 1,
}
}
pub fn new_double(cond: ARMDeepConditionCode, second_is_then: bool) -> Self {
ITBlockState {
cond,
mask: if second_is_then { 0b1100 } else { 0b1000 },
num_insts: 2,
}
}
pub fn new_triple(
cond: ARMDeepConditionCode,
pattern: (bool, bool), ) -> Self {
let mut mask = 0b1000u8; if pattern.0 {
mask |= 0b0100;
}
if pattern.1 {
mask |= 0b0010;
}
ITBlockState {
cond,
mask,
num_insts: 3,
}
}
pub fn new_quad(
cond: ARMDeepConditionCode,
pattern: (bool, bool, bool), ) -> Self {
let mut mask = 0b1000u8;
if pattern.0 {
mask |= 0b0100;
}
if pattern.1 {
mask |= 0b0010;
}
if pattern.2 {
mask |= 0b0001;
}
ITBlockState {
cond,
mask,
num_insts: 4,
}
}
pub fn condition_for_inst(&self, index: u8) -> ARMDeepConditionCode {
if index >= self.num_insts {
return ARMDeepConditionCode::AL;
}
if index == 0 {
self.cond
} else {
let bit = 3 - index as usize; let is_then = (self.mask >> bit) & 1 != 0;
if is_then {
self.cond
} else {
self.cond.invert()
}
}
}
pub fn format_it_instruction(&self) -> String {
match self.num_insts {
1 => format!("it {}", self.cond.suffix()),
2 => {
let x = if (self.mask >> 2) & 1 != 0 { 't' } else { 'e' };
format!("it{} {}", x, self.cond.suffix())
}
3 => {
let x = if (self.mask >> 2) & 1 != 0 { 't' } else { 'e' };
let y = if (self.mask >> 1) & 1 != 0 { 't' } else { 'e' };
format!("it{}{} {}", x, y, self.cond.suffix())
}
4 => {
let x = if (self.mask >> 2) & 1 != 0 { 't' } else { 'e' };
let y = if (self.mask >> 1) & 1 != 0 { 't' } else { 'e' };
let z = if self.mask & 1 != 0 { 't' } else { 'e' };
format!("it{}{}{} {}", x, y, z, self.cond.suffix())
}
_ => "it".to_string(),
}
}
pub fn is_single_foldable(&self) -> bool {
self.num_insts == 1
}
pub fn all_conditions(&self) -> Vec<ARMDeepConditionCode> {
(0..self.num_insts)
.map(|i| self.condition_for_inst(i))
.collect()
}
}
impl Default for ITBlockState {
fn default() -> Self {
ITBlockState {
cond: ARMDeepConditionCode::AL,
mask: 0b1000,
num_insts: 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NEONElementSize {
E8,
E16,
E32,
E64,
}
impl NEONElementSize {
pub fn bits(&self) -> u8 {
match self {
NEONElementSize::E8 => 8,
NEONElementSize::E16 => 16,
NEONElementSize::E32 => 32,
NEONElementSize::E64 => 64,
}
}
pub fn elements_per_dreg(&self) -> u8 {
64 / self.bits()
}
pub fn max_lane_dreg(&self) -> u8 {
self.elements_per_dreg() - 1
}
pub fn suffix(&self) -> &'static str {
match self {
NEONElementSize::E8 => "8",
NEONElementSize::E16 => "16",
NEONElementSize::E32 => "32",
NEONElementSize::E64 => "64",
}
}
pub fn from_bytes(bytes: u8) -> Option<Self> {
match bytes {
1 => Some(NEONElementSize::E8),
2 => Some(NEONElementSize::E16),
4 => Some(NEONElementSize::E32),
8 => Some(NEONElementSize::E64),
_ => None,
}
}
pub fn c_type(&self) -> &'static str {
match self {
NEONElementSize::E8 => "int8_t",
NEONElementSize::E16 => "int16_t",
NEONElementSize::E32 => "int32_t",
NEONElementSize::E64 => "int64_t",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NEONLane {
pub reg: u16,
pub size: NEONElementSize,
pub lane: u8,
}
impl NEONLane {
pub fn new(reg: u16, size: NEONElementSize, lane: u8) -> Option<Self> {
let max = size.max_lane_dreg();
if lane > max {
return None;
}
Some(NEONLane { reg, size, lane })
}
pub fn format(&self) -> String {
format!(
"{}[{}]",
ARMRegisterInfoDeep::get_asm_name(self.reg),
self.lane
)
}
pub fn byte_offset(&self) -> u8 {
self.lane * (self.size.bits() / 8)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ARMProcessorMode {
User = 0b10000,
FIQ = 0b10001,
IRQ = 0b10010,
Supervisor = 0b10011,
Monitor = 0b10110,
Abort = 0b10111,
Hypervisor = 0b11010,
Undefined = 0b11011,
System = 0b11111,
}
impl ARMProcessorMode {
pub fn name(&self) -> &'static str {
match self {
ARMProcessorMode::User => "usr",
ARMProcessorMode::FIQ => "fiq",
ARMProcessorMode::IRQ => "irq",
ARMProcessorMode::Supervisor => "svc",
ARMProcessorMode::Monitor => "mon",
ARMProcessorMode::Abort => "abt",
ARMProcessorMode::Hypervisor => "hyp",
ARMProcessorMode::Undefined => "und",
ARMProcessorMode::System => "sys",
}
}
pub fn is_privileged(&self) -> bool {
!matches!(self, ARMProcessorMode::User)
}
pub fn banked_regs(&self) -> &[u8] {
match self {
ARMProcessorMode::FIQ => &[8, 9, 10, 11, 12, 13, 14],
ARMProcessorMode::IRQ => &[13, 14],
ARMProcessorMode::Supervisor => &[13, 14],
ARMProcessorMode::Monitor => &[13, 14],
ARMProcessorMode::Abort => &[13, 14],
ARMProcessorMode::Hypervisor => &[13, 14],
ARMProcessorMode::Undefined => &[13, 14],
_ => &[],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstructionSetState {
ARM,
Thumb,
Jazelle,
ThumbEE,
}
impl InstructionSetState {
pub fn from_address(addr: u32) -> Self {
if addr & 1 != 0 {
InstructionSetState::Thumb
} else {
InstructionSetState::ARM
}
}
pub fn is_32bit(&self) -> bool {
matches!(self, InstructionSetState::ARM)
}
pub fn has_conditional_execution(&self) -> bool {
matches!(self, InstructionSetState::ARM)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ARMPipelineStage {
F1,
F2,
D1,
D2,
I1,
I2,
E1,
E2,
E3,
E4,
M1,
M2,
W1,
}
#[derive(Debug, Clone)]
pub struct ARMPipelineUsage {
pub latency: u8,
pub uses_alu: bool,
pub uses_multiply: bool,
pub uses_load_store: bool,
pub uses_branch: bool,
pub uses_vfp_neon: bool,
pub issue_cycles: u8,
}
impl ARMPipelineUsage {
pub fn simple_alu() -> Self {
ARMPipelineUsage {
latency: 1,
uses_alu: true,
uses_multiply: false,
uses_load_store: false,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn multiply() -> Self {
ARMPipelineUsage {
latency: 2,
uses_alu: true,
uses_multiply: true,
uses_load_store: false,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn multiply_accumulate() -> Self {
ARMPipelineUsage {
latency: 3,
uses_alu: true,
uses_multiply: true,
uses_load_store: false,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn load() -> Self {
ARMPipelineUsage {
latency: 3,
uses_alu: true,
uses_multiply: false,
uses_load_store: true,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn store() -> Self {
ARMPipelineUsage {
latency: 1, uses_alu: true,
uses_multiply: false,
uses_load_store: true,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn load_multiple(num_regs: u8) -> Self {
ARMPipelineUsage {
latency: 2 + num_regs as u8,
uses_alu: false,
uses_multiply: false,
uses_load_store: true,
uses_branch: false,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn branch() -> Self {
ARMPipelineUsage {
latency: 0, uses_alu: false,
uses_multiply: false,
uses_load_store: false,
uses_branch: true,
uses_vfp_neon: false,
issue_cycles: 1,
}
}
pub fn vfp_scalar() -> Self {
ARMPipelineUsage {
latency: 8,
uses_alu: false,
uses_multiply: false,
uses_load_store: false,
uses_branch: false,
uses_vfp_neon: true,
issue_cycles: 1,
}
}
pub fn neon(cycles: u8) -> Self {
ARMPipelineUsage {
latency: cycles,
uses_alu: false,
uses_multiply: false,
uses_load_store: false,
uses_branch: false,
uses_vfp_neon: true,
issue_cycles: 1,
}
}
pub fn for_opcode(opcode: ARMDeepOpcode) -> Self {
match opcode {
ARMDeepOpcode::ADD
| ARMDeepOpcode::SUB
| ARMDeepOpcode::AND
| ARMDeepOpcode::ORR
| ARMDeepOpcode::EOR
| ARMDeepOpcode::BIC
| ARMDeepOpcode::ORN
| ARMDeepOpcode::MOV
| ARMDeepOpcode::MVN
| ARMDeepOpcode::ADC
| ARMDeepOpcode::SBC
| ARMDeepOpcode::RSB
| ARMDeepOpcode::RSC
| ARMDeepOpcode::CMP
| ARMDeepOpcode::CMN
| ARMDeepOpcode::TST
| ARMDeepOpcode::TEQ
| ARMDeepOpcode::LSL
| ARMDeepOpcode::LSR
| ARMDeepOpcode::ASR
| ARMDeepOpcode::ROR
| ARMDeepOpcode::RRX => ARMPipelineUsage::simple_alu(),
ARMDeepOpcode::MUL => ARMPipelineUsage::multiply(),
ARMDeepOpcode::MLA | ARMDeepOpcode::MLS => ARMPipelineUsage::multiply_accumulate(),
ARMDeepOpcode::UMULL
| ARMDeepOpcode::SMULL
| ARMDeepOpcode::UMLAL
| ARMDeepOpcode::SMLAL => ARMPipelineUsage {
latency: 4,
uses_multiply: true,
issue_cycles: 2,
..ARMPipelineUsage::multiply()
},
ARMDeepOpcode::SDIV | ARMDeepOpcode::UDIV => ARMPipelineUsage {
latency: 12,
uses_alu: true,
issue_cycles: 12,
..ARMPipelineUsage::simple_alu()
},
ARMDeepOpcode::LDR
| ARMDeepOpcode::LDRB
| ARMDeepOpcode::LDRH
| ARMDeepOpcode::LDRSB
| ARMDeepOpcode::LDRSH => ARMPipelineUsage::load(),
ARMDeepOpcode::STR | ARMDeepOpcode::STRB | ARMDeepOpcode::STRH => {
ARMPipelineUsage::store()
}
ARMDeepOpcode::LDM | ARMDeepOpcode::LDMIA => ARMPipelineUsage::load_multiple(2),
ARMDeepOpcode::STM | ARMDeepOpcode::STMIA => ARMPipelineUsage {
latency: 0,
uses_load_store: true,
issue_cycles: 1,
..ARMPipelineUsage::simple_alu()
},
ARMDeepOpcode::B | ARMDeepOpcode::BX | ARMDeepOpcode::BL | ARMDeepOpcode::BLX => {
ARMPipelineUsage::branch()
}
ARMDeepOpcode::VADD_F32
| ARMDeepOpcode::VSUB_F32
| ARMDeepOpcode::VMUL_F32
| ARMDeepOpcode::VNEG_F32
| ARMDeepOpcode::VABS_F32 => ARMPipelineUsage::vfp_scalar(),
ARMDeepOpcode::VDIV_F32 | ARMDeepOpcode::VSQRT_F32 => ARMPipelineUsage {
latency: 14,
uses_vfp_neon: true,
issue_cycles: 14,
..ARMPipelineUsage::simple_alu()
},
_ => ARMPipelineUsage::simple_alu(),
}
}
}
pub mod cpsr_flags {
pub const N: u32 = 1 << 31;
pub const Z: u32 = 1 << 30;
pub const C: u32 = 1 << 29;
pub const V: u32 = 1 << 28;
pub const Q: u32 = 1 << 27;
pub const J: u32 = 1 << 24;
pub const GE: u32 = 0xF << 16;
pub const E: u32 = 1 << 9;
pub const A: u32 = 1 << 8;
pub const I: u32 = 1 << 7;
pub const F: u32 = 1 << 6;
pub const T: u32 = 1 << 5;
pub const M: u32 = 0x1F;
pub fn evaluate_condition(cond: ARMDeepConditionCode, cpsr: u32) -> bool {
let n = (cpsr & N) != 0;
let z = (cpsr & Z) != 0;
let c = (cpsr & C) != 0;
let v = (cpsr & V) != 0;
match cond {
ARMDeepConditionCode::EQ => z,
ARMDeepConditionCode::NE => !z,
ARMDeepConditionCode::CS | ARMDeepConditionCode::HS => c,
ARMDeepConditionCode::CC | ARMDeepConditionCode::LO => !c,
ARMDeepConditionCode::MI => n,
ARMDeepConditionCode::PL => !n,
ARMDeepConditionCode::VS => v,
ARMDeepConditionCode::VC => !v,
ARMDeepConditionCode::HI => c && !z,
ARMDeepConditionCode::LS => !c || z,
ARMDeepConditionCode::GE => n == v,
ARMDeepConditionCode::LT => n != v,
ARMDeepConditionCode::GT => !z && (n == v),
ARMDeepConditionCode::LE => z || (n != v),
ARMDeepConditionCode::AL => true,
ARMDeepConditionCode::NV => false,
}
}
pub fn update_nzcv(result: u32, carry_out: bool, overflow: bool) -> u32 {
let mut flags = 0u32;
if (result as i32) < 0 {
flags |= N;
}
if result == 0 {
flags |= Z;
}
if carry_out {
flags |= C;
}
if overflow {
flags |= V;
}
flags
}
}
#[derive(Debug, Clone)]
pub struct ARMSFrameLayout {
pub lr_offset: i64,
pub callee_saved_start: i64,
pub locals_offset: i64,
pub outgoing_args_offset: i64,
pub frame_size: i64,
}
impl ARMSFrameLayout {
pub fn compute(saved_regs: &[u16], local_size: i64, outgoing_args: i64) -> Self {
let push_size = ARM_DEEP_PUSH_SIZE;
let fixed = push_size * 2;
let callee_size = saved_regs.len() as i64 * push_size;
let lr_offset = -push_size;
let callee_saved_start = -(fixed + callee_size);
let locals_offset = callee_saved_start - local_size;
let outgoing_args_offset = locals_offset - outgoing_args;
let frame_size = (-outgoing_args_offset).max(0);
ARMSFrameLayout {
lr_offset,
callee_saved_start,
locals_offset,
outgoing_args_offset,
frame_size,
}
}
pub fn callee_saved_sp_offset(&self, reg_index: usize) -> i64 {
let push_size = ARM_DEEP_PUSH_SIZE;
self.callee_saved_start + (reg_index as i64 * push_size)
}
}
#[derive(Debug, Clone)]
pub struct ARMArchFeatures {
pub has_dsp: bool,
pub has_simd: bool,
pub has_thumb2: bool,
pub has_exclusive: bool,
pub has_hw_divide: bool,
pub has_vfp: bool,
pub has_vfp_d32: bool,
pub has_neon: bool,
pub has_unaligned_access: bool,
pub has_security_ext: bool,
pub has_virtualization: bool,
pub has_setend: bool,
}
impl ARMArchFeatures {
pub fn armv4() -> Self {
ARMArchFeatures {
has_dsp: false,
has_simd: false,
has_thumb2: false,
has_exclusive: false,
has_hw_divide: false,
has_vfp: false,
has_vfp_d32: false,
has_neon: false,
has_unaligned_access: false,
has_security_ext: false,
has_virtualization: false,
has_setend: false,
}
}
pub fn armv5te() -> Self {
ARMArchFeatures {
has_dsp: true,
..Self::armv4()
}
}
pub fn armv6() -> Self {
ARMArchFeatures {
has_dsp: true,
has_simd: true,
has_exclusive: false,
has_unaligned_access: true,
..Self::armv4()
}
}
pub fn armv6t2() -> Self {
ARMArchFeatures {
has_thumb2: true,
..Self::armv6()
}
}
pub fn armv7a() -> Self {
ARMArchFeatures {
has_dsp: true,
has_simd: true,
has_thumb2: true,
has_exclusive: true,
has_hw_divide: true,
has_vfp: true,
has_vfp_d32: true,
has_neon: true,
has_unaligned_access: true,
has_security_ext: true,
has_virtualization: true,
has_setend: true,
}
}
pub fn armv7m() -> Self {
ARMArchFeatures {
has_dsp: true,
has_simd: true,
has_thumb2: true,
has_exclusive: true,
has_hw_divide: true,
has_vfp: false,
has_vfp_d32: false,
has_neon: false,
has_unaligned_access: true,
has_security_ext: false,
has_virtualization: false,
has_setend: false,
}
}
pub fn can_use_parallel_addsub(&self) -> bool {
self.has_simd
}
pub fn can_use_thumb2(&self) -> bool {
self.has_thumb2
}
pub fn can_use_hw_divide(&self) -> bool {
self.has_hw_divide
}
pub fn can_use_vfp(&self) -> bool {
self.has_vfp
}
pub fn can_use_neon(&self) -> bool {
self.has_neon
}
pub fn vfp_dreg_count(&self) -> u8 {
if self.has_vfp_d32 {
32
} else if self.has_vfp {
16
} else {
0
}
}
}
impl Default for ARMArchFeatures {
fn default() -> Self {
Self::armv7a()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand};
#[test]
fn test_opcode_count() {
let count = ARMDeepOpcode::count();
assert!(count >= 120, "Expected at least 120 opcodes, got {}", count);
assert!(count <= 1000, "Opcode count unexpectedly large: {}", count);
}
#[test]
fn test_mnemonic_consistency() {
let count = ARMDeepOpcode::count();
for i in 0..count {
let op: ARMDeepOpcode = unsafe { std::mem::transmute(i as u16) };
let mnem = op.mnemonic();
assert!(!mnem.is_empty(), "Opcode {:?} has empty mnemonic", op);
}
}
#[test]
fn test_add_mnemonic() {
assert_eq!(ARMDeepOpcode::ADD.mnemonic(), "add");
}
#[test]
fn test_sub_mnemonic() {
assert_eq!(ARMDeepOpcode::SUB.mnemonic(), "sub");
}
#[test]
fn test_branch_mnemonics() {
assert_eq!(ARMDeepOpcode::B.mnemonic(), "b");
assert_eq!(ARMDeepOpcode::BL.mnemonic(), "bl");
assert_eq!(ARMDeepOpcode::BX.mnemonic(), "bx");
assert_eq!(ARMDeepOpcode::BLX.mnemonic(), "blx");
}
#[test]
fn test_load_store_mnemonics() {
assert_eq!(ARMDeepOpcode::LDR.mnemonic(), "ldr");
assert_eq!(ARMDeepOpcode::STR.mnemonic(), "str");
assert_eq!(ARMDeepOpcode::LDRB.mnemonic(), "ldrb");
assert_eq!(ARMDeepOpcode::STRB.mnemonic(), "strb");
}
#[test]
fn test_push_pop_mnemonics() {
assert_eq!(ARMDeepOpcode::PUSH.mnemonic(), "push");
assert_eq!(ARMDeepOpcode::POP.mnemonic(), "pop");
}
#[test]
fn test_vfp_mnemonics() {
assert_eq!(ARMDeepOpcode::VADD_F32.mnemonic(), "vadd.f32");
assert_eq!(ARMDeepOpcode::VSUB_F32.mnemonic(), "vsub.f32");
assert_eq!(ARMDeepOpcode::VMUL_F32.mnemonic(), "vmul.f32");
assert_eq!(ARMDeepOpcode::VDIV_F32.mnemonic(), "vdiv.f32");
}
#[test]
fn test_neon_mnemonics() {
assert_eq!(ARMDeepOpcode::VADD_I8.mnemonic(), "vadd.i8");
assert_eq!(ARMDeepOpcode::VMUL_I16.mnemonic(), "vmul.i16");
assert_eq!(ARMDeepOpcode::VPADD_I32.mnemonic(), "vpadd.i32");
}
#[test]
fn test_thumb_mnemonics() {
assert_eq!(ARMDeepOpcode::T_ADD_LOW.mnemonic(), "add");
assert_eq!(ARMDeepOpcode::T_B.mnemonic(), "b");
assert_eq!(ARMDeepOpcode::T_PUSH.mnemonic(), "push");
}
#[test]
fn test_thumb2_mnemonics() {
assert_eq!(ARMDeepOpcode::T2_ADD.mnemonic(), "add.w");
assert_eq!(ARMDeepOpcode::T2_SUB.mnemonic(), "sub.w");
assert_eq!(ARMDeepOpcode::T2_LDR.mnemonic(), "ldr.w");
}
#[test]
fn test_invalid_mnemonic() {
assert_eq!(ARMDeepOpcode::INVALID.mnemonic(), "???");
}
#[test]
fn test_condition_code_count() {
assert_eq!(ARMDeepConditionCode::ALL.len(), 16);
}
#[test]
fn test_condition_code_suffix() {
assert_eq!(ARMDeepConditionCode::EQ.suffix(), "eq");
assert_eq!(ARMDeepConditionCode::NE.suffix(), "ne");
assert_eq!(ARMDeepConditionCode::CS.suffix(), "cs");
assert_eq!(ARMDeepConditionCode::CC.suffix(), "cc");
assert_eq!(ARMDeepConditionCode::MI.suffix(), "mi");
assert_eq!(ARMDeepConditionCode::PL.suffix(), "pl");
assert_eq!(ARMDeepConditionCode::VS.suffix(), "vs");
assert_eq!(ARMDeepConditionCode::VC.suffix(), "vc");
assert_eq!(ARMDeepConditionCode::HI.suffix(), "hi");
assert_eq!(ARMDeepConditionCode::LS.suffix(), "ls");
assert_eq!(ARMDeepConditionCode::GE.suffix(), "ge");
assert_eq!(ARMDeepConditionCode::LT.suffix(), "lt");
assert_eq!(ARMDeepConditionCode::GT.suffix(), "gt");
assert_eq!(ARMDeepConditionCode::LE.suffix(), "le");
assert_eq!(ARMDeepConditionCode::AL.suffix(), "");
assert_eq!(ARMDeepConditionCode::NV.suffix(), "nv");
}
#[test]
fn test_condition_code_aliases() {
assert_eq!(ARMDeepConditionCode::CS, ARMDeepConditionCode::HS);
assert_eq!(ARMDeepConditionCode::CC, ARMDeepConditionCode::LO);
}
#[test]
fn test_condition_code_invert() {
assert_eq!(ARMDeepConditionCode::EQ.invert(), ARMDeepConditionCode::NE);
assert_eq!(ARMDeepConditionCode::NE.invert(), ARMDeepConditionCode::EQ);
assert_eq!(ARMDeepConditionCode::CS.invert(), ARMDeepConditionCode::CC);
assert_eq!(ARMDeepConditionCode::HI.invert(), ARMDeepConditionCode::LS);
assert_eq!(ARMDeepConditionCode::GE.invert(), ARMDeepConditionCode::LT);
assert_eq!(ARMDeepConditionCode::GT.invert(), ARMDeepConditionCode::LE);
assert_eq!(ARMDeepConditionCode::AL.invert(), ARMDeepConditionCode::NV);
}
#[test]
fn test_condition_code_double_invert() {
for cond in &ARMDeepConditionCode::ALL {
assert_eq!(cond.invert().invert(), *cond);
}
}
#[test]
fn test_condition_code_from_bits() {
assert_eq!(
ARMDeepConditionCode::from_bits(0),
Some(ARMDeepConditionCode::EQ)
);
assert_eq!(
ARMDeepConditionCode::from_bits(14),
Some(ARMDeepConditionCode::AL)
);
assert_eq!(
ARMDeepConditionCode::from_bits(15),
Some(ARMDeepConditionCode::NV)
);
assert_eq!(ARMDeepConditionCode::from_bits(16), None);
assert_eq!(ARMDeepConditionCode::from_bits(255), None);
}
#[test]
fn test_condition_code_display() {
assert_eq!(format!("{}", ARMDeepConditionCode::EQ), "eq");
assert_eq!(format!("{}", ARMDeepConditionCode::AL), "");
}
#[test]
fn test_instr_info_new() {
let info = ARMInstrInfoDeep::new();
assert!(
!info.is_empty(),
"Instruction info table should not be empty"
);
}
#[test]
fn test_instr_info_get_add() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::ADD).expect("ADD should exist");
assert_eq!(desc.mnemonic, "add");
assert_eq!(desc.num_operands, 3);
assert!(desc.is_arm);
assert!(!desc.is_thumb);
assert!(desc.is_commutative);
}
#[test]
fn test_instr_info_get_branch() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::B).expect("B should exist");
assert!(desc.is_terminator);
assert!(desc.is_branch);
assert!(!desc.is_call);
assert!(!desc.is_return);
}
#[test]
fn test_instr_info_get_bl_call() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::BL).expect("BL should exist");
assert!(desc.is_terminator);
assert!(desc.is_branch);
assert!(desc.is_call);
assert!(!desc.is_return);
assert!(desc.implicit_defs.contains(&LR_ARM32));
}
#[test]
fn test_instr_info_get_bx_return() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::BX).expect("BX should exist");
assert!(desc.is_terminator);
assert!(desc.is_branch);
assert!(desc.is_return);
}
#[test]
fn test_instr_info_get_cmp() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::CMP).expect("CMP should exist");
assert!(desc.is_compare);
assert!(!desc.is_terminator);
assert!(desc.implicit_defs.contains(&CPSR));
}
#[test]
fn test_instr_info_load_store() {
let info = ARMInstrInfoDeep::new();
let ldr = info.get(ARMDeepOpcode::LDR).expect("LDR should exist");
assert!(ldr.may_load);
assert!(!ldr.may_store);
let str_ = info.get(ARMDeepOpcode::STR).expect("STR should exist");
assert!(!str_.may_load);
assert!(str_.may_store);
}
#[test]
fn test_instr_info_push_pop() {
let info = ARMInstrInfoDeep::new();
let push = info.get(ARMDeepOpcode::PUSH).expect("PUSH should exist");
assert!(!push.may_load);
assert!(push.may_store);
assert!(push.implicit_defs.contains(&SP_ARM32));
let pop = info.get(ARMDeepOpcode::POP).expect("POP should exist");
assert!(pop.may_load);
assert!(!pop.may_store);
assert!(pop.implicit_defs.contains(&SP_ARM32));
}
#[test]
fn test_instr_info_find_by_mnemonic() {
let info = ARMInstrInfoDeep::new();
assert_eq!(info.find_by_mnemonic("add"), Some(ARMDeepOpcode::ADD));
assert_eq!(
info.find_by_mnemonic("vadd.f32"),
Some(ARMDeepOpcode::VADD_F32)
);
assert_eq!(info.find_by_mnemonic("nonexistent"), None);
}
#[test]
fn test_instr_info_is_terminator() {
let info = ARMInstrInfoDeep::new();
assert!(info.is_terminator(ARMDeepOpcode::B));
assert!(info.is_terminator(ARMDeepOpcode::BL));
assert!(!info.is_terminator(ARMDeepOpcode::ADD));
}
#[test]
fn test_instr_info_get_arm_opcodes() {
let info = ARMInstrInfoDeep::new();
let arm_ops = info.get_arm_opcodes();
assert!(!arm_ops.is_empty(), "Should have ARM opcodes");
assert!(arm_ops.contains(&ARMDeepOpcode::ADD));
assert!(arm_ops.contains(&ARMDeepOpcode::LDR));
}
#[test]
fn test_instr_info_get_thumb_opcodes() {
let info = ARMInstrInfoDeep::new();
let thumb_ops = info.get_thumb_opcodes();
assert!(!thumb_ops.is_empty(), "Should have Thumb opcodes");
assert!(thumb_ops.contains(&ARMDeepOpcode::T_PUSH));
assert!(thumb_ops.contains(&ARMDeepOpcode::T_POP));
}
#[test]
fn test_instr_info_get_thumb2_opcodes() {
let info = ARMInstrInfoDeep::new();
let t2_ops = info.get_thumb2_opcodes();
assert!(!t2_ops.is_empty(), "Should have Thumb-2 opcodes");
assert!(t2_ops.contains(&ARMDeepOpcode::T2_ADD));
assert!(t2_ops.contains(&ARMDeepOpcode::T2_LDR));
}
#[test]
fn test_instr_info_get_vfp_opcodes() {
let info = ARMInstrInfoDeep::new();
let vfp_ops = info.get_vfp_opcodes();
assert!(!vfp_ops.is_empty(), "Should have VFP opcodes");
assert!(vfp_ops.contains(&ARMDeepOpcode::VADD_F32));
}
#[test]
fn test_instr_info_get_neon_opcodes() {
let info = ARMInstrInfoDeep::new();
let neon_ops = info.get_neon_opcodes();
assert!(!neon_ops.is_empty(), "Should have NEON opcodes");
assert!(neon_ops.contains(&ARMDeepOpcode::VADD_I8));
}
#[test]
fn test_instr_info_commutative() {
let info = ARMInstrInfoDeep::new();
assert!(info.get(ARMDeepOpcode::ADD).unwrap().is_commutative);
assert!(info.get(ARMDeepOpcode::MUL).unwrap().is_commutative);
assert!(info.get(ARMDeepOpcode::AND).unwrap().is_commutative);
assert!(!info.get(ARMDeepOpcode::SUB).unwrap().is_commutative);
assert!(!info.get(ARMDeepOpcode::SBC).unwrap().is_commutative);
}
#[test]
fn test_instr_info_default() {
let info = ARMInstrInfoDeep::default();
assert!(!info.is_empty());
}
#[test]
fn test_get_asm_name_gprs() {
assert_eq!(ARMRegisterInfoDeep::get_asm_name(R0), "r0");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(R4), "r4");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(R11), "r11");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(R12), "r12");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(SP_ARM32), "sp");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(LR_ARM32), "lr");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(PC_ARM32), "pc");
}
#[test]
fn test_get_asm_name_dprs() {
assert_eq!(ARMRegisterInfoDeep::get_asm_name(D0_ARM32), "d0");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(D15_ARM32), "d15");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(D31_ARM32), "d31");
}
#[test]
fn test_get_asm_name_sprs() {
assert_eq!(ARMRegisterInfoDeep::get_asm_name(S0_ARM32), "s0");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(S15_ARM32), "s15");
assert_eq!(ARMRegisterInfoDeep::get_asm_name(S31_ARM32), "s31");
}
#[test]
fn test_get_asm_name_cpsr() {
assert_eq!(ARMRegisterInfoDeep::get_asm_name(CPSR), "cpsr");
}
#[test]
fn test_gpr_name() {
assert_eq!(ARMRegisterInfoDeep::gpr_name(0), "r0");
assert_eq!(ARMRegisterInfoDeep::gpr_name(13), "sp");
assert_eq!(ARMRegisterInfoDeep::gpr_name(14), "lr");
assert_eq!(ARMRegisterInfoDeep::gpr_name(15), "pc");
}
#[test]
fn test_dpr_name() {
assert_eq!(ARMRegisterInfoDeep::dpr_name(0), "d0");
assert_eq!(ARMRegisterInfoDeep::dpr_name(31), "d31");
}
#[test]
fn test_spr_name() {
assert_eq!(ARMRegisterInfoDeep::spr_name(0), "s0");
assert_eq!(ARMRegisterInfoDeep::spr_name(31), "s31");
}
#[test]
fn test_is_callee_saved() {
assert!(ARMRegisterInfoDeep::is_callee_saved(R4));
assert!(ARMRegisterInfoDeep::is_callee_saved(R11));
assert!(!ARMRegisterInfoDeep::is_callee_saved(R0));
assert!(!ARMRegisterInfoDeep::is_callee_saved(R12));
}
#[test]
fn test_is_caller_saved() {
assert!(ARMRegisterInfoDeep::is_caller_saved(R0));
assert!(ARMRegisterInfoDeep::is_caller_saved(R3));
assert!(ARMRegisterInfoDeep::is_caller_saved(R12));
assert!(!ARMRegisterInfoDeep::is_caller_saved(R4));
}
#[test]
fn test_get_callee_saved_gprs() {
let saved = ARMRegisterInfoDeep::get_callee_saved_gprs();
assert_eq!(saved.len(), 8);
assert_eq!(saved[0], R4);
assert_eq!(saved[7], R11);
}
#[test]
fn test_get_caller_saved_gprs() {
let caller = ARMRegisterInfoDeep::get_caller_saved_gprs();
assert_eq!(caller.len(), 5);
assert!(caller.contains(&R0));
assert!(caller.contains(&R12));
}
#[test]
fn test_get_arg_regs() {
let args = ARMRegisterInfoDeep::get_arg_regs();
assert_eq!(args, vec![R0, R1, R2, R3]);
}
#[test]
fn test_get_return_regs() {
let ret = ARMRegisterInfoDeep::get_return_regs();
assert_eq!(ret, vec![R0, R1]);
}
#[test]
fn test_get_allocatable_gprs() {
let gprs = ARMRegisterInfoDeep::get_allocatable_gprs();
assert_eq!(gprs.len(), 13);
assert!(!gprs.contains(&SP_ARM32));
assert!(!gprs.contains(&LR_ARM32));
assert!(!gprs.contains(&PC_ARM32));
}
#[test]
fn test_is_reserved() {
assert!(ARMRegisterInfoDeep::is_reserved(SP_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(LR_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(PC_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(CPSR));
assert!(!ARMRegisterInfoDeep::is_reserved(R0));
assert!(!ARMRegisterInfoDeep::is_reserved(R12));
}
#[test]
fn test_get_reg_width() {
assert_eq!(ARMRegisterInfoDeep::get_reg_width(R0), 32);
assert_eq!(ARMRegisterInfoDeep::get_reg_width(CPSR), 32);
assert_eq!(ARMRegisterInfoDeep::get_reg_width(D0_ARM32), 64);
assert_eq!(ARMRegisterInfoDeep::get_reg_width(S0_ARM32), 32);
}
#[test]
fn test_get_dwarf_num() {
assert_eq!(ARMRegisterInfoDeep::get_dwarf_num(R0), Some(0));
assert_eq!(ARMRegisterInfoDeep::get_dwarf_num(R4), Some(4));
assert_eq!(ARMRegisterInfoDeep::get_dwarf_num(R15), Some(15));
assert_eq!(ARMRegisterInfoDeep::get_dwarf_num(CPSR), None);
}
#[test]
fn test_is_gpr_dpr_spr() {
assert!(ARMRegisterInfoDeep::is_gpr(R0));
assert!(ARMRegisterInfoDeep::is_gpr(R15));
assert!(!ARMRegisterInfoDeep::is_gpr(D0_ARM32));
assert!(ARMRegisterInfoDeep::is_dpr(D0_ARM32));
assert!(ARMRegisterInfoDeep::is_dpr(D31_ARM32));
assert!(!ARMRegisterInfoDeep::is_dpr(R0));
assert!(ARMRegisterInfoDeep::is_spr(S0_ARM32));
assert!(ARMRegisterInfoDeep::is_spr(S31_ARM32));
assert!(!ARMRegisterInfoDeep::is_spr(D0_ARM32));
}
#[test]
fn test_spr_to_dpr() {
assert_eq!(ARMRegisterInfoDeep::spr_to_dpr(S0_ARM32), D0_ARM32);
assert_eq!(ARMRegisterInfoDeep::spr_to_dpr(S1_ARM32), D0_ARM32);
assert_eq!(ARMRegisterInfoDeep::spr_to_dpr(S2_ARM32), D1_ARM32);
assert_eq!(ARMRegisterInfoDeep::spr_to_dpr(S30_ARM32), D15_ARM32);
assert_eq!(ARMRegisterInfoDeep::spr_to_dpr(S31_ARM32), D15_ARM32);
}
#[test]
fn test_frame_info_new() {
let info = ARMDeepFrameInfo::new();
assert_eq!(info.frame_size, 0);
assert!(info.has_frame_pointer);
assert!(!info.has_calls);
assert_eq!(info.fixed_frame_size, 8); assert_eq!(info.saved_fp_offset, -8);
assert_eq!(info.saved_lr_offset, -4);
assert!(info.saved_regs.is_empty());
}
#[test]
fn test_frame_info_default() {
let info = ARMDeepFrameInfo::default();
assert_eq!(info.frame_size, 0);
assert!(info.saved_regs.is_empty());
}
#[test]
fn test_frame_info_total_frame_size() {
let mut info = ARMDeepFrameInfo::new();
info.frame_size = 32;
assert_eq!(info.total_frame_size(), 32);
}
#[test]
fn test_frame_info_local_start_offset() {
let mut info = ARMDeepFrameInfo::new();
assert_eq!(info.local_start_offset(), -8);
info.callee_saved_size = 16; assert_eq!(info.local_start_offset(), -24);
}
#[test]
fn test_frame_lowering_new() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
assert_eq!(fl.call_conv, ArmCallingConvention::AAPCS);
}
#[test]
fn test_emit_prologue_basic() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let info = ARMDeepFrameInfo::new();
let instrs = fl.emit_prologue(&info);
assert!(
instrs.len() >= 2,
"Expected at least 2 prologue instructions"
);
assert_eq!(instrs[0].opcode, ARMDeepOpcode::PUSH as u32);
assert_eq!(instrs[1].opcode, ARMDeepOpcode::MOV as u32);
}
#[test]
fn test_emit_prologue_with_frame() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut info = ARMDeepFrameInfo::new();
info.frame_size = 64;
info.saved_regs = vec![R4, R5, R6];
let instrs = fl.emit_prologue(&info);
assert_eq!(instrs.len(), 4);
assert_eq!(instrs[0].opcode, ARMDeepOpcode::PUSH as u32); assert_eq!(instrs[1].opcode, ARMDeepOpcode::MOV as u32); assert_eq!(instrs[2].opcode, ARMDeepOpcode::SUB as u32); assert_eq!(instrs[3].opcode, ARMDeepOpcode::PUSH as u32); }
#[test]
fn test_emit_epilogue_basic() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let info = ARMDeepFrameInfo::new();
let instrs = fl.emit_epilogue(&info);
assert_eq!(instrs.len(), 2);
assert_eq!(instrs[0].opcode, ARMDeepOpcode::MOV as u32); assert_eq!(instrs[1].opcode, ARMDeepOpcode::POP as u32); }
#[test]
fn test_emit_epilogue_with_callee_saved() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut info = ARMDeepFrameInfo::new();
info.saved_regs = vec![R4, R5, R6];
let instrs = fl.emit_epilogue(&info);
assert_eq!(instrs.len(), 3);
assert_eq!(instrs[0].opcode, ARMDeepOpcode::POP as u32);
assert_eq!(instrs[1].opcode, ARMDeepOpcode::MOV as u32);
assert_eq!(instrs[2].opcode, ARMDeepOpcode::POP as u32);
}
#[test]
fn test_build_frame_info_empty() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mf = MachineFunction::new("test_empty");
let info = fl.build_frame_info(&mf);
assert_eq!(info.frame_size, 0);
assert!(!info.has_calls);
assert!(info.saved_regs.is_empty());
}
#[test]
fn test_build_frame_info_with_call() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut mf = MachineFunction::new("test_call");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
bb.instructions
.push(MachineInstr::new(ARMDeepOpcode::BL as u32));
mf.push_block(bb);
let info = fl.build_frame_info(&mf);
assert!(info.has_calls);
}
#[test]
fn test_build_frame_info_with_sub_sp() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut mf = MachineFunction::new("test_stack");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::SUB as u32);
mi.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
mi.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
mi.push_imm(48);
bb.instructions.push(mi);
mf.push_block(bb);
let info = fl.build_frame_info(&mf);
assert_eq!(info.frame_size, 48);
}
#[test]
fn test_calling_convention_arg_regs() {
let args = ARMCallingConventionDeep::get_arg_regs();
assert_eq!(args, vec![R0, R1, R2, R3]);
}
#[test]
fn test_calling_convention_return_regs() {
let ret = ARMCallingConventionDeep::get_return_regs();
assert_eq!(ret, vec![R0, R1]);
}
#[test]
fn test_calling_convention_num_int_param_regs() {
assert_eq!(ARMCallingConventionDeep::get_num_int_param_regs(), 4);
}
#[test]
fn test_calling_convention_stack_alignment() {
assert_eq!(ARMCallingConventionDeep::get_stack_alignment(), 8);
}
#[test]
fn test_calling_convention_frame_pointer() {
assert_eq!(
ARMCallingConventionDeep::get_frame_pointer_reg(),
ARM_DEEP_FP
);
}
#[test]
fn test_calling_convention_link_register() {
assert_eq!(
ARMCallingConventionDeep::get_link_register_reg(),
ARM_DEEP_LR
);
}
#[test]
fn test_calling_convention_stack_pointer() {
assert_eq!(
ARMCallingConventionDeep::get_stack_pointer_reg(),
ARM_DEEP_SP
);
}
#[test]
fn test_calling_convention_return_sizes() {
assert_eq!(
ARMCallingConventionDeep::get_return_regs_for_size(0),
vec![]
);
assert_eq!(
ARMCallingConventionDeep::get_return_regs_for_size(4),
vec![R0]
);
assert_eq!(
ARMCallingConventionDeep::get_return_regs_for_size(8),
vec![R0, R1]
);
}
#[test]
fn test_calling_convention_indirect_return() {
assert!(!ARMCallingConventionDeep::needs_indirect_return(4));
assert!(!ARMCallingConventionDeep::needs_indirect_return(8));
assert!(ARMCallingConventionDeep::needs_indirect_return(9));
assert!(ARMCallingConventionDeep::needs_indirect_return(16));
}
#[test]
fn test_calling_convention_call_clobbered() {
let clobbered = ARMCallingConventionDeep::get_call_clobbered_regs();
assert!(clobbered.contains(&R0));
assert!(clobbered.contains(&R3));
assert!(clobbered.contains(&R12));
assert!(clobbered.contains(&CPSR));
assert!(!clobbered.contains(&R4));
}
#[test]
fn test_calling_convention_callee_saved() {
let saved = ARMCallingConventionDeep::get_callee_saved_regs();
assert_eq!(saved.len(), 8);
assert!(saved.contains(&R4));
assert!(saved.contains(&R11));
assert!(!saved.contains(&R0));
}
#[test]
fn test_calling_convention_implicit_uses() {
let uses = ARMCallingConventionDeep::get_implicit_uses_at_call();
assert!(uses.contains(&ARM_DEEP_SP));
assert!(uses.contains(&ARM_DEEP_LR));
}
#[test]
fn test_reg_alloc_new() {
let ra = ARMDeepRegisterAllocator::new();
assert!(ra.assignments.is_empty());
assert_eq!(ra.available.len(), 13); }
#[test]
fn test_reg_alloc_default() {
let ra = ARMDeepRegisterAllocator::default();
assert!(ra.assignments.is_empty());
}
#[test]
fn test_reg_alloc_allocate_simple() {
let mut ra = ARMDeepRegisterAllocator::new();
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let vreg = mf.new_vreg();
let mut mi = MachineInstr::new(ARMDeepOpcode::ADD as u32).with_def(vreg);
mi.push_reg(0); mi.push_imm(42);
bb.instructions.push(mi);
mf.push_block(bb);
ra.allocate(&mut mf);
assert!(!ra.assignments.is_empty());
}
#[test]
fn test_reg_alloc_reset() {
let mut ra = ARMDeepRegisterAllocator::new();
ra.assignments.insert(0, 5);
ra.reset();
assert!(ra.assignments.is_empty());
}
#[test]
fn test_asm_printer_new() {
let printer = ARMDeepAsmPrinter::new();
assert!(printer.output.is_empty());
}
#[test]
fn test_asm_printer_default() {
let printer = ARMDeepAsmPrinter::default();
assert!(printer.output.is_empty());
}
#[test]
fn test_asm_printer_print_empty_function() {
let mut printer = ARMDeepAsmPrinter::new();
let mf = MachineFunction::new("test_empty");
printer.print_function(&mf);
let output = printer.output;
assert!(output.contains(".globl test_empty"));
assert!(output.contains("test_empty:"));
assert!(output.contains(".fnstart"));
assert!(output.contains(".fnend"));
}
#[test]
fn test_asm_printer_print_function_with_instrs() {
let mut printer = ARMDeepAsmPrinter::new();
let mut mf = MachineFunction::new("test_func");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R1 as u32));
mi.operands.push(MachineOperand::PhysReg(R2 as u32));
bb.instructions.push(mi);
let mut bx = MachineInstr::new(ARMDeepOpcode::BX as u32);
bx.operands.push(MachineOperand::PhysReg(LR_ARM32 as u32));
bb.instructions.push(bx);
mf.push_block(bb);
printer.print_function(&mf);
let output = printer.output;
assert!(output.contains("add r0, r1, r2"));
assert!(output.contains("bx lr"));
}
#[test]
fn test_asm_printer_immediates() {
let mut printer = ARMDeepAsmPrinter::new();
let mut mf = MachineFunction::new("test_imm");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.push_imm(42);
bb.instructions.push(mi);
mf.push_block(bb);
printer.print_function(&mf);
let output = printer.output;
assert!(output.contains("mov r0, #42"));
}
#[test]
fn test_asm_printer_labels() {
let mut printer = ARMDeepAsmPrinter::new();
let mut mf = MachineFunction::new("test_labels");
let mut bb = MachineBasicBlock {
name: "loop".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::B as u32);
mi.push_label("loop");
bb.instructions.push(mi);
mf.push_block(bb);
printer.print_function(&mf);
let output = printer.output;
assert!(output.contains(".Lloop:"));
assert!(output.contains("b .Lloop"));
}
#[test]
fn test_format_reg_list() {
let printer = ARMDeepAsmPrinter::new();
let list = printer.format_reg_list(&[R0, R1, R2, R3]);
assert_eq!(list, "{r0, r1, r2, r3}");
}
#[test]
fn test_format_cond_instr() {
let printer = ARMDeepAsmPrinter::new();
assert_eq!(
printer.format_cond_instr("add", ARMDeepConditionCode::EQ),
"addeq"
);
assert_eq!(
printer.format_cond_instr("add", ARMDeepConditionCode::AL),
"add"
);
}
#[test]
fn test_peephole_new() {
let opt = ARMDeepPeepholeOptimizer::new();
let _ = opt;
}
#[test]
fn test_peephole_remove_redundant_mov() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "test".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi);
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 1);
assert!(bb.instructions.is_empty());
}
#[test]
fn test_peephole_remove_double_mov() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "test".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi1 = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi1.operands.push(MachineOperand::PhysReg(R0 as u32));
mi1.operands.push(MachineOperand::PhysReg(R1 as u32));
bb.instructions.push(mi1);
let mut mi2 = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi2.operands.push(MachineOperand::PhysReg(R0 as u32));
mi2.operands.push(MachineOperand::PhysReg(R2 as u32));
bb.instructions.push(mi2);
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 1);
assert_eq!(bb.instructions.len(), 1);
}
#[test]
fn test_peephole_remove_push_pop_pair() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "test".to_string(),
instructions: vec![],
successors: vec![],
};
let mut push = MachineInstr::new(ARMDeepOpcode::PUSH as u32);
push.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(push);
let mut pop = MachineInstr::new(ARMDeepOpcode::POP as u32);
pop.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(pop);
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 2);
assert!(bb.instructions.is_empty());
}
#[test]
fn test_peephole_optimize_function() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi);
let mut add = MachineInstr::new(ARMDeepOpcode::ADD as u32);
add.operands.push(MachineOperand::PhysReg(R0 as u32));
add.operands.push(MachineOperand::PhysReg(R1 as u32));
add.operands.push(MachineOperand::PhysReg(R2 as u32));
bb.instructions.push(add);
mf.push_block(bb);
let removed = opt.optimize_function(&mut mf);
assert_eq!(removed, 1);
}
#[test]
fn test_isel_new() {
let isel = ARMDeepInstructionSelector::new();
assert!(!isel.instr_info.is_empty());
}
#[test]
fn test_isel_map_binary_op() {
let isel = ARMDeepInstructionSelector::new();
assert_eq!(isel.map_binary_op("add"), ARMDeepOpcode::ADD);
assert_eq!(isel.map_binary_op("sub"), ARMDeepOpcode::SUB);
assert_eq!(isel.map_binary_op("mul"), ARMDeepOpcode::MUL);
assert_eq!(isel.map_binary_op("sdiv"), ARMDeepOpcode::SDIV);
assert_eq!(isel.map_binary_op("udiv"), ARMDeepOpcode::UDIV);
assert_eq!(isel.map_binary_op("and"), ARMDeepOpcode::AND);
assert_eq!(isel.map_binary_op("or"), ARMDeepOpcode::ORR);
assert_eq!(isel.map_binary_op("xor"), ARMDeepOpcode::EOR);
assert_eq!(isel.map_binary_op("shl"), ARMDeepOpcode::LSL);
assert_eq!(isel.map_binary_op("lshr"), ARMDeepOpcode::LSR);
assert_eq!(isel.map_binary_op("ashr"), ARMDeepOpcode::ASR);
}
#[test]
fn test_isel_select_simple() {
let isel = ARMDeepInstructionSelector::new();
let mut mf = MachineFunction::new("test_isel");
let ops = vec![
(
ARMDeepOpcode::MOV as u32,
vec![MachineOperand::PhysReg(R0 as u32), MachineOperand::Imm(42)],
),
(
ARMDeepOpcode::ADD as u32,
vec![
MachineOperand::PhysReg(R1 as u32),
MachineOperand::PhysReg(R0 as u32),
MachineOperand::Imm(1),
],
),
];
isel.select_simple(&mut mf, &ops);
assert_eq!(mf.blocks.len(), 1);
assert_eq!(mf.blocks[0].instructions.len(), 2);
assert_eq!(
mf.blocks[0].instructions[0].opcode,
ARMDeepOpcode::MOV as u32
);
assert_eq!(
mf.blocks[0].instructions[1].opcode,
ARMDeepOpcode::ADD as u32
);
}
#[test]
fn test_arm_deep_new() {
let backend = ARMDeep::new();
assert!(!backend.instr_info.is_empty());
assert_eq!(backend.call_conv, ArmCallingConvention::AAPCS);
assert!(!backend.is_thumb);
assert!(!backend.is_thumb2);
}
#[test]
fn test_arm_deep_new_thumb() {
let backend = ARMDeep::new_thumb();
assert!(backend.is_thumb);
assert!(!backend.is_thumb2);
}
#[test]
fn test_arm_deep_new_thumb2() {
let backend = ARMDeep::new_thumb2();
assert!(backend.is_thumb);
assert!(backend.is_thumb2);
}
#[test]
fn test_arm_deep_new_vfp() {
let backend = ARMDeep::new_vfp();
assert_eq!(backend.call_conv, ArmCallingConvention::AAPCS_VFP);
}
#[test]
fn test_arm_deep_default() {
let backend = ARMDeep::default();
assert!(!backend.instr_info.is_empty());
assert!(!backend.is_thumb);
}
#[test]
fn test_arm_deep_instruction_count() {
let backend = ARMDeep::new();
let count = backend.instruction_count();
assert!(count > 50, "Expected >50 instructions, got {}", count);
assert!(count <= 500, "Instruction count unusually large: {}", count);
}
#[test]
fn test_arm_deep_get_target_triple() {
let arm = ARMDeep::new();
assert_eq!(arm.get_target_triple(), "armv7a-none-eabi");
let thumb = ARMDeep::new_thumb();
assert_eq!(thumb.get_target_triple(), "thumbv6m-none-eabi");
let thumb2 = ARMDeep::new_thumb2();
assert_eq!(thumb2.get_target_triple(), "thumbv7m-none-eabi");
}
#[test]
fn test_arm_deep_get_data_layout() {
let backend = ARMDeep::new();
let dl = backend.get_data_layout();
assert!(!dl.is_empty());
assert!(dl.contains("e-m:e-p:32:32"));
}
#[test]
fn test_arm_deep_get_opcode() {
let backend = ARMDeep::new();
assert_eq!(backend.get_opcode("add"), Some(ARMDeepOpcode::ADD));
assert_eq!(backend.get_opcode("nop"), Some(ARMDeepOpcode::NOP));
assert_eq!(backend.get_opcode("nonexistent"), None);
}
#[test]
fn test_arm_deep_is_terminator() {
let backend = ARMDeep::new();
assert!(backend.is_terminator(ARMDeepOpcode::B));
assert!(backend.is_terminator(ARMDeepOpcode::BX));
assert!(!backend.is_terminator(ARMDeepOpcode::ADD));
}
#[test]
fn test_arm_deep_validate_empty() {
let backend = ARMDeep::new();
let mf = MachineFunction::new("test_empty");
let errors = backend.validate(&mf);
assert!(errors.is_empty());
}
#[test]
fn test_arm_deep_validate_invalid_opcode() {
let backend = ARMDeep::new();
let mut mf = MachineFunction::new("test_invalid");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
bb.instructions.push(MachineInstr::new(99999));
mf.push_block(bb);
let errors = backend.validate(&mf);
assert!(!errors.is_empty());
}
#[test]
fn test_arm_deep_compile_function() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("test_compile");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let vreg0 = mf.new_vreg();
let mut add_mi = MachineInstr::new(ARMDeepOpcode::ADD as u32).with_def(vreg0);
add_mi.push_reg(1); add_mi.push_imm(42);
bb.instructions.push(add_mi);
mf.push_block(bb);
let output = backend.compile_function(&mut mf);
assert!(output.contains(".globl test_compile"));
assert!(output.contains("test_compile:"));
assert!(output.contains("add"));
}
#[test]
fn test_arm_deep_compile_with_frame() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("test_with_frame");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut sub_mi = MachineInstr::new(ARMDeepOpcode::SUB as u32);
sub_mi
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub_mi
.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub_mi.push_imm(16);
bb.instructions.push(sub_mi);
let mut add_mi = MachineInstr::new(ARMDeepOpcode::ADD as u32);
add_mi.operands.push(MachineOperand::PhysReg(R0 as u32));
add_mi.operands.push(MachineOperand::PhysReg(R1 as u32));
add_mi.operands.push(MachineOperand::PhysReg(R2 as u32));
bb.instructions.push(add_mi);
mf.push_block(bb);
let output = backend.compile_with_frame(&mut mf);
assert!(output.contains("push"));
assert!(output.contains("mov fp, sp"));
assert!(output.contains("add r0, r1, r2"));
}
#[test]
fn test_arm_deep_display() {
let backend = ARMDeep::new();
let display = format!("{}", backend);
assert!(display.contains("ARMDeep"));
assert!(display.contains("ARM32"));
}
#[test]
fn test_arm_deep_debug() {
let backend = ARMDeep::new();
let debug = format!("{:?}", backend);
assert!(debug.contains("ARMDeep"));
assert!(debug.contains("instruction_count"));
}
#[test]
fn test_parse_reglist_mask_all() {
let regs = parse_reglist_mask(0xFFFF, true);
assert_eq!(regs.len(), 16);
assert_eq!(regs[0], R0);
assert_eq!(regs[15], R15);
}
#[test]
fn test_parse_reglist_mask_selective() {
let regs = parse_reglist_mask(0x000F, true); assert_eq!(regs, vec![R0, R1, R2, R3]);
}
#[test]
fn test_encode_reglist_mask() {
let mask = encode_reglist_mask(&[R0, R1, R2, R3]);
assert_eq!(mask, 0x000F);
let mask = encode_reglist_mask(&[R0, R4, R8, R12]);
assert_eq!(mask, (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12));
}
#[test]
fn test_roundtrip_reglist() {
let original = vec![R0, R4, R8, R12, LR_ARM32];
let mask = encode_reglist_mask(&original);
let parsed = parse_reglist_mask(mask, true);
for reg in &original {
assert!(parsed.contains(reg), "Missing reg {:?}", reg);
}
}
#[test]
fn test_format_reglist() {
let formatted = format_reglist(&[R0, R4, R8, R12]);
assert_eq!(formatted, "{r0, r4, r8, r12}");
}
#[test]
fn test_full_pipeline_simple() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("simple_add");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mov = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mov.operands.push(MachineOperand::PhysReg(R0 as u32));
mov.push_imm(10);
bb.instructions.push(mov);
let mut add = MachineInstr::new(ARMDeepOpcode::ADD as u32);
add.operands.push(MachineOperand::PhysReg(R0 as u32));
add.operands.push(MachineOperand::PhysReg(R0 as u32));
add.push_imm(5);
bb.instructions.push(add);
let mut bx = MachineInstr::new(ARMDeepOpcode::BX as u32);
bx.operands.push(MachineOperand::PhysReg(LR_ARM32 as u32));
bb.instructions.push(bx);
mf.push_block(bb);
let output = backend.compile_function(&mut mf);
assert!(output.contains("simple_add"));
assert!(output.contains("mov r0, #10"));
assert!(output.contains("add r0, r0, #5"));
assert!(output.contains("bx lr"));
}
#[test]
fn test_full_pipeline_with_frame() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("stack_user");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut sub = MachineInstr::new(ARMDeepOpcode::SUB as u32);
sub.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
sub.push_imm(32);
bb.instructions.push(sub);
let mut str_ = MachineInstr::new(ARMDeepOpcode::STR as u32);
str_.operands.push(MachineOperand::PhysReg(R0 as u32));
str_.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
str_.push_imm(0);
bb.instructions.push(str_);
let mut ldr = MachineInstr::new(ARMDeepOpcode::LDR as u32);
ldr.operands.push(MachineOperand::PhysReg(R0 as u32));
ldr.operands
.push(MachineOperand::PhysReg(ARM_DEEP_SP as u32));
ldr.push_imm(0);
bb.instructions.push(ldr);
mf.push_block(bb);
let output = backend.compile_with_frame(&mut mf);
assert!(output.contains("push {fp, lr}"));
assert!(output.contains("mov fp, sp"));
assert!(output.contains("sub sp, sp, #32"));
assert!(output.contains("str r0, sp, #0"));
assert!(output.contains("ldr r0, sp, #0"));
}
#[test]
fn test_opcode_count_is_reasonable() {
let count = ARMDeepOpcode::count();
assert!(count >= 120, "Need at least 120 opcodes, have {}", count);
}
#[test]
fn test_all_arm_condition_codes_unique() {
let mut seen = std::collections::HashSet::new();
for cond in &ARMDeepConditionCode::ALL {
let bits = *cond as u8;
assert!(seen.insert(bits), "Duplicate condition code: {:?}", cond);
}
}
#[test]
fn test_all_opcodes_have_unique_mnemonic_for_arm_data_processing() {
let dp_ops = vec![
ARMDeepOpcode::ADD,
ARMDeepOpcode::SUB,
ARMDeepOpcode::ADC,
ARMDeepOpcode::SBC,
ARMDeepOpcode::RSB,
ARMDeepOpcode::RSC,
ARMDeepOpcode::AND,
ARMDeepOpcode::ORR,
ARMDeepOpcode::EOR,
ARMDeepOpcode::BIC,
];
for op in &dp_ops {
let mnem = op.mnemonic();
assert!(!mnem.is_empty());
}
}
#[test]
fn test_vfp_instructions_exist() {
assert_eq!(ARMDeepOpcode::VADD_F32.mnemonic(), "vadd.f32");
assert_eq!(ARMDeepOpcode::VMUL_F64.mnemonic(), "vmul.f64");
}
#[test]
fn test_neon_load_store_exist() {
assert_eq!(ARMDeepOpcode::VLD1_8.mnemonic(), "vld1.8");
assert_eq!(ARMDeepOpcode::VST1_32.mnemonic(), "vst1.32");
assert_eq!(ARMDeepOpcode::VLD2_16.mnemonic(), "vld2.16");
assert_eq!(ARMDeepOpcode::VLD3_32.mnemonic(), "vld3.32");
assert_eq!(ARMDeepOpcode::VLD4_8.mnemonic(), "vld4.8");
assert_eq!(ARMDeepOpcode::VST4_32.mnemonic(), "vst4.32");
}
#[test]
fn test_barrier_instructions() {
assert_eq!(ARMDeepOpcode::DMB.mnemonic(), "dmb");
assert_eq!(ARMDeepOpcode::DSB.mnemonic(), "dsb");
assert_eq!(ARMDeepOpcode::ISB.mnemonic(), "isb");
}
#[test]
fn test_system_instructions() {
assert_eq!(ARMDeepOpcode::SVC.mnemonic(), "svc");
assert_eq!(ARMDeepOpcode::BKPT.mnemonic(), "bkpt");
assert_eq!(ARMDeepOpcode::MRS.mnemonic(), "mrs");
assert_eq!(ARMDeepOpcode::MSR.mnemonic(), "msr");
}
#[test]
fn test_saturating_instructions() {
assert_eq!(ARMDeepOpcode::QADD.mnemonic(), "qadd");
assert_eq!(ARMDeepOpcode::QSUB.mnemonic(), "qsub");
assert_eq!(ARMDeepOpcode::QDADD.mnemonic(), "qdadd");
}
#[test]
fn test_simd_parallel_addsub() {
assert_eq!(ARMDeepOpcode::SADD16.mnemonic(), "sadd16");
assert_eq!(ARMDeepOpcode::UADD8.mnemonic(), "uadd8");
assert_eq!(ARMDeepOpcode::SSUB16.mnemonic(), "ssub16");
assert_eq!(ARMDeepOpcode::USUB8.mnemonic(), "usub8");
}
#[test]
fn test_exclusive_access() {
assert_eq!(ARMDeepOpcode::LDREX.mnemonic(), "ldrex");
assert_eq!(ARMDeepOpcode::STREX.mnemonic(), "strex");
assert_eq!(ARMDeepOpcode::CLREX.mnemonic(), "clrex");
}
#[test]
fn test_thumb2_wide_instructions() {
assert_eq!(ARMDeepOpcode::T2_ADD.mnemonic(), "add.w");
assert_eq!(ARMDeepOpcode::T2_SUB.mnemonic(), "sub.w");
assert_eq!(ARMDeepOpcode::T2_MUL.mnemonic(), "mul.w");
assert_eq!(ARMDeepOpcode::T2_SDIV.mnemonic(), "sdiv.w");
assert_eq!(ARMDeepOpcode::T2_UDIV.mnemonic(), "udiv.w");
}
#[test]
fn test_thumb2_bitfield() {
assert_eq!(ARMDeepOpcode::T2_BFC.mnemonic(), "bfc.w");
assert_eq!(ARMDeepOpcode::T2_BFI.mnemonic(), "bfi.w");
assert_eq!(ARMDeepOpcode::T2_SBFX.mnemonic(), "sbfx.w");
assert_eq!(ARMDeepOpcode::T2_UBFX.mnemonic(), "ubfx.w");
}
#[test]
fn test_neon_interleave_sizes() {
assert_eq!(ARMDeepOpcode::VTRN_8.mnemonic(), "vtrn.8");
assert_eq!(ARMDeepOpcode::VTRN_16.mnemonic(), "vtrn.16");
assert_eq!(ARMDeepOpcode::VTRN_32.mnemonic(), "vtrn.32");
assert_eq!(ARMDeepOpcode::VZIP_8.mnemonic(), "vzip.8");
assert_eq!(ARMDeepOpcode::VUZP_16.mnemonic(), "vuzp.16");
}
#[test]
fn test_neon_comparison_ops() {
assert_eq!(ARMDeepOpcode::VCEQ_I8.mnemonic(), "vceq.i8");
assert_eq!(ARMDeepOpcode::VCGT_S16.mnemonic(), "vcgt.s16");
assert_eq!(ARMDeepOpcode::VCGT_U32.mnemonic(), "vcgt.u32");
}
#[test]
fn test_neon_count_leading() {
assert_eq!(ARMDeepOpcode::VCLS_S8.mnemonic(), "vcls.s8");
assert_eq!(ARMDeepOpcode::VCLZ_I32.mnemonic(), "vclz.i32");
assert_eq!(ARMDeepOpcode::VCNT.mnemonic(), "vcnt.8");
}
#[test]
fn test_neon_bitwise_selectors() {
assert_eq!(ARMDeepOpcode::VBSL.mnemonic(), "vbsl");
assert_eq!(ARMDeepOpcode::VBIT.mnemonic(), "vbit");
assert_eq!(ARMDeepOpcode::VBIF.mnemonic(), "vbif");
}
#[test]
fn test_move_wide_instructions() {
assert_eq!(ARMDeepOpcode::MOVW.mnemonic(), "movw");
assert_eq!(ARMDeepOpcode::MOVT.mnemonic(), "movt");
}
#[test]
fn test_multiply_variants() {
assert_eq!(ARMDeepOpcode::UMULL.mnemonic(), "umull");
assert_eq!(ARMDeepOpcode::SMULL.mnemonic(), "smull");
assert_eq!(ARMDeepOpcode::UMLAL.mnemonic(), "umlal");
assert_eq!(ARMDeepOpcode::SMLAL.mnemonic(), "smlal");
}
#[test]
fn test_neon_reverse() {
assert_eq!(ARMDeepOpcode::VREV64_8.mnemonic(), "vrev64.8");
assert_eq!(ARMDeepOpcode::VREV32_16.mnemonic(), "vrev32.16");
assert_eq!(ARMDeepOpcode::VREV16_8.mnemonic(), "vrev16.8");
}
#[test]
fn test_neon_move_long_narrow() {
assert_eq!(ARMDeepOpcode::VMOVL_S8.mnemonic(), "vmovl.s8");
assert_eq!(ARMDeepOpcode::VMOVL_U16.mnemonic(), "vmovl.u16");
assert_eq!(ARMDeepOpcode::VMOVN_I32.mnemonic(), "vmovn.i32");
}
#[test]
fn test_neon_absolute_negate() {
assert_eq!(ARMDeepOpcode::VABS_S16.mnemonic(), "vabs.s16");
assert_eq!(ARMDeepOpcode::VNEG_S32.mnemonic(), "vneg.s32");
}
#[test]
fn test_neon_min_max_pairwise() {
assert_eq!(ARMDeepOpcode::VPMAX_S8.mnemonic(), "vpmax.s8");
assert_eq!(ARMDeepOpcode::VPMIN_U32.mnemonic(), "vpmin.u32");
assert_eq!(ARMDeepOpcode::VMAX_S16.mnemonic(), "vmax.s16");
assert_eq!(ARMDeepOpcode::VMIN_U8.mnemonic(), "vmin.u8");
}
#[test]
fn test_neon_shift_variants() {
assert_eq!(ARMDeepOpcode::VSHR_S8.mnemonic(), "vshr.s8");
assert_eq!(ARMDeepOpcode::VSHR_U64.mnemonic(), "vshr.u64");
assert_eq!(ARMDeepOpcode::VRSHR_S32.mnemonic(), "vrshr.s32");
assert_eq!(ARMDeepOpcode::VSLI_I64.mnemonic(), "vsli.i64");
assert_eq!(ARMDeepOpcode::VSRI_I16.mnemonic(), "vsri.i16");
}
#[test]
fn test_extend_instructions() {
assert_eq!(ARMDeepOpcode::UXTB.mnemonic(), "uxtb");
assert_eq!(ARMDeepOpcode::UXTH.mnemonic(), "uxth");
assert_eq!(ARMDeepOpcode::SXTB.mnemonic(), "sxtb");
assert_eq!(ARMDeepOpcode::SXTH.mnemonic(), "sxth");
}
#[test]
fn test_reverse_instructions() {
assert_eq!(ARMDeepOpcode::CLZ.mnemonic(), "clz");
assert_eq!(ARMDeepOpcode::RBIT.mnemonic(), "rbit");
assert_eq!(ARMDeepOpcode::REV.mnemonic(), "rev");
assert_eq!(ARMDeepOpcode::REV16.mnemonic(), "rev16");
assert_eq!(ARMDeepOpcode::REVSH.mnemonic(), "revsh");
}
#[test]
fn test_thumb_vs_thumb2_mnemonics_differ() {
assert_eq!(ARMDeepOpcode::T_ADD_LOW.mnemonic(), "add");
assert_eq!(ARMDeepOpcode::T2_ADD.mnemonic(), "add.w");
assert_ne!(
ARMDeepOpcode::T_ADD_LOW.mnemonic(),
ARMDeepOpcode::T2_ADD.mnemonic()
);
}
#[test]
fn test_all_thumb_opcodes_have_mnemonics() {
let info = ARMInstrInfoDeep::new();
for op in info.get_thumb_opcodes() {
let mnem = op.mnemonic();
assert!(!mnem.is_empty(), "Thumb opcode {:?} has empty mnemonic", op);
}
}
#[test]
fn test_all_thumb2_opcodes_have_mnemonics() {
let info = ARMInstrInfoDeep::new();
for op in info.get_thumb2_opcodes() {
let mnem = op.mnemonic();
assert!(
!mnem.is_empty(),
"Thumb-2 opcode {:?} has empty mnemonic",
op
);
}
}
#[test]
fn test_condition_eq_invert_is_ne() {
assert_eq!(ARMDeepConditionCode::EQ.invert(), ARMDeepConditionCode::NE);
}
#[test]
fn test_condition_hi_invert_is_ls() {
assert_eq!(ARMDeepConditionCode::HI.invert(), ARMDeepConditionCode::LS);
}
#[test]
fn test_condition_ge_invert_is_lt() {
assert_eq!(ARMDeepConditionCode::GE.invert(), ARMDeepConditionCode::LT);
}
#[test]
fn test_condition_gt_invert_is_le() {
assert_eq!(ARMDeepConditionCode::GT.invert(), ARMDeepConditionCode::LE);
}
#[test]
fn test_condition_vs_invert_is_vc() {
assert_eq!(ARMDeepConditionCode::VS.invert(), ARMDeepConditionCode::VC);
}
#[test]
fn test_condition_mi_invert_is_pl() {
assert_eq!(ARMDeepConditionCode::MI.invert(), ARMDeepConditionCode::PL);
}
#[test]
fn test_condition_cs_invert_is_cc() {
assert_eq!(ARMDeepConditionCode::CS.invert(), ARMDeepConditionCode::CC);
}
#[test]
fn test_condition_aliases_have_same_suffix() {
assert_eq!(
ARMDeepConditionCode::CS.suffix(),
ARMDeepConditionCode::HS.suffix()
);
assert_eq!(
ARMDeepConditionCode::CC.suffix(),
ARMDeepConditionCode::LO.suffix()
);
}
#[test]
fn test_condition_descriptions_non_empty() {
for cond in &ARMDeepConditionCode::ALL {
let desc = cond.description();
assert!(!desc.is_empty(), "Empty description for {:?}", cond);
}
}
#[test]
fn test_get_asm_name_unknown_reg() {
let name = ARMRegisterInfoDeep::get_asm_name(65000);
assert!(!name.is_empty());
}
#[test]
fn test_gpr_name_out_of_range() {
assert_eq!(ARMRegisterInfoDeep::gpr_name(16), "??");
assert_eq!(ARMRegisterInfoDeep::gpr_name(255), "??");
}
#[test]
fn test_get_gpr_id() {
assert_eq!(ARMRegisterInfoDeep::get_gpr_id(0), R0);
assert_eq!(ARMRegisterInfoDeep::get_gpr_id(15), R15);
assert_eq!(ARMRegisterInfoDeep::get_gpr_id(16), R0); }
#[test]
fn test_spr_to_dpr_all_pairs() {
for i in 0..16u16 {
assert_eq!(
ARMRegisterInfoDeep::spr_to_dpr(S0_ARM32 + 2 * i),
D0_ARM32 + i
);
assert_eq!(
ARMRegisterInfoDeep::spr_to_dpr(S0_ARM32 + 2 * i + 1),
D0_ARM32 + i
);
}
}
#[test]
fn test_is_gpr_special_regs() {
assert!(ARMRegisterInfoDeep::is_gpr(SP_ARM32));
assert!(ARMRegisterInfoDeep::is_gpr(LR_ARM32));
assert!(ARMRegisterInfoDeep::is_gpr(PC_ARM32));
}
#[test]
fn test_is_reserved_all_reserved() {
assert!(ARMRegisterInfoDeep::is_reserved(SP_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(LR_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(PC_ARM32));
assert!(ARMRegisterInfoDeep::is_reserved(CPSR));
}
#[test]
fn test_get_reg_width_zero_for_unknown() {
assert_eq!(ARMRegisterInfoDeep::get_reg_width(65000), 0);
}
#[test]
fn test_dwarf_num_all_gprs() {
for i in 0..16u16 {
assert_eq!(ARMRegisterInfoDeep::get_dwarf_num(R0 + i), Some(i));
}
}
#[test]
fn test_emit_prologue_zero_frame() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut info = ARMDeepFrameInfo::new();
info.frame_size = 0;
info.saved_regs = vec![];
let instrs = fl.emit_prologue(&info);
assert_eq!(instrs.len(), 2);
}
#[test]
fn test_emit_prologue_large_frame() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut info = ARMDeepFrameInfo::new();
info.frame_size = 4096;
let instrs = fl.emit_prologue(&info);
assert!(instrs.len() >= 3);
let sub = &instrs[2];
assert_eq!(sub.opcode, ARMDeepOpcode::SUB as u32);
}
#[test]
fn test_emit_epilogue_no_callee_saved() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let info = ARMDeepFrameInfo::new();
let instrs = fl.emit_epilogue(&info);
assert_eq!(instrs.len(), 2);
assert_eq!(instrs[1].opcode, ARMDeepOpcode::POP as u32);
}
#[test]
fn test_build_frame_info_detects_callee_saved_usage() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands.push(MachineOperand::PhysReg(R4 as u32));
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi);
mf.push_block(bb);
let info = fl.build_frame_info(&mf);
assert!(info.saved_regs.contains(&R4));
}
#[test]
fn test_build_frame_info_ignores_fp_lr_in_saved() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands
.push(MachineOperand::PhysReg(ARM_DEEP_FP as u32));
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi);
mf.push_block(bb);
let info = fl.build_frame_info(&mf);
assert!(!info.saved_regs.contains(&ARM_DEEP_FP));
}
#[test]
fn test_get_return_regs_for_size_exact_4() {
let regs = ARMCallingConventionDeep::get_return_regs_for_size(4);
assert_eq!(regs, vec![R0]);
}
#[test]
fn test_get_return_regs_for_size_exact_8() {
let regs = ARMCallingConventionDeep::get_return_regs_for_size(8);
assert_eq!(regs, vec![R0, R1]);
}
#[test]
fn test_get_return_regs_for_size_1() {
let regs = ARMCallingConventionDeep::get_return_regs_for_size(1);
assert_eq!(regs, vec![R0]);
}
#[test]
fn test_needs_indirect_return_exact_boundary() {
assert!(!ARMCallingConventionDeep::needs_indirect_return(8));
assert!(ARMCallingConventionDeep::needs_indirect_return(9));
}
#[test]
fn test_callee_saved_count() {
assert_eq!(ARMCallingConventionDeep::CALLEE_SAVED.len(), 8);
}
#[test]
fn test_caller_saved_count() {
assert_eq!(ARMCallingConventionDeep::CALLER_SAVED.len(), 5);
}
#[test]
fn test_callee_saved_doesnt_include_r0() {
assert!(!ARMCallingConventionDeep::CALLEE_SAVED.contains(&R0));
assert!(!ARMCallingConventionDeep::CALLEE_SAVED.contains(&R3));
}
#[test]
fn test_caller_saved_doesnt_include_r4() {
assert!(!ARMCallingConventionDeep::CALLER_SAVED.contains(&R4));
}
#[test]
fn test_arg_regs_non_empty() {
assert!(!ARMCallingConventionDeep::get_arg_regs().is_empty());
}
#[test]
fn test_reg_alloc_multiple_defs() {
let mut ra = ARMDeepRegisterAllocator::new();
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
for _ in 0..20 {
let vreg = mf.new_vreg();
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32).with_def(vreg);
mi.push_imm(0);
bb.instructions.push(mi);
}
mf.push_block(bb);
ra.allocate(&mut mf);
assert!(ra.assignments.len() >= 20);
}
#[test]
fn test_reg_alloc_preserves_phys_regs() {
let mut ra = ARMDeepRegisterAllocator::new();
let mut mf = MachineFunction::new("test");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R5 as u32));
mi.operands.push(MachineOperand::PhysReg(R10 as u32));
bb.instructions.push(mi);
mf.push_block(bb);
ra.allocate(&mut mf);
let first_instr = &mf.blocks[0].instructions[0];
assert!(first_instr
.operands
.contains(&MachineOperand::PhysReg(R0 as u32)));
}
#[test]
fn test_asm_printer_empty_blocks() {
let mut printer = ARMDeepAsmPrinter::new();
let mf = MachineFunction::new("empty");
printer.print_function(&mf);
assert!(printer.output.contains("empty:"));
}
#[test]
fn test_asm_printer_multiple_blocks() {
let mut printer = ARMDeepAsmPrinter::new();
let mut mf = MachineFunction::new("multi");
let mut bb1 = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mov = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mov.operands.push(MachineOperand::PhysReg(R0 as u32));
mov.push_imm(1);
bb1.instructions.push(mov);
mf.push_block(bb1);
let mut bb2 = MachineBasicBlock {
name: "loop".to_string(),
instructions: vec![],
successors: vec![],
};
let mut add = MachineInstr::new(ARMDeepOpcode::ADD as u32);
add.operands.push(MachineOperand::PhysReg(R0 as u32));
add.operands.push(MachineOperand::PhysReg(R0 as u32));
add.push_imm(1);
bb2.instructions.push(add);
mf.push_block(bb2);
printer.print_function(&mf);
assert!(printer.output.contains(".Lloop:"));
}
#[test]
fn test_asm_printer_handles_global_operand() {
let mut printer = ARMDeepAsmPrinter::new();
let mut mf = MachineFunction::new("ref_global");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::BL as u32);
mi.operands.push(MachineOperand::Global("printf".into()));
bb.instructions.push(mi);
mf.push_block(bb);
printer.print_function(&mf);
assert!(printer.output.contains("printf"));
}
#[test]
fn test_peephole_empty_block() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "empty".to_string(),
instructions: vec![],
successors: vec![],
};
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 0);
}
#[test]
fn test_peephole_single_instr() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "test".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::ADD as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R1 as u32));
mi.operands.push(MachineOperand::PhysReg(R2 as u32));
bb.instructions.push(mi);
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 0);
assert_eq!(bb.instructions.len(), 1);
}
#[test]
fn test_peephole_does_not_remove_useful_mov() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut bb = MachineBasicBlock {
name: "test".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi.operands.push(MachineOperand::PhysReg(R0 as u32));
mi.operands.push(MachineOperand::PhysReg(R1 as u32));
bb.instructions.push(mi);
let removed = opt.optimize_block(&mut bb);
assert_eq!(removed, 0);
assert_eq!(bb.instructions.len(), 1);
}
#[test]
fn test_arm_deep_instruction_count_consistent() {
let b1 = ARMDeep::new();
let b2 = ARMDeep::new();
assert_eq!(b1.instruction_count(), b2.instruction_count());
}
#[test]
fn test_arm_deep_all_variants_have_valid_triple() {
let arm = ARMDeep::new();
let thumb = ARMDeep::new_thumb();
let thumb2 = ARMDeep::new_thumb2();
let vfp = ARMDeep::new_vfp();
assert!(!arm.get_target_triple().is_empty());
assert!(!thumb.get_target_triple().is_empty());
assert!(!thumb2.get_target_triple().is_empty());
assert_eq!(vfp.get_target_triple(), arm.get_target_triple());
}
#[test]
fn test_arm_deep_validate_accepts_valid_function() {
let backend = ARMDeep::new();
let mut mf = MachineFunction::new("valid");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
bb.instructions
.push(MachineInstr::new(ARMDeepOpcode::NOP as u32));
mf.push_block(bb);
let errors = backend.validate(&mf);
assert!(errors.is_empty());
}
#[test]
fn test_arm_deep_compile_truly_empty() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("empty");
let bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
mf.push_block(bb);
let output = backend.compile_function(&mut mf);
assert!(output.contains("empty:"));
assert!(output.contains(".fnend"));
}
#[test]
fn test_arm_deep_compile_with_register_aliases() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("aliases");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut push = MachineInstr::new(ARMDeepOpcode::PUSH as u32);
push.operands.push(MachineOperand::PhysReg(LR_ARM32 as u32));
bb.instructions.push(push);
mf.push_block(bb);
let output = backend.compile_function(&mut mf);
assert!(output.contains("push"));
assert!(output.contains("lr"));
}
#[test]
fn test_parse_reglist_mask_empty() {
let regs = parse_reglist_mask(0x0000, true);
assert!(regs.is_empty());
}
#[test]
fn test_encode_reglist_mask_empty() {
assert_eq!(encode_reglist_mask(&[]), 0);
}
#[test]
fn test_format_reglist_empty() {
assert_eq!(format_reglist(&[]), "{}");
}
#[test]
fn test_encode_reglist_mask_single() {
assert_eq!(encode_reglist_mask(&[R0]), 1);
assert_eq!(encode_reglist_mask(&[R7]), 1 << 7);
assert_eq!(encode_reglist_mask(&[R15]), 1 << 15);
}
#[test]
fn test_parse_reglist_mask_single() {
assert_eq!(parse_reglist_mask(1, true), vec![R0]);
assert_eq!(parse_reglist_mask(1 << 3, true), vec![R3]);
}
#[test]
fn test_integration_prologue_epilogue_roundtrip() {
let fl = ARMFrameLoweringDeep::new(ArmCallingConvention::AAPCS);
let info = ARMDeepFrameInfo::new();
let prologue = fl.emit_prologue(&info);
let epilogue = fl.emit_epilogue(&info);
let push_count = prologue
.iter()
.filter(|i| i.opcode == ARMDeepOpcode::PUSH as u32)
.count();
let pop_count = epilogue
.iter()
.filter(|i| i.opcode == ARMDeepOpcode::POP as u32)
.count();
assert!(push_count >= 1);
assert!(pop_count >= 1);
}
#[test]
fn test_integration_call_site_analysis() {
let backend = ARMDeep::new();
let mut mf = MachineFunction::new("caller");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
bb.instructions
.push(MachineInstr::new(ARMDeepOpcode::BL as u32));
mf.push_block(bb);
let frame_info = backend.frame_lowering.build_frame_info(&mf);
assert!(frame_info.has_calls);
}
#[test]
fn test_integration_peephole_before_print() {
let mut backend = ARMDeep::new();
let mut mf = MachineFunction::new("redundant");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mov = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mov.operands.push(MachineOperand::PhysReg(R0 as u32));
mov.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mov);
mf.push_block(bb);
let output = backend.compile_function(&mut mf);
assert!(output.contains("redundant:"));
}
#[test]
fn test_opcode_round_trip_via_mnemonic() {
let info = ARMInstrInfoDeep::new();
let count = ARMDeepOpcode::count();
let mut found = 0;
for i in 0..count {
let op: ARMDeepOpcode = unsafe { std::mem::transmute(i as u16) };
let mnem = op.mnemonic();
if let Some(found_op) = info.find_by_mnemonic(mnem) {
found += 1;
}
}
assert!(found > 0, "No opcodes found via mnemonic lookup");
}
#[test]
fn test_no_opcodes_beyond_invalid() {
let count = ARMDeepOpcode::count();
let invalid_idx = ARMDeepOpcode::INVALID as usize;
assert_eq!(count, invalid_idx, "Count should equal INVALID's position");
}
#[test]
fn test_condition_code_all_enum_variants() {
let mut codes = Vec::new();
for bits in 0..16u8 {
if let Some(cond) = ARMDeepConditionCode::from_bits(bits) {
codes.push(cond);
}
}
assert_eq!(codes.len(), 16);
}
#[test]
fn test_vfp_load_store() {
assert_eq!(ARMDeepOpcode::VLDR.mnemonic(), "vldr");
assert_eq!(ARMDeepOpcode::VSTR.mnemonic(), "vstr");
assert_eq!(ARMDeepOpcode::VPUSH.mnemonic(), "vpush");
assert_eq!(ARMDeepOpcode::VPOP.mnemonic(), "vpop");
}
#[test]
fn test_thumb_branch_variants() {
assert_eq!(ARMDeepOpcode::T_B.mnemonic(), "b");
assert_eq!(ARMDeepOpcode::T_BL.mnemonic(), "bl");
assert_eq!(ARMDeepOpcode::T_BX.mnemonic(), "bx");
assert_eq!(ARMDeepOpcode::T_BLX.mnemonic(), "blx");
}
#[test]
fn test_thumb2_branch_variants() {
assert_eq!(ARMDeepOpcode::T2_B.mnemonic(), "b.w");
assert_eq!(ARMDeepOpcode::T2_BL.mnemonic(), "bl");
assert_eq!(ARMDeepOpcode::T2_BX.mnemonic(), "bx");
}
#[test]
fn test_floating_point_negate_abs_sqrt() {
assert_eq!(ARMDeepOpcode::VNEG_F32.mnemonic(), "vneg.f32");
assert_eq!(ARMDeepOpcode::VABS_F64.mnemonic(), "vabs.f64");
assert_eq!(ARMDeepOpcode::VSQRT_F32.mnemonic(), "vsqrt.f32");
}
#[test]
fn test_arm_deep_isel_map_unknown_op() {
let isel = ARMDeepInstructionSelector::new();
assert_eq!(isel.map_binary_op("unknown_op"), ARMDeepOpcode::ADD); }
#[test]
fn test_peephole_optimize_function_empty() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut mf = MachineFunction::new("empty");
let removed = opt.optimize_function(&mut mf);
assert_eq!(removed, 0);
}
#[test]
fn test_peephole_only_redundant_removed() {
let opt = ARMDeepPeepholeOptimizer::new();
let mut mf = MachineFunction::new("mixed");
let mut bb = MachineBasicBlock {
name: "entry".to_string(),
instructions: vec![],
successors: vec![],
};
let mut mi1 = MachineInstr::new(ARMDeepOpcode::MOV as u32);
mi1.operands.push(MachineOperand::PhysReg(R0 as u32));
mi1.operands.push(MachineOperand::PhysReg(R0 as u32));
bb.instructions.push(mi1);
let mut mi2 = MachineInstr::new(ARMDeepOpcode::ADD as u32);
mi2.operands.push(MachineOperand::PhysReg(R1 as u32));
mi2.operands.push(MachineOperand::PhysReg(R2 as u32));
mi2.operands.push(MachineOperand::PhysReg(R3 as u32));
bb.instructions.push(mi2);
mf.push_block(bb);
let removed = opt.optimize_function(&mut mf);
assert_eq!(removed, 1);
assert_eq!(mf.blocks[0].instructions.len(), 1);
}
#[test]
fn test_neon_table_lookup() {
assert_eq!(ARMDeepOpcode::VTBL_1.mnemonic(), "vtbl.8");
assert_eq!(ARMDeepOpcode::VTBL_2.mnemonic(), "vtbl.8");
assert_eq!(ARMDeepOpcode::VTBX_3.mnemonic(), "vtbx.8");
}
#[test]
fn test_vfp_compare_implicit_defs() {
let info = ARMInstrInfoDeep::new();
let desc = info.get(ARMDeepOpcode::VCMP_F32).unwrap();
assert!(desc.implicit_defs.contains(&CPSR));
}
#[test]
fn test_ldm_stm_all_modes() {
assert_eq!(ARMDeepOpcode::LDMIA.mnemonic(), "ldmia");
assert_eq!(ARMDeepOpcode::LDMIB.mnemonic(), "ldmib");
assert_eq!(ARMDeepOpcode::LDMDA.mnemonic(), "ldmda");
assert_eq!(ARMDeepOpcode::LDMDB.mnemonic(), "ldmdb");
assert_eq!(ARMDeepOpcode::STMIA.mnemonic(), "stmia");
assert_eq!(ARMDeepOpcode::STMIB.mnemonic(), "stmib");
assert_eq!(ARMDeepOpcode::STMDA.mnemonic(), "stmda");
assert_eq!(ARMDeepOpcode::STMDB.mnemonic(), "stmdb");
}
#[test]
fn test_arm_deep_vec_capacity_consistent() {
let backend = ARMDeep::new();
let info = &backend.instr_info;
let count = info.len();
assert_eq!(backend.instruction_count(), count);
}
#[test]
fn test_formatted_reglist_roundtrip() {
let regs = vec![R0, R4, R8];
let formatted = format_reglist(®s);
assert!(formatted.starts_with('{'));
assert!(formatted.ends_with('}'));
assert!(formatted.contains("r0"));
assert!(formatted.contains("r4"));
assert!(formatted.contains("r8"));
}
#[test]
fn test_move_register_core_to_vfp() {
assert_eq!(ARMDeepOpcode::VMOV_S_R.mnemonic(), "vmov");
assert_eq!(ARMDeepOpcode::VMOV_R_S.mnemonic(), "vmov");
assert_eq!(ARMDeepOpcode::VMOV_2S.mnemonic(), "vmov");
}
#[test]
fn test_vfp_double_precision_ops() {
assert_eq!(ARMDeepOpcode::VADD_F64.mnemonic(), "vadd.f64");
assert_eq!(ARMDeepOpcode::VSUB_F64.mnemonic(), "vsub.f64");
assert_eq!(ARMDeepOpcode::VMUL_F64.mnemonic(), "vmul.f64");
assert_eq!(ARMDeepOpcode::VDIV_F64.mnemonic(), "vdiv.f64");
}
#[test]
fn test_thumb_load_store_variants() {
assert_eq!(ARMDeepOpcode::T_LDRB.mnemonic(), "ldrb");
assert_eq!(ARMDeepOpcode::T_STRB.mnemonic(), "strb");
assert_eq!(ARMDeepOpcode::T_LDRH.mnemonic(), "ldrh");
assert_eq!(ARMDeepOpcode::T_STRH.mnemonic(), "strh");
assert_eq!(ARMDeepOpcode::T_LDRSB.mnemonic(), "ldrsb");
assert_eq!(ARMDeepOpcode::T_LDRSH.mnemonic(), "ldrsh");
}
#[test]
fn test_thumb_shift_operations() {
assert_eq!(ARMDeepOpcode::T_ASR.mnemonic(), "asrs");
assert_eq!(ARMDeepOpcode::T_LSL.mnemonic(), "lsls");
assert_eq!(ARMDeepOpcode::T_LSR.mnemonic(), "lsrs");
}
#[test]
fn test_thumb_extend_instructions() {
assert_eq!(ARMDeepOpcode::T_SXTB.mnemonic(), "sxtb");
assert_eq!(ARMDeepOpcode::T_SXTH.mnemonic(), "sxth");
assert_eq!(ARMDeepOpcode::T_UXTB.mnemonic(), "uxtb");
assert_eq!(ARMDeepOpcode::T_UXTH.mnemonic(), "uxth");
}
#[test]
fn test_thumb_reverse_instructions() {
assert_eq!(ARMDeepOpcode::T_REV.mnemonic(), "rev");
assert_eq!(ARMDeepOpcode::T_REV16.mnemonic(), "rev16");
assert_eq!(ARMDeepOpcode::T_REVSH.mnemonic(), "revsh");
}
#[test]
fn test_neon_shift_insert() {
assert_eq!(ARMDeepOpcode::VSLI_I32.mnemonic(), "vsli.i32");
assert_eq!(ARMDeepOpcode::VSRI_I64.mnemonic(), "vsri.i64");
}
#[test]
fn test_saturated_signed_unsigned() {
assert_eq!(ARMDeepOpcode::SSAT.mnemonic(), "ssat");
assert_eq!(ARMDeepOpcode::USAT.mnemonic(), "usat");
}
#[test]
fn test_sel_instruction() {
assert_eq!(ARMDeepOpcode::SEL.mnemonic(), "sel");
}
#[test]
fn test_smx_operations() {
assert_eq!(ARMDeepOpcode::SMMLA.mnemonic(), "smmla");
assert_eq!(ARMDeepOpcode::SMMUL.mnemonic(), "smmul");
assert_eq!(ARMDeepOpcode::SMMLS.mnemonic(), "smmls");
}
#[test]
fn test_swap_operations() {
assert_eq!(ARMDeepOpcode::SWP.mnemonic(), "swp");
assert_eq!(ARMDeepOpcode::SWPB.mnemonic(), "swpb");
}
#[test]
fn test_exclusive_doubleword() {
assert_eq!(ARMDeepOpcode::LDREXD.mnemonic(), "ldrexd");
assert_eq!(ARMDeepOpcode::STREXD.mnemonic(), "strexd");
}
#[test]
fn test_hint_instructions() {
assert_eq!(ARMDeepOpcode::NOP.mnemonic(), "nop");
assert_eq!(ARMDeepOpcode::YIELD.mnemonic(), "yield");
assert_eq!(ARMDeepOpcode::WFE.mnemonic(), "wfe");
assert_eq!(ARMDeepOpcode::WFI.mnemonic(), "wfi");
assert_eq!(ARMDeepOpcode::SEV.mnemonic(), "sev");
}
#[test]
fn test_security_instructions() {
assert_eq!(ARMDeepOpcode::SMC.mnemonic(), "smc");
assert_eq!(ARMDeepOpcode::HVC.mnemonic(), "hvc");
}
#[test]
fn test_coprocessor_instructions() {
assert_eq!(ARMDeepOpcode::MCR.mnemonic(), "mcr");
assert_eq!(ARMDeepOpcode::MRC.mnemonic(), "mrc");
assert_eq!(ARMDeepOpcode::MRRC.mnemonic(), "mrrc");
assert_eq!(ARMDeepOpcode::MCRR.mnemonic(), "mcrr");
assert_eq!(ARMDeepOpcode::CDP.mnemonic(), "cdp");
assert_eq!(ARMDeepOpcode::LDC.mnemonic(), "ldc");
}
#[test]
fn test_cache_preload() {
assert_eq!(ARMDeepOpcode::PLD.mnemonic(), "pld");
assert_eq!(ARMDeepOpcode::PLI.mnemonic(), "pli");
}
#[test]
fn test_thumb2_load_store_double() {
assert_eq!(ARMDeepOpcode::T2_LDRD.mnemonic(), "ldrd.w");
assert_eq!(ARMDeepOpcode::T2_STRD.mnemonic(), "strd.w");
}
#[test]
fn test_thumb2_table_branch() {
assert_eq!(ARMDeepOpcode::T2_TBB.mnemonic(), "tbb");
assert_eq!(ARMDeepOpcode::T2_TBH.mnemonic(), "tbh");
}
#[test]
fn test_arm_deep_find_by_mnemonic_thumb() {
let backend = ARMDeep::new();
assert!(backend.get_opcode("nop").is_some());
}
#[test]
fn test_parallel_addsub_saturation() {
assert_eq!(ARMDeepOpcode::QADD16.mnemonic(), "qadd16");
assert_eq!(ARMDeepOpcode::UQSUB16.mnemonic(), "uqsub16");
assert_eq!(ARMDeepOpcode::QADD8.mnemonic(), "qadd8");
assert_eq!(ARMDeepOpcode::UQSUB8.mnemonic(), "uqsub8");
}
#[test]
fn test_sum_absolute_differences() {
assert_eq!(ARMDeepOpcode::USAD8.mnemonic(), "usad8");
assert_eq!(ARMDeepOpcode::USADA8.mnemonic(), "usada8");
}
#[test]
fn test_neon_load_store_all_sizes() {
assert_eq!(ARMDeepOpcode::VLD1_8.mnemonic(), "vld1.8");
assert_eq!(ARMDeepOpcode::VLD1_16.mnemonic(), "vld1.16");
assert_eq!(ARMDeepOpcode::VLD1_32.mnemonic(), "vld1.32");
assert_eq!(ARMDeepOpcode::VLD1_64.mnemonic(), "vld1.64");
assert_eq!(ARMDeepOpcode::VST1_8.mnemonic(), "vst1.8");
assert_eq!(ARMDeepOpcode::VST1_16.mnemonic(), "vst1.16");
assert_eq!(ARMDeepOpcode::VST1_32.mnemonic(), "vst1.32");
assert_eq!(ARMDeepOpcode::VST1_64.mnemonic(), "vst1.64");
}
#[test]
fn test_neon_multielement_loads() {
assert_eq!(ARMDeepOpcode::VLD2_8.mnemonic(), "vld2.8");
assert_eq!(ARMDeepOpcode::VLD2_16.mnemonic(), "vld2.16");
assert_eq!(ARMDeepOpcode::VLD2_32.mnemonic(), "vld2.32");
assert_eq!(ARMDeepOpcode::VLD3_8.mnemonic(), "vld3.8");
assert_eq!(ARMDeepOpcode::VLD3_16.mnemonic(), "vld3.16");
assert_eq!(ARMDeepOpcode::VLD3_32.mnemonic(), "vld3.32");
assert_eq!(ARMDeepOpcode::VLD4_8.mnemonic(), "vld4.8");
assert_eq!(ARMDeepOpcode::VLD4_16.mnemonic(), "vld4.16");
assert_eq!(ARMDeepOpcode::VLD4_32.mnemonic(), "vld4.32");
}
#[test]
fn test_preindexed_load_store_exist() {
assert_eq!(ARMDeepOpcode::LDR_PRE.mnemonic(), "ldr");
assert_eq!(ARMDeepOpcode::LDR_POST.mnemonic(), "ldr");
assert_eq!(ARMDeepOpcode::STR_PRE.mnemonic(), "str");
assert_eq!(ARMDeepOpcode::STR_POST.mnemonic(), "str");
}
#[test]
fn test_bxj_instruction() {
assert_eq!(ARMDeepOpcode::BXJ.mnemonic(), "bxj");
}
#[test]
fn test_setend_instruction() {
assert_eq!(ARMDeepOpcode::SETEND.mnemonic(), "setend");
}
#[test]
fn test_cps_instruction() {
assert_eq!(ARMDeepOpcode::CPS.mnemonic(), "cps");
}
#[test]
fn test_all_floating_point_conversions() {
assert_eq!(ARMDeepOpcode::VCVT_F32_F64.mnemonic(), "vcvt.f32.f64");
assert_eq!(ARMDeepOpcode::VCVT_F64_F32.mnemonic(), "vcvt.f64.f32");
assert_eq!(ARMDeepOpcode::VCVT_S32_F32.mnemonic(), "vcvt.s32.f32");
assert_eq!(ARMDeepOpcode::VCVT_U32_F32.mnemonic(), "vcvt.u32.f32");
assert_eq!(ARMDeepOpcode::VCVT_F32_S32.mnemonic(), "vcvt.f32.s32");
assert_eq!(ARMDeepOpcode::VCVT_F32_U32.mnemonic(), "vcvt.f32.u32");
}
#[test]
fn test_neon_vector_bitwise() {
assert_eq!(ARMDeepOpcode::VAND.mnemonic(), "vand");
assert_eq!(ARMDeepOpcode::VORR.mnemonic(), "vorr");
assert_eq!(ARMDeepOpcode::VEOR.mnemonic(), "veor");
assert_eq!(ARMDeepOpcode::VBIC.mnemonic(), "vbic");
assert_eq!(ARMDeepOpcode::VORN.mnemonic(), "vorn");
}
#[test]
fn test_all_operand_types_enum() {
let types = vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::SPR,
ArmDeepOperandType::DPR,
ArmDeepOperandType::Imm8Rotate,
ArmDeepOperandType::Imm16,
ArmDeepOperandType::Cond,
ArmDeepOperandType::RegList,
ArmDeepOperandType::MemAddr,
ArmDeepOperandType::Label,
];
assert_eq!(types.len(), 9);
}
#[test]
fn test_instr_desc_builder_methods() {
let desc = ArmDeepInstrDesc::new(ARMDeepOpcode::ADD, "add")
.with_operands(vec![
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
ArmDeepOperandType::GPR,
])
.with_flags(
false, false, false, false, false, false, false, false, false, true, true, false,
false, false, false,
)
.with_implicit_defs(vec![CPSR])
.with_implicit_uses(vec![SP_ARM32]);
assert_eq!(desc.mnemonic, "add");
assert_eq!(desc.num_operands, 3);
assert!(desc.is_commutative);
assert!(desc.is_arm);
assert!(desc.implicit_defs.contains(&CPSR));
assert!(desc.implicit_uses.contains(&SP_ARM32));
}
#[test]
fn test_arm_deep_can_create_all_variants() {
let _arm = ARMDeep::new();
let _thumb = ARMDeep::new_thumb();
let _thumb2 = ARMDeep::new_thumb2();
let _vfp = ARMDeep::new_vfp();
let _default = ARMDeep::default();
}
#[test]
fn test_shifter_immediate_zero() {
let op = ARMShifterOperand::immediate(0).unwrap();
assert!(op.is_immediate());
if let ARMShifterOperand::Immediate { imm8, rotate } = op {
assert_eq!(imm8, 0);
assert_eq!(rotate, 0);
}
}
#[test]
fn test_shifter_immediate_small() {
let op = ARMShifterOperand::immediate(42).unwrap();
assert!(op.is_immediate());
}
#[test]
fn test_shifter_immediate_rotated() {
let op = ARMShifterOperand::immediate(0xFF000000);
assert!(op.is_some());
}
#[test]
fn test_shifter_immediate_unencodable() {
let op = ARMShifterOperand::immediate(0x0101);
assert!(op.is_none());
}
#[test]
fn test_shifter_register() {
let op = ARMShifterOperand::reg(R0);
assert!(op.is_register());
assert!(!op.is_immediate());
}
#[test]
fn test_shifter_reg_shift() {
let op = ARMShifterOperand::reg_shift(R1, ARMShiftType::LSL, 3);
let formatted = op.format();
assert!(formatted.contains("lsl"));
assert!(formatted.contains("r1"));
assert!(formatted.contains("#3"));
}
#[test]
fn test_shifter_rrx() {
let op = ARMShifterOperand::rrx(R2);
let formatted = op.format();
assert!(formatted.contains("rrx"));
assert!(formatted.contains("r2"));
}
#[test]
fn test_shift_type_encode_decode() {
assert_eq!(ARMShiftType::LSL.encode(), 0b00);
assert_eq!(ARMShiftType::LSR.encode(), 0b01);
assert_eq!(ARMShiftType::ASR.encode(), 0b10);
assert_eq!(ARMShiftType::ROR.encode(), 0b11);
assert_eq!(ARMShiftType::RRX.encode(), 0b11);
assert_eq!(ARMShiftType::decode(0b00), Some(ARMShiftType::LSL));
assert_eq!(ARMShiftType::decode(0b01), Some(ARMShiftType::LSR));
assert_eq!(ARMShiftType::decode(0b10), Some(ARMShiftType::ASR));
assert_eq!(ARMShiftType::decode(0b11), Some(ARMShiftType::ROR));
}
#[test]
fn test_shift_type_mnemonics() {
assert_eq!(ARMShiftType::LSL.mnemonic(), "lsl");
assert_eq!(ARMShiftType::LSR.mnemonic(), "lsr");
assert_eq!(ARMShiftType::ASR.mnemonic(), "asr");
assert_eq!(ARMShiftType::ROR.mnemonic(), "ror");
}
#[test]
fn test_encoding_format_has_condition() {
assert!(ARMEncodingFormat::DataProcessing.has_condition_field());
assert!(ARMEncodingFormat::LoadStore.has_condition_field());
assert!(ARMEncodingFormat::Branch.has_condition_field());
assert!(!ARMEncodingFormat::Thumb16.has_condition_field());
assert!(!ARMEncodingFormat::Thumb2_32.has_condition_field());
}
#[test]
fn test_encoding_format_is_16bit_32bit() {
assert!(ARMEncodingFormat::Thumb16.is_16bit());
assert!(!ARMEncodingFormat::Thumb16.is_32bit());
assert!(!ARMEncodingFormat::DataProcessing.is_16bit());
assert!(ARMEncodingFormat::DataProcessing.is_32bit());
}
#[test]
fn test_it_single() {
let it = ITBlockState::new_single(ARMDeepConditionCode::EQ);
assert_eq!(it.num_insts, 1);
assert_eq!(it.condition_for_inst(0), ARMDeepConditionCode::EQ);
}
#[test]
fn test_it_double_then() {
let it = ITBlockState::new_double(ARMDeepConditionCode::NE, true);
assert_eq!(it.num_insts, 2);
assert_eq!(it.condition_for_inst(0), ARMDeepConditionCode::NE);
assert_eq!(it.condition_for_inst(1), ARMDeepConditionCode::NE);
}
#[test]
fn test_it_double_else() {
let it = ITBlockState::new_double(ARMDeepConditionCode::GT, false);
assert_eq!(it.condition_for_inst(0), ARMDeepConditionCode::GT);
assert_eq!(it.condition_for_inst(1), ARMDeepConditionCode::LE); }
#[test]
fn test_it_triple() {
let it = ITBlockState::new_triple(
ARMDeepConditionCode::GE,
(true, false), );
assert_eq!(it.num_insts, 3);
assert_eq!(it.condition_for_inst(0), ARMDeepConditionCode::GE);
assert_eq!(it.condition_for_inst(1), ARMDeepConditionCode::GE); assert_eq!(it.condition_for_inst(2), ARMDeepConditionCode::LT); }
#[test]
fn test_it_quad() {
let it = ITBlockState::new_quad(
ARMDeepConditionCode::MI,
(true, false, true), );
assert_eq!(it.num_insts, 4);
assert_eq!(it.condition_for_inst(0), ARMDeepConditionCode::MI);
assert_eq!(it.condition_for_inst(1), ARMDeepConditionCode::MI); assert_eq!(it.condition_for_inst(2), ARMDeepConditionCode::PL); assert_eq!(it.condition_for_inst(3), ARMDeepConditionCode::MI); }
#[test]
fn test_it_format_single() {
let it = ITBlockState::new_single(ARMDeepConditionCode::EQ);
assert_eq!(it.format_it_instruction(), "it eq");
}
#[test]
fn test_it_format_double_then() {
let it = ITBlockState::new_double(ARMDeepConditionCode::NE, true);
assert_eq!(it.format_it_instruction(), "itt ne");
}
#[test]
fn test_it_format_double_else() {
let it = ITBlockState::new_double(ARMDeepConditionCode::HI, false);
assert_eq!(it.format_it_instruction(), "ite hi");
}
#[test]
fn test_it_format_triple_tte() {
let it = ITBlockState::new_triple(ARMDeepConditionCode::GE, (true, false));
assert_eq!(it.format_it_instruction(), "itte ge");
}
#[test]
fn test_it_format_quad_tete() {
let it = ITBlockState::new_quad(ARMDeepConditionCode::LT, (false, true, false));
assert_eq!(it.format_it_instruction(), "itete lt");
}
#[test]
fn test_it_out_of_range_is_al() {
let it = ITBlockState::new_single(ARMDeepConditionCode::EQ);
assert_eq!(it.condition_for_inst(5), ARMDeepConditionCode::AL);
}
#[test]
fn test_it_all_conditions() {
let it = ITBlockState::new_quad(ARMDeepConditionCode::EQ, (true, false, true));
let conditions = it.all_conditions();
assert_eq!(conditions.len(), 4);
assert_eq!(conditions[0], ARMDeepConditionCode::EQ);
assert_eq!(conditions[1], ARMDeepConditionCode::EQ);
assert_eq!(conditions[2], ARMDeepConditionCode::NE);
assert_eq!(conditions[3], ARMDeepConditionCode::EQ);
}
#[test]
fn test_it_default() {
let it = ITBlockState::default();
assert_eq!(it.cond, ARMDeepConditionCode::AL);
assert_eq!(it.num_insts, 1);
}
#[test]
fn test_neon_element_size_bits() {
assert_eq!(NEONElementSize::E8.bits(), 8);
assert_eq!(NEONElementSize::E16.bits(), 16);
assert_eq!(NEONElementSize::E32.bits(), 32);
assert_eq!(NEONElementSize::E64.bits(), 64);
}
#[test]
fn test_neon_elements_per_dreg() {
assert_eq!(NEONElementSize::E8.elements_per_dreg(), 8);
assert_eq!(NEONElementSize::E16.elements_per_dreg(), 4);
assert_eq!(NEONElementSize::E32.elements_per_dreg(), 2);
assert_eq!(NEONElementSize::E64.elements_per_dreg(), 1);
}
#[test]
fn test_neon_max_lane_dreg() {
assert_eq!(NEONElementSize::E8.max_lane_dreg(), 7);
assert_eq!(NEONElementSize::E16.max_lane_dreg(), 3);
assert_eq!(NEONElementSize::E32.max_lane_dreg(), 1);
assert_eq!(NEONElementSize::E64.max_lane_dreg(), 0);
}
#[test]
fn test_neon_lane_valid() {
let lane = NEONLane::new(D0_ARM32, NEONElementSize::E8, 3);
assert!(lane.is_some());
}
#[test]
fn test_neon_lane_invalid() {
let lane = NEONLane::new(D0_ARM32, NEONElementSize::E8, 8); assert!(lane.is_none());
}
#[test]
fn test_neon_lane_format() {
let lane = NEONLane::new(D5_ARM32, NEONElementSize::E32, 1).unwrap();
assert_eq!(lane.format(), "d5[1]");
}
#[test]
fn test_neon_suffix() {
assert_eq!(NEONElementSize::E8.suffix(), "8");
assert_eq!(NEONElementSize::E16.suffix(), "16");
assert_eq!(NEONElementSize::E32.suffix(), "32");
assert_eq!(NEONElementSize::E64.suffix(), "64");
}
#[test]
fn test_neon_from_bytes() {
assert_eq!(NEONElementSize::from_bytes(1), Some(NEONElementSize::E8));
assert_eq!(NEONElementSize::from_bytes(2), Some(NEONElementSize::E16));
assert_eq!(NEONElementSize::from_bytes(4), Some(NEONElementSize::E32));
assert_eq!(NEONElementSize::from_bytes(8), Some(NEONElementSize::E64));
assert_eq!(NEONElementSize::from_bytes(3), None);
}
#[test]
fn test_processor_mode_names() {
assert_eq!(ARMProcessorMode::User.name(), "usr");
assert_eq!(ARMProcessorMode::Supervisor.name(), "svc");
assert_eq!(ARMProcessorMode::IRQ.name(), "irq");
assert_eq!(ARMProcessorMode::FIQ.name(), "fiq");
}
#[test]
fn test_processor_mode_privileged() {
assert!(!ARMProcessorMode::User.is_privileged());
assert!(ARMProcessorMode::Supervisor.is_privileged());
assert!(ARMProcessorMode::System.is_privileged());
}
#[test]
fn test_processor_mode_banked_regs() {
let fiq_regs = ARMProcessorMode::FIQ.banked_regs();
assert_eq!(fiq_regs.len(), 7);
assert!(fiq_regs.contains(&8));
let svc_regs = ARMProcessorMode::Supervisor.banked_regs();
assert_eq!(svc_regs.len(), 2);
assert!(svc_regs.contains(&13));
assert!(svc_regs.contains(&14));
}
#[test]
fn test_instruction_set_from_address_arm() {
let state = InstructionSetState::from_address(0x8000); assert!(matches!(state, InstructionSetState::ARM));
}
#[test]
fn test_instruction_set_from_address_thumb() {
let state = InstructionSetState::from_address(0x8001); assert!(matches!(state, InstructionSetState::Thumb));
}
#[test]
fn test_instruction_set_properties() {
assert!(InstructionSetState::ARM.is_32bit());
assert!(!InstructionSetState::Thumb.is_32bit());
assert!(InstructionSetState::ARM.has_conditional_execution());
assert!(!InstructionSetState::Thumb.has_conditional_execution());
}
#[test]
fn test_pipeline_simple_alu() {
let usage = ARMPipelineUsage::simple_alu();
assert_eq!(usage.latency, 1);
assert!(usage.uses_alu);
assert!(!usage.uses_multiply);
}
#[test]
fn test_pipeline_multiply() {
let usage = ARMPipelineUsage::multiply();
assert_eq!(usage.latency, 2);
assert!(usage.uses_multiply);
}
#[test]
fn test_pipeline_load() {
let usage = ARMPipelineUsage::load();
assert_eq!(usage.latency, 3);
assert!(usage.uses_load_store);
}
#[test]
fn test_pipeline_vfp() {
let usage = ARMPipelineUsage::vfp_scalar();
assert!(usage.uses_vfp_neon);
}
#[test]
fn test_pipeline_for_opcode_add() {
let usage = ARMPipelineUsage::for_opcode(ARMDeepOpcode::ADD);
assert_eq!(usage.latency, 1);
}
#[test]
fn test_pipeline_for_opcode_mul() {
let usage = ARMPipelineUsage::for_opcode(ARMDeepOpcode::MUL);
assert_eq!(usage.latency, 2);
}
#[test]
fn test_pipeline_for_opcode_sdiv() {
let usage = ARMPipelineUsage::for_opcode(ARMDeepOpcode::SDIV);
assert!(usage.latency >= 2); }
#[test]
fn test_pipeline_for_opcode_vdiv() {
let usage = ARMPipelineUsage::for_opcode(ARMDeepOpcode::VDIV_F32);
assert!(usage.latency >= 10);
}
#[test]
fn test_cpsr_flag_constants() {
assert_eq!(cpsr_flags::N, 1 << 31);
assert_eq!(cpsr_flags::Z, 1 << 30);
assert_eq!(cpsr_flags::C, 1 << 29);
assert_eq!(cpsr_flags::V, 1 << 28);
assert_eq!(cpsr_flags::T, 1 << 5);
}
#[test]
fn test_cpsr_evaluate_eq() {
let cpsr = cpsr_flags::Z;
assert!(cpsr_flags::evaluate_condition(
ARMDeepConditionCode::EQ,
cpsr
));
assert!(!cpsr_flags::evaluate_condition(
ARMDeepConditionCode::NE,
cpsr
));
}
#[test]
fn test_cpsr_evaluate_hi_ls() {
let cpsr = cpsr_flags::C;
assert!(cpsr_flags::evaluate_condition(
ARMDeepConditionCode::HI,
cpsr
));
assert!(!cpsr_flags::evaluate_condition(
ARMDeepConditionCode::LS,
cpsr
));
}
#[test]
fn test_cpsr_evaluate_ge_lt() {
let cpsr = 0;
assert!(cpsr_flags::evaluate_condition(
ARMDeepConditionCode::GE,
cpsr
));
assert!(!cpsr_flags::evaluate_condition(
ARMDeepConditionCode::LT,
cpsr
));
}
#[test]
fn test_cpsr_evaluate_always() {
assert!(cpsr_flags::evaluate_condition(ARMDeepConditionCode::AL, 0));
}
#[test]
fn test_cpsr_evaluate_never() {
assert!(!cpsr_flags::evaluate_condition(ARMDeepConditionCode::NV, 0));
}
#[test]
fn test_cpsr_update_nzcv_zero() {
let flags = cpsr_flags::update_nzcv(0, false, false);
assert_eq!(flags, cpsr_flags::Z); }
#[test]
fn test_cpsr_update_nzcv_negative() {
let flags = cpsr_flags::update_nzcv(0x80000000, false, false);
assert_eq!(flags, cpsr_flags::N);
}
#[test]
fn test_cpsr_update_nzcv_carry() {
let flags = cpsr_flags::update_nzcv(42, true, false);
assert!(flags & cpsr_flags::C != 0);
assert!(flags & cpsr_flags::Z == 0);
}
#[test]
fn test_cpsr_update_nzcv_overflow() {
let flags = cpsr_flags::update_nzcv(0, false, true);
assert!(flags & cpsr_flags::V != 0);
assert!(flags & cpsr_flags::Z != 0);
}
#[test]
fn test_frame_layout_compute_empty() {
let layout = ARMSFrameLayout::compute(&[], 0, 0);
assert_eq!(layout.frame_size, 0);
assert_eq!(layout.lr_offset, -4);
}
#[test]
fn test_frame_layout_compute_with_locals() {
let layout = ARMSFrameLayout::compute(&[], 64, 0);
assert_eq!(layout.frame_size, 72); }
#[test]
fn test_frame_layout_compute_with_callee_saved() {
let layout = ARMSFrameLayout::compute(&[R4, R5, R6], 0, 0);
assert_eq!(layout.frame_size, 20);
}
#[test]
fn test_frame_layout_callee_saved_offset() {
let layout = ARMSFrameLayout::compute(&[R4, R5, R6], 0, 0);
assert_eq!(layout.callee_saved_sp_offset(0), layout.callee_saved_start);
assert_eq!(
layout.callee_saved_sp_offset(1),
layout.callee_saved_start + 4
);
}
#[test]
fn test_arch_features_armv4() {
let feat = ARMArchFeatures::armv4();
assert!(!feat.has_dsp);
assert!(!feat.has_simd);
assert!(!feat.has_thumb2);
assert!(!feat.can_use_hw_divide());
assert!(!feat.can_use_vfp());
assert!(!feat.can_use_neon());
}
#[test]
fn test_arch_features_armv7a() {
let feat = ARMArchFeatures::armv7a();
assert!(feat.has_dsp);
assert!(feat.has_simd);
assert!(feat.has_thumb2);
assert!(feat.can_use_hw_divide());
assert!(feat.can_use_vfp());
assert!(feat.can_use_neon());
assert_eq!(feat.vfp_dreg_count(), 32);
}
#[test]
fn test_arch_features_armv7m() {
let feat = ARMArchFeatures::armv7m();
assert!(feat.has_thumb2);
assert!(feat.can_use_hw_divide());
assert!(!feat.can_use_vfp());
assert!(!feat.can_use_neon());
}
#[test]
fn test_arch_features_default() {
let feat = ARMArchFeatures::default();
assert!(feat.has_neon);
assert_eq!(feat.vfp_dreg_count(), 32);
}
#[test]
fn test_arch_features_vfp_reg_count() {
assert_eq!(ARMArchFeatures::armv7a().vfp_dreg_count(), 32);
assert_eq!(ARMArchFeatures::armv7m().vfp_dreg_count(), 0);
}
#[test]
fn test_every_cond_has_invert() {
for cond in &ARMDeepConditionCode::ALL {
let inv = cond.invert();
match cond {
ARMDeepConditionCode::AL => assert_eq!(*cond, inv.invert()),
ARMDeepConditionCode::NV => assert_eq!(*cond, inv.invert()),
_ => assert_ne!(cond, &inv),
}
}
}
#[test]
fn test_condition_codes_from_bits_complete() {
for bits in 0..16u8 {
assert!(ARMDeepConditionCode::from_bits(bits).is_some());
}
}
#[test]
fn test_condition_double_invert_all() {
for cond in &ARMDeepConditionCode::ALL {
assert_eq!(cond.invert().invert(), *cond);
}
}
#[test]
fn test_div_instructions_non_commutative() {
let info = ARMInstrInfoDeep::new();
assert!(!info.get(ARMDeepOpcode::SDIV).unwrap().is_commutative);
assert!(!info.get(ARMDeepOpcode::UDIV).unwrap().is_commutative);
}
#[test]
fn test_cmp_has_implicit_cpsr_def() {
let info = ARMInstrInfoDeep::new();
let cmp = info.get(ARMDeepOpcode::CMP).unwrap();
assert!(cmp.implicit_defs.contains(&CPSR));
}
#[test]
fn test_blx_implicit_def_lr() {
let info = ARMInstrInfoDeep::new();
let blx = info.get(ARMDeepOpcode::BLX).unwrap();
assert!(blx.implicit_defs.contains(&LR_ARM32));
}
#[test]
fn test_dmb_has_side_effects() {
let info = ARMInstrInfoDeep::new();
assert!(info.get(ARMDeepOpcode::DMB).unwrap().has_side_effects);
assert!(info.get(ARMDeepOpcode::DSB).unwrap().has_side_effects);
assert!(info.get(ARMDeepOpcode::ISB).unwrap().has_side_effects);
}
#[test]
fn test_svc_has_side_effects() {
let info = ARMInstrInfoDeep::new();
assert!(info.get(ARMDeepOpcode::SVC).unwrap().has_side_effects);
}
#[test]
fn test_brkpt_has_side_effects() {
let info = ARMInstrInfoDeep::new();
assert!(info.get(ARMDeepOpcode::BKPT).unwrap().has_side_effects);
}
}