use crate::arm::arm_instr_info::ArmOpcode;
use crate::opcode::Opcode;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct IselPattern {
pub ir_opcode: Opcode,
pub description: &'static str,
pub result_opcode: ArmOpcode,
pub priority: u32,
pub num_operands: u8,
pub imm_constraint: Option<ImmConstraint>,
pub is_two_address: bool,
pub required_feature: Option<&'static str>,
pub cond: Option<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImmConstraint {
IsPowerOfTwo,
IsValidImm12,
IsValidShiftImm,
IsValidBitmaskImm,
IsZero,
IsNonZero,
IsShiftAmount,
IsShiftAmount64,
IsImm8,
IsSmallOffset,
}
#[derive(Debug, Clone)]
pub struct IselMatch {
pub pattern: IselPattern,
pub operands: Vec<IselOperand>,
}
#[derive(Debug, Clone)]
pub enum IselOperand {
Reg(usize),
Imm(i64),
Label(String),
Cond(u8),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AArch64AddrMode {
BasePlusOffset { base: usize, offset: i64 },
BasePlusIndex { base: usize, index: usize },
BasePlusScaledIndex {
base: usize,
index: usize,
scale: u8,
},
PreIndex { base: usize, offset: i64 },
PostIndex { base: usize, offset: i64 },
PcRelative { label: String },
AdrpPlusLo12 { page: String, offset: i64 },
}
impl AArch64AddrMode {
pub fn fits_ldr_str_unsigned(&self, elem_size: u8) -> bool {
match self {
AArch64AddrMode::BasePlusOffset { offset, .. } => {
let scaled = offset / elem_size as i64;
scaled >= 0 && scaled <= 4095
}
_ => false,
}
}
pub fn fits_ldur_stur(&self) -> bool {
match self {
AArch64AddrMode::BasePlusOffset { offset, .. } => *offset >= -256 && *offset <= 255,
_ => false,
}
}
pub fn is_extended_reg(&self) -> bool {
matches!(
self,
AArch64AddrMode::BasePlusIndex { .. } | AArch64AddrMode::BasePlusScaledIndex { .. }
)
}
}
pub fn is_valid_imm12(value: i64) -> bool {
value >= 0 && value <= 4095
}
pub fn is_valid_shift_imm(value: i64, is_64bit: bool) -> bool {
if is_64bit {
value >= 0 && value <= 63
} else {
value >= 0 && value <= 31
}
}
pub fn is_valid_bitmask_imm(value: i64, size_bits: u8) -> bool {
let mask = if size_bits == 64 {
value as u64
} else {
(value as u64) & ((1u64 << size_bits) - 1)
};
if mask == 0 || mask == u64::MAX {
return false;
}
let bits = size_bits as u32;
let inv = !mask;
let ones = mask;
for esize in &[2u32, 4, 8, 16, 32, 64] {
if *esize >= bits as u32 {
let ebits = *esize;
let emask = if ebits == 64 {
u64::MAX
} else {
(1u64 << ebits) - 1
};
let pattern = ones & emask;
let mut repeats = true;
let mut offset = ebits;
while offset < 64 {
let next = (ones >> offset) & emask;
if next != pattern {
repeats = false;
break;
}
offset += ebits;
}
if repeats {
let run = pattern;
if run == 0 || run == emask {
return false;
}
return true;
}
}
}
false
}
pub fn is_power_of_two(value: i64) -> bool {
value > 0 && (value & (value - 1)) == 0
}
pub fn log2_pow2(value: i64) -> u8 {
assert!(is_power_of_two(value));
value.trailing_zeros() as u8
}
pub fn evaluate_imm_constraint(constraint: &ImmConstraint, value: i64, is_64bit: bool) -> bool {
match constraint {
ImmConstraint::IsPowerOfTwo => is_power_of_two(value),
ImmConstraint::IsValidImm12 => is_valid_imm12(value),
ImmConstraint::IsValidShiftImm => is_valid_shift_imm(value, is_64bit),
ImmConstraint::IsValidBitmaskImm => {
is_valid_bitmask_imm(value, if is_64bit { 64 } else { 32 })
}
ImmConstraint::IsZero => value == 0,
ImmConstraint::IsNonZero => value != 0,
ImmConstraint::IsShiftAmount => value >= 0 && value <= 31,
ImmConstraint::IsShiftAmount64 => value >= 0 && value <= 63,
ImmConstraint::IsImm8 => value >= 0 && value <= 255,
ImmConstraint::IsSmallOffset => value >= -256 && value <= 255,
}
}
pub fn aarch64_isel_table() -> Vec<IselPattern> {
let mut table = Vec::new();
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add xd, xn, xm",
result_opcode: ArmOpcode::ADD,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add xd, xn, #imm12",
result_opcode: ArmOpcode::ADD,
priority: 1,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsValidImm12),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub xd, xn, xm",
result_opcode: ArmOpcode::SUB,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub xd, xn, #imm12",
result_opcode: ArmOpcode::SUB,
priority: 1,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsValidImm12),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "mul xd, xn, xm",
result_opcode: ArmOpcode::MUL,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "madd xd, xn, xm, xzr — mul as madd",
result_opcode: ArmOpcode::MADD,
priority: 1,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SDiv,
description: "sdiv xd, xn, xm",
result_opcode: ArmOpcode::SDIV,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::UDiv,
description: "udiv xd, xn, xm",
result_opcode: ArmOpcode::UDIV,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SDiv,
description: "asr xd, xn, #log2 — sdiv by power of 2",
result_opcode: ArmOpcode::ASR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsPowerOfTwo),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::UDiv,
description: "lsr xd, xn, #log2 — udiv by power of 2",
result_opcode: ArmOpcode::LSR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsPowerOfTwo),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::And,
description: "and xd, xn, xm",
result_opcode: ArmOpcode::AND,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Or,
description: "orr xd, xn, xm",
result_opcode: ArmOpcode::ORR,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Xor,
description: "eor xd, xn, xm",
result_opcode: ArmOpcode::EOR,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "lsl xd, xn, xm",
result_opcode: ArmOpcode::LSL,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "lsl xd, xn, #shift — shift by immediate",
result_opcode: ArmOpcode::LSL,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount64),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::LShr,
description: "lsr xd, xn, xm",
result_opcode: ArmOpcode::LSR,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::LShr,
description: "lsr xd, xn, #shift — logical shift right by immediate",
result_opcode: ArmOpcode::LSR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount64),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::AShr,
description: "asr xd, xn, xm",
result_opcode: ArmOpcode::ASR,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::AShr,
description: "asr xd, xn, #shift — arithmetic shift right by immediate",
result_opcode: ArmOpcode::ASR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount64),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ldr xd, [xn, #offset] — base + unsigned scaled offset",
result_opcode: ArmOpcode::LDR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsSmallOffset),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ldr xd, [xn, xm, lsl #3] — base + scaled reg index",
result_opcode: ArmOpcode::LDR,
priority: 1,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "str xd, [xn, #offset] — base + unsigned scaled offset",
result_opcode: ArmOpcode::STR,
priority: 0,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsSmallOffset),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "str xd, [xn, xm, lsl #3] — base + scaled reg index",
result_opcode: ArmOpcode::STR,
priority: 1,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "subs xzr, xn, xm — sets flags for compare",
result_opcode: ArmOpcode::SUBS,
priority: 0,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Br,
description: "b label — unconditional branch",
result_opcode: ArmOpcode::B,
priority: 0,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Ret,
description: "ret — function return",
result_opcode: ArmOpcode::RET,
priority: 0,
num_operands: 0,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Select,
description: "csel xd, xn, xm, cond",
result_opcode: ArmOpcode::CSEL,
priority: 0,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::And,
description: "ubfx xd, xn, #lsb, #width — extract bitfield with and-mask",
result_opcode: ArmOpcode::UBFX,
priority: 2,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsValidBitmaskImm),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "stp xd1, xd2, [xn, #offset] — store pair",
result_opcode: ArmOpcode::STP,
priority: 1,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsSmallOffset),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ldp xd1, xd2, [xn, #offset] — load pair",
result_opcode: ArmOpcode::LDP,
priority: 1,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsSmallOffset),
is_two_address: false,
required_feature: None,
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd v2s, v2s, v2s — NEON FP add 2S",
result_opcode: ArmOpcode::FADD,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd v4s, v4s, v4s — NEON FP add 4S",
result_opcode: ArmOpcode::FADD,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd v2d, v2d, v2d — NEON FP add 2D",
result_opcode: ArmOpcode::FADD,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub v2s, v2s, v2s — NEON FP sub 2S",
result_opcode: ArmOpcode::FSUB,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub v4s, v4s, v4s — NEON FP sub 4S",
result_opcode: ArmOpcode::FSUB,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub v2d, v2d, v2d — NEON FP sub 2D",
result_opcode: ArmOpcode::FSUB,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul v2s, v2s, v2s — NEON FP mul 2S",
result_opcode: ArmOpcode::FMUL,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul v4s, v4s, v4s — NEON FP mul 4S",
result_opcode: ArmOpcode::FMUL,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul v2d, v2d, v2d — NEON FP mul 2D",
result_opcode: ArmOpcode::FMUL,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "fdiv v2s, v2s, v2s — NEON FP div 2S",
result_opcode: ArmOpcode::FDIV,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "fdiv v4s, v4s, v4s — NEON FP div 4S",
result_opcode: ArmOpcode::FDIV,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "fdiv v2d, v2d, v2d — NEON FP div 2D",
result_opcode: ArmOpcode::FDIV,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fneg vN — NEON FP negate",
result_opcode: ArmOpcode::FNEG,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fabs vN — NEON FP absolute value",
result_opcode: ArmOpcode::FABS,
priority: 11,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "fsqrt vN — NEON FP sqrt",
result_opcode: ArmOpcode::FSQRT,
priority: 12,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmeq vN, vN, vN — NEON FP compare equal",
result_opcode: ArmOpcode::FCMEQ,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmgt vN, vN, vN — NEON FP compare greater-than",
result_opcode: ArmOpcode::FCMGT,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmge vN, vN, vN — NEON FP compare greater-equal",
result_opcode: ArmOpcode::FCMGE,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "facgt vN, vN, vN — NEON FP abs-compare greater-than",
result_opcode: ArmOpcode::FACGT,
priority: 13,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "facge vN, vN, vN — NEON FP abs-compare greater-equal",
result_opcode: ArmOpcode::FACGE,
priority: 14,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmax vN, vN, vN — NEON FP max",
result_opcode: ArmOpcode::FMAX,
priority: 20,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmin vN, vN, vN — NEON FP min",
result_opcode: ArmOpcode::FMIN,
priority: 21,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmaxnm vN, vN, vN — NEON FP max (IEEE)",
result_opcode: ArmOpcode::FMAXNM,
priority: 22,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fminnm vN, vN, vN — NEON FP min (IEEE)",
result_opcode: ArmOpcode::FMINNM,
priority: 23,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmaxp vN, vN, vN — NEON FP max pairwise",
result_opcode: ArmOpcode::FMAXP,
priority: 24,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fminp vN, vN, vN — NEON FP min pairwise",
result_opcode: ArmOpcode::FMINP,
priority: 25,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frintz vN — NEON FP round toward zero",
result_opcode: ArmOpcode::FRINTZ,
priority: 20,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frintm vN — NEON FP round toward minus infinity",
result_opcode: ArmOpcode::FRINTM,
priority: 21,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frintp vN — NEON FP round toward plus infinity",
result_opcode: ArmOpcode::FRINTP,
priority: 22,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frintn vN — NEON FP round to nearest even",
result_opcode: ArmOpcode::FRINTN,
priority: 23,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frinta vN — NEON FP round to nearest away",
result_opcode: ArmOpcode::FRINTA,
priority: 24,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frintx vN — NEON FP round using current mode",
result_opcode: ArmOpcode::FRINTX,
priority: 25,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "frinti vN — NEON FP round using FPCR",
result_opcode: ArmOpcode::FRINTI,
priority: 26,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SIToFP,
description: "scvtf vN, vN — NEON signed int to float",
result_opcode: ArmOpcode::SCVTF_V,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::UIToFP,
description: "ucvtf vN, vN — NEON unsigned int to float",
result_opcode: ArmOpcode::UCVTF_V,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPToSI,
description: "fcvtzs vN, vN — NEON float to signed int (trunc)",
result_opcode: ArmOpcode::FCVTZS_V,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPToUI,
description: "fcvtzu vN, vN — NEON float to unsigned int (trunc)",
result_opcode: ArmOpcode::FCVTZU_V,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPExt,
description: "fcvt vN, vN — NEON FP extend (e.g., f16→f32)",
result_opcode: ArmOpcode::FCVT,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v8b, v8b, v8b — NEON int add 8B",
result_opcode: ArmOpcode::ADD_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v16b, v16b, v16b — NEON int add 16B",
result_opcode: ArmOpcode::ADD_V,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v4h, v4h, v4h — NEON int add 4H",
result_opcode: ArmOpcode::ADD_V,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v8h, v8h, v8h — NEON int add 8H",
result_opcode: ArmOpcode::ADD_V,
priority: 13,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v2s, v2s, v2s — NEON int add 2S",
result_opcode: ArmOpcode::ADD_V,
priority: 14,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v4s, v4s, v4s — NEON int add 4S",
result_opcode: ArmOpcode::ADD_V,
priority: 15,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add v2d, v2d, v2d — NEON int add 2D",
result_opcode: ArmOpcode::ADD_V,
priority: 16,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub v16b, v16b, v16b — NEON int sub 16B",
result_opcode: ArmOpcode::SUB_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub v8h, v8h, v8h — NEON int sub 8H",
result_opcode: ArmOpcode::SUB_V,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub v4s, v4s, v4s — NEON int sub 4S",
result_opcode: ArmOpcode::SUB_V,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub v2d, v2d, v2d — NEON int sub 2D",
result_opcode: ArmOpcode::SUB_V,
priority: 13,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "mul v16b, v16b, v16b — NEON int mul 16B",
result_opcode: ArmOpcode::MUL_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "mul v8h, v8h, v8h — NEON int mul 8H",
result_opcode: ArmOpcode::MUL_V,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "mul v4s, v4s, v4s — NEON int mul 4S",
result_opcode: ArmOpcode::MUL_V,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "mls v16b, v16b, v16b — NEON multiply-subtract 16B",
result_opcode: ArmOpcode::MLS_V,
priority: 30,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "mls v8h, v8h, v8h — NEON multiply-subtract 8H",
result_opcode: ArmOpcode::MLS_V,
priority: 31,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "mls v4s, v4s, v4s — NEON multiply-subtract 4S",
result_opcode: ArmOpcode::MLS_V,
priority: 32,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmeq vN, vN, vN — NEON int compare equal",
result_opcode: ArmOpcode::CMEQ,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmgt vN, vN, vN — NEON int signed greater-than",
result_opcode: ArmOpcode::CMGT,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmge vN, vN, vN — NEON int signed greater-equal",
result_opcode: ArmOpcode::CMGE,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmhi vN, vN, vN — NEON int unsigned higher",
result_opcode: ArmOpcode::CMHI,
priority: 13,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmhs vN, vN, vN — NEON int unsigned higher-or-same",
result_opcode: ArmOpcode::CMHS,
priority: 14,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmtst vN, vN, vN — NEON int bitwise test",
result_opcode: ArmOpcode::CMTST,
priority: 15,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmle vN, vN, vN — NEON int signed less-equal",
result_opcode: ArmOpcode::CMLE,
priority: 16,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ICmp,
description: "cmlt vN, vN, vN — NEON int signed less-than",
result_opcode: ArmOpcode::CMLT,
priority: 17,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "smin vN, vN, vN — NEON signed min",
result_opcode: ArmOpcode::SMIN,
priority: 20,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "smax vN, vN, vN — NEON signed max",
result_opcode: ArmOpcode::SMAX,
priority: 21,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "umin vN, vN, vN — NEON unsigned min",
result_opcode: ArmOpcode::UMIN,
priority: 22,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "umax vN, vN, vN — NEON unsigned max",
result_opcode: ArmOpcode::UMAX,
priority: 23,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "abs vN, vN — NEON integer absolute value",
result_opcode: ArmOpcode::ABS_V,
priority: 20,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "neg vN, vN — NEON integer negate",
result_opcode: ArmOpcode::NEG_V,
priority: 21,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Xor,
description: "not vN, vN — NEON bitwise NOT",
result_opcode: ArmOpcode::NOT_V,
priority: 22,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "sshl vN, vN, vN — NEON signed shift left (variable)",
result_opcode: ArmOpcode::SSHL,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "ushl vN, vN, vN — NEON unsigned shift left (variable)",
result_opcode: ArmOpcode::USHL,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::AShr,
description: "sshr vN, vN, #imm — NEON signed shift right (immediate)",
result_opcode: ArmOpcode::SSHR,
priority: 12,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::LShr,
description: "ushr vN, vN, #imm — NEON unsigned shift right (immediate)",
result_opcode: ArmOpcode::USHR,
priority: 13,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "srshl vN, vN, vN — NEON signed rounding shift left",
result_opcode: ArmOpcode::SRSHL,
priority: 14,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::AShr,
description: "srshr vN, vN, #imm — NEON signed rounding shift right (imm)",
result_opcode: ArmOpcode::SRSHR,
priority: 15,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::LShr,
description: "urshr vN, vN, #imm — NEON unsigned rounding shift right (imm)",
result_opcode: ArmOpcode::URSHR,
priority: 16,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "shl vN, vN, #imm — NEON shift left by immediate",
result_opcode: ArmOpcode::SHL_V,
priority: 17,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Trunc,
description: "xtn vN, vN — NEON extract narrow",
result_opcode: ArmOpcode::XTN,
priority: 10,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Trunc,
description: "sqxtn vN, vN — NEON signed saturating extract narrow",
result_opcode: ArmOpcode::SQXTN,
priority: 11,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Trunc,
description: "uqxtn vN, vN — NEON unsigned saturating extract narrow",
result_opcode: ArmOpcode::UQXTN,
priority: 12,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Trunc,
description: "sqxtun vN, vN — NEON signed saturating extract narrow unsigned",
result_opcode: ArmOpcode::SQXTUN,
priority: 13,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "saddl vN, vN, vN — NEON signed add long",
result_opcode: ArmOpcode::SADDL,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uaddl vN, vN, vN — NEON unsigned add long",
result_opcode: ArmOpcode::UADDL,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "ssubl vN, vN, vN — NEON signed sub long",
result_opcode: ArmOpcode::SSUBL,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "usubl vN, vN, vN — NEON unsigned sub long",
result_opcode: ArmOpcode::USUBL,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "smull vN, vN, vN — NEON signed mul long",
result_opcode: ArmOpcode::SMULL,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "umull vN, vN, vN — NEON unsigned mul long",
result_opcode: ArmOpcode::UMULL,
priority: 12,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "addv s, vN — NEON add across vector",
result_opcode: ArmOpcode::SADDLV,
priority: 20,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "uaddlv s, vN — NEON unsigned add long across vector",
result_opcode: ArmOpcode::UADDLV,
priority: 21,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "smaxv s, vN — NEON signed max across vector",
result_opcode: ArmOpcode::SMAXV,
priority: 22,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "umaxv s, vN — NEON unsigned max across vector",
result_opcode: ArmOpcode::UMAXV,
priority: 23,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "sminv s, vN — NEON signed min across vector",
result_opcode: ArmOpcode::SMINV,
priority: 24,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "uminv s, vN — NEON unsigned min across vector",
result_opcode: ArmOpcode::UMINV,
priority: 25,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fmaxv s, vN — NEON FP max across vector",
result_opcode: ArmOpcode::FMAXV,
priority: 30,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fminv s, vN — NEON FP min across vector",
result_opcode: ArmOpcode::FMINV,
priority: 31,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fmaxnmv s, vN — NEON FP IEEE max across vector",
result_opcode: ArmOpcode::FMAXNMV,
priority: 32,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fminnmv s, vN — NEON FP IEEE min across vector",
result_opcode: ArmOpcode::FMINNMV,
priority: 33,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fmla vN, vN, vN — NEON FP multiply-add",
result_opcode: ArmOpcode::FMLA,
priority: 20,
num_operands: 2,
imm_constraint: None,
is_two_address: true,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fmls vN, vN, vN — NEON FP multiply-subtract",
result_opcode: ArmOpcode::FMLS,
priority: 20,
num_operands: 2,
imm_constraint: None,
is_two_address: true,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::And,
description: "and vN, vN, vN — NEON bitwise AND",
result_opcode: ArmOpcode::AND_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Or,
description: "orr vN, vN, vN — NEON bitwise OR",
result_opcode: ArmOpcode::ORR_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Xor,
description: "eor vN, vN, vN — NEON bitwise EOR",
result_opcode: ArmOpcode::EOR_V,
priority: 10,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::And,
description: "bic vN, vN, vN — NEON bitwise BIC",
result_opcode: ArmOpcode::BIC_V,
priority: 11,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "sqadd vN, vN, vN — NEON signed saturating add",
result_opcode: ArmOpcode::SQADD,
priority: 30,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "uqadd vN, vN, vN — NEON unsigned saturating add",
result_opcode: ArmOpcode::UQADD,
priority: 31,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sqsub vN, vN, vN — NEON signed saturating sub",
result_opcode: ArmOpcode::SQSUB,
priority: 32,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "uqsub vN, vN, vN — NEON unsigned saturating sub",
result_opcode: ArmOpcode::UQSUB,
priority: 33,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "addp vN, vN, vN — NEON pairwise add",
result_opcode: ArmOpcode::ADDP,
priority: 30,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "smaxp vN, vN, vN — NEON signed pairwise max",
result_opcode: ArmOpcode::SMAXP,
priority: 31,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "umaxp vN, vN, vN — NEON unsigned pairwise max",
result_opcode: ArmOpcode::UMAXP,
priority: 32,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "sminp vN, vN, vN — NEON signed pairwise min",
result_opcode: ArmOpcode::SMINP,
priority: 33,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "uminp vN, vN, vN — NEON unsigned pairwise min",
result_opcode: ArmOpcode::UMINP,
priority: 34,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "frecpe vN, vN — NEON FP reciprocal estimate",
result_opcode: ArmOpcode::FRECPE,
priority: 30,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FDiv,
description: "frsqrte vN, vN — NEON FP reciprocal sqrt estimate",
result_opcode: ArmOpcode::FRSQRTE,
priority: 31,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "frecps vN, vN, vN — NEON FP reciprocal step",
result_opcode: ArmOpcode::FRECPS,
priority: 32,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "frsqrts vN, vN, vN — NEON FP reciprocal sqrt step",
result_opcode: ArmOpcode::FRSQRTS,
priority: 33,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sabd vN, vN, vN — NEON signed absolute difference",
result_opcode: ArmOpcode::SABD,
priority: 30,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "uabd vN, vN, vN — NEON unsigned absolute difference",
result_opcode: ArmOpcode::UABD,
priority: 31,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "saba vN, vN, vN — NEON signed absolute difference accumulate",
result_opcode: ArmOpcode::SABA,
priority: 32,
num_operands: 2,
imm_constraint: None,
is_two_address: true,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "uaba vN, vN, vN — NEON unsigned absolute difference accumulate",
result_opcode: ArmOpcode::UABA,
priority: 33,
num_operands: 2,
imm_constraint: None,
is_two_address: true,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmulx vN, vN, vN — NEON FP extended multiply",
result_opcode: ArmOpcode::FMULX,
priority: 30,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "cnt vN, vN — NEON population count per byte",
result_opcode: ArmOpcode::CNT,
priority: 30,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "clz vN, vN — NEON count leading zeros",
result_opcode: ArmOpcode::CLZ_V,
priority: 31,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "rbit vN, vN — NEON reverse bits",
result_opcode: ArmOpcode::RBIT_V,
priority: 32,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sxtl vN, vN — NEON signed extend long",
result_opcode: ArmOpcode::SXTL,
priority: 30,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uxtl vN, vN — NEON unsigned extend long",
result_opcode: ArmOpcode::UXTL,
priority: 31,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description: "shll vN, vN, #imm — NEON shift left long",
result_opcode: ArmOpcode::SHLL,
priority: 32,
num_operands: 2,
imm_constraint: Some(ImmConstraint::IsShiftAmount),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmeq vN, vN, #0.0 — NEON FP compare equal to zero",
result_opcode: ArmOpcode::FCMEQ,
priority: 20,
num_operands: 1,
imm_constraint: Some(ImmConstraint::IsZero),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmgt vN, vN, #0.0 — NEON FP compare greater-than zero",
result_opcode: ArmOpcode::FCMGT,
priority: 21,
num_operands: 1,
imm_constraint: Some(ImmConstraint::IsZero),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FCmp,
description: "fcmge vN, vN, #0.0 — NEON FP compare greater-equal zero",
result_opcode: ArmOpcode::FCMGE,
priority: 22,
num_operands: 1,
imm_constraint: Some(ImmConstraint::IsZero),
is_two_address: false,
required_feature: Some("neon"),
cond: None,
});
table
}
pub struct AArch64IselTable {
pub patterns: Vec<IselPattern>,
pub is_64bit: bool,
pub stats: IselStats,
}
#[derive(Debug, Clone, Default)]
pub struct IselStats {
pub total_lookups: u64,
pub matches: u64,
pub misses: u64,
pub fallthroughs: u64,
pub opcode_matches: HashMap<u32, u64>,
pub opcode_misses: HashMap<u32, u64>,
}
impl IselStats {
pub fn new() -> Self {
Self::default()
}
pub fn record_match(&mut self, opcode: u32) {
self.total_lookups += 1;
self.matches += 1;
*self.opcode_matches.entry(opcode).or_insert(0) += 1;
}
pub fn record_miss(&mut self, opcode: u32) {
self.total_lookups += 1;
self.misses += 1;
*self.opcode_misses.entry(opcode).or_insert(0) += 1;
}
pub fn record_fallthrough(&mut self) {
self.fallthroughs += 1;
}
pub fn hit_rate(&self) -> f64 {
if self.total_lookups == 0 {
return 100.0;
}
(self.matches as f64 / self.total_lookups as f64) * 100.0
}
pub fn uncovered_opcodes(&self, all_opcodes: &[u32]) -> Vec<u32> {
all_opcodes
.iter()
.filter(|op| !self.opcode_matches.contains_key(op))
.copied()
.collect()
}
pub fn report(&self, all_opcodes: &[u32]) -> String {
let mut s = String::new();
s.push_str(&format!(
"ISel Statistics: {} lookups, {} matches ({:.1}%), {} misses\n",
self.total_lookups,
self.matches,
self.hit_rate(),
self.misses
));
s.push_str(&format!(" {} fallthroughs\n", self.fallthroughs));
let uncovered = self.uncovered_opcodes(all_opcodes);
if !uncovered.is_empty() {
s.push_str(&format!(
" Uncovered opcodes ({}): {:?}\n",
uncovered.len(),
uncovered
));
}
s
}
pub fn reset(&mut self) {
self.total_lookups = 0;
self.matches = 0;
self.misses = 0;
self.fallthroughs = 0;
self.opcode_matches.clear();
self.opcode_misses.clear();
}
}
impl AArch64IselTable {
pub fn new(is_64bit: bool) -> Self {
let mut patterns = aarch64_isel_table();
patterns.sort_by_key(|p| p.priority);
Self {
patterns,
is_64bit,
stats: IselStats::new(),
}
}
pub fn lookup(
&mut self,
ir_opcode: Opcode,
_operands: &[IselOperand],
_imm_value: Option<i64>,
) -> Option<(ArmOpcode, &IselPattern)> {
let opcode_id = ir_opcode as u32;
for pattern in &self.patterns {
if pattern.ir_opcode == ir_opcode {
if let Some(constraint) = &pattern.imm_constraint {
if let Some(val) = _imm_value {
if !evaluate_imm_constraint(constraint, val, self.is_64bit) {
continue;
}
} else {
continue;
}
}
if let Some(_feature) = pattern.required_feature {
}
self.stats.record_match(opcode_id);
return Some((pattern.result_opcode, pattern));
}
}
self.stats.record_miss(opcode_id);
None
}
pub fn patterns_for(&self, ir_opcode: Opcode) -> Vec<&IselPattern> {
self.patterns
.iter()
.filter(|p| p.ir_opcode == ir_opcode)
.collect()
}
pub fn has_coverage(&self, ir_opcode: Opcode) -> bool {
self.patterns.iter().any(|p| p.ir_opcode == ir_opcode)
}
pub fn covered_opcodes(&self) -> Vec<Opcode> {
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
for p in &self.patterns {
if seen.insert(p.ir_opcode as u32) {
result.push(p.ir_opcode);
}
}
result
}
pub fn coverage_report(&self, all_opcodes: &[Opcode]) -> String {
let covered: std::collections::HashSet<_> =
self.patterns.iter().map(|p| p.ir_opcode as u32).collect();
let uncovered: Vec<_> = all_opcodes
.iter()
.filter(|op| !covered.contains(&(**op as u32)))
.collect();
let mut s = String::new();
s.push_str(&format!(
"AArch64 ISel Coverage: {}/{} opcodes covered ({} uncovered)\n",
covered.len(),
all_opcodes.len(),
uncovered.len()
));
if !uncovered.is_empty() {
s.push_str("Uncovered opcodes:\n");
for op in &uncovered {
s.push_str(&format!(" {:?}\n", op));
}
}
s
}
pub fn lookup_with_features(
&mut self,
ir_opcode: Opcode,
operands: &[IselOperand],
imm_value: Option<i64>,
_available_features: &[&str],
) -> Option<(ArmOpcode, &IselPattern)> {
self.lookup(ir_opcode, operands, imm_value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_table_not_empty() {
let table = aarch64_isel_table();
assert!(!table.is_empty(), "ISel table should have patterns");
}
#[test]
fn test_lookup_add() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::Add, &[], None);
assert!(result.is_some());
assert_eq!(result.unwrap().0, ArmOpcode::ADD);
}
#[test]
fn test_lookup_sub() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::Sub, &[], None);
assert!(result.is_some());
assert_eq!(result.unwrap().0, ArmOpcode::SUB);
}
#[test]
fn test_lookup_mul() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::Mul, &[], None);
assert!(result.is_some());
}
#[test]
fn test_lookup_sdiv() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::SDiv, &[], None);
assert!(result.is_some());
assert_eq!(result.unwrap().0, ArmOpcode::SDIV);
}
#[test]
fn test_lookup_udiv_pow2() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::UDiv, &[], Some(8));
assert!(result.is_some());
assert_eq!(result.unwrap().0, ArmOpcode::LSR);
}
#[test]
fn test_lookup_ret() {
let mut isel = AArch64IselTable::new(true);
let result = isel.lookup(Opcode::Ret, &[], None);
assert!(result.is_some());
assert_eq!(result.unwrap().0, ArmOpcode::RET);
}
#[test]
fn test_imm_constraints_is_power_of_two() {
assert!(evaluate_imm_constraint(
&ImmConstraint::IsPowerOfTwo,
4,
true
));
assert!(evaluate_imm_constraint(
&ImmConstraint::IsPowerOfTwo,
16,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsPowerOfTwo,
6,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsPowerOfTwo,
0,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsPowerOfTwo,
-2,
true
));
}
#[test]
fn test_imm_constraints_is_valid_imm12() {
assert!(evaluate_imm_constraint(
&ImmConstraint::IsValidImm12,
0,
true
));
assert!(evaluate_imm_constraint(
&ImmConstraint::IsValidImm12,
4095,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsValidImm12,
4096,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsValidImm12,
-1,
true
));
}
#[test]
fn test_imm_constraints_shift_amount() {
assert!(evaluate_imm_constraint(
&ImmConstraint::IsShiftAmount64,
0,
true
));
assert!(evaluate_imm_constraint(
&ImmConstraint::IsShiftAmount64,
63,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsShiftAmount64,
64,
true
));
assert!(evaluate_imm_constraint(
&ImmConstraint::IsShiftAmount,
31,
true
));
assert!(!evaluate_imm_constraint(
&ImmConstraint::IsShiftAmount,
32,
true
));
}
#[test]
fn test_bitmask_validation() {
assert!(is_valid_bitmask_imm(0x5555555555555555, 64));
assert!(is_valid_bitmask_imm(0x0000FFFF0000FFFF, 64));
assert!(!is_valid_bitmask_imm(0x0000ABCD0000ABCD, 64));
assert!(!is_valid_bitmask_imm(0, 64));
}
#[test]
fn test_coverage_report() {
let isel = AArch64IselTable::new(true);
let all_ops = &[
Opcode::Add,
Opcode::Sub,
Opcode::Mul,
Opcode::SDiv,
Opcode::UDiv,
Opcode::And,
Opcode::Or,
Opcode::Xor,
Opcode::Ret,
Opcode::Br,
];
let report = isel.coverage_report(all_ops);
assert!(report.contains("AArch64 ISel Coverage"));
}
#[test]
fn test_has_coverage() {
let isel = AArch64IselTable::new(true);
assert!(isel.has_coverage(Opcode::Add));
assert!(isel.has_coverage(Opcode::Ret));
}
#[test]
fn test_priority_ordering() {
let table = aarch64_isel_table();
for i in 1..table.len() {
assert!(
table[i].priority >= table[i - 1].priority,
"Pattern table not sorted by priority at index {}: {} < {}",
i,
table[i].priority,
table[i - 1].priority
);
}
}
#[test]
fn test_stats_recording() {
let mut isel = AArch64IselTable::new(true);
assert_eq!(isel.stats.total_lookups, 0);
isel.lookup(Opcode::Add, &[], None);
assert_eq!(isel.stats.matches, 1);
isel.lookup(Opcode::Add, &[], None);
assert_eq!(isel.stats.matches, 2);
assert_eq!(isel.stats.total_lookups, 2);
isel.lookup(Opcode::FAdd, &[], None);
assert_eq!(isel.stats.misses, 1);
}
#[test]
fn test_stats_reset() {
let mut isel = AArch64IselTable::new(true);
isel.lookup(Opcode::Add, &[], None);
isel.stats.reset();
assert_eq!(isel.stats.matches, 0);
assert_eq!(isel.stats.total_lookups, 0);
}
#[test]
fn test_addr_mode_fits_ldr_str() {
let mode = AArch64AddrMode::BasePlusOffset {
base: 0,
offset: 32760,
};
assert!(mode.fits_ldr_str_unsigned(8));
let mode2 = AArch64AddrMode::BasePlusOffset {
base: 0,
offset: 32768,
};
assert!(!mode2.fits_ldr_str_unsigned(8));
}
#[test]
fn test_addr_mode_fits_ldur_stur() {
let mode = AArch64AddrMode::BasePlusOffset {
base: 0,
offset: -256,
};
assert!(mode.fits_ldur_stur());
let mode2 = AArch64AddrMode::BasePlusOffset {
base: 0,
offset: -257,
};
assert!(!mode2.fits_ldur_stur());
}
}