use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand, VirtReg};
use crate::opcode::Opcode;
use crate::value::ValueRef;
use crate::x86::x86_full_instr_info::X86ConditionCode;
use crate::x86::x86_instr_info::X86Opcode;
use crate::x86::x86_register_info::*;
use crate::x86::x86_subtarget::X86Subtarget;
use std::collections::HashMap;
pub struct X86InstructionSelector {
pub subtarget: X86Subtarget,
pub vreg_map: HashMap<usize, VirtReg>,
pub mbb: MachineBasicBlock,
pub func_name: String,
}
impl X86InstructionSelector {
pub fn new(subtarget: X86Subtarget) -> Self {
Self {
subtarget,
vreg_map: HashMap::new(),
mbb: MachineBasicBlock {
name: String::new(),
instructions: Vec::new(),
successors: Vec::new(),
..Default::default()
},
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(),
..Default::default()
};
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(&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::FAdd) => self.lower_fadd(inst),
Some(Opcode::FSub) => self.lower_fsub(inst),
Some(Opcode::FMul) => self.lower_fmul(inst),
Some(Opcode::FDiv) => self.lower_fdiv(inst),
Some(Opcode::FRem) => self.lower_frem(inst),
Some(Opcode::FPTrunc) => vec![self.lower_fptrunc(inst)],
Some(Opcode::FPExt) => vec![self.lower_fpext(inst)],
Some(Opcode::FPToSI) => vec![self.lower_fptosi(inst)],
Some(Opcode::FPToUI) => vec![self.lower_fptoui(inst)],
Some(Opcode::SIToFP) => vec![self.lower_sitofp(inst)],
Some(Opcode::UIToFP) => vec![self.lower_uitofp(inst)],
Some(Opcode::ExtractElement) => vec![self.lower_extract_element(inst)],
Some(Opcode::InsertElement) => self.lower_insert_element(inst),
Some(Opcode::ShuffleVector) => vec![self.lower_shuffle_vector(inst)],
Some(Opcode::Phi) => self.lower_phi(inst),
Some(Opcode::GetElementPtr) => vec![self.lower_getelementptr(inst)],
Some(Opcode::Select) => vec![self.lower_select(inst)],
_ => Vec::new(),
}
}
pub fn lower_add(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::ADD)
}
pub fn lower_sub(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::SUB)
}
pub fn lower_mul(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::IMUL)
}
pub fn lower_sdiv(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_division(inst, true)
}
pub fn lower_udiv(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_division(inst, false)
}
fn lower_division(&self, inst: &crate::value::Value, signed: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_or_create_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 mov_rax = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov_rax.operands.push(MachineOperand::PhysReg(RAX as u32));
mov_rax.push_reg(src1);
instrs.push(mov_rax);
if signed {
instrs.push(MachineInstr::new(X86Opcode::CMP as u32 + 1000)); } else {
instrs.push(self.emit_binary_op(
X86Opcode::XOR as u32,
RDX as u32,
RDX as u32,
RDX as u32,
));
}
let div_opcode = if signed {
X86Opcode::IDIV as u32
} else {
X86Opcode::DIV as u32
};
let mut div = MachineInstr::new(div_opcode);
div.push_reg(src2);
instrs.push(div);
let mut mov_result = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov_result
.operands
.push(MachineOperand::PhysReg(RAX as u32));
instrs.push(mov_result);
instrs
}
pub fn lower_and(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::AND)
}
pub fn lower_or(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::OR)
}
pub fn lower_xor(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::XOR)
}
pub fn lower_shl(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::SHL)
}
pub fn lower_lshr(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::SHR)
}
pub fn lower_ashr(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::SAR)
}
pub fn lower_icmp(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_or_create_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 cmp = MachineInstr::new(X86Opcode::CMP as u32);
cmp.push_reg(src1);
cmp.push_reg(src2);
instrs.push(cmp);
let pred = inst.name.strip_prefix("icmp.").unwrap_or("eq");
let setcc_opcode = Self::icmp_pred_to_setcc(pred);
let mut set = MachineInstr::new(setcc_opcode).with_def(def_vr);
set.push_reg(def_vr);
instrs.push(set);
instrs
}
fn icmp_pred_to_setcc(pred: &str) -> u32 {
match pred {
"eq" => X86Opcode::SETE as u32,
"ne" => X86Opcode::SETNE as u32,
"ugt" => X86Opcode::SETA as u32,
"uge" => X86Opcode::SETAE as u32,
"ult" => X86Opcode::SETB as u32,
"ule" => X86Opcode::SETBE as u32,
"sgt" => X86Opcode::SETG as u32,
"sge" => X86Opcode::SETGE as u32,
"slt" => X86Opcode::SETL as u32,
"sle" => X86Opcode::SETLE as u32,
_ => X86Opcode::SETE as u32,
}
}
#[allow(dead_code)]
fn icmp_pred_to_jcc(pred: &str) -> u32 {
match pred {
"eq" => X86Opcode::JE as u32,
"ne" => X86Opcode::JNE as u32,
"ugt" => X86Opcode::JA as u32,
"uge" => X86Opcode::JAE as u32,
"ult" => X86Opcode::JB as u32,
"ule" => X86Opcode::JBE as u32,
"sgt" => X86Opcode::JG as u32,
"sge" => X86Opcode::JGE as u32,
"slt" => X86Opcode::JL as u32,
"sle" => X86Opcode::JLE as u32,
_ => X86Opcode::JE as u32,
}
}
pub fn lower_br(&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();
let mut jmp = MachineInstr::new(X86Opcode::JMP as u32);
jmp.push_label(&dest);
instrs.push(jmp);
} else if num_ops == 3 {
let cond = &inst.operands[0];
let t_label = inst.operands[1].borrow().name.clone();
let f_label = inst.operands[2].borrow().name.clone();
let cond_borrow = cond.borrow();
let cond_vr = self.get_or_create_vreg_by_vid(cond_borrow.vid as usize);
drop(cond_borrow);
let mut test = MachineInstr::new(X86Opcode::TEST as u32);
test.push_reg(cond_vr);
test.push_imm(1);
instrs.push(test);
let mut jne = MachineInstr::new(X86Opcode::JNE as u32);
jne.push_label(&t_label);
instrs.push(jne);
let mut jmp = MachineInstr::new(X86Opcode::JMP as u32);
jmp.push_label(&f_label);
instrs.push(jmp);
}
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 ret_val = &inst.operands[0];
let val_vr = self.get_operand_vreg(ret_val);
let mut mov = MachineInstr::new(X86Opcode::MOV as u32);
mov.operands.push(MachineOperand::PhysReg(RAX as u32));
mov.push_reg(val_vr);
instrs.push(mov);
}
instrs.push(MachineInstr::new(X86Opcode::RET as u32));
instrs
}
pub fn lower_call(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let num_ops = inst.operands.len();
if num_ops >= 1 {
let func_name = inst.operands[0].borrow().name.clone();
for i in (1..num_ops).rev() {
let arg_vr = self.get_operand_vreg(&inst.operands[i]);
let mut push = MachineInstr::new(X86Opcode::PUSH as u32);
push.push_reg(arg_vr);
instrs.push(push);
}
let mut call = MachineInstr::new(X86Opcode::CALL as u32);
call.operands.push(MachineOperand::Global(func_name));
instrs.push(call);
if !inst.ty.is_void() {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let mut mov = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov.operands.push(MachineOperand::PhysReg(RAX as u32));
instrs.push(mov);
}
}
instrs
}
pub fn lower_alloca(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
self.emit_binary_op(X86Opcode::MOV as u32, def_vr, RSP as u32, 0)
}
pub fn lower_load(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mi.push_reg(ptr_vr);
mi
}
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 mut mi = MachineInstr::new(X86Opcode::MOV as u32);
mi.push_reg(ptr_vr);
mi.push_reg(val_vr);
mi
}
pub fn lower_zext(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst, X86Opcode::MOVZX)
}
pub fn lower_sext(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst, X86Opcode::MOVSX)
}
pub fn lower_trunc(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst, X86Opcode::MOV)
}
pub fn lower_bitcast(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_simple_cast(inst, X86Opcode::MOV)
}
fn lower_simple_cast(&self, inst: &crate::value::Value, opcode: X86Opcode) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
self.emit_binary_op(opcode as u32, def_vr, src_vr, 0)
}
pub fn lower_phi(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let mut instrs = Vec::new();
if inst.operands.len() >= 2 {
let first_val = &inst.operands[1]; let src_vr = self.get_operand_vreg(first_val);
let mut mi = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mi.push_reg(src_vr);
instrs.push(mi);
}
instrs
}
pub fn lower_getelementptr(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let base_vr = self.get_operand_vreg(&inst.operands[0]);
let mut lea = MachineInstr::new(X86Opcode::LEA as u32).with_def(def_vr);
lea.push_reg(base_vr);
lea.push_imm(0);
lea
}
pub fn lower_select(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_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]);
let mut mov_false = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov_false.push_reg(false_vr);
let mut instrs = Vec::new();
instrs.push(mov_false);
let mut test = MachineInstr::new(X86Opcode::TEST as u32);
test.push_reg(cond_vr);
test.push_imm(1);
let mut cmov = MachineInstr::new(X86Opcode::CMOVNE as u32).with_def(def_vr);
cmov.push_reg(def_vr);
cmov.push_reg(true_vr);
let mut cmov_simple = MachineInstr::new(X86Opcode::CMOVNE as u32).with_def(def_vr);
cmov_simple.push_reg(def_vr);
cmov_simple.push_reg(true_vr);
cmov_simple
}
pub fn lower_fadd(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::ADDSS
} else {
X86Opcode::ADDSD
};
vec![self.lower_fp_binop(inst, opcode)]
}
pub fn lower_fsub(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::SUBSS
} else {
X86Opcode::SUBSD
};
vec![self.lower_fp_binop(inst, opcode)]
}
pub fn lower_fmul(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::MULSS
} else {
X86Opcode::MULSD
};
vec![self.lower_fp_binop(inst, opcode)]
}
pub fn lower_fdiv(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::DIVSS
} else {
X86Opcode::DIVSD
};
vec![self.lower_fp_binop(inst, opcode)]
}
pub fn lower_frem(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_or_create_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 mov = MachineInstr::new(X86Opcode::MOVSS as u32).with_def(def_vr);
mov.push_reg(def_vr);
mov.push_reg(src1);
vec![mov]
}
fn lower_fp_binop(&self, inst: &crate::value::Value, opcode: X86Opcode) -> MachineInstr {
let def_vr = self.get_or_create_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(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src1);
mi.push_reg(src2);
mi
}
pub fn lower_fcmp(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let ucomi_op = if inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32
{
X86Opcode::UCOMISS
} else {
X86Opcode::UCOMISD
};
let mut ucomi = MachineInstr::new(ucomi_op as u32);
ucomi.push_reg(src1);
ucomi.push_reg(src2);
instrs.push(ucomi);
let mut setcc = MachineInstr::new(X86Opcode::SETE as u32).with_def(def_vr);
instrs.push(setcc);
let mut movzx = MachineInstr::new(X86Opcode::MOVZX as u32).with_def(def_vr);
movzx.push_reg(def_vr);
instrs.push(movzx);
instrs
}
pub fn lower_fptosi(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::CVTTSS2SI
} else {
X86Opcode::CVTTSD2SI
};
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_fptoui(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_fptosi(inst)
}
pub fn lower_sitofp(&self, inst: &crate::value::Value) -> MachineInstr {
let is_f32 = inst.ty.is_floating_point() && inst.ty.get_primitive_size_in_bits() == 32;
let opcode = if is_f32 {
X86Opcode::CVTSI2SS
} else {
X86Opcode::CVTSI2SD
};
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_uitofp(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_sitofp(inst)
}
pub fn lower_fpext(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::CVTSS2SD as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_fptrunc(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::CVTSD2SS as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_extract_element(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let vec_vr = self.get_operand_vreg(&inst.operands[0]);
let idx_val = &inst.operands[1];
let idx = if let Some(imm) = self.get_constant_value(idx_val) {
imm
} else {
0
};
let mut mi = MachineInstr::new(X86Opcode::EXTRACTPS as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(vec_vr);
mi.push_imm(idx);
mi
}
pub fn lower_insert_element(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let vec_vr = self.get_operand_vreg(&inst.operands[0]);
let elt_vr = self.get_operand_vreg(&inst.operands[1]);
let idx_val = &inst.operands[2];
let idx = if let Some(imm) = self.get_constant_value(idx_val) {
imm
} else {
0
};
let mut mov = MachineInstr::new(X86Opcode::MOVSS as u32).with_def(def_vr);
mov.push_reg(def_vr);
mov.push_reg(vec_vr);
let mut insert = MachineInstr::new(X86Opcode::INSERTPS as u32).with_def(def_vr);
insert.push_reg(def_vr);
insert.push_reg(elt_vr);
insert.push_imm(idx << 4);
vec![mov, insert]
}
pub fn lower_shuffle_vector(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let v1_vr = self.get_operand_vreg(&inst.operands[0]);
let v2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(X86Opcode::SHUFPS as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(v1_vr);
mi.push_reg(v2_vr);
mi.push_imm(0);
mi
}
pub fn emit_packed_fp_binop(
&self,
dest: VirtReg,
src1: VirtReg,
src2: VirtReg,
opcode: X86Opcode,
) -> MachineInstr {
let mut mi = MachineInstr::new(opcode as u32).with_def(dest);
mi.push_reg(dest);
mi.push_reg(src1);
mi.push_reg(src2);
mi
}
pub fn emit_zero_vector(&self, dest: VirtReg) -> MachineInstr {
let mut mi = MachineInstr::new(X86Opcode::XORPS as u32).with_def(dest);
mi.push_reg(dest);
mi.push_reg(dest);
mi
}
pub fn emit_scalar_splat(&self, dest: VirtReg, src: VirtReg) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let mut movd = MachineInstr::new(X86Opcode::MOVD as u32).with_def(dest);
movd.push_reg(dest);
movd.push_reg(src);
instrs.push(movd);
let mut shuf = MachineInstr::new(X86Opcode::PSHUFD as u32).with_def(dest);
shuf.push_reg(dest);
shuf.push_reg(dest);
shuf.push_imm(0x00);
instrs.push(shuf);
instrs
}
pub fn get_or_create_vreg_by_vid(&self, vid: usize) -> VirtReg {
*self.vreg_map.get(&vid).unwrap_or(&0)
}
pub fn get_operand_vreg(&self, value: &ValueRef) -> VirtReg {
let v = value.borrow();
if v.is_constant() {
return 0;
}
*self.vreg_map.get(&(v.vid as usize)).unwrap_or(&0)
}
pub fn is_constant_operand(&self, value: &ValueRef) -> bool {
value.borrow().is_constant()
}
pub fn get_constant_value(&self, value: &ValueRef) -> Option<i64> {
let v = value.borrow();
if v.is_constant() {
v.name.parse().ok()
} else {
None
}
}
pub fn emit_mov_imm(&self, dest: VirtReg, imm: i64) -> MachineInstr {
let mut mi = MachineInstr::new(X86Opcode::MOV as u32).with_def(dest);
mi.push_imm(imm);
mi
}
pub fn emit_binary_op(
&self,
opcode: u32,
dest: VirtReg,
src: VirtReg,
_dummy: u32,
) -> MachineInstr {
let mut mi = MachineInstr::new(opcode).with_def(dest);
mi.push_reg(dest);
mi.push_reg(src);
mi
}
pub fn emit_binary_op_imm(&self, opcode: u32, dest: VirtReg, imm: i64) -> MachineInstr {
let mut mi = MachineInstr::new(opcode).with_def(dest);
mi.push_reg(dest);
mi.push_imm(imm);
mi
}
fn lower_binary_op_inst(&self, inst: &crate::value::Value, opcode: X86Opcode) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2_val = &inst.operands[1];
if self.is_constant_operand(src2_val) {
if let Some(imm) = self.get_constant_value(src2_val) {
return self.emit_binary_op_imm(opcode as u32, def_vr, imm);
}
}
let src2 = self.get_operand_vreg(src2_val);
self.emit_binary_op(opcode as u32, def_vr, src1, src2)
}
}
impl Default for X86InstructionSelector {
fn default() -> Self {
Self::new(X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", ""))
}
}
#[derive(Debug, Clone)]
pub struct ISelPattern {
pub name: &'static str,
pub ir_op: &'static str,
pub x86_op: X86Opcode,
pub num_operands: u8,
pub sets_flags: bool,
pub is_conditional: bool,
pub feature: &'static str,
}
pub static ISEL_PATTERNS: &[ISelPattern] = &[
ISelPattern {
name: "add_i32",
ir_op: "add",
x86_op: X86Opcode::ADD,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "add_i64",
ir_op: "add",
x86_op: X86Opcode::ADD,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "sub_i32",
ir_op: "sub",
x86_op: X86Opcode::SUB,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "sub_i64",
ir_op: "sub",
x86_op: X86Opcode::SUB,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "mul_i32",
ir_op: "mul",
x86_op: X86Opcode::IMUL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "mul_i64",
ir_op: "mul",
x86_op: X86Opcode::IMUL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "and_i32",
ir_op: "and",
x86_op: X86Opcode::AND,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "or_i32",
ir_op: "or",
x86_op: X86Opcode::OR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "xor_i32",
ir_op: "xor",
x86_op: X86Opcode::XOR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "shl_i32",
ir_op: "shl",
x86_op: X86Opcode::SHL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "shr_i32",
ir_op: "lshr",
x86_op: X86Opcode::SHR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "ashr_i32",
ir_op: "ashr",
x86_op: X86Opcode::SAR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "sdiv_i32",
ir_op: "sdiv",
x86_op: X86Opcode::IDIV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "udiv_i32",
ir_op: "udiv",
x86_op: X86Opcode::DIV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "icmp_eq",
ir_op: "icmp",
x86_op: X86Opcode::CMP,
num_operands: 2,
sets_flags: true,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "select_eq",
ir_op: "select",
x86_op: X86Opcode::CMOVE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "select_ne",
ir_op: "select",
x86_op: X86Opcode::CMOVNE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "select_lt",
ir_op: "select",
x86_op: X86Opcode::CMOVL,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "select_gt",
ir_op: "select",
x86_op: X86Opcode::CMOVG,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cttz_i32",
ir_op: "cttz",
x86_op: X86Opcode::TZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "cttz_i32_bmi",
ir_op: "cttz",
x86_op: X86Opcode::TZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "ctlz_i32",
ir_op: "ctlz",
x86_op: X86Opcode::TZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "ctlz_i32",
ir_op: "ctlz",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "abm",
},
ISelPattern {
name: "load_i32",
ir_op: "load",
x86_op: X86Opcode::MOV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "store_i32",
ir_op: "store",
x86_op: X86Opcode::MOV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "br",
ir_op: "br",
x86_op: X86Opcode::JMP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "je",
ir_op: "br_cond",
x86_op: X86Opcode::JE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jne",
ir_op: "br_cond",
x86_op: X86Opcode::JNE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jl",
ir_op: "br_cond",
x86_op: X86Opcode::JL,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jg",
ir_op: "br_cond",
x86_op: X86Opcode::JG,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "ret_void",
ir_op: "ret",
x86_op: X86Opcode::RET,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "alloca",
ir_op: "alloca",
x86_op: X86Opcode::SUB,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "call",
ir_op: "call",
x86_op: X86Opcode::CALL,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "fadd_f32",
ir_op: "fadd",
x86_op: X86Opcode::ADDSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fadd_f64",
ir_op: "fadd",
x86_op: X86Opcode::ADDSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fsub_f32",
ir_op: "fsub",
x86_op: X86Opcode::SUBSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fsub_f64",
ir_op: "fsub",
x86_op: X86Opcode::SUBSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fmul_f32",
ir_op: "fmul",
x86_op: X86Opcode::MULSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fmul_f64",
ir_op: "fmul",
x86_op: X86Opcode::MULSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fdiv_f32",
ir_op: "fdiv",
x86_op: X86Opcode::DIVSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fdiv_f64",
ir_op: "fdiv",
x86_op: X86Opcode::DIVSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fcmp_f32",
ir_op: "fcmp",
x86_op: X86Opcode::UCOMISS,
num_operands: 2,
sets_flags: true,
is_conditional: true,
feature: "sse",
},
ISelPattern {
name: "fcmp_f64",
ir_op: "fcmp",
x86_op: X86Opcode::UCOMISD,
num_operands: 2,
sets_flags: true,
is_conditional: true,
feature: "sse2",
},
ISelPattern {
name: "fptosi_f32",
ir_op: "fptosi",
x86_op: X86Opcode::CVTTSS2SI,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fptosi_f64",
ir_op: "fptosi",
x86_op: X86Opcode::CVTTSD2SI,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "sitofp_i32_f32",
ir_op: "sitofp",
x86_op: X86Opcode::CVTSI2SS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "sitofp_i32_f64",
ir_op: "sitofp",
x86_op: X86Opcode::CVTSI2SD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fpext_f32_to_f64",
ir_op: "fpext",
x86_op: X86Opcode::CVTSS2SD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "fptrunc_f64_to_f32",
ir_op: "fptrunc",
x86_op: X86Opcode::CVTSD2SS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "v4f32_add",
ir_op: "fadd",
x86_op: X86Opcode::ADDPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v2f64_add",
ir_op: "fadd",
x86_op: X86Opcode::ADDPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "v4f32_sub",
ir_op: "fsub",
x86_op: X86Opcode::SUBPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v2f64_sub",
ir_op: "fsub",
x86_op: X86Opcode::SUBPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "v4f32_mul",
ir_op: "fmul",
x86_op: X86Opcode::MULPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v2f64_mul",
ir_op: "fmul",
x86_op: X86Opcode::MULPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "v4f32_div",
ir_op: "fdiv",
x86_op: X86Opcode::DIVPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v2f64_div",
ir_op: "fdiv",
x86_op: X86Opcode::DIVPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "v4i32_and",
ir_op: "and",
x86_op: X86Opcode::ANDPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v4i32_or",
ir_op: "or",
x86_op: X86Opcode::ORPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "v4i32_xor",
ir_op: "xor",
x86_op: X86Opcode::XORPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "shuffle_v4f32",
ir_op: "shufflevector",
x86_op: X86Opcode::SHUFPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "shuffle_v2f64",
ir_op: "shufflevector",
x86_op: X86Opcode::SHUFPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "extract_element",
ir_op: "extractelement",
x86_op: X86Opcode::EXTRACTPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "insert_element",
ir_op: "insertelement",
x86_op: X86Opcode::INSERTPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "zero_vector",
ir_op: "zeroinitializer",
x86_op: X86Opcode::XORPS,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "fadd_f32_avx",
ir_op: "fadd",
x86_op: X86Opcode::VADDSS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "v8f32_add_avx",
ir_op: "fadd",
x86_op: X86Opcode::VADDPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "v4f64_add_avx",
ir_op: "fadd",
x86_op: X86Opcode::VADDPD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "v8f32_mul_avx",
ir_op: "fmul",
x86_op: X86Opcode::VMULPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "v8f32_and_avx",
ir_op: "and",
x86_op: X86Opcode::VANDPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "v8f32_xor_avx",
ir_op: "xor",
x86_op: X86Opcode::VXORPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "fma_f32",
ir_op: "fma",
x86_op: X86Opcode::VFMADD132SS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "fma",
},
ISelPattern {
name: "fma_v4f32",
ir_op: "fma",
x86_op: X86Opcode::VFMADD132PS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "fma",
},
ISelPattern {
name: "fma_v2f64",
ir_op: "fma",
x86_op: X86Opcode::VFMADD132PD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "fma",
},
ISelPattern {
name: "adc_i32",
ir_op: "adc",
x86_op: X86Opcode::ADC,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "sbb_i32",
ir_op: "sbb",
x86_op: X86Opcode::SBB,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "inc_i32",
ir_op: "inc",
x86_op: X86Opcode::INC,
num_operands: 1,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "dec_i32",
ir_op: "dec",
x86_op: X86Opcode::DEC,
num_operands: 1,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "neg_i32",
ir_op: "neg",
x86_op: X86Opcode::NEG,
num_operands: 1,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "not_i32",
ir_op: "not",
x86_op: X86Opcode::NOT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "rol_i32",
ir_op: "rotl",
x86_op: X86Opcode::ROL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "ror_i32",
ir_op: "rotr",
x86_op: X86Opcode::ROR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "rcl_i32",
ir_op: "rcl",
x86_op: X86Opcode::RCL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "rcr_i32",
ir_op: "rcr",
x86_op: X86Opcode::RCR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "shld_i32",
ir_op: "shld",
x86_op: X86Opcode::SHLD,
num_operands: 3,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "shrd_i32",
ir_op: "shrd",
x86_op: X86Opcode::SHRD,
num_operands: 3,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "mov_reg_reg",
ir_op: "mov",
x86_op: X86Opcode::MOV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movsx_i8_i32",
ir_op: "sext",
x86_op: X86Opcode::MOVSX,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movzx_i8_i32",
ir_op: "zext",
x86_op: X86Opcode::MOVZX,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movabs_i64",
ir_op: "mov",
x86_op: X86Opcode::MOVABS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "lea",
ir_op: "getelementptr",
x86_op: X86Opcode::LEA,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "cmp_reg_reg",
ir_op: "icmp",
x86_op: X86Opcode::CMP,
num_operands: 2,
sets_flags: true,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "test_reg_reg",
ir_op: "test",
x86_op: X86Opcode::TEST,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "seto",
ir_op: "setcc",
x86_op: X86Opcode::SETO,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setno",
ir_op: "setcc",
x86_op: X86Opcode::SETNO,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setb",
ir_op: "setcc",
x86_op: X86Opcode::SETB,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setae",
ir_op: "setcc",
x86_op: X86Opcode::SETAE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "sete",
ir_op: "setcc",
x86_op: X86Opcode::SETE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setne",
ir_op: "setcc",
x86_op: X86Opcode::SETNE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setbe",
ir_op: "setcc",
x86_op: X86Opcode::SETBE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "seta",
ir_op: "setcc",
x86_op: X86Opcode::SETA,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "sets",
ir_op: "setcc",
x86_op: X86Opcode::SETS,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setns",
ir_op: "setcc",
x86_op: X86Opcode::SETNS,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setp",
ir_op: "setcc",
x86_op: X86Opcode::SETP,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setnp",
ir_op: "setcc",
x86_op: X86Opcode::SETNP,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setl",
ir_op: "setcc",
x86_op: X86Opcode::SETL,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setge",
ir_op: "setcc",
x86_op: X86Opcode::SETGE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setle",
ir_op: "setcc",
x86_op: X86Opcode::SETLE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "setg",
ir_op: "setcc",
x86_op: X86Opcode::SETG,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "cmovo_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVO,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovno_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVNO,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovb_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVB,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovae_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVAE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmove_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovne_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVNE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovbe_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVBE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmova_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVA,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovs_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVS,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovns_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVNS,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovp_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVP,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovnp_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVNP,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovl_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVL,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovge_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVGE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovle_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVLE,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "cmovg_i32",
ir_op: "cmov",
x86_op: X86Opcode::CMOVG,
num_operands: 3,
sets_flags: false,
is_conditional: true,
feature: "cmov",
},
ISelPattern {
name: "movsb",
ir_op: "movs",
x86_op: X86Opcode::MOVSB,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movsw",
ir_op: "movs",
x86_op: X86Opcode::MOVSW,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movsd_str",
ir_op: "movs",
x86_op: X86Opcode::MOVSD_STR,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "movsq",
ir_op: "movs",
x86_op: X86Opcode::MOVSQ,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "cmpsb",
ir_op: "cmps",
x86_op: X86Opcode::CMPSB,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "cmpsw",
ir_op: "cmps",
x86_op: X86Opcode::CMPSW,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "cmpsd_str",
ir_op: "cmps",
x86_op: X86Opcode::CMPSD_STR,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "cmpsq",
ir_op: "cmps",
x86_op: X86Opcode::CMPSQ,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "stosb",
ir_op: "stos",
x86_op: X86Opcode::STOSB,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "stosw",
ir_op: "stos",
x86_op: X86Opcode::STOSW,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "stosd_str",
ir_op: "stos",
x86_op: X86Opcode::STOSD_STR,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "stosq",
ir_op: "stos",
x86_op: X86Opcode::STOSQ,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "lodsb",
ir_op: "lods",
x86_op: X86Opcode::LODSB,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "lodsw",
ir_op: "lods",
x86_op: X86Opcode::LODSW,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "lodsd_str",
ir_op: "lods",
x86_op: X86Opcode::LODSD_STR,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "lodsq",
ir_op: "lods",
x86_op: X86Opcode::LODSQ,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "scasb",
ir_op: "scas",
x86_op: X86Opcode::SCASB,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "scasw",
ir_op: "scas",
x86_op: X86Opcode::SCASW,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "scasd_str",
ir_op: "scas",
x86_op: X86Opcode::SCASD_STR,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "scasq",
ir_op: "scas",
x86_op: X86Opcode::SCASQ,
num_operands: 0,
sets_flags: true,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "rep",
ir_op: "rep",
x86_op: X86Opcode::REP,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "repe",
ir_op: "repe",
x86_op: X86Opcode::REPE,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "repne",
ir_op: "repne",
x86_op: X86Opcode::REPNE,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "bswap_i32",
ir_op: "bswap",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "bsf_i32",
ir_op: "cttz",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "bsr_i32",
ir_op: "ctlz",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "tzcnt_i32",
ir_op: "cttz",
x86_op: X86Opcode::TZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "lzcnt_i32",
ir_op: "ctlz",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "abm",
},
ISelPattern {
name: "popcnt_i32",
ir_op: "ctpop",
x86_op: X86Opcode::LZCNT,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "popcnt",
},
ISelPattern {
name: "crc32_i32",
ir_op: "crc32",
x86_op: X86Opcode::CRC32,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.2",
},
ISelPattern {
name: "push_reg",
ir_op: "push",
x86_op: X86Opcode::PUSH,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "pop_reg",
ir_op: "pop",
x86_op: X86Opcode::POP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "pushf",
ir_op: "pushf",
x86_op: X86Opcode::PUSHF,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "popf",
ir_op: "popf",
x86_op: X86Opcode::POPF,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "xchg",
ir_op: "xchg",
x86_op: X86Opcode::XCHG,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "jmp_uncond",
ir_op: "br",
x86_op: X86Opcode::JMP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "call_direct",
ir_op: "call",
x86_op: X86Opcode::CALL,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "ret",
ir_op: "ret",
x86_op: X86Opcode::RET,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "loop",
ir_op: "loop",
x86_op: X86Opcode::LOOP,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "loope",
ir_op: "loope",
x86_op: X86Opcode::LOOPE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "loopne",
ir_op: "loopne",
x86_op: X86Opcode::LOOPNE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jo",
ir_op: "br_cond",
x86_op: X86Opcode::JO,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jno",
ir_op: "br_cond",
x86_op: X86Opcode::JNO,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jb",
ir_op: "br_cond",
x86_op: X86Opcode::JB,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jae",
ir_op: "br_cond",
x86_op: X86Opcode::JAE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "je_cond",
ir_op: "br_cond",
x86_op: X86Opcode::JE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jne_cond",
ir_op: "br_cond",
x86_op: X86Opcode::JNE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jbe",
ir_op: "br_cond",
x86_op: X86Opcode::JBE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "ja",
ir_op: "br_cond",
x86_op: X86Opcode::JA,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "js",
ir_op: "br_cond",
x86_op: X86Opcode::JS,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jns",
ir_op: "br_cond",
x86_op: X86Opcode::JNS,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jp",
ir_op: "br_cond",
x86_op: X86Opcode::JP,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jnp",
ir_op: "br_cond",
x86_op: X86Opcode::JNP,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jl_cond",
ir_op: "br_cond",
x86_op: X86Opcode::JL,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jge",
ir_op: "br_cond",
x86_op: X86Opcode::JGE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jle",
ir_op: "br_cond",
x86_op: X86Opcode::JLE,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "jg_cond",
ir_op: "br_cond",
x86_op: X86Opcode::JG,
num_operands: 1,
sets_flags: false,
is_conditional: true,
feature: "",
},
ISelPattern {
name: "andn_i32",
ir_op: "andn",
x86_op: X86Opcode::ANDN,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "bextr_i32",
ir_op: "bextr",
x86_op: X86Opcode::BEXTR,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "blsi_i32",
ir_op: "blsi",
x86_op: X86Opcode::BLSI,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "blsmsk_i32",
ir_op: "blsmsk",
x86_op: X86Opcode::BLSMSK,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "blsr_i32",
ir_op: "blsr",
x86_op: X86Opcode::BLSR,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "bmi1",
},
ISelPattern {
name: "bzhi_i32",
ir_op: "bzhi",
x86_op: X86Opcode::BZHI,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "mulx_i32",
ir_op: "mulx",
x86_op: X86Opcode::MULX,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "pdep_i32",
ir_op: "pdep",
x86_op: X86Opcode::PDEP,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "pext_i32",
ir_op: "pext",
x86_op: X86Opcode::PEXT,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "rorx_i32",
ir_op: "rotl",
x86_op: X86Opcode::RORX,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "sarx_i32",
ir_op: "ashr",
x86_op: X86Opcode::SARX,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "shlx_i32",
ir_op: "shl",
x86_op: X86Opcode::SHLX,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "shrx_i32",
ir_op: "lshr",
x86_op: X86Opcode::SHRX,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "bmi2",
},
ISelPattern {
name: "add_i64",
ir_op: "add",
x86_op: X86Opcode::ADD,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "sub_i64_alt",
ir_op: "sub",
x86_op: X86Opcode::SUB,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "imul_i64",
ir_op: "mul",
x86_op: X86Opcode::IMUL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "and_i64",
ir_op: "and",
x86_op: X86Opcode::AND,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "or_i64_alt",
ir_op: "or",
x86_op: X86Opcode::OR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "xor_i64_alt",
ir_op: "xor",
x86_op: X86Opcode::XOR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "shl_i64",
ir_op: "shl",
x86_op: X86Opcode::SHL,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "shr_i64",
ir_op: "lshr",
x86_op: X86Opcode::SHR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "sar_i64",
ir_op: "ashr",
x86_op: X86Opcode::SAR,
num_operands: 2,
sets_flags: true,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "sdiv_i64",
ir_op: "sdiv",
x86_op: X86Opcode::IDIV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "udiv_i64",
ir_op: "udiv",
x86_op: X86Opcode::DIV,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "concat_v4i32",
ir_op: "concat_vectors",
x86_op: X86Opcode::PUNPCKLDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "extract_subvec",
ir_op: "extract_subvector",
x86_op: X86Opcode::PEXTRD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "insert_subvec",
ir_op: "insert_subvector",
x86_op: X86Opcode::PINSRD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "gather_dd",
ir_op: "gather",
x86_op: X86Opcode::VPGATHERDD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx512f",
},
ISelPattern {
name: "gather_dq",
ir_op: "gather",
x86_op: X86Opcode::VPGATHERDQ,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx512f",
},
ISelPattern {
name: "gather_qd",
ir_op: "gather",
x86_op: X86Opcode::VPGATHERQD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx512f",
},
ISelPattern {
name: "gather_qq",
ir_op: "gather",
x86_op: X86Opcode::VPGATHERQQ,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx512f",
},
ISelPattern {
name: "broadcast_ss",
ir_op: "broadcast",
x86_op: X86Opcode::VBROADCASTSS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "broadcast_sd",
ir_op: "broadcast",
x86_op: X86Opcode::VBROADCASTSD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "pbroadcast_b",
ir_op: "broadcast",
x86_op: X86Opcode::VPBROADCASTB,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "pbroadcast_w",
ir_op: "broadcast",
x86_op: X86Opcode::VPBROADCASTW,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "pbroadcast_d",
ir_op: "broadcast",
x86_op: X86Opcode::VPBROADCASTD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "pbroadcast_q",
ir_op: "broadcast",
x86_op: X86Opcode::VPBROADCASTQ,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "vpermilps",
ir_op: "shufflevector",
x86_op: X86Opcode::VPERMILPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "vperm2f128",
ir_op: "shufflevector",
x86_op: X86Opcode::VPERM2F128,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "vpermq_i64",
ir_op: "shufflevector",
x86_op: X86Opcode::VPERMQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "vpermpd",
ir_op: "shufflevector",
x86_op: X86Opcode::VPERMPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "avx2",
},
ISelPattern {
name: "haddps",
ir_op: "hadd",
x86_op: X86Opcode::HADDPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "haddpd",
ir_op: "hadd",
x86_op: X86Opcode::HADDPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "hsubps",
ir_op: "hsub",
x86_op: X86Opcode::HSUBPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "hsubpd",
ir_op: "hsub",
x86_op: X86Opcode::HSUBPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "addsubps",
ir_op: "addsub",
x86_op: X86Opcode::ADDSUBPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "addsubpd",
ir_op: "addsub",
x86_op: X86Opcode::ADDSUBPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "blendps",
ir_op: "blend",
x86_op: X86Opcode::BLENDPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "blendpd",
ir_op: "blend",
x86_op: X86Opcode::BLENDPD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "blendvps",
ir_op: "blend",
x86_op: X86Opcode::BLENDVPS,
num_operands: 4,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "blendvpd",
ir_op: "blend",
x86_op: X86Opcode::BLENDVPD,
num_operands: 4,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "dpps",
ir_op: "dot_product",
x86_op: X86Opcode::DPPS,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "dppd",
ir_op: "dot_product",
x86_op: X86Opcode::DPPD,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "unpcklps",
ir_op: "unpack",
x86_op: X86Opcode::UNPCKLPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "unpckhps",
ir_op: "unpack",
x86_op: X86Opcode::UNPCKHPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "unpcklpd",
ir_op: "unpack",
x86_op: X86Opcode::UNPCKLPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "unpckhpd",
ir_op: "unpack",
x86_op: X86Opcode::UNPCKHPD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "movhlps",
ir_op: "movhl",
x86_op: X86Opcode::MOVHLPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "movlhps",
ir_op: "movlh",
x86_op: X86Opcode::MOVLHPS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "psadbw",
ir_op: "sad",
x86_op: X86Opcode::PHADDW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "sqrtss",
ir_op: "fsqrt",
x86_op: X86Opcode::SQRTSS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "sqrtsd",
ir_op: "fsqrt",
x86_op: X86Opcode::SQRTSD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "sqrtps",
ir_op: "fsqrt",
x86_op: X86Opcode::SQRTPS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "minss",
ir_op: "minnum",
x86_op: X86Opcode::MINSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "minsd",
ir_op: "minnum",
x86_op: X86Opcode::MINSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "maxss",
ir_op: "maxnum",
x86_op: X86Opcode::MAXSS,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "maxsd",
ir_op: "maxnum",
x86_op: X86Opcode::MAXSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "pminsb",
ir_op: "smin",
x86_op: X86Opcode::PMINSB,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pminsd",
ir_op: "smin",
x86_op: X86Opcode::PMINSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pminuw",
ir_op: "umin",
x86_op: X86Opcode::PMINUW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pminud",
ir_op: "umin",
x86_op: X86Opcode::PMINUD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pmaxsb",
ir_op: "smax",
x86_op: X86Opcode::PMAXSB,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pmaxsd",
ir_op: "smax",
x86_op: X86Opcode::PMAXSD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pmaxuw",
ir_op: "umax",
x86_op: X86Opcode::PMAXUW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pmaxud",
ir_op: "umax",
x86_op: X86Opcode::PMAXUD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pabsb",
ir_op: "abs",
x86_op: X86Opcode::PABSB,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "pabsw",
ir_op: "abs",
x86_op: X86Opcode::PABSW,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "pabsd",
ir_op: "abs",
x86_op: X86Opcode::PABSD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "psignb",
ir_op: "sign",
x86_op: X86Opcode::PSIGNB,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "psignw",
ir_op: "sign",
x86_op: X86Opcode::PSIGNW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "psignd",
ir_op: "sign",
x86_op: X86Opcode::PSIGND,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phaddw",
ir_op: "hadd",
x86_op: X86Opcode::PHADDW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phaddd",
ir_op: "hadd",
x86_op: X86Opcode::PHADDD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phaddsw",
ir_op: "hadd_sat",
x86_op: X86Opcode::PHADDSW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phsubw",
ir_op: "hsub",
x86_op: X86Opcode::PHSUBW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phsubd",
ir_op: "hsub",
x86_op: X86Opcode::PHSUBD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "phsubsw",
ir_op: "hsub_sat",
x86_op: X86Opcode::PHSUBSW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "pmaddubsw",
ir_op: "madd",
x86_op: X86Opcode::PMADDUBSW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "ssse3",
},
ISelPattern {
name: "pmulld",
ir_op: "mul",
x86_op: X86Opcode::PMULLD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pmuldq",
ir_op: "mul",
x86_op: X86Opcode::PMULDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pcmpeqq",
ir_op: "icmp",
x86_op: X86Opcode::PCMPEQQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "packusdw",
ir_op: "pack",
x86_op: X86Opcode::PACKUSDW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "syscall",
ir_op: "syscall",
x86_op: X86Opcode::SYSCALL,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "sysret",
ir_op: "sysret",
x86_op: X86Opcode::SYSRET,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "x86_64",
},
ISelPattern {
name: "sysenter",
ir_op: "sysenter",
x86_op: X86Opcode::SYSENTER,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "sysexit",
ir_op: "sysexit",
x86_op: X86Opcode::SYSEXIT,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "nop",
ir_op: "nop",
x86_op: X86Opcode::NOP,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "nop1",
ir_op: "nop",
x86_op: X86Opcode::NOP1,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "nop2",
ir_op: "nop",
x86_op: X86Opcode::NOP2,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "nop3",
ir_op: "nop",
x86_op: X86Opcode::NOP3,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "nop4",
ir_op: "nop",
x86_op: X86Opcode::NOP4,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "",
},
ISelPattern {
name: "rcpps",
ir_op: "fdiv",
x86_op: X86Opcode::RCPPS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "rsqrtps",
ir_op: "fsqrt",
x86_op: X86Opcode::RSQRTPS,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse",
},
ISelPattern {
name: "movsldup",
ir_op: "shufflevector",
x86_op: X86Opcode::MOVSLDUP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse3",
},
ISelPattern {
name: "movshdup",
ir_op: "shufflevector",
x86_op: X86Opcode::MOVSHDUP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse3",
},
ISelPattern {
name: "movddup",
ir_op: "shufflevector",
x86_op: X86Opcode::MOVDDUP,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse3",
},
ISelPattern {
name: "punpcklbw",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKLBW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpcklwd",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKLWD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpckldq_i",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKLDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpcklqdq",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKLQDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpckhbw",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKHBW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpckhwd",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKHWD,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpckhdq",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKHDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "punpckhqdq",
ir_op: "unpack",
x86_op: X86Opcode::PUNPCKHQDQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "vzeroall",
ir_op: "zeroall",
x86_op: X86Opcode::VZEROALL,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "vzeroupper",
ir_op: "zeroupper",
x86_op: X86Opcode::VZEROUPPER,
num_operands: 0,
sets_flags: false,
is_conditional: false,
feature: "avx",
},
ISelPattern {
name: "pextrb",
ir_op: "extractelement",
x86_op: X86Opcode::PEXTRB,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pextrw_sse",
ir_op: "extractelement",
x86_op: X86Opcode::PEXTRW,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pextrq",
ir_op: "extractelement",
x86_op: X86Opcode::PEXTRQ,
num_operands: 2,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pinsrb",
ir_op: "insertelement",
x86_op: X86Opcode::PINSRB,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pinsrw_sse",
ir_op: "insertelement",
x86_op: X86Opcode::PINSRW,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "pinsrq",
ir_op: "insertelement",
x86_op: X86Opcode::PINSRQ,
num_operands: 3,
sets_flags: false,
is_conditional: false,
feature: "sse4.1",
},
ISelPattern {
name: "movd",
ir_op: "bitcast",
x86_op: X86Opcode::MOVD,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
ISelPattern {
name: "movq",
ir_op: "bitcast",
x86_op: X86Opcode::MOVQ,
num_operands: 1,
sets_flags: false,
is_conditional: false,
feature: "sse2",
},
];
pub fn lookup_isel_pattern(
ir_op: &str,
has_feature: &dyn Fn(&str) -> bool,
) -> Option<&'static ISelPattern> {
ISEL_PATTERNS
.iter()
.find(|p| p.ir_op == ir_op && (p.feature.is_empty() || has_feature(p.feature)))
}
pub fn patterns_for_ir_op(ir_op: &str) -> Vec<&'static ISelPattern> {
ISEL_PATTERNS.iter().filter(|p| p.ir_op == ir_op).collect()
}
pub fn pattern_counts() -> Vec<(&'static str, usize)> {
let mut counts: Vec<(&'static str, usize)> = Vec::new();
for p in ISEL_PATTERNS {
if let Some(entry) = counts.iter_mut().find(|(op, _)| *op == p.ir_op) {
entry.1 += 1;
} else {
counts.push((p.ir_op, 1));
}
}
counts
}
#[derive(Debug, Clone, Copy)]
pub struct ConditionCodeMapping {
pub ir_pred: &'static str,
pub is_signed: bool,
pub cc: X86ConditionCode,
pub cmov_op: X86Opcode,
pub jcc_op: X86Opcode,
pub setcc_op: X86Opcode,
}
pub static CONDITION_CODE_MAPPINGS: &[ConditionCodeMapping] = &[
ConditionCodeMapping {
ir_pred: "eq",
is_signed: true,
cc: X86ConditionCode::Z,
cmov_op: X86Opcode::CMOVE,
jcc_op: X86Opcode::JE,
setcc_op: X86Opcode::SETE,
},
ConditionCodeMapping {
ir_pred: "ne",
is_signed: true,
cc: X86ConditionCode::NZ,
cmov_op: X86Opcode::CMOVNE,
jcc_op: X86Opcode::JNE,
setcc_op: X86Opcode::SETNE,
},
ConditionCodeMapping {
ir_pred: "slt",
is_signed: true,
cc: X86ConditionCode::L,
cmov_op: X86Opcode::CMOVL,
jcc_op: X86Opcode::JL,
setcc_op: X86Opcode::SETL,
},
ConditionCodeMapping {
ir_pred: "sle",
is_signed: true,
cc: X86ConditionCode::LE,
cmov_op: X86Opcode::CMOVLE,
jcc_op: X86Opcode::JLE,
setcc_op: X86Opcode::SETLE,
},
ConditionCodeMapping {
ir_pred: "sgt",
is_signed: true,
cc: X86ConditionCode::G,
cmov_op: X86Opcode::CMOVG,
jcc_op: X86Opcode::JG,
setcc_op: X86Opcode::SETG,
},
ConditionCodeMapping {
ir_pred: "sge",
is_signed: true,
cc: X86ConditionCode::GE,
cmov_op: X86Opcode::CMOVGE,
jcc_op: X86Opcode::JGE,
setcc_op: X86Opcode::SETGE,
},
ConditionCodeMapping {
ir_pred: "ult",
is_signed: false,
cc: X86ConditionCode::B,
cmov_op: X86Opcode::CMOVB,
jcc_op: X86Opcode::JB,
setcc_op: X86Opcode::SETB,
},
ConditionCodeMapping {
ir_pred: "ule",
is_signed: false,
cc: X86ConditionCode::BE,
cmov_op: X86Opcode::CMOVBE,
jcc_op: X86Opcode::JBE,
setcc_op: X86Opcode::SETBE,
},
ConditionCodeMapping {
ir_pred: "ugt",
is_signed: false,
cc: X86ConditionCode::A,
cmov_op: X86Opcode::CMOVA,
jcc_op: X86Opcode::JA,
setcc_op: X86Opcode::SETA,
},
ConditionCodeMapping {
ir_pred: "uge",
is_signed: false,
cc: X86ConditionCode::NB,
cmov_op: X86Opcode::CMOVAE,
jcc_op: X86Opcode::JAE,
setcc_op: X86Opcode::SETAE,
},
];
pub fn lookup_cc_mapping(pred: &str, is_signed: bool) -> Option<&'static ConditionCodeMapping> {
CONDITION_CODE_MAPPINGS
.iter()
.find(|m| m.ir_pred == pred && m.is_signed == is_signed)
}
#[derive(Debug, Clone)]
pub struct FastISelState {
pub vreg_counter: u32,
pub is_64bit: bool,
pub fast_path: bool,
}
impl FastISelState {
pub fn new(is_64bit: bool) -> Self {
Self {
vreg_counter: 0,
is_64bit,
fast_path: true,
}
}
pub fn new_vreg(&mut self) -> u32 {
let r = self.vreg_counter;
self.vreg_counter += 1;
r
}
pub fn fast_opcode(ir_op: &str) -> Option<X86Opcode> {
match ir_op {
"add" => Some(X86Opcode::ADD),
"sub" => Some(X86Opcode::SUB),
"mul" => Some(X86Opcode::IMUL),
"sdiv" => Some(X86Opcode::IDIV),
"udiv" => Some(X86Opcode::DIV),
"and" => Some(X86Opcode::AND),
"or" => Some(X86Opcode::OR),
"xor" => Some(X86Opcode::XOR),
"shl" => Some(X86Opcode::SHL),
"shr" | "lshr" => Some(X86Opcode::SHR),
"ashr" => Some(X86Opcode::SAR),
"icmp" => Some(X86Opcode::CMP),
"load" => Some(X86Opcode::MOV),
"store" => Some(X86Opcode::MOV),
"alloca" => Some(X86Opcode::SUB),
"br" => Some(X86Opcode::JMP),
"ret" => Some(X86Opcode::RET),
"call" => Some(X86Opcode::CALL),
"select" => Some(X86Opcode::CMOVE),
"cttz" => Some(X86Opcode::TZCNT),
"ctlz" => Some(X86Opcode::LZCNT),
_ => None,
}
}
pub fn preferred_opcode(ir_op: &str, has_feature: &dyn Fn(&str) -> bool) -> Option<X86Opcode> {
match ir_op {
"cttz" if has_feature("bmi1") => Some(X86Opcode::TZCNT),
"ctlz" if has_feature("abm") => Some(X86Opcode::LZCNT),
_ => Self::fast_opcode(ir_op),
}
}
pub fn produces_flags(ir_op: &str) -> bool {
matches!(
ir_op,
"add" | "sub" | "mul" | "and" | "or" | "xor" | "shl" | "shr" | "ashr" | "icmp"
)
}
pub fn is_terminator(ir_op: &str) -> bool {
matches!(ir_op, "br" | "ret" | "switch" | "unreachable")
}
}
impl Default for FastISelState {
fn default() -> Self {
Self::new(true)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressingModePattern {
BaseOnly,
BaseDisp,
IndexScale,
BaseIndexScale,
BaseIndexScaleDisp,
RIPRelative,
Absolute,
}
impl AddressingModePattern {
pub fn has_base(&self) -> bool {
matches!(
self,
Self::BaseOnly | Self::BaseDisp | Self::BaseIndexScale | Self::BaseIndexScaleDisp
)
}
pub fn has_index(&self) -> bool {
matches!(
self,
Self::IndexScale | Self::BaseIndexScale | Self::BaseIndexScaleDisp
)
}
pub fn has_disp(&self) -> bool {
matches!(
self,
Self::BaseDisp | Self::BaseIndexScaleDisp | Self::RIPRelative | Self::Absolute
)
}
pub fn num_registers(&self) -> u8 {
match self {
Self::BaseOnly => 1,
Self::BaseDisp => 1,
Self::IndexScale => 1,
Self::BaseIndexScale => 2,
Self::BaseIndexScaleDisp => 2,
Self::RIPRelative => 0,
Self::Absolute => 0,
}
}
pub fn classify(
has_base: bool,
has_index: bool,
has_disp: bool,
is_rip: bool,
is_absolute: bool,
) -> Self {
if is_rip {
return Self::RIPRelative;
}
if is_absolute {
return Self::Absolute;
}
match (has_base, has_index, has_disp) {
(true, false, false) => Self::BaseOnly,
(true, false, true) => Self::BaseDisp,
(false, true, false) => Self::IndexScale,
(true, true, false) => Self::BaseIndexScale,
(true, true, true) => Self::BaseIndexScaleDisp,
_ => Self::Absolute,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressScale {
Scale1 = 1,
Scale2 = 2,
Scale4 = 4,
Scale8 = 8,
}
impl AddressScale {
pub fn from_u32(scale: u32) -> Option<Self> {
match scale {
1 => Some(Self::Scale1),
2 => Some(Self::Scale2),
4 => Some(Self::Scale4),
8 => Some(Self::Scale8),
_ => None,
}
}
pub fn to_sib_bits(&self) -> u8 {
match self {
Self::Scale1 => 0,
Self::Scale2 => 1,
Self::Scale4 => 2,
Self::Scale8 => 3,
}
}
pub fn from_sib_bits(bits: u8) -> Self {
match bits & 0x3 {
0 => Self::Scale1,
1 => Self::Scale2,
2 => Self::Scale4,
_ => Self::Scale8,
}
}
}
#[derive(Debug, Clone)]
pub struct ResolvedAddress {
pub base: Option<u16>,
pub index: Option<u16>,
pub scale: AddressScale,
pub displacement: i32,
pub pattern: AddressingModePattern,
pub is_rip_relative: bool,
pub segment: u8,
}
impl ResolvedAddress {
pub fn new() -> Self {
Self {
base: None,
index: None,
scale: AddressScale::Scale1,
displacement: 0,
pattern: AddressingModePattern::Absolute,
is_rip_relative: false,
segment: 0,
}
}
pub fn base_disp(base: u16, disp: i32) -> Self {
Self {
base: Some(base),
index: None,
scale: AddressScale::Scale1,
displacement: disp,
pattern: if disp == 0 {
AddressingModePattern::BaseOnly
} else {
AddressingModePattern::BaseDisp
},
is_rip_relative: false,
segment: 0,
}
}
pub fn base_index_scale_disp(base: u16, index: u16, scale: AddressScale, disp: i32) -> Self {
Self {
base: Some(base),
index: Some(index),
scale,
displacement: disp,
pattern: if disp == 0 {
AddressingModePattern::BaseIndexScale
} else {
AddressingModePattern::BaseIndexScaleDisp
},
is_rip_relative: false,
segment: 0,
}
}
pub fn rip_relative(disp: i32) -> Self {
Self {
base: None,
index: None,
scale: AddressScale::Scale1,
displacement: disp,
pattern: AddressingModePattern::RIPRelative,
is_rip_relative: true,
segment: 0,
}
}
pub fn fits_disp8(&self) -> bool {
self.displacement >= -128 && self.displacement <= 127
}
pub fn fits_disp32(&self) -> bool {
true }
pub fn address_byte_size(&self) -> u8 {
let mut size: u8 = 1; if self.pattern.has_index() || self.base == Some(RSP) || self.base == Some(ESP) {
size += 1; }
if self.pattern.has_disp() {
if self.fits_disp8() && !self.is_rip_relative {
size += 1; } else {
size += 4; }
}
size
}
}
impl Default for ResolvedAddress {
fn default() -> Self {
Self::new()
}
}
impl X86InstructionSelector {
pub fn lower_adc(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::ADC)
}
pub fn lower_sbb(&self, inst: &crate::value::Value) -> MachineInstr {
self.lower_binary_op_inst(inst, X86Opcode::SBB)
}
pub fn lower_inc(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::INC as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_dec(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::DEC as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_neg(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::NEG as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_not(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::NOT as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_rotl(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_rotate(inst, X86Opcode::ROL)
}
pub fn lower_rotr(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_rotate(inst, X86Opcode::ROR)
}
pub fn lower_rcl(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_rotate(inst, X86Opcode::RCL)
}
pub fn lower_rcr(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_rotate(inst, X86Opcode::RCR)
}
pub fn lower_shld(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_double_shift(inst, X86Opcode::SHLD)
}
pub fn lower_shrd(&self, inst: &crate::value::Value) -> Vec<MachineInstr> {
self.lower_double_shift(inst, X86Opcode::SHRD)
}
fn lower_rotate(&self, inst: &crate::value::Value, opcode: X86Opcode) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src1 = self.get_operand_vreg(&inst.operands[0]);
let count_val = &inst.operands[1];
let mut mov_instr = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov_instr.push_reg(src1);
instrs.push(mov_instr);
if self.is_constant_operand(count_val) {
if let Some(c) = self.get_constant_value(count_val) {
if c == 1 {
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_imm(1);
instrs.push(mi);
return instrs;
} else {
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_imm(c & 0xFF);
instrs.push(mi);
return instrs;
}
}
}
let count_vr = self.get_operand_vreg(count_val);
let mut mov_cl = MachineInstr::new(X86Opcode::MOV as u32);
mov_cl.operands.push(MachineOperand::PhysReg(RCX as u32));
mov_cl.push_reg(count_vr);
instrs.push(mov_cl);
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.operands.push(MachineOperand::PhysReg(RCX as u32));
instrs.push(mi);
instrs
}
fn lower_double_shift(
&self,
inst: &crate::value::Value,
opcode: X86Opcode,
) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_or_create_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 count_val = &inst.operands[2];
let mut mov = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov.push_reg(src1);
instrs.push(mov);
let mut mi = MachineInstr::new(opcode as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src2);
if self.is_constant_operand(count_val) {
if let Some(c) = self.get_constant_value(count_val) {
mi.push_imm(c & 0xFF);
}
} else {
let count_vr = self.get_operand_vreg(count_val);
mi.operands.push(MachineOperand::PhysReg(RCX as u32));
let mut mov_cl = MachineInstr::new(X86Opcode::MOV as u32);
mov_cl.operands.push(MachineOperand::PhysReg(RCX as u32));
mov_cl.push_reg(count_vr);
instrs.insert(instrs.len() - 1, mov_cl);
}
instrs.push(mi);
instrs
}
pub fn lower_mov_reg_reg(&self, def_vr: VirtReg, src_vr: VirtReg) -> MachineInstr {
let mut mi = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_movsx(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::MOVSX as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_movzx(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::MOVZX as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_movabs(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::MOVABS as u32).with_def(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_lea(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let mut mi = MachineInstr::new(X86Opcode::LEA as u32).with_def(def_vr);
let src = self.get_operand_vreg(&inst.operands[0]);
mi.push_reg(src);
if inst.operands.len() > 1 {
let src2 = self.get_operand_vreg(&inst.operands[1]);
mi.push_reg(src2);
}
mi
}
pub fn lower_lea_complex(
&self,
def_vr: VirtReg,
base: VirtReg,
index: VirtReg,
scale: u8,
disp: i32,
) -> MachineInstr {
let mut mi = MachineInstr::new(X86Opcode::LEA as u32).with_def(def_vr);
mi.push_reg(base);
mi.push_reg(index);
mi.push_imm(scale as i64);
mi.push_imm(disp as i64);
mi
}
pub fn lower_test(&self, inst: &crate::value::Value) -> MachineInstr {
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2_val = &inst.operands[1];
let mut mi = MachineInstr::new(X86Opcode::TEST as u32);
mi.push_reg(src1);
if self.is_constant_operand(src2_val) {
if let Some(imm) = self.get_constant_value(src2_val) {
mi.push_imm(imm);
}
} else {
let src2 = self.get_operand_vreg(src2_val);
mi.push_reg(src2);
}
mi
}
pub fn lower_setcc(&self, inst: &crate::value::Value, cc: X86Opcode) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let mut mi = MachineInstr::new(cc as u32).with_def(def_vr);
mi
}
pub fn lower_setcc_by_name(&self, def_vr: VirtReg, cc_name: &str) -> MachineInstr {
let cc = match cc_name {
"o" => X86Opcode::SETO,
"no" => X86Opcode::SETNO,
"b" | "c" | "nae" => X86Opcode::SETB,
"ae" | "nb" | "nc" => X86Opcode::SETAE,
"e" | "z" => X86Opcode::SETE,
"ne" | "nz" => X86Opcode::SETNE,
"be" | "na" => X86Opcode::SETBE,
"a" | "nbe" => X86Opcode::SETA,
"s" => X86Opcode::SETS,
"ns" => X86Opcode::SETNS,
"p" | "pe" => X86Opcode::SETP,
"np" | "po" => X86Opcode::SETNP,
"l" | "nge" => X86Opcode::SETL,
"ge" | "nl" => X86Opcode::SETGE,
"le" | "ng" => X86Opcode::SETLE,
"g" | "nle" => X86Opcode::SETG,
_ => X86Opcode::SETE,
};
let mut mi = MachineInstr::new(cc as u32).with_def(def_vr);
mi
}
pub fn lower_cmovcc(&self, inst: &crate::value::Value, cc: X86Opcode) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let cond = self.get_operand_vreg(&inst.operands[0]);
let true_val = self.get_operand_vreg(&inst.operands[1]);
let false_val = self.get_operand_vreg(&inst.operands[2]);
let _mov = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
let mut cmov = MachineInstr::new(cc as u32).with_def(def_vr);
cmov.push_reg(def_vr);
cmov.push_reg(true_val);
cmov
}
pub fn lower_cmovcc_by_name(&self, inst: &crate::value::Value, cc_name: &str) -> MachineInstr {
let cc = match cc_name {
"o" => X86Opcode::CMOVO,
"no" => X86Opcode::CMOVNO,
"b" | "c" | "nae" => X86Opcode::CMOVB,
"ae" | "nb" | "nc" => X86Opcode::CMOVAE,
"e" | "z" => X86Opcode::CMOVE,
"ne" | "nz" => X86Opcode::CMOVNE,
"be" | "na" => X86Opcode::CMOVBE,
"a" | "nbe" => X86Opcode::CMOVA,
"s" => X86Opcode::CMOVS,
"ns" => X86Opcode::CMOVNS,
"p" | "pe" => X86Opcode::CMOVP,
"np" | "po" => X86Opcode::CMOVNP,
"l" | "nge" => X86Opcode::CMOVL,
"ge" | "nl" => X86Opcode::CMOVGE,
"le" | "ng" => X86Opcode::CMOVLE,
"g" | "nle" => X86Opcode::CMOVG,
_ => X86Opcode::CMOVE,
};
self.lower_cmovcc(inst, cc)
}
pub fn lower_bswap(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mov = MachineInstr::new(X86Opcode::MOV as u32).with_def(def_vr);
mov.push_reg(src);
mov
}
pub fn lower_bsf(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::TZCNT as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_bsr(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::LZCNT as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_popcnt(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::ANDN as u32).with_def(def_vr);
mi.push_reg(def_vr);
mi.push_reg(src);
mi
}
pub fn lower_crc32(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let crc = self.get_operand_vreg(&inst.operands[0]);
let data = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(X86Opcode::CRC32 as u32).with_def(def_vr);
mi.push_reg(crc);
mi.push_reg(data);
mi
}
pub fn lower_push(&self, inst: &crate::value::Value) -> MachineInstr {
let src = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(X86Opcode::PUSH as u32);
mi.push_reg(src);
mi
}
pub fn lower_push_imm(&self, imm: i64) -> MachineInstr {
let mut mi = MachineInstr::new(X86Opcode::PUSH as u32);
mi.push_imm(imm);
mi
}
pub fn lower_pop(&self, inst: &crate::value::Value) -> MachineInstr {
let def_vr = self.get_or_create_vreg_by_vid(inst.vid as usize);
let mut mi = MachineInstr::new(X86Opcode::POP as u32).with_def(def_vr);
mi
}
pub fn lower_pushf(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::PUSHF as u32)
}
pub fn lower_popf(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::POPF as u32)
}
pub fn lower_xchg(&self, inst: &crate::value::Value) -> MachineInstr {
let src1 = self.get_operand_vreg(&inst.operands[0]);
let src2 = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(X86Opcode::XCHG as u32);
mi.push_reg(src1);
mi.push_reg(src2);
mi
}
pub fn emit_string_movs(&self, width: u8, rep: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if rep {
instrs.push(MachineInstr::new(X86Opcode::REP as u32));
}
let opcode = match width {
1 => X86Opcode::MOVSB,
2 => X86Opcode::MOVSW,
4 => X86Opcode::MOVSD_STR,
8 => X86Opcode::MOVSQ,
_ => X86Opcode::MOVSB,
};
instrs.push(MachineInstr::new(opcode as u32));
instrs
}
pub fn emit_string_lods(&self, width: u8, rep: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if rep {
instrs.push(MachineInstr::new(X86Opcode::REP as u32));
}
let opcode = match width {
1 => X86Opcode::LODSB,
2 => X86Opcode::LODSW,
4 => X86Opcode::LODSD_STR,
8 => X86Opcode::LODSQ,
_ => X86Opcode::LODSB,
};
instrs.push(MachineInstr::new(opcode as u32));
instrs
}
pub fn emit_string_stos(&self, width: u8, rep: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if rep {
instrs.push(MachineInstr::new(X86Opcode::REP as u32));
}
let opcode = match width {
1 => X86Opcode::STOSB,
2 => X86Opcode::STOSW,
4 => X86Opcode::STOSD_STR,
8 => X86Opcode::STOSQ,
_ => X86Opcode::STOSB,
};
instrs.push(MachineInstr::new(opcode as u32));
instrs
}
pub fn emit_string_cmps(&self, width: u8, rep: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let rep_op = if rep { Some(X86Opcode::REPE) } else { None };
if let Some(rop) = rep_op {
instrs.push(MachineInstr::new(rop as u32));
}
let opcode = match width {
1 => X86Opcode::CMPSB,
2 => X86Opcode::CMPSW,
4 => X86Opcode::CMPSD_STR,
8 => X86Opcode::CMPSQ,
_ => X86Opcode::CMPSB,
};
instrs.push(MachineInstr::new(opcode as u32));
instrs
}
pub fn emit_string_scas(&self, width: u8, rep: bool) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let rep_op = if rep { Some(X86Opcode::REPNE) } else { None };
if let Some(rop) = rep_op {
instrs.push(MachineInstr::new(rop as u32));
}
let opcode = match width {
1 => X86Opcode::SCASB,
2 => X86Opcode::SCASW,
4 => X86Opcode::SCASD_STR,
8 => X86Opcode::SCASQ,
_ => X86Opcode::SCASB,
};
instrs.push(MachineInstr::new(opcode as u32));
instrs
}
pub fn emit_syscall(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::SYSCALL as u32)
}
pub fn emit_sysret(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::SYSRET as u32)
}
pub fn emit_int3(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::INT3 as u32)
}
pub fn emit_ud2(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::UD2 as u32)
}
pub fn emit_nop(&self, length: u8) -> MachineInstr {
let opcode = match length {
0 | 1 => X86Opcode::NOP,
2 => X86Opcode::NOP1, 3 => X86Opcode::NOP2,
4 => X86Opcode::NOP3,
5 => X86Opcode::NOP4,
6 => X86Opcode::NOP5,
7 => X86Opcode::NOP6,
8 => X86Opcode::NOP7,
9 => X86Opcode::NOP8,
_ => X86Opcode::NOP9,
};
MachineInstr::new(opcode as u32)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::basic_block;
use crate::constants;
use crate::function;
use crate::instruction;
use crate::types::Type;
use crate::value::ValueRef;
use std::rc::Rc;
fn build_single_block_func(name: &str, return_ty: Type, instrs: Vec<ValueRef>) -> ValueRef {
let func = function::new_function(name, return_ty, &[]);
let entry = basic_block::new_basic_block("entry");
for i in instrs {
entry.borrow_mut().push_operand(i);
}
func.borrow_mut().push_operand(entry);
func
}
fn select_function(func: &ValueRef) -> MachineFunction {
let f = func.borrow();
let mut mf = MachineFunction::new(&f.name);
let mut isel = X86InstructionSelector::default();
isel.select(&mut mf, func);
mf
}
fn all_instructions(mf: &MachineFunction) -> Vec<&MachineInstr> {
mf.blocks
.iter()
.flat_map(|b| b.instructions.iter())
.collect()
}
fn find_by_opcode<'a>(instrs: &[&'a MachineInstr], opcode: u32) -> Option<&'a MachineInstr> {
instrs.iter().find(|mi| mi.opcode == opcode).copied()
}
#[test]
fn test_select_add() {
let a = constants::const_i32(10);
let b = constants::const_i32(20);
let add = instruction::add(a, b);
add.borrow_mut().name = "sum".into();
let ret = instruction::ret_val(add.clone());
let func = build_single_block_func("add_test", Type::i32(), vec![add, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let add_instr = find_by_opcode(&instrs, X86Opcode::ADD as u32);
assert!(
add_instr.is_some(),
"Expected ADD instruction, got: {:?}",
instrs.iter().map(|i| i.opcode).collect::<Vec<_>>()
);
}
#[test]
fn test_select_sub() {
let a = constants::const_i32(50);
let b = constants::const_i32(10);
let sub = instruction::sub(a, b);
sub.borrow_mut().name = "diff".into();
let ret = instruction::ret_val(sub.clone());
let func = build_single_block_func("sub_test", Type::i32(), vec![sub, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::SUB as u32).is_some());
}
#[test]
fn test_select_mul() {
let a = constants::const_i32(6);
let b = constants::const_i32(7);
let mul = instruction::mul(a, b);
mul.borrow_mut().name = "prod".into();
let ret = instruction::ret_val(mul.clone());
let func = build_single_block_func("mul_test", Type::i32(), vec![mul, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(
find_by_opcode(&instrs, X86Opcode::IMUL as u32).is_some(),
"Expected IMUL, got ops: {:?}",
instrs.iter().map(|i| i.opcode).collect::<Vec<_>>()
);
}
#[test]
fn test_select_and() {
let a = constants::const_i32(0xFF);
let b = constants::const_i32(0x0F);
let and = instruction::and(a, b);
and.borrow_mut().name = "masked".into();
let ret = instruction::ret_val(and.clone());
let func = build_single_block_func("and_test", Type::i32(), vec![and, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::AND as u32).is_some());
}
#[test]
fn test_select_or() {
let a = constants::const_i32(0x0F);
let b = constants::const_i32(0xF0);
let or = instruction::or(a, b);
or.borrow_mut().name = "combined".into();
let ret = instruction::ret_val(or.clone());
let func = build_single_block_func("or_test", Type::i32(), vec![or, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::OR as u32).is_some());
}
#[test]
fn test_select_xor() {
let a = constants::const_i32(42);
let b = constants::const_i32(42);
let xor = instruction::xor(a, b);
xor.borrow_mut().name = "zeroed".into();
let ret = instruction::ret_val(xor.clone());
let func = build_single_block_func("xor_test", Type::i32(), vec![xor, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::XOR as u32).is_some());
}
#[test]
fn test_select_shl() {
let a = constants::const_i32(1);
let b = constants::const_i32(3);
let shl = instruction::shl(a, b);
shl.borrow_mut().name = "shifted".into();
let ret = instruction::ret_val(shl.clone());
let func = build_single_block_func("shl_test", Type::i32(), vec![shl, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::SHL as u32).is_some());
}
#[test]
fn test_select_lshr() {
let a = constants::const_i32(8);
let b = constants::const_i32(2);
let lshr = instruction::lshr(a, b);
lshr.borrow_mut().name = "lshred".into();
let ret = instruction::ret_val(lshr.clone());
let func = build_single_block_func("lshr_test", Type::i32(), vec![lshr, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::SHR as u32).is_some());
}
#[test]
fn test_select_ashr() {
let a = constants::const_i32(-8);
let b = constants::const_i32(2);
let ashr = instruction::ashr(a, b);
ashr.borrow_mut().name = "ashred".into();
let ret = instruction::ret_val(ashr.clone());
let func = build_single_block_func("ashr_test", Type::i32(), vec![ashr, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::SAR as u32).is_some());
}
#[test]
fn test_select_sdiv() {
let a = constants::const_i32(100);
let b = constants::const_i32(7);
let sdiv = instruction::sdiv(a, b);
sdiv.borrow_mut().name = "quot".into();
let ret = instruction::ret_val(sdiv.clone());
let func = build_single_block_func("sdiv_test", Type::i32(), vec![sdiv, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_mov = instrs.iter().any(|i| i.opcode == X86Opcode::MOV as u32);
let has_idiv = instrs.iter().any(|i| i.opcode == X86Opcode::IDIV as u32);
assert!(has_mov, "SDiv should contain MOV to RAX");
assert!(has_idiv, "SDiv should contain IDIV");
}
#[test]
fn test_select_udiv() {
let a = constants::const_i32(100);
let b = constants::const_i32(7);
let udiv = instruction::udiv(a, b);
udiv.borrow_mut().name = "uquot".into();
let ret = instruction::ret_val(udiv.clone());
let func = build_single_block_func("udiv_test", Type::i32(), vec![udiv, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_div = instrs.iter().any(|i| i.opcode == X86Opcode::DIV as u32);
assert!(has_div, "UDiv should contain DIV");
}
#[test]
fn test_select_alloca() {
let alloca = instruction::alloca(Type::i32());
alloca.borrow_mut().name = "p".into();
let ret = instruction::ret_void();
let func = build_single_block_func("alloca_test", Type::void(), vec![alloca, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(!instrs.is_empty(), "alloca should produce instructions");
}
#[test]
fn test_select_load() {
let alloca = instruction::alloca(Type::i32());
alloca.borrow_mut().name = "p".into();
let load = instruction::load(Type::i32(), alloca.clone());
load.borrow_mut().name = "v".into();
let ret = instruction::ret_val(load.clone());
let func = build_single_block_func("load_test", Type::i32(), vec![alloca, load, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let mov_count = instrs
.iter()
.filter(|i| i.opcode == X86Opcode::MOV as u32)
.count();
assert!(
mov_count >= 2,
"Load should produce at least 2 MOV instructions"
);
}
#[test]
fn test_select_store() {
let alloca = instruction::alloca(Type::i32());
alloca.borrow_mut().name = "p".into();
let val = constants::const_i32(42);
let store = instruction::store(val, alloca.clone());
let ret = instruction::ret_void();
let func = build_single_block_func("store_test", Type::void(), vec![alloca, store, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let mov_count = instrs
.iter()
.filter(|i| i.opcode == X86Opcode::MOV as u32)
.count();
assert!(
mov_count >= 1,
"Store should produce at least 1 MOV instruction"
);
}
#[test]
fn test_select_icmp_eq() {
let a = constants::const_i32(5);
let b = constants::const_i32(5);
let icmp = instruction::icmp(crate::opcode::ICmpPred::Eq, a, b);
let ret = instruction::ret_val(icmp.clone());
let func = build_single_block_func("icmp_eq_test", Type::i1(), vec![icmp, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_cmp = instrs.iter().any(|i| i.opcode == X86Opcode::CMP as u32);
let has_sete = instrs.iter().any(|i| i.opcode == X86Opcode::SETE as u32);
assert!(has_cmp, "ICmp should produce CMP");
assert!(has_sete, "ICmp eq should produce SETE");
}
#[test]
fn test_select_icmp_ne() {
let a = constants::const_i32(5);
let b = constants::const_i32(10);
let icmp = instruction::icmp(crate::opcode::ICmpPred::Ne, a, b);
let ret = instruction::ret_val(icmp.clone());
let func = build_single_block_func("icmp_ne_test", Type::i1(), vec![icmp, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_setne = instrs.iter().any(|i| i.opcode == X86Opcode::SETNE as u32);
assert!(has_setne, "ICmp ne should produce SETNE");
}
#[test]
fn test_select_icmp_slt() {
let a = constants::const_i32(3);
let b = constants::const_i32(7);
let icmp = instruction::icmp(crate::opcode::ICmpPred::Slt, a, b);
let ret = instruction::ret_val(icmp.clone());
let func = build_single_block_func("icmp_slt_test", Type::i1(), vec![icmp, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_setl = instrs.iter().any(|i| i.opcode == X86Opcode::SETL as u32);
assert!(has_setl, "ICmp slt should produce SETL");
}
#[test]
fn test_select_icmp_sgt() {
let a = constants::const_i32(7);
let b = constants::const_i32(3);
let icmp = instruction::icmp(crate::opcode::ICmpPred::Sgt, a, b);
let ret = instruction::ret_val(icmp.clone());
let func = build_single_block_func("icmp_sgt_test", Type::i1(), vec![icmp, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_setg = instrs.iter().any(|i| i.opcode == X86Opcode::SETG as u32);
assert!(has_setg, "ICmp sgt should produce SETG");
}
#[test]
fn test_select_ret_void() {
let ret = instruction::ret_void();
let func = build_single_block_func("ret_void_test", Type::void(), vec![ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::RET as u32).is_some());
}
#[test]
fn test_select_ret_val() {
let val = constants::const_i32(42);
let ret = instruction::ret_val(val);
let func = build_single_block_func("ret_val_test", Type::i32(), vec![ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::RET as u32).is_some());
}
#[test]
fn test_select_br_unconditional() {
let target = basic_block::new_basic_block("target");
target.borrow_mut().push_operand(instruction::ret_void());
let entry = basic_block::new_basic_block("entry");
entry
.borrow_mut()
.push_operand(instruction::br(target.clone()));
let func = function::new_function("br_test", Type::void(), &[]);
func.borrow_mut().push_operand(entry);
func.borrow_mut().push_operand(target);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(find_by_opcode(&instrs, X86Opcode::JMP as u32).is_some());
}
#[test]
fn test_select_br_conditional() {
let then_bb = basic_block::new_basic_block("then");
then_bb.borrow_mut().push_operand(instruction::ret_void());
let else_bb = basic_block::new_basic_block("else");
else_bb.borrow_mut().push_operand(instruction::ret_void());
let cond = constants::const_bool(true);
let entry = basic_block::new_basic_block("entry");
entry.borrow_mut().push_operand(instruction::br_cond(
cond,
then_bb.clone(),
else_bb.clone(),
));
let func = function::new_function("br_cond_test", Type::void(), &[]);
func.borrow_mut().push_operand(entry);
func.borrow_mut().push_operand(then_bb);
func.borrow_mut().push_operand(else_bb);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_jmp = find_by_opcode(&instrs, X86Opcode::JMP as u32).is_some();
let has_jne = find_by_opcode(&instrs, X86Opcode::JNE as u32).is_some();
assert!(
has_jmp || has_jne,
"Conditional branch should have jmp or jne"
);
}
#[test]
fn test_select_call_void() {
let callee = function::new_function("other", Type::void(), &[]);
let call = instruction::call(Type::void(), callee, vec![]);
let ret = instruction::ret_void();
let func = build_single_block_func("caller_test", Type::void(), vec![call, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_call = instrs.iter().any(|i| i.opcode == X86Opcode::CALL as u32);
assert!(has_call, "Call should produce CALL");
}
#[test]
fn test_select_gep() {
let ptr = instruction::alloca(Type::i32());
ptr.borrow_mut().name = "arr".into();
let idx = constants::const_i32(0);
let gep = instruction::getelementptr(Type::i32(), ptr.clone(), vec![idx]);
gep.borrow_mut().name = "elem".into();
let ret = instruction::ret_val(gep.clone());
let func = build_single_block_func("gep_test", Type::pointer(0), vec![ptr, gep, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_lea = find_by_opcode(&instrs, X86Opcode::LEA as u32).is_some();
let has_mov = find_by_opcode(&instrs, X86Opcode::MOV as u32).is_some();
assert!(has_lea || has_mov, "GEP should produce LEA or MOV");
}
#[test]
fn test_select_select() {
let cond = constants::const_bool(true);
let true_val = constants::const_i32(100);
let false_val = constants::const_i32(200);
let _sel = instruction::phi(
Type::i32(),
vec![
(
true_val.clone(),
Rc::new(std::cell::RefCell::new(crate::value::Value::new(
Type::label(),
))),
),
(
false_val.clone(),
Rc::new(std::cell::RefCell::new(crate::value::Value::new(
Type::label(),
))),
),
],
);
let mut v = crate::value::Value::new(Type::i32());
v.opcode = Some(Opcode::Select);
v.operands = vec![cond, true_val, false_val];
v.subclass = crate::value::SubclassKind::Instruction;
v.name = "sel".into();
let sel_ref = crate::value::valref(v);
let ret = instruction::ret_val(sel_ref.clone());
let func = build_single_block_func("select_test", Type::i32(), vec![sel_ref, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_cmov = find_by_opcode(&instrs, X86Opcode::CMOVNE as u32).is_some();
let has_mov = find_by_opcode(&instrs, X86Opcode::MOV as u32).is_some();
assert!(has_cmov || has_mov, "Select should produce CMOVNE or MOV");
}
#[test]
fn test_select_zext() {
let val = constants::const_i32(42);
let _zext = instruction::add(val.clone(), val.clone()); let mut v = crate::value::Value::new(Type::i64());
v.opcode = Some(Opcode::ZExt);
v.operands = vec![val.clone()];
v.subclass = crate::value::SubclassKind::Instruction;
v.name = "ext".into();
let zext_ref = crate::value::valref(v);
let ret = instruction::ret_val(zext_ref.clone());
let func = build_single_block_func("zext_test", Type::i64(), vec![zext_ref, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(
!instrs.is_empty(),
"ZExt should produce at least one instruction"
);
}
#[test]
fn test_select_sext() {
let val = constants::const_i32(-1);
let mut v = crate::value::Value::new(Type::i64());
v.opcode = Some(Opcode::SExt);
v.operands = vec![val];
v.subclass = crate::value::SubclassKind::Instruction;
v.name = "ext".into();
let sext_ref = crate::value::valref(v);
let ret = instruction::ret_val(sext_ref.clone());
let func = build_single_block_func("sext_test", Type::i64(), vec![sext_ref, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(
!instrs.is_empty(),
"SExt should produce at least one instruction"
);
}
#[test]
fn test_select_trunc() {
let val = constants::const_i64(0x1234567890ABCDEF);
let mut v = crate::value::Value::new(Type::i32());
v.opcode = Some(Opcode::Trunc);
v.operands = vec![val];
v.subclass = crate::value::SubclassKind::Instruction;
v.name = "trunc".into();
let trunc_ref = crate::value::valref(v);
let ret = instruction::ret_val(trunc_ref.clone());
let func = build_single_block_func("trunc_test", Type::i32(), vec![trunc_ref, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(
!instrs.is_empty(),
"Trunc should produce at least one instruction"
);
}
#[test]
fn test_select_bitcast() {
let val = constants::const_i32(0x3F800000u32 as i32); let mut v = crate::value::Value::new(Type::float());
v.opcode = Some(Opcode::BitCast);
v.operands = vec![val];
v.subclass = crate::value::SubclassKind::Instruction;
v.name = "bc".into();
let bc_ref = crate::value::valref(v);
let ret = instruction::ret_val(bc_ref.clone());
let func = build_single_block_func("bitcast_test", Type::float(), vec![bc_ref, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
assert!(
!instrs.is_empty(),
"BitCast should produce at least one instruction"
);
}
#[test]
fn test_select_phi() {
let val_a = constants::const_i32(10);
let bb_a = basic_block::new_basic_block("a");
let val_b = constants::const_i32(20);
let bb_b = basic_block::new_basic_block("b");
let phi = instruction::phi(
Type::i32(),
vec![(val_a, bb_a.clone()), (val_b, bb_b.clone())],
);
phi.borrow_mut().name = "p".into();
let ret = instruction::ret_val(phi.clone());
let func = build_single_block_func("phi_test", Type::i32(), vec![phi, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let has_mov = find_by_opcode(&instrs, X86Opcode::MOV as u32).is_some();
assert!(
has_mov || !instrs.is_empty(),
"Phi should produce at least one instruction"
);
}
#[test]
fn test_select_empty_function() {
let ret = instruction::ret_void();
let func = build_single_block_func("empty", Type::void(), vec![ret]);
let mf = select_function(&func);
assert!(!mf.blocks.is_empty());
let ret_count = all_instructions(&mf)
.iter()
.filter(|i| i.opcode == X86Opcode::RET as u32)
.count();
assert_eq!(ret_count, 1);
}
#[test]
fn test_default_selector() {
let sel = X86InstructionSelector::default();
assert!(sel.subtarget.is_64_bit);
assert!(sel.vreg_map.is_empty());
}
#[test]
fn test_emit_mov_imm() {
let sel = X86InstructionSelector::default();
let mi = sel.emit_mov_imm(5, 42);
assert_eq!(mi.opcode, X86Opcode::MOV as u32);
assert_eq!(mi.def, Some(5));
assert_eq!(mi.operands.len(), 1);
}
#[test]
fn test_emit_binary_op() {
let sel = X86InstructionSelector::default();
let mi = sel.emit_binary_op(X86Opcode::ADD as u32, 1, 2, 0);
assert_eq!(mi.opcode, X86Opcode::ADD as u32);
assert_eq!(mi.def, Some(1));
assert_eq!(mi.operands.len(), 2);
}
#[test]
fn test_icmp_pred_to_setcc() {
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("eq"),
X86Opcode::SETE as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("ne"),
X86Opcode::SETNE as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("slt"),
X86Opcode::SETL as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("sle"),
X86Opcode::SETLE as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("sgt"),
X86Opcode::SETG as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_setcc("sge"),
X86Opcode::SETGE as u32
);
}
#[test]
fn test_icmp_pred_to_jcc() {
assert_eq!(
X86InstructionSelector::icmp_pred_to_jcc("eq"),
X86Opcode::JE as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_jcc("ne"),
X86Opcode::JNE as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_jcc("ult"),
X86Opcode::JB as u32
);
assert_eq!(
X86InstructionSelector::icmp_pred_to_jcc("ule"),
X86Opcode::JBE as u32
);
}
#[test]
fn test_is_constant_operand() {
let sel = X86InstructionSelector::default();
let c = constants::const_i32(42);
assert!(sel.is_constant_operand(&c));
let inst = instruction::add(constants::const_i32(1), constants::const_i32(2));
assert!(!sel.is_constant_operand(&inst));
}
#[test]
fn test_get_constant_value() {
let sel = X86InstructionSelector::default();
let c = constants::const_i32(42);
assert_eq!(sel.get_constant_value(&c), Some(42));
let neg = constants::const_i32(-1);
assert_eq!(sel.get_constant_value(&neg), Some(-1));
}
#[test]
fn test_select_multiple_instructions_in_block() {
let a = constants::const_i32(1);
let b = constants::const_i32(2);
let add1 = instruction::add(a, b);
add1.borrow_mut().name = "x".into();
let c = constants::const_i32(3);
let add2 = instruction::add(add1.clone(), c);
add2.borrow_mut().name = "y".into();
let ret = instruction::ret_val(add2.clone());
let func = build_single_block_func("multi_test", Type::i32(), vec![add1, add2, ret]);
let mf = select_function(&func);
let instrs = all_instructions(&mf);
let add_count = instrs
.iter()
.filter(|i| i.opcode == X86Opcode::ADD as u32)
.count();
assert_eq!(add_count, 2, "Should have 2 ADD instructions");
let ret_count = instrs
.iter()
.filter(|i| i.opcode == X86Opcode::RET as u32)
.count();
assert_eq!(ret_count, 1);
}
#[test]
fn test_vreg_assignment() {
let a = constants::const_i32(10);
let b = constants::const_i32(20);
let add = instruction::add(a, b);
add.borrow_mut().name = "result".into();
let ret = instruction::ret_val(add.clone());
let func = build_single_block_func("vreg_test", Type::i32(), vec![add, ret]);
let f = func.borrow();
let mut mf = MachineFunction::new(&f.name);
let mut isel = X86InstructionSelector::default();
isel.select(&mut mf, &func);
assert!(
mf.virt_reg_counter > 0,
"Should have allocated virtual registers"
);
}
}