use crate::arm::arm_calling_convention::ArmCallingConvention;
use crate::arm::arm_instr_info::ArmOpcode;
use crate::arm::arm_register_info::*;
use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand, VirtReg};
use crate::opcode::Opcode;
use crate::value::ValueRef;
use std::collections::HashMap;
pub struct ArmInstructionSelector {
pub is_64bit: bool,
pub vreg_map: HashMap<usize, VirtReg>,
pub mbb: MachineBasicBlock,
pub func_name: String,
}
impl ArmInstructionSelector {
pub fn new(is_64bit: bool) -> Self {
Self {
is_64bit,
vreg_map: HashMap::new(),
mbb: MachineBasicBlock {
name: String::new(),
instructions: Vec::new(),
successors: Vec::new(),
},
func_name: String::new(),
}
}
pub fn select(&mut self, mf: &mut MachineFunction, func: &ValueRef) {
let f = func.borrow();
self.func_name = f.name.clone();
self.vreg_map.clear();
for op in &f.operands {
let ir_bb = op.borrow();
if ir_bb.is_basic_block() {
for inst_val in &ir_bb.operands {
let inst = inst_val.borrow();
if inst.is_instruction() && !inst.ty.is_void() {
let vr = mf.new_vreg();
self.vreg_map.insert(inst.vid as usize, vr);
}
}
}
}
for op in &f.operands {
let ir_bb = op.borrow();
if !ir_bb.is_basic_block() {
continue;
}
self.mbb = MachineBasicBlock {
name: ir_bb.name.clone(),
instructions: Vec::new(),
successors: Vec::new(),
};
for inst_val in &ir_bb.operands {
let inst = inst_val.borrow();
if !inst.is_instruction() {
continue;
}
let instrs = self.select_instruction(&inst);
for mi in instrs {
self.mbb.instructions.push(mi);
}
}
mf.push_block(self.mbb.clone());
}
}
pub fn select_instruction(&mut self, inst: &crate::value::Value) -> Vec<MachineInstr> {
match inst.opcode {
Some(Opcode::Add) => vec![self.lower_add(inst)],
Some(Opcode::Sub) => vec![self.lower_sub(inst)],
Some(Opcode::Mul) => vec![self.lower_mul(inst)],
Some(Opcode::SDiv) => self.lower_sdiv(inst),
Some(Opcode::UDiv) => self.lower_udiv(inst),
Some(Opcode::And) => vec![self.lower_and(inst)],
Some(Opcode::Or) => vec![self.lower_or(inst)],
Some(Opcode::Xor) => vec![self.lower_xor(inst)],
Some(Opcode::Shl) => vec![self.lower_shl(inst)],
Some(Opcode::LShr) => vec![self.lower_lshr(inst)],
Some(Opcode::AShr) => vec![self.lower_ashr(inst)],
Some(Opcode::ICmp) => self.lower_icmp(inst),
Some(Opcode::FCmp) => self.lower_fcmp(inst),
Some(Opcode::Br) => self.lower_br(inst),
Some(Opcode::Ret) => self.lower_ret(inst),
Some(Opcode::Call) => self.lower_call(inst),
Some(Opcode::Alloca) => vec![self.lower_alloca(inst)],
Some(Opcode::Load) => vec![self.lower_load(inst)],
Some(Opcode::Store) => vec![self.lower_store(inst)],
Some(Opcode::ZExt) => vec![self.lower_zext(inst)],
Some(Opcode::SExt) => vec![self.lower_sext(inst)],
Some(Opcode::Trunc) => vec![self.lower_trunc(inst)],
Some(Opcode::BitCast) => vec![self.lower_bitcast(inst)],
Some(Opcode::GetElementPtr) => vec![self.lower_getelementptr(inst)],
Some(Opcode::Select) => vec![self.lower_select(inst)],
Some(Opcode::Phi) => self.lower_phi(inst),
_ => Vec::new(),
}
}
pub fn lower_add(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::ADD as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_ADD as u32)
}
}
pub fn lower_sub(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::SUB as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_SUB as u32)
}
}
pub fn lower_mul(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::MUL as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_MUL as u32)
}
}
pub fn lower_sdiv(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, ArmOpcode::SDIV as u32)]
} else {
self.lower_soft_div(inst, "__aeabi_idiv")
}
}
pub fn lower_udiv(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, ArmOpcode::UDIV as u32)]
} else {
self.lower_soft_div(inst, "__aeabi_uidiv")
}
}
fn lower_soft_div(&self, inst: &crate::value::Value, func: &str) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
instrs.push(self.emit_mov_reg_reg(R0 as u32, src1));
instrs.push(self.emit_mov_reg_reg(R1 as u32, src2));
let mut call = MachineInstr::new(ArmOpcode::ARM_BL as u32);
call.operands.push(MachineOperand::Global(func.to_string()));
instrs.push(call);
instrs.push(self.emit_mov_reg_reg_def(def_vr, R0 as u32));
instrs
}
pub fn lower_and(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::AND as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_AND as u32)
}
}
pub fn lower_or(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::ORR as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_ORR as u32)
}
}
pub fn lower_xor(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::EOR as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_EOR as u32)
}
}
pub fn lower_shl(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::LSL as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_MOV as u32)
}
}
pub fn lower_lshr(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::LSR as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_MOV as u32)
}
}
pub fn lower_ashr(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, ArmOpcode::ASR as u32)
} else {
self.lower_three_reg_op(inst, ArmOpcode::ARM_MOV as u32)
}
}
pub fn lower_icmp(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
if self.is_64bit {
let mut cmp = MachineInstr::new(ArmOpcode::SUBS as u32);
cmp.operands.push(MachineOperand::PhysReg(XZR as u32));
cmp.push_reg(src1);
cmp.push_reg(src2_vr);
instrs.push(cmp);
let mut cset = MachineInstr::new(ArmOpcode::CSET as u32).with_def(def_vr);
cset.push_imm(0); instrs.push(cset);
} else {
let mut cmp = MachineInstr::new(ArmOpcode::ARM_CMP as u32);
cmp.push_reg(src1);
cmp.push_reg(src2_vr);
instrs.push(cmp);
let mut mov = MachineInstr::new(ArmOpcode::ARM_MOV as u32).with_def(def_vr);
mov.push_imm(0);
instrs.push(mov);
let mut mov1 = MachineInstr::new(ArmOpcode::ARM_MOV as u32).with_def(def_vr);
mov1.push_imm(1);
instrs.push(mov1);
}
instrs
}
pub fn lower_fcmp(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
if self.is_64bit {
let mut fcmp = MachineInstr::new(ArmOpcode::FCMP as u32);
fcmp.push_reg(src1);
fcmp.push_reg(src2_vr);
instrs.push(fcmp);
let mut cset = MachineInstr::new(ArmOpcode::CSET as u32).with_def(def_vr);
cset.push_imm(0); instrs.push(cset);
} else {
let mut vcmp = MachineInstr::new(ArmOpcode::ARM_VCMP as u32);
vcmp.push_reg(src1);
vcmp.push_reg(src2_vr);
instrs.push(vcmp);
let mut mov = MachineInstr::new(ArmOpcode::ARM_MOV as u32).with_def(def_vr);
mov.push_imm(0);
instrs.push(mov);
let mut mov1 = MachineInstr::new(ArmOpcode::ARM_MOV as u32).with_def(def_vr);
mov1.push_imm(1);
instrs.push(mov1);
}
instrs
}
pub fn lower_br(&mut self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let num_ops = inst.operands.len();
if num_ops == 1 {
let dest = inst.operands[0].borrow().name.clone();
self.mbb.successors.push(dest.clone());
if self.is_64bit {
let mut b = MachineInstr::new(ArmOpcode::B as u32);
b.push_label(&dest);
instrs.push(b);
} else {
let mut b = MachineInstr::new(ArmOpcode::ARM_B as u32);
b.push_label(&dest);
instrs.push(b);
}
} else if num_ops == 3 {
let cond_vid = inst.operands[0].borrow().vid as usize;
let t_label = inst.operands[1].borrow().name.clone();
let f_label = inst.operands[2].borrow().name.clone();
self.mbb.successors.push(t_label.clone());
self.mbb.successors.push(f_label.clone());
if let Some(&cond_vr) = self.vreg_map.get(&cond_vid) {
if self.is_64bit {
let mut cbnz = MachineInstr::new(ArmOpcode::CBNZ as u32);
cbnz.push_reg(cond_vr);
cbnz.push_label(&t_label);
instrs.push(cbnz);
let mut b = MachineInstr::new(ArmOpcode::B as u32);
b.push_label(&f_label);
instrs.push(b);
} else {
let mut cmp = MachineInstr::new(ArmOpcode::ARM_CMP as u32);
cmp.push_reg(cond_vr);
cmp.push_imm(0);
instrs.push(cmp);
let mut bne = MachineInstr::new(ArmOpcode::B_COND as u32);
bne.push_imm(1); bne.push_label(&t_label);
instrs.push(bne);
let mut b = MachineInstr::new(ArmOpcode::ARM_B as u32);
b.push_label(&f_label);
instrs.push(b);
}
}
}
instrs
}
pub fn lower_ret(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let num_ops = inst.operands.len();
if num_ops > 0 {
let val_vid = inst.operands[0].borrow().vid as usize;
if let Some(&vr) = self.vreg_map.get(&val_vid) {
if self.is_64bit {
instrs.push(self.emit_mov_reg_reg(X0 as u32, vr));
} else {
instrs.push(self.emit_mov_reg_reg(R0 as u32, vr));
}
}
}
if self.is_64bit {
instrs.push(MachineInstr::new(ArmOpcode::RET as u32));
} else {
let mut bx_lr = MachineInstr::new(ArmOpcode::ARM_BX as u32);
bx_lr
.operands
.push(MachineOperand::PhysReg(LR_ARM32 as u32));
instrs.push(bx_lr);
}
instrs
}
pub fn lower_call(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if inst.operands.is_empty() {
return instrs;
}
let callee = &inst.operands[0];
let callee_name = callee.borrow().name.clone();
let callee_vid = callee.borrow().vid as usize;
let is_direct = callee.borrow().is_function();
let arg_regs: &[u16] = if self.is_64bit {
&[X0, X1, X2, X3]
} else {
&[R0, R1, R2, R3]
};
for i in 1..inst.operands.len().min(arg_regs.len() + 1) {
let arg_vid = inst.operands[i].borrow().vid as usize;
if let Some(&arg_vr) = self.vreg_map.get(&arg_vid) {
let mut mov = MachineInstr::new(if self.is_64bit {
ArmOpcode::MOV as u32
} else {
ArmOpcode::ARM_MOV as u32
});
mov.operands
.push(MachineOperand::PhysReg(arg_regs[i - 1] as u32));
mov.push_reg(arg_vr);
instrs.push(mov);
}
}
if is_direct {
let mut call = MachineInstr::new(if self.is_64bit {
ArmOpcode::BL as u32
} else {
ArmOpcode::ARM_BL as u32
});
call.operands
.push(MachineOperand::Global(callee_name.clone()));
instrs.push(call);
} else {
if let Some(&callee_vr) = self.vreg_map.get(&callee_vid) {
let mut call = MachineInstr::new(if self.is_64bit {
ArmOpcode::BLR as u32
} else {
ArmOpcode::ARM_BLX as u32
});
call.push_reg(callee_vr);
instrs.push(call);
}
}
if !inst.ty.is_void() {
if let Some(&def_vr) = self.vreg_map.get(&(inst.vid as usize)) {
if self.is_64bit {
instrs.push(self.emit_mov_reg_reg_def(def_vr, X0 as u32));
} else {
instrs.push(self.emit_mov_reg_reg_def(def_vr, R0 as u32));
}
}
}
instrs
}
pub fn lower_alloca(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let opcode = if self.is_64bit {
ArmOpcode::SUB as u32
} else {
ArmOpcode::ARM_SUB as u32
};
let sp_reg = if self.is_64bit {
SP as u32
} else {
SP_ARM32 as u32
};
let mut sub = MachineInstr::new(opcode).with_def(def_vr);
sub.operands.push(MachineOperand::PhysReg(sp_reg));
sub.push_imm(8); sub
}
pub fn lower_load(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let opcode = if self.is_64bit {
ArmOpcode::LDR as u32
} else {
ArmOpcode::ARM_LDR as u32
};
let mut ldr = MachineInstr::new(opcode).with_def(def_vr);
ldr.push_reg(ptr_vr); ldr.push_imm(0); ldr
}
pub fn lower_store(&self, inst: &crate::value::Value) -> MachineInstr {
let val_vr = self.get_operand_vreg(&inst.operands[0]);
let ptr_vr = self.get_operand_vreg(&inst.operands[1]);
let opcode = if self.is_64bit {
ArmOpcode::STR as u32
} else {
ArmOpcode::ARM_STR as u32
};
let mut str_instr = MachineInstr::new(opcode);
str_instr.push_reg(val_vr);
str_instr.push_reg(ptr_vr);
str_instr.push_imm(0);
str_instr
}
pub fn lower_zext(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mov = MachineInstr::new(ArmOpcode::MOV as u32).with_def(def_vr);
mov.push_reg(src_vr);
mov
} else {
self.lower_simple_cast(inst)
}
}
pub fn lower_sext(&self, inst: &crate::value::Value) -> MachineInstr {
if self.is_64bit {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mov = MachineInstr::new(ArmOpcode::SXTW as u32).with_def(def_vr);
mov.push_reg(src_vr);
mov
} else {
self.lower_simple_cast(inst)
}
}
pub fn lower_trunc(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst)
}
pub fn lower_bitcast(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst)
}
fn lower_simple_cast(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let opcode = if self.is_64bit {
ArmOpcode::MOV as u32
} else {
ArmOpcode::ARM_MOV as u32
};
let mut mov = MachineInstr::new(opcode).with_def(def_vr);
mov.push_reg(src_vr);
mov
}
pub fn lower_getelementptr(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let base_vr = self.get_operand_vreg(&inst.operands[0]);
let mut offset: i64 = 0;
if inst.operands.len() > 1 {
let idx_val = &inst.operands[1];
let idx_name = idx_val.borrow().name.clone();
offset = idx_name.parse::<i64>().unwrap_or(0) * 8; }
let opcode = if self.is_64bit {
ArmOpcode::ADD as u32
} else {
ArmOpcode::ARM_ADD as u32
};
let mut add = MachineInstr::new(opcode).with_def(def_vr);
add.push_reg(base_vr);
add.push_imm(offset);
add
}
pub fn lower_select(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let _cond_vr = self.get_operand_vreg(&inst.operands[0]);
let true_vr = self.get_operand_vreg(&inst.operands[1]);
let false_vr = self.get_operand_vreg(&inst.operands[2]);
if self.is_64bit {
let mut csel = MachineInstr::new(ArmOpcode::CSEL as u32).with_def(def_vr);
csel.push_reg(true_vr);
csel.push_reg(false_vr);
csel.push_imm(0); csel
} else {
let mut mov = MachineInstr::new(ArmOpcode::ARM_MOV as u32).with_def(def_vr);
mov.push_reg(true_vr);
mov
}
}
pub fn lower_phi(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
Vec::new()
}
pub fn lower_bitfield_extract(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let lsb = &inst.operands[1];
let width = &inst.operands[2];
let lsb_val: i64 = lsb.borrow().name.parse().unwrap_or(0);
let width_val: i64 = width.borrow().name.parse().unwrap_or(1);
let mut mi = MachineInstr::new(ArmOpcode::UBFX as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(lsb_val);
mi.push_imm(width_val);
vec![mi]
}
pub fn lower_bitfield_insert(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::BFI as u32).with_def(def_vr);
mi.push_reg(src);
vec![mi]
}
pub fn lower_smull(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(ArmOpcode::SMULL as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
vec![mi]
}
pub fn lower_umull(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(ArmOpcode::UMULL as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
vec![mi]
}
pub fn lower_smaddl(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let acc = self.get_operand_vreg(&inst.operands[0]);
let src1 = self.get_operand_vreg(&inst.operands[1]);
let src2 = self.get_operand_vreg(&inst.operands[2]);
let mut mi = MachineInstr::new(ArmOpcode::SMADDL as u32).with_def(def_vr);
mi.push_reg(acc);
mi.push_reg(src1);
mi.push_reg(src2);
vec![mi]
}
pub fn lower_smsubl(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let acc = self.get_operand_vreg(&inst.operands[0]);
let src1 = self.get_operand_vreg(&inst.operands[1]);
let src2 = self.get_operand_vreg(&inst.operands[2]);
let mut mi = MachineInstr::new(ArmOpcode::SMSUBL as u32).with_def(def_vr);
mi.push_reg(acc);
mi.push_reg(src1);
mi.push_reg(src2);
vec![mi]
}
pub fn lower_sdiv_opt(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let src2 = &inst.operands[1];
if src2.borrow().is_constant() {
if let Ok(val) = src2.borrow().name.parse::<i64>() {
if val > 0 && (val & (val - 1)) == 0 {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let shift = val.trailing_zeros() as i64;
let mut mi = MachineInstr::new(ArmOpcode::ASR as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_imm(shift);
return vec![mi];
}
}
}
self.lower_sdiv(inst)
}
pub fn lower_udiv_opt(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let src2 = &inst.operands[1];
if src2.borrow().is_constant() {
if let Ok(val) = src2.borrow().name.parse::<i64>() {
if val > 0 && (val & (val - 1)) == 0 {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let shift = val.trailing_zeros() as i64;
let mut mi = MachineInstr::new(ArmOpcode::LSR as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_imm(shift);
return vec![mi];
}
}
}
self.lower_udiv(inst)
}
pub fn lower_ror(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = &inst.operands[1];
if src2.borrow().is_constant() {
let imm: i64 = src2.borrow().name.parse().unwrap_or(0);
let mut mi = MachineInstr::new(ArmOpcode::ROR as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_imm(imm);
vec![mi]
} else {
let src2_vr = self.get_operand_vreg(src2);
let mut mi = MachineInstr::new(ArmOpcode::ROR as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2_vr);
vec![mi]
}
}
pub fn lower_extr(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let lsb = &inst.operands[2];
let lsb_val: i64 = lsb.borrow().name.parse().unwrap_or(0);
let mut mi = MachineInstr::new(ArmOpcode::EXTR as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(lsb_val);
vec![mi]
}
pub fn lower_tst(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = &inst.operands[1];
let src2_vr = self.get_operand_vreg(src2);
let mut mi = MachineInstr::new(ArmOpcode::TST as u32);
mi.push_reg(src1);
mi.push_reg(src2_vr);
vec![mi]
}
pub fn lower_cmp_imm(&self, src_reg: VirtReg, imm: i64) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::SUBS as u32);
mi.operands.push(MachineOperand::PhysReg(XZR as u32));
mi.push_reg(src_reg);
mi.push_imm(imm);
mi
}
pub fn lower_ccmp(&self, src1: VirtReg, src2: VirtReg, nzcv: u32, cond: u32) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CCMP as u32);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(nzcv as i64);
mi.push_imm(cond as i64);
mi
}
pub fn lower_cbz(&self, reg: VirtReg, label: &str) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CBZ as u32);
mi.push_reg(reg);
mi.operands.push(MachineOperand::Label(label.to_string()));
mi
}
pub fn lower_cbnz(&self, reg: VirtReg, label: &str) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CBNZ as u32);
mi.push_reg(reg);
mi.operands.push(MachineOperand::Label(label.to_string()));
mi
}
pub fn lower_tbz(&self, reg: VirtReg, bit: u32, label: &str) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::TBZ as u32);
mi.push_reg(reg);
mi.push_imm(bit as i64);
mi.operands.push(MachineOperand::Label(label.to_string()));
mi
}
pub fn lower_tbnz(&self, reg: VirtReg, bit: u32, label: &str) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::TBNZ as u32);
mi.push_reg(reg);
mi.push_imm(bit as i64);
mi.operands.push(MachineOperand::Label(label.to_string()));
mi
}
pub fn lower_csel(
&self,
def_vr: VirtReg,
true_vr: VirtReg,
false_vr: VirtReg,
cond: u32,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CSEL as u32).with_def(def_vr);
mi.push_reg(true_vr);
mi.push_reg(false_vr);
mi.push_imm(cond as i64);
mi
}
pub fn lower_csinc(
&self,
def_vr: VirtReg,
src1: VirtReg,
src2: VirtReg,
cond: u32,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CSINC as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(cond as i64);
mi
}
pub fn lower_csinv(
&self,
def_vr: VirtReg,
src1: VirtReg,
src2: VirtReg,
cond: u32,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CSINV as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(cond as i64);
mi
}
pub fn lower_csneg(
&self,
def_vr: VirtReg,
src1: VirtReg,
src2: VirtReg,
cond: u32,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::CSNEG as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(cond as i64);
mi
}
pub fn lower_neon_add(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ADD_V as u32)
}
pub fn lower_neon_sub(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SUB_V as u32)
}
pub fn lower_neon_mul(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::MUL_V as u32)
}
pub fn lower_neon_mla(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::MLA_V as u32)
}
pub fn lower_neon_mls(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::MLS_V as u32)
}
pub fn lower_neon_and(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::AND_V as u32)
}
pub fn lower_neon_orr(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ORR_V as u32)
}
pub fn lower_neon_eor(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::EOR_V as u32)
}
pub fn lower_neon_bic(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::BIC_V as u32)
}
pub fn lower_neon_orn(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ORN_V as u32)
}
pub fn lower_neon_shl_imm(
&self,
inst: &crate::value::Value,
_elem_size: u8,
shift: u8,
) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SHL_V as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(shift as i64);
mi
}
pub fn lower_neon_ushr_imm(
&self,
inst: &crate::value::Value,
_elem_size: u8,
shift: u8,
) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::USHR as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(shift as i64);
mi
}
pub fn lower_neon_sshr_imm(
&self,
inst: &crate::value::Value,
_elem_size: u8,
shift: u8,
) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SSHR as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(shift as i64);
mi
}
pub fn lower_neon_sshl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SSHL as u32)
}
pub fn lower_neon_ushl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::USHL as u32)
}
pub fn lower_neon_cmeq(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMEQ as u32)
}
pub fn lower_neon_cmge(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMGE as u32)
}
pub fn lower_neon_cmgt(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMGT as u32)
}
pub fn lower_neon_cmhs(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMHS as u32)
}
pub fn lower_neon_cmhi(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMHI as u32)
}
pub fn lower_neon_cmtst(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::CMTST as u32)
}
pub fn lower_neon_addv(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SADDLV as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_smaxv(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SMAXV as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_umaxv(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::UMAXV as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_sminv(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SMINV as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_uminv(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::UMINV as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_tbl(
&self,
def_vr: VirtReg,
table_vr: VirtReg,
idx_vr: VirtReg,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::TBL as u32).with_def(def_vr);
mi.push_reg(table_vr);
mi.push_reg(idx_vr);
mi
}
pub fn lower_neon_trn1(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::TRN1 as u32)
}
pub fn lower_neon_trn2(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::TRN2 as u32)
}
pub fn lower_neon_zip1(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ZIP1 as u32)
}
pub fn lower_neon_zip2(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ZIP2 as u32)
}
pub fn lower_neon_uzp1(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UZP1 as u32)
}
pub fn lower_neon_uzp2(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UZP2 as u32)
}
pub fn lower_neon_ext(&self, inst: &crate::value::Value, index: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(ArmOpcode::EXT as u32).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi.push_imm(index as i64);
mi
}
pub fn lower_neon_xtn(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::XTN as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_sqxtn(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SQXTN as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_uqxtn(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::UQXTN as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_rshrn(
&self,
inst: &crate::value::Value,
_elem_size: u8,
shift: u8,
) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::RSHRN as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(shift as i64);
mi
}
pub fn lower_neon_shrn(
&self,
inst: &crate::value::Value,
_elem_size: u8,
shift: u8,
) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SHRN as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(shift as i64);
mi
}
pub fn lower_neon_saddl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SADDL as u32)
}
pub fn lower_neon_uaddl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UADDL as u32)
}
pub fn lower_neon_ssubl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SSUBL as u32)
}
pub fn lower_neon_usubl(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::USUBL as u32)
}
pub fn lower_neon_smull(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SMULL as u32)
}
pub fn lower_neon_umull(&self, inst: &crate::value::Value, _elem_size: u8) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UMULL as u32)
}
pub fn lower_neon_dup_scalar(
&self,
def_vr: VirtReg,
src_vr: VirtReg,
index: u8,
_elem_size: u8,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::DUP as u32).with_def(def_vr);
mi.push_reg(src_vr);
mi.push_imm(index as i64);
mi
}
pub fn lower_neon_mov_element(
&self,
def_vr: VirtReg,
src_vr: VirtReg,
dst_idx: u8,
src_idx: u8,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::SMOV as u32).with_def(def_vr);
mi.push_reg(src_vr);
mi.push_imm(dst_idx as i64);
mi.push_imm(src_idx as i64);
mi
}
pub fn lower_neon_ins_element(
&self,
def_vr: VirtReg,
src_vr: VirtReg,
dst_idx: u8,
src_idx: u8,
) -> MachineInstr {
let mut mi = MachineInstr::new(ArmOpcode::INS as u32).with_def(def_vr);
mi.push_reg(src_vr);
mi.push_imm(dst_idx as i64);
mi.push_imm(src_idx as i64);
mi
}
pub fn lower_neon_fadd(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FADD as u32)
}
pub fn lower_neon_fsub(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FSUB as u32)
}
pub fn lower_neon_fmul(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMUL as u32)
}
pub fn lower_neon_fdiv(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FDIV as u32)
}
pub fn lower_neon_fabs(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FABS as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fneg(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FNEG as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fsqrt(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FSQRT as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fmla(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMLA as u32)
}
pub fn lower_neon_fmls(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMLS as u32)
}
pub fn lower_neon_fcmeq(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FCMEQ as u32)
}
pub fn lower_neon_fcmge(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FCMGE as u32)
}
pub fn lower_neon_fcmgt(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FCMGT as u32)
}
pub fn lower_neon_facge(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FACGE as u32)
}
pub fn lower_neon_facgt(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FACGT as u32)
}
pub fn lower_neon_fmax(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMAX as u32)
}
pub fn lower_neon_fmin(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMIN as u32)
}
pub fn lower_neon_fmaxnm(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMAXNM as u32)
}
pub fn lower_neon_fminnm(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMINNM as u32)
}
pub fn lower_neon_fmaxp(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMAXP as u32)
}
pub fn lower_neon_fminp(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::FMINP as u32)
}
pub fn lower_neon_addp(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::ADDP as u32)
}
pub fn lower_neon_smax(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SMAX as u32)
}
pub fn lower_neon_umax(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UMAX as u32)
}
pub fn lower_neon_smin(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::SMIN as u32)
}
pub fn lower_neon_umin(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_three_reg_op(inst, ArmOpcode::UMIN as u32)
}
pub fn lower_neon_frinti(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTI as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frintz(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTZ as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frintp(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTP as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frintm(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTM as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frintx(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTX as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frinta(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTA as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_frintn(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FRINTN as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_scvtf(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::SCVTF as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_ucvtf(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::UCVTF as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fcvtzs(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FCVTZS as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fcvtzu(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FCVTZU as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_fcvt(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::FCVT as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_rev16(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::REV16_V as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_rev32(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::REV32_V as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neon_rev64(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(ArmOpcode::REV64 as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
fn lower_three_reg_op(&self, inst: &crate::value::Value, opcode: u32) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = &inst.operands[1];
if src2.borrow().is_constant() {
let imm_val: i64 = src2.borrow().name.parse().unwrap_or(0);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src1);
mi.push_imm(imm_val);
mi
} else {
let src2_vr = self.get_operand_vreg(src2);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src1);
mi.push_reg(src2_vr);
mi
}
}
fn get_vreg_by_vid(&self, vid: usize) -> VirtReg {
self.vreg_map.get(&vid).copied().unwrap_or(0)
}
fn get_operand_vreg(&self, op: &ValueRef) -> VirtReg {
let vid = op.borrow().vid as usize;
self.vreg_map.get(&vid).copied().unwrap_or(0)
}
fn emit_mov_reg_reg(&self, dest: u32, src: VirtReg) -> MachineInstr {
let mut mov = MachineInstr::new(if self.is_64bit {
ArmOpcode::MOV as u32
} else {
ArmOpcode::ARM_MOV as u32
});
mov.operands.push(MachineOperand::PhysReg(dest));
mov.push_reg(src);
mov
}
fn emit_mov_reg_reg_def(&self, def: VirtReg, src: u32) -> MachineInstr {
let mut mov = MachineInstr::new(if self.is_64bit {
ArmOpcode::MOV as u32
} else {
ArmOpcode::ARM_MOV as u32
})
.with_def(def);
mov.operands.push(MachineOperand::PhysReg(src));
mov
}
}
use crate::arm::arm_register_info::{
LR_ARM32, R0, R1, R10, R2, R3, R4, R5, R6, R7, R8, R9, SP, SP_ARM32, X0, X1, X2, X3, XZR,
};
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::{MachineBasicBlock, MachineFunction};
use crate::ir_builder::IRBuilder;
use crate::module::Module;
use crate::opcode::Opcode;
use crate::types::{Type, TypeKind};
use crate::value::{SubclassKind, Value, ValueRef};
use std::cell::RefCell;
use std::rc::Rc;
fn build_simple_ir() -> (Module, ValueRef) {
let mut module = Module::new("test_module");
let i32_type = Type::i32();
let i32_id = i32_type.id;
let func_ty = Type::function_type_with(i32_id, vec![i32_id, i32_id], false);
let func_val = Rc::new(RefCell::new(
Value::new(func_ty)
.named("test_func")
.with_subclass(SubclassKind::Function),
));
let bb = Rc::new(RefCell::new(
Value::new(Type::void())
.named("entry")
.with_subclass(SubclassKind::BasicBlock),
));
let inst_ret = Rc::new(RefCell::new({
let mut v = Value::new(Type::void()).named("ret");
v.set_opcode(Opcode::Ret);
v.subclass = SubclassKind::Instruction;
v
}));
{
let mut func = func_val.borrow_mut();
func.operands.push(Rc::clone(&bb));
let mut block = bb.borrow_mut();
block.operands.push(Rc::clone(&inst_ret));
}
module.add_function(Rc::clone(&func_val));
(module, func_val)
}
fn build_add_ir() -> (ValueRef, ValueRef) {
let i32_type = Type::i32();
let i32_id = i32_type.id;
let func_ty = Type::function_type_with(i32_id, vec![], false);
let func_val = Rc::new(RefCell::new(
Value::new(func_ty)
.named("add_func")
.with_subclass(SubclassKind::Function),
));
let bb = Rc::new(RefCell::new(
Value::new(Type::void())
.named("entry")
.with_subclass(SubclassKind::BasicBlock),
));
let const_5 = Rc::new(RefCell::new({
let mut v = Value::new(i32_type.clone());
v.name = "5".to_string();
v.subclass = SubclassKind::Constant;
v
}));
let const_3 = Rc::new(RefCell::new({
let mut v = Value::new(i32_type.clone());
v.name = "3".to_string();
v.subclass = SubclassKind::Constant;
v
}));
let add_inst = Rc::new(RefCell::new({
let mut v = Value::new(i32_type.clone()).named("add_result");
v.set_opcode(Opcode::Add);
v.subclass = SubclassKind::Instruction;
v.operands.push(Rc::clone(&const_5));
v.operands.push(Rc::clone(&const_3));
v
}));
let ret_inst = Rc::new(RefCell::new({
let mut v = Value::new(Type::void()).named("ret");
v.set_opcode(Opcode::Ret);
v.subclass = SubclassKind::Instruction;
v.operands.push(Rc::clone(&add_inst));
v
}));
{
let mut func = func_val.borrow_mut();
func.operands.push(Rc::clone(&bb));
let mut block = bb.borrow_mut();
block.operands.push(Rc::clone(&add_inst));
block.operands.push(Rc::clone(&ret_inst));
}
(func_val, add_inst)
}
fn build_branch_ir() -> ValueRef {
let i1_type = Type::i1();
let void_id = Type::void().id;
let func_ty = Type::function_type_with(void_id, vec![], false);
let func_val = Rc::new(RefCell::new(
Value::new(func_ty)
.named("br_func")
.with_subclass(SubclassKind::Function),
));
let entry_bb = Rc::new(RefCell::new(
Value::new(Type::void())
.named("entry")
.with_subclass(SubclassKind::BasicBlock),
));
let true_bb = Rc::new(RefCell::new(
Value::new(Type::void())
.named("true_bb")
.with_subclass(SubclassKind::BasicBlock),
));
let false_bb = Rc::new(RefCell::new(
Value::new(Type::void())
.named("false_bb")
.with_subclass(SubclassKind::BasicBlock),
));
let const_true = Rc::new(RefCell::new({
let mut v = Value::new(i1_type.clone());
v.name = "1".to_string();
v.subclass = SubclassKind::Constant;
v
}));
let br_inst = Rc::new(RefCell::new({
let mut v = Value::new(Type::void()).named("br");
v.set_opcode(Opcode::Br);
v.subclass = SubclassKind::Instruction;
v.operands.push(Rc::clone(&const_true));
v.operands.push(Rc::clone(&true_bb));
v.operands.push(Rc::clone(&false_bb));
v
}));
let ret_true = Rc::new(RefCell::new({
let mut v = Value::new(Type::void()).named("ret_true");
v.set_opcode(Opcode::Ret);
v.subclass = SubclassKind::Instruction;
v
}));
let ret_false = Rc::new(RefCell::new({
let mut v = Value::new(Type::void()).named("ret_false");
v.set_opcode(Opcode::Ret);
v.subclass = SubclassKind::Instruction;
v
}));
{
let mut func = func_val.borrow_mut();
func.operands.push(Rc::clone(&entry_bb));
func.operands.push(Rc::clone(&true_bb));
func.operands.push(Rc::clone(&false_bb));
let mut entry = entry_bb.borrow_mut();
entry.operands.push(Rc::clone(&br_inst));
let mut tbb = true_bb.borrow_mut();
tbb.operands.push(Rc::clone(&ret_true));
let mut fbb = false_bb.borrow_mut();
fbb.operands.push(Rc::clone(&ret_false));
}
func_val
}
#[test]
fn test_new_aarch64() {
let isel = ArmInstructionSelector::new(true);
assert!(isel.is_64bit);
assert!(isel.vreg_map.is_empty());
}
#[test]
fn test_new_arm32() {
let isel = ArmInstructionSelector::new(false);
assert!(!isel.is_64bit);
}
#[test]
fn test_select_simple_function() {
let mut isel = ArmInstructionSelector::new(true);
let (_, func) = build_simple_ir();
let mut mf = MachineFunction::new("test");
isel.select(&mut mf, &func);
assert!(!mf.blocks.is_empty());
let bb = &mf.blocks[0];
assert!(!bb.instructions.is_empty());
}
#[test]
fn test_select_add_instruction() {
let mut isel = ArmInstructionSelector::new(true);
let (func, _add_inst) = build_add_ir();
let mut mf = MachineFunction::new("add_func");
isel.select(&mut mf, &func);
assert!(!mf.blocks.is_empty());
let bb = &mf.blocks[0];
assert!(bb.instructions.len() >= 2);
}
#[test]
fn test_select_branch_function() {
let mut isel = ArmInstructionSelector::new(true);
let func = build_branch_ir();
let mut mf = MachineFunction::new("br_func");
isel.select(&mut mf, &func);
assert!(mf.blocks.len() >= 2);
}
#[test]
fn test_lower_add_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let mi = isel.lower_add(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::ADD as u32);
assert!(mi.def.is_some());
}
#[test]
fn test_lower_add_arm32() {
let isel = ArmInstructionSelector::new(false);
let (_, add_inst) = build_add_ir();
let mi = isel.lower_add(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::ARM_ADD as u32);
}
#[test]
fn test_lower_sub_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Sub);
}
let mi = isel.lower_sub(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::SUB as u32);
}
#[test]
fn test_lower_mul_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Mul);
}
let mi = isel.lower_mul(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::MUL as u32);
}
#[test]
fn test_lower_and_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::And);
}
let mi = isel.lower_and(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::AND as u32);
}
#[test]
fn test_lower_or_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Or);
}
let mi = isel.lower_or(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::ORR as u32);
}
#[test]
fn test_lower_xor_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Xor);
}
let mi = isel.lower_xor(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::EOR as u32);
}
#[test]
fn test_lower_shl_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Shl);
}
let mi = isel.lower_shl(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::LSL as u32);
}
#[test]
fn test_lower_lshr_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::LShr);
}
let mi = isel.lower_lshr(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::LSR as u32);
}
#[test]
fn test_lower_ashr_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::AShr);
}
let mi = isel.lower_ashr(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::ASR as u32);
}
#[test]
fn test_lower_icmp_aarch64_has_subs_and_cset() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::ICmp);
}
let mut isel_full = ArmInstructionSelector::new(true);
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let vr3 = mf.new_vreg();
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
let const5_vid = inst_ref.operands[0].borrow().vid as usize;
let const3_vid = inst_ref.operands[1].borrow().vid as usize;
isel_full.vreg_map.insert(const5_vid, vr2);
isel_full.vreg_map.insert(const3_vid, vr3);
let instrs = isel_full.lower_icmp(&inst_ref);
assert!(instrs.len() >= 2);
assert_eq!(instrs[0].opcode, ArmOpcode::SUBS as u32);
assert_eq!(instrs[1].opcode, ArmOpcode::CSET as u32);
}
#[test]
fn test_lower_zext_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::ZExt);
}
let mi = isel.lower_zext(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::MOV as u32);
}
#[test]
fn test_lower_trunc_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Trunc);
}
let mi = isel.lower_trunc(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::MOV as u32);
}
#[test]
fn test_lower_alloca() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Alloca);
}
let mi = isel.lower_alloca(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::SUB as u32);
}
#[test]
fn test_lower_load_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Load);
}
let mi = isel.lower_load(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::LDR as u32);
}
#[test]
fn test_lower_store_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::Store);
}
let mi = isel.lower_store(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::STR as u32);
}
#[test]
fn test_lower_getelementptr() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.set_opcode(Opcode::GetElementPtr);
}
let mi = isel.lower_getelementptr(&add_inst.borrow());
assert_eq!(mi.opcode, ArmOpcode::ADD as u32);
}
#[test]
fn test_lower_ret_aarch64() {
let isel = ArmInstructionSelector::new(true);
let (_, func) = build_simple_ir();
let f = func.borrow();
let bb = f.operands[0].borrow();
let ret_inst = &bb.operands[0];
let instrs = isel.lower_ret(&ret_inst.borrow());
assert!(!instrs.is_empty());
assert_eq!(instrs.last().unwrap().opcode, ArmOpcode::RET as u32);
}
#[test]
fn test_lower_ret_arm32() {
let isel = ArmInstructionSelector::new(false);
let (_, func) = build_simple_ir();
let f = func.borrow();
let bb = f.operands[0].borrow();
let ret_inst = &bb.operands[0];
let instrs = isel.lower_ret(&ret_inst.borrow());
assert!(!instrs.is_empty());
assert_eq!(instrs.last().unwrap().opcode, ArmOpcode::ARM_BX as u32);
}
#[test]
fn test_select_instruction_dispatch() {
let mut isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let instrs = isel.select_instruction(&add_inst.borrow());
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::ADD as u32);
}
#[test]
fn test_select_instruction_unknown_returns_empty() {
let mut isel = ArmInstructionSelector::new(true);
let mut unknown = Value::new(Type::void());
unknown.set_opcode(Opcode::Fence); let instrs = isel.select_instruction(&unknown);
assert!(instrs.is_empty());
}
#[test]
fn test_lower_smull() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let vr3 = mf.new_vreg();
let mut isel_full = ArmInstructionSelector::new(true);
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
isel_full
.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
isel_full
.vreg_map
.insert(inst_ref.operands[1].borrow().vid as usize, vr3);
let instrs = isel_full.lower_smull(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SMULL as u32);
}
#[test]
fn test_lower_umull() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let vr3 = mf.new_vreg();
let mut isel_full = ArmInstructionSelector::new(true);
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
isel_full
.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
isel_full
.vreg_map
.insert(inst_ref.operands[1].borrow().vid as usize, vr3);
let instrs = isel_full.lower_umull(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::UMULL as u32);
}
#[test]
fn test_lower_smaddl() {
let isel_full = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let mut mf = MachineFunction::new("test");
let vr_d = mf.new_vreg();
let vr_s1 = mf.new_vreg();
let vr_s2 = mf.new_vreg();
let vr_s3 = mf.new_vreg();
let inst_ref = add_inst.borrow();
let mut map = std::collections::HashMap::new();
map.insert(inst_ref.vid as usize, vr_d);
map.insert(inst_ref.operands[0].borrow().vid as usize, vr_s1);
map.insert(inst_ref.operands[1].borrow().vid as usize, vr_s2);
let (_, add_inst2) = build_add_ir();
map.insert(add_inst2.borrow().operands[0].borrow().vid as usize, vr_s3);
let instrs = isel_full.lower_smaddl(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SMADDL as u32);
}
#[test]
fn test_lower_smsubl() {
let isel_full = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel_full.lower_smsubl(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SMSUBL as u32);
}
#[test]
fn test_lower_sdiv_opt_pow2() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.operands[1].borrow_mut().name = "8".to_string();
}
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let mut isel_full = ArmInstructionSelector::new(true);
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
isel_full
.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel_full.lower_sdiv_opt(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::ASR as u32);
}
#[test]
fn test_lower_udiv_opt_pow2() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.operands[1].borrow_mut().name = "4".to_string();
}
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let mut isel_full = ArmInstructionSelector::new(true);
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
isel_full
.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel_full.lower_udiv_opt(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::LSR as u32);
}
#[test]
fn test_lower_bitfield_extract() {
let isel_full = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let mut isel = ArmInstructionSelector::new(true);
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel.lower_bitfield_extract(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::UBFX as u32);
}
#[test]
fn test_lower_bitfield_insert() {
let isel_full = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let mut isel = ArmInstructionSelector::new(true);
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel.lower_bitfield_insert(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::BFI as u32);
}
#[test]
fn test_lower_cbz() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_cbz(1, "target");
assert_eq!(mi.opcode, ArmOpcode::CBZ as u32);
}
#[test]
fn test_lower_cbnz() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_cbnz(2, "loop");
assert_eq!(mi.opcode, ArmOpcode::CBNZ as u32);
}
#[test]
fn test_lower_tbz() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_tbz(3, 5, "label");
assert_eq!(mi.opcode, ArmOpcode::TBZ as u32);
}
#[test]
fn test_lower_tbnz() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_tbnz(4, 7, "label");
assert_eq!(mi.opcode, ArmOpcode::TBNZ as u32);
}
#[test]
fn test_lower_csel() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_csel(10, 11, 12, 0);
assert_eq!(mi.opcode, ArmOpcode::CSEL as u32);
}
#[test]
fn test_lower_csinc() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_csinc(13, 14, 15, 1);
assert_eq!(mi.opcode, ArmOpcode::CSINC as u32);
}
#[test]
fn test_lower_csinv() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_csinv(16, 17, 18, 2);
assert_eq!(mi.opcode, ArmOpcode::CSINV as u32);
}
#[test]
fn test_lower_csneg() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_csneg(19, 20, 21, 3);
assert_eq!(mi.opcode, ArmOpcode::CSNEG as u32);
}
#[test]
fn test_lower_tst() {
let isel_obj = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let vr3 = mf.new_vreg();
let mut isel = ArmInstructionSelector::new(true);
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
isel.vreg_map
.insert(inst_ref.operands[1].borrow().vid as usize, vr3);
let instrs = isel.lower_tst(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::TST as u32);
}
#[test]
fn test_lower_cmp_imm() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_cmp_imm(5, 42);
assert_eq!(mi.opcode, ArmOpcode::SUBS as u32);
}
#[test]
fn test_lower_ccmp() {
let isel = ArmInstructionSelector::new(true);
let mi = isel.lower_ccmp(1, 2, 0, 0);
assert_eq!(mi.opcode, ArmOpcode::CCMP as u32);
}
#[test]
fn test_lower_extr() {
let isel_full = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let vr3 = mf.new_vreg();
let mut isel = ArmInstructionSelector::new(true);
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
isel.vreg_map
.insert(inst_ref.operands[1].borrow().vid as usize, vr3);
let instrs = isel.lower_extr(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::EXTR as u32);
}
#[test]
fn test_lower_ror_imm() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
{
let mut inst = add_inst.borrow_mut();
inst.operands[1].borrow_mut().name = "3".to_string();
}
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
let mut isel_full = ArmInstructionSelector::new(true);
let inst_ref = add_inst.borrow();
isel_full.vreg_map.insert(inst_ref.vid as usize, vr1);
isel_full
.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel_full.lower_ror(&inst_ref);
assert!(!instrs.is_empty());
assert_eq!(instrs[0].opcode, ArmOpcode::ROR as u32);
}
#[test]
fn test_neon_add_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_add(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::ADD_V as u32);
}
#[test]
fn test_neon_sub_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_sub(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SUB_V as u32);
}
#[test]
fn test_neon_mul_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_mul(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::MUL_V as u32);
}
#[test]
fn test_neon_and_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_and(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::AND_V as u32);
}
#[test]
fn test_neon_orr_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_orr(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::ORR_V as u32);
}
#[test]
fn test_neon_eor_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_eor(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::EOR_V as u32);
}
#[test]
fn test_neon_cmeq_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_cmeq(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::CMEQ as u32);
}
#[test]
fn test_neon_cmge_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_cmge(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::CMGE as u32);
}
#[test]
fn test_neon_cmgt_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_cmgt(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::CMGT as u32);
}
#[test]
fn test_neon_saddlv_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel.lower_neon_addv(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SADDLV as u32);
}
#[test]
fn test_neon_saddl_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_saddl(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SADDL as u32);
}
#[test]
fn test_neon_ssubl_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_ssubl(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SSUBL as u32);
}
#[test]
fn test_neon_smull_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_smull(&inst_ref, 4);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SMULL as u32);
}
#[test]
fn test_neon_fadd_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_fadd(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FADD as u32);
}
#[test]
fn test_neon_fmla_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_fmla(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FMLA as u32);
}
#[test]
fn test_neon_fcmeq_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_fcmeq(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FCMEQ as u32);
}
#[test]
fn test_neon_fmax_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_fmax(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FMAX as u32);
}
#[test]
fn test_neon_fmin_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_fmin(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FMIN as u32);
}
#[test]
fn test_neon_addp_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let instrs = isel.lower_neon_addp(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::ADDP as u32);
}
#[test]
fn test_neon_scvtf_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel.lower_neon_scvtf(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::SCVTF as u32);
}
#[test]
fn test_neon_frinti_isel() {
let isel = ArmInstructionSelector::new(true);
let (_, add_inst) = build_add_ir();
let inst_ref = add_inst.borrow();
let mut mf = MachineFunction::new("test");
let vr1 = mf.new_vreg();
let vr2 = mf.new_vreg();
isel.vreg_map.insert(inst_ref.vid as usize, vr1);
isel.vreg_map
.insert(inst_ref.operands[0].borrow().vid as usize, vr2);
let instrs = isel.lower_neon_frinti(&inst_ref);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, ArmOpcode::FRINTI as u32);
}
}