use super::riscv_instr_info::RiscVOpcode;
use super::riscv_register_info::*;
use crate::codegen::*;
use crate::opcode::Opcode;
use crate::value::Value;
use std::collections::HashMap;
const I_TYPE_IMM_BITS: i64 = 12;
const I_TYPE_IMM_MIN: i64 = -(1 << (I_TYPE_IMM_BITS - 1));
const I_TYPE_IMM_MAX: i64 = (1 << (I_TYPE_IMM_BITS - 1)) - 1;
const U_TYPE_IMM_SHIFT: i64 = 12;
const LS_OFFSET_MIN: i64 = I_TYPE_IMM_MIN;
const LS_OFFSET_MAX: i64 = I_TYPE_IMM_MAX;
const BRANCH_OFFSET_MIN: i64 = -(1 << 11);
const BRANCH_OFFSET_MAX: i64 = (1 << 11) - 1;
const JAL_OFFSET_MIN: i64 = -(1 << 19);
const JAL_OFFSET_MAX: i64 = (1 << 19) - 1;
pub struct RiscVInstructionSelector {
pub is_64bit: bool,
pub vreg_map: HashMap<usize, VirtReg>,
pub mbb: MachineBasicBlock,
pub func_name: String,
}
impl RiscVInstructionSelector {
pub fn new(is_64bit: bool) -> Self {
RiscVInstructionSelector {
is_64bit,
vreg_map: HashMap::new(),
mbb: MachineBasicBlock {
name: String::new(),
instructions: Vec::new(),
successors: Vec::new(),
},
func_name: String::new(),
}
}
pub fn select(&mut self, mf: &mut MachineFunction, func: &Value) {
self.func_name = func.name.clone();
if self.func_name.is_empty() {
self.func_name = format!(".Lfunc{}", func.vid);
}
self.vreg_map.clear();
for bb_ref in &func.successors {
let bb = bb_ref.borrow();
self.mbb = MachineBasicBlock {
name: bb.name.clone(),
instructions: Vec::new(),
successors: Vec::new(),
};
for inst_ref in &bb.operands {
let inst = inst_ref.borrow();
if inst.is_instruction() {
let instrs = self.select_instruction(&inst);
self.mbb.instructions.extend(instrs);
}
}
mf.push_block(self.mbb.clone());
}
}
pub fn select_instruction(&mut self, inst: &Value) -> Vec<MachineInstr> {
let opcode = match inst.get_opcode() {
Some(op) => op,
None => return Vec::new(),
};
match opcode {
Opcode::Add => vec![self.lower_add(inst)],
Opcode::Sub => vec![self.lower_sub(inst)],
Opcode::Mul => vec![self.lower_mul(inst)],
Opcode::SDiv => self.lower_sdiv(inst),
Opcode::UDiv => self.lower_udiv(inst),
Opcode::SRem => self.lower_srem(inst),
Opcode::URem => self.lower_urem(inst),
Opcode::And => vec![self.lower_and(inst)],
Opcode::Or => vec![self.lower_or(inst)],
Opcode::Xor => vec![self.lower_xor(inst)],
Opcode::Shl => vec![self.lower_shl(inst)],
Opcode::LShr => vec![self.lower_lshr(inst)],
Opcode::AShr => vec![self.lower_ashr(inst)],
Opcode::FAdd => self.lower_fadd(inst),
Opcode::FSub => self.lower_fsub(inst),
Opcode::FMul => self.lower_fmul(inst),
Opcode::FDiv => self.lower_fdiv(inst),
Opcode::FRem => self.lower_frem(inst),
Opcode::ICmp => self.lower_icmp(inst),
Opcode::FCmp => self.lower_fcmp(inst),
Opcode::Br => self.lower_br(inst),
Opcode::Ret => self.lower_ret(inst),
Opcode::Call => self.lower_call(inst),
Opcode::Switch => self.lower_switch(inst),
Opcode::IndirectBr => self.lower_indirect_br(inst),
Opcode::Alloca => vec![self.lower_alloca(inst)],
Opcode::Load => vec![self.lower_load(inst)],
Opcode::Store => vec![self.lower_store(inst)],
Opcode::Fence => vec![self.lower_fence(inst)],
Opcode::AtomicRMW => self.lower_atomic_rmw(inst),
Opcode::CmpXchg => self.lower_cmpxchg(inst),
Opcode::ZExt => vec![self.lower_zext(inst)],
Opcode::SExt => vec![self.lower_sext(inst)],
Opcode::Trunc => vec![self.lower_trunc(inst)],
Opcode::BitCast => vec![self.lower_bitcast(inst)],
Opcode::FPToSI => self.lower_fptosi(inst),
Opcode::FPToUI => self.lower_fptoui(inst),
Opcode::SIToFP => self.lower_sitofp(inst),
Opcode::UIToFP => self.lower_uitofp(inst),
Opcode::FPTrunc => self.lower_fptrunc(inst),
Opcode::FPExt => self.lower_fpext(inst),
Opcode::PtrToInt => vec![self.lower_ptrtoint(inst)],
Opcode::IntToPtr => vec![self.lower_inttoptr(inst)],
Opcode::AddrSpaceCast => vec![self.lower_addrspacecast(inst)],
Opcode::GetElementPtr => vec![self.lower_gep(inst)],
Opcode::ExtractValue => vec![self.lower_extractvalue(inst)],
Opcode::InsertValue => self.lower_insertvalue(inst),
Opcode::ExtractElement => vec![self.lower_extractelement(inst)],
Opcode::InsertElement => self.lower_insertelement(inst),
Opcode::ShuffleVector => self.lower_shufflevector(inst),
Opcode::Select => self.lower_select(inst),
Opcode::Phi => Vec::new(),
Opcode::VAArg => vec![self.lower_vaarg(inst)],
Opcode::Freeze => vec![self.lower_freeze(inst)],
_ => Vec::new(),
}
}
pub fn lower_add(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_imm(inst, RiscVOpcode::ADD as u32, RiscVOpcode::ADDI as u32)
}
pub fn lower_sub(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::SUB as u32)
}
pub fn lower_mul(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::MUL as u32)
}
pub fn lower_sdiv(&self, inst: &Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, RiscVOpcode::DIVW as u32)]
} else {
vec![self.lower_three_reg_op(inst, RiscVOpcode::DIV as u32)]
}
}
pub fn lower_udiv(&self, inst: &Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, RiscVOpcode::DIVUW as u32)]
} else {
vec![self.lower_three_reg_op(inst, RiscVOpcode::DIVU as u32)]
}
}
pub fn lower_srem(&self, inst: &Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, RiscVOpcode::REMW as u32)]
} else {
vec![self.lower_three_reg_op(inst, RiscVOpcode::REM as u32)]
}
}
pub fn lower_urem(&self, inst: &Value) -> Vec<MachineInstr> {
if self.is_64bit {
vec![self.lower_three_reg_op(inst, RiscVOpcode::REMUW as u32)]
} else {
vec![self.lower_three_reg_op(inst, RiscVOpcode::REMU as u32)]
}
}
pub fn lower_and(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_imm(inst, RiscVOpcode::AND as u32, RiscVOpcode::ANDI as u32)
}
pub fn lower_or(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_imm(inst, RiscVOpcode::OR as u32, RiscVOpcode::ORI as u32)
}
pub fn lower_xor(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_imm(inst, RiscVOpcode::XOR as u32, RiscVOpcode::XORI as u32)
}
pub fn lower_shl(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_shift_imm(inst, RiscVOpcode::SLL as u32, RiscVOpcode::SLLI as u32)
}
pub fn lower_lshr(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_shift_imm(inst, RiscVOpcode::SRL as u32, RiscVOpcode::SRLI as u32)
}
pub fn lower_ashr(&self, inst: &Value) -> MachineInstr {
self.lower_binary_op_with_shift_imm(inst, RiscVOpcode::SRA as u32, RiscVOpcode::SRAI as u32)
}
pub fn lower_icmp(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let tmp_vr = 0xFFFF_FFFE; let mut sub = MachineInstr::new(RiscVOpcode::SUB as u32);
sub.push_reg(src1_vr);
sub.push_reg(src2_vr);
sub.def = Some(tmp_vr);
instrs.push(sub);
let mut sltiu = MachineInstr::new(RiscVOpcode::SLTIU as u32).with_def(def_vr);
sltiu.push_reg(tmp_vr);
sltiu.push_imm(1);
instrs.push(sltiu);
instrs
}
pub fn lower_br(&mut self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let num_ops = inst.operands.len();
if num_ops == 1 {
let dest = inst.operands[0].borrow().name.clone();
self.mbb.successors.push(dest.clone());
let mut j = MachineInstr::new(RiscVOpcode::JAL as u32);
j.operands.push(MachineOperand::PhysReg(ZERO as u32));
j.push_label(&dest);
instrs.push(j);
} else if num_ops == 3 {
let cond_vid = inst.operands[0].borrow().vid as usize;
let t_label = inst.operands[1].borrow().name.clone();
let f_label = inst.operands[2].borrow().name.clone();
self.mbb.successors.push(t_label.clone());
self.mbb.successors.push(f_label.clone());
if let Some(&cond_vr) = self.vreg_map.get(&cond_vid) {
let mut bne = MachineInstr::new(RiscVOpcode::BNE as u32);
bne.push_reg(cond_vr);
bne.operands.push(MachineOperand::PhysReg(ZERO as u32));
bne.push_label(&t_label);
instrs.push(bne);
let mut j = MachineInstr::new(RiscVOpcode::JAL as u32);
j.operands.push(MachineOperand::PhysReg(ZERO as u32));
j.push_label(&f_label);
instrs.push(j);
}
}
instrs
}
pub fn lower_ret(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if !inst.operands.is_empty() {
let val_vid = inst.operands[0].borrow().vid as usize;
if let Some(&vr) = self.vreg_map.get(&val_vid) {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32);
mv.operands.push(MachineOperand::PhysReg(A0 as u32));
mv.push_reg(vr);
mv.push_imm(0);
instrs.push(mv);
}
}
instrs.push(MachineInstr::new(RiscVOpcode::RET as u32));
instrs
}
pub fn lower_call(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if inst.operands.is_empty() {
return instrs;
}
let callee = &inst.operands[0];
let callee_name = callee.borrow().name.clone();
let is_direct = callee.borrow().is_function();
let arg_regs: &[u16] = &[A0, A1, A2, A3, A4, A5, A6, A7];
for i in 1..inst.operands.len().min(arg_regs.len() + 1) {
let arg_vid = inst.operands[i].borrow().vid as usize;
if let Some(&arg_vr) = self.vreg_map.get(&arg_vid) {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32);
mv.operands
.push(MachineOperand::PhysReg(arg_regs[i - 1] as u32));
mv.push_reg(arg_vr);
mv.push_imm(0);
instrs.push(mv);
}
}
if is_direct {
let mut call = MachineInstr::new(RiscVOpcode::JAL as u32);
call.operands.push(MachineOperand::PhysReg(RA as u32));
call.operands.push(MachineOperand::Global(callee_name));
instrs.push(call);
} else {
let callee_vid = callee.borrow().vid as usize;
if let Some(&callee_vr) = self.vreg_map.get(&callee_vid) {
let mut call = MachineInstr::new(RiscVOpcode::JALR as u32);
call.operands.push(MachineOperand::PhysReg(RA as u32));
call.push_reg(callee_vr);
call.push_imm(0);
instrs.push(call);
}
}
if !inst.ty.is_void() {
if let Some(&def_vr) = self.vreg_map.get(&(inst.vid as usize)) {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(A0 as u32);
mv.push_imm(0);
instrs.push(mv);
}
}
instrs
}
pub fn lower_alloca(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
addi.operands.push(MachineOperand::PhysReg(SP as u32));
addi.push_imm(0);
addi
}
pub fn lower_load(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let opcode = if self.is_64bit {
RiscVOpcode::LD as u32
} else {
RiscVOpcode::LW as u32
};
let mut load = MachineInstr::new(opcode).with_def(def_vr);
load.push_reg(ptr_vr);
load.push_imm(0);
load
}
pub fn lower_store(&self, inst: &Value) -> MachineInstr {
let val_vr = self.get_operand_vreg(&inst.operands[0]);
let ptr_vr = self.get_operand_vreg(&inst.operands[1]);
let opcode = if self.is_64bit {
RiscVOpcode::SD as u32
} else {
RiscVOpcode::SW as u32
};
let mut store = MachineInstr::new(opcode);
store.push_reg(val_vr); store.push_reg(ptr_vr); store.push_imm(0); store
}
pub fn lower_zext(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
if self.is_64bit {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
} else {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
}
pub fn lower_sext(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
if self.is_64bit {
let mut addiw = MachineInstr::new(RiscVOpcode::ADDIW as u32).with_def(def_vr);
addiw.push_reg(src_vr);
addiw.push_imm(0);
addiw
} else {
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
}
pub fn lower_trunc(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_bitcast(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_gep(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let base_vr = self.get_operand_vreg(&inst.operands[0]);
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
addi.push_reg(base_vr);
addi.push_imm(0);
addi
}
pub fn lower_select(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
if inst.operands.len() < 3 {
return instrs;
}
let cond_vid = inst.operands[0].borrow().vid as usize;
let true_vr = self.get_operand_vreg(&inst.operands[1]);
let false_vr = self.get_operand_vreg(&inst.operands[2]);
if let Some(&cond_vr) = self.vreg_map.get(&cond_vid) {
let label_false = format!(".Lselect_false_{}", inst.vid);
let label_end = format!(".Lselect_end_{}", inst.vid);
let mut beq = MachineInstr::new(RiscVOpcode::BEQ as u32);
beq.push_reg(cond_vr);
beq.operands.push(MachineOperand::PhysReg(ZERO as u32));
beq.push_label(&label_false);
instrs.push(beq);
let mut mv_true = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv_true.push_reg(true_vr);
mv_true.push_imm(0);
instrs.push(mv_true);
let mut j = MachineInstr::new(RiscVOpcode::JAL as u32);
j.operands.push(MachineOperand::PhysReg(ZERO as u32));
j.push_label(&label_end);
instrs.push(j);
let mut mv_false = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv_false.push_reg(false_vr);
mv_false.push_imm(0);
instrs.push(mv_false);
}
instrs
}
pub fn lower_mulh(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::MULH as u32)
}
pub fn lower_mulhu(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::MULHU as u32)
}
pub fn lower_mulhsu(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::MULHSU as u32)
}
fn fp_opcode_pair(&self, op_s: u32, op_d: u32) -> u32 {
op_s }
fn is_double_type(&self, ty: &crate::types::Type) -> bool {
ty.is_floating_point() && ty.size_in_bits() == 64
}
fn is_float_type(&self, ty: &crate::types::Type) -> bool {
ty.is_floating_point() && ty.size_in_bits() == 32
}
fn int_bit_width(&self, ty: &crate::types::Type) -> u32 {
if ty.is_integer() {
ty.integer_bit_width()
} else {
0
}
}
pub fn lower_fadd(&self, inst: &Value) -> Vec<MachineInstr> {
let is_double = self.is_double_type(&inst.ty);
let opcode = if is_double {
RiscVOpcode::FADD_D as u32
} else {
RiscVOpcode::FADD_S as u32
};
vec![self.lower_three_reg_op(inst, opcode)]
}
pub fn lower_fsub(&self, inst: &Value) -> Vec<MachineInstr> {
let is_double = self.is_double_type(&inst.ty);
let opcode = if is_double {
RiscVOpcode::FSUB_D as u32
} else {
RiscVOpcode::FSUB_S as u32
};
vec![self.lower_three_reg_op(inst, opcode)]
}
pub fn lower_fmul(&self, inst: &Value) -> Vec<MachineInstr> {
let is_double = self.is_double_type(&inst.ty);
let opcode = if is_double {
RiscVOpcode::FMUL_D as u32
} else {
RiscVOpcode::FMUL_S as u32
};
vec![self.lower_three_reg_op(inst, opcode)]
}
pub fn lower_fdiv(&self, inst: &Value) -> Vec<MachineInstr> {
let is_double = self.is_double_type(&inst.ty);
let opcode = if is_double {
RiscVOpcode::FDIV_D as u32
} else {
RiscVOpcode::FDIV_S as u32
};
vec![self.lower_three_reg_op(inst, opcode)]
}
pub fn lower_frem(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let is_double = self.is_double_type(&inst.ty);
let func_name = if is_double { "fmod" } else { "fmodf" };
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut mv0 = MachineInstr::new(RiscVOpcode::ADDI as u32);
mv0.operands.push(MachineOperand::PhysReg(FA0 as u32));
mv0.push_reg(src1_vr);
mv0.push_imm(0);
instrs.push(mv0);
let mut mv1 = MachineInstr::new(RiscVOpcode::ADDI as u32);
mv1.operands.push(MachineOperand::PhysReg(FA1 as u32));
mv1.push_reg(src2_vr);
mv1.push_imm(0);
instrs.push(mv1);
let mut call = MachineInstr::new(RiscVOpcode::JAL as u32);
call.operands.push(MachineOperand::PhysReg(RA as u32));
call.operands.push(MachineOperand::Global(func_name.to_string()));
instrs.push(call);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(FA0 as u32);
mv.push_imm(0);
instrs.push(mv);
instrs
}
pub fn lower_fsqrt(&self, inst: &Value) -> MachineInstr {
let is_double = self.is_double_type(&inst.ty);
let opcode = if is_double {
RiscVOpcode::FSQRT_D as u32
} else {
RiscVOpcode::FSQRT_S as u32
};
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src_vr);
mi
}
pub fn lower_fcmp(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let is_double = self.is_double_type(&inst.ty);
let (feq, flt, fle) = if is_double {
(
RiscVOpcode::FEQ_D as u32,
RiscVOpcode::FLT_D as u32,
RiscVOpcode::FLE_D as u32,
)
} else {
(
RiscVOpcode::FEQ_S as u32,
RiscVOpcode::FLT_S as u32,
RiscVOpcode::FLE_S as u32,
)
};
let mut compare = MachineInstr::new(feq).with_def(def_vr);
compare.push_reg(src1_vr);
compare.push_reg(src2_vr);
instrs.push(compare);
instrs
}
pub fn lower_fptosi(&self, inst: &Value) -> Vec<MachineInstr> {
let src_ty = &inst.operands[0].borrow().ty;
let is_double_src = src_ty.is_floating_point() && src_ty.size_in_bits() == 64;
let is_64bit_dst = self.is_64bit;
let opcode = match (is_double_src, is_64bit_dst) {
(true, true) => RiscVOpcode::FCVT_L_D as u32,
(true, false) => RiscVOpcode::FCVT_W_D as u32,
(false, true) => RiscVOpcode::FCVT_L_S as u32,
(false, false) => RiscVOpcode::FCVT_W_S as u32,
};
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_fptoui(&self, inst: &Value) -> Vec<MachineInstr> {
let src_ty = &inst.operands[0].borrow().ty;
let is_double_src = src_ty.is_floating_point() && src_ty.size_in_bits() == 64;
let is_64bit_dst = self.is_64bit;
let opcode = match (is_double_src, is_64bit_dst) {
(true, true) => RiscVOpcode::FCVT_LU_D as u32,
(true, false) => RiscVOpcode::FCVT_WU_D as u32,
(false, true) => RiscVOpcode::FCVT_LU_S as u32,
(false, false) => RiscVOpcode::FCVT_WU_S as u32,
};
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_sitofp(&self, inst: &Value) -> Vec<MachineInstr> {
let dst_is_double = self.is_double_type(&inst.ty);
let src_ty = &inst.operands[0].borrow().ty;
let src_is_64bit = src_ty.is_integer() && src_ty.integer_bit_width() == 64;
let opcode = match (dst_is_double, src_is_64bit) {
(true, true) => RiscVOpcode::FCVT_D_L as u32,
(true, false) => RiscVOpcode::FCVT_D_W as u32,
(false, true) => RiscVOpcode::FCVT_S_L as u32,
(false, false) => RiscVOpcode::FCVT_S_W as u32,
};
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_uitofp(&self, inst: &Value) -> Vec<MachineInstr> {
let dst_is_double = self.is_double_type(&inst.ty);
let src_ty = &inst.operands[0].borrow().ty;
let src_is_64bit = src_ty.is_integer() && src_ty.integer_bit_width() == 64;
let opcode = match (dst_is_double, src_is_64bit) {
(true, true) => RiscVOpcode::FCVT_D_LU as u32,
(true, false) => RiscVOpcode::FCVT_D_WU as u32,
(false, true) => RiscVOpcode::FCVT_S_LU as u32,
(false, false) => RiscVOpcode::FCVT_S_WU as u32,
};
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_fptrunc(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(RiscVOpcode::FCVT_S_D as u32).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_fpext(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mi = MachineInstr::new(RiscVOpcode::FCVT_D_S as u32).with_def(def_vr);
mi.push_reg(src_vr);
vec![mi]
}
pub fn lower_atomic_rmw(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let val_vr = self.get_operand_vreg(&inst.operands[1]);
let is_64bit = self.is_64bit;
let opcode = if is_64bit {
RiscVOpcode::AMOSWAP_D as u32
} else {
RiscVOpcode::AMOSWAP_W as u32
};
let lr_op = if is_64bit {
RiscVOpcode::LR_D as u32
} else {
RiscVOpcode::LR_W as u32
};
let mut lr = MachineInstr::new(lr_op).with_def(def_vr);
lr.push_reg(ptr_vr);
lr.push_imm(0);
instrs.push(lr);
let sc_op = if is_64bit {
RiscVOpcode::SC_D as u32
} else {
RiscVOpcode::SC_W as u32
};
let tmp_vr = 0xFFFF_FFFD;
let mut sc = MachineInstr::new(sc_op).with_def(tmp_vr);
sc.push_reg(val_vr);
sc.push_reg(ptr_vr);
sc.push_imm(0);
instrs.push(sc);
let label_retry = format!(".Latomic_retry_{}", inst.vid);
let mut bne = MachineInstr::new(RiscVOpcode::BNE as u32);
bne.push_reg(tmp_vr);
bne.operands.push(MachineOperand::PhysReg(ZERO as u32));
bne.push_label(&label_retry);
instrs.push(bne);
instrs
}
pub fn lower_cmpxchg(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let expected_vr = self.get_operand_vreg(&inst.operands[1]);
let new_vr = self.get_operand_vreg(&inst.operands[2]);
let is_64bit = self.is_64bit;
let lr_op = if is_64bit {
RiscVOpcode::LR_D as u32
} else {
RiscVOpcode::LR_W as u32
};
let sc_op = if is_64bit {
RiscVOpcode::SC_D as u32
} else {
RiscVOpcode::SC_W as u32
};
let label_retry = format!(".Lcmpxchg_retry_{}", inst.vid);
let label_done = format!(".Lcmpxchg_done_{}", inst.vid);
let mut lr = MachineInstr::new(lr_op).with_def(def_vr);
lr.push_reg(ptr_vr);
lr.push_imm(0);
instrs.push(lr);
let mut bne = MachineInstr::new(RiscVOpcode::BNE as u32);
bne.push_reg(def_vr);
bne.push_reg(expected_vr);
bne.push_label(&label_done);
instrs.push(bne);
let tmp_vr = 0xFFFF_FFFC;
let mut sc = MachineInstr::new(sc_op).with_def(tmp_vr);
sc.push_reg(new_vr);
sc.push_reg(ptr_vr);
sc.push_imm(0);
instrs.push(sc);
let mut bne2 = MachineInstr::new(RiscVOpcode::BNE as u32);
bne2.push_reg(tmp_vr);
bne2.operands.push(MachineOperand::PhysReg(ZERO as u32));
bne2.push_label(&label_retry);
instrs.push(bne2);
instrs
}
pub fn lower_clz(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut instrs = Vec::new();
let xlen = if self.is_64bit { 64 } else { 32 };
let cpop_op = RiscVOpcode::SLTIU as u32; let mut li = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
li.operands.push(MachineOperand::PhysReg(ZERO as u32));
li.push_imm(xlen);
instrs.push(li);
instrs
}
pub fn lower_bext(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, RiscVOpcode::SRL as u32)
}
pub fn lower_bclr(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut instrs = Vec::new();
let tmp1 = 0xFFFF_FFFB;
let tmp2 = 0xFFFF_FFFA;
let mut li = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(tmp1);
li.operands.push(MachineOperand::PhysReg(ZERO as u32));
li.push_imm(1);
instrs.push(li);
let mut sll = MachineInstr::new(RiscVOpcode::SLL as u32).with_def(tmp2);
sll.push_reg(tmp1);
sll.push_reg(src2_vr);
instrs.push(sll);
let mut noti = MachineInstr::new(RiscVOpcode::XORI as u32);
noti.push_reg(tmp2);
noti.push_reg(tmp2);
noti.push_imm(-1);
instrs.push(noti);
let mut and_mi = MachineInstr::new(RiscVOpcode::AND as u32).with_def(def_vr);
and_mi.push_reg(src1_vr);
and_mi.push_reg(tmp2);
instrs.push(and_mi);
instrs
}
pub fn lower_bset(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut instrs = Vec::new();
let tmp1 = 0xFFFF_FFF9;
let mut li = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(tmp1);
li.operands.push(MachineOperand::PhysReg(ZERO as u32));
li.push_imm(1);
instrs.push(li);
let mut sll = MachineInstr::new(RiscVOpcode::SLL as u32).with_def(tmp1);
sll.push_reg(tmp1);
sll.push_reg(src2_vr);
instrs.push(sll);
let mut or_mi = MachineInstr::new(RiscVOpcode::OR as u32).with_def(def_vr);
or_mi.push_reg(src1_vr);
or_mi.push_reg(tmp1);
instrs.push(or_mi);
instrs
}
pub fn lower_binv(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut instrs = Vec::new();
let tmp = 0xFFFF_FFF8;
let mut li = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(tmp);
li.operands.push(MachineOperand::PhysReg(ZERO as u32));
li.push_imm(1);
instrs.push(li);
let mut sll = MachineInstr::new(RiscVOpcode::SLL as u32).with_def(tmp);
sll.push_reg(tmp);
sll.push_reg(src2_vr);
instrs.push(sll);
let mut xor_mi = MachineInstr::new(RiscVOpcode::XOR as u32).with_def(def_vr);
xor_mi.push_reg(src1_vr);
xor_mi.push_reg(tmp);
instrs.push(xor_mi);
instrs
}
pub fn lower_sext_b(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut instrs = Vec::new();
let shift_amt = if self.is_64bit { 56 } else { 24 };
let mut slli = MachineInstr::new(RiscVOpcode::SLLI as u32).with_def(def_vr);
slli.push_reg(src_vr);
slli.push_imm(shift_amt);
instrs.push(slli);
let mut srai = MachineInstr::new(RiscVOpcode::SRAI as u32).with_def(def_vr);
srai.push_reg(def_vr);
srai.push_imm(shift_amt);
instrs.push(srai);
instrs
}
pub fn lower_sext_h(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut instrs = Vec::new();
let shift_amt = if self.is_64bit { 48 } else { 16 };
let mut slli = MachineInstr::new(RiscVOpcode::SLLI as u32).with_def(def_vr);
slli.push_reg(src_vr);
slli.push_imm(shift_amt);
instrs.push(slli);
let mut srai = MachineInstr::new(RiscVOpcode::SRAI as u32).with_def(def_vr);
srai.push_reg(def_vr);
srai.push_imm(shift_amt);
instrs.push(srai);
instrs
}
pub fn lower_zext_h(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut instrs = Vec::new();
let shift_amt = if self.is_64bit { 48 } else { 16 };
let mut slli = MachineInstr::new(RiscVOpcode::SLLI as u32).with_def(def_vr);
slli.push_reg(src_vr);
slli.push_imm(shift_amt);
instrs.push(slli);
let mut srli = MachineInstr::new(RiscVOpcode::SRLI as u32).with_def(def_vr);
srli.push_reg(def_vr);
srli.push_imm(shift_amt);
instrs.push(srli);
instrs
}
pub fn lower_load_sized(&self, inst: &Value, op_byte: u32, op_half: u32, op_word: u32, op_dword: u32) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let ty = &inst.ty;
let bits = if ty.is_integer() { ty.integer_bit_width() } else { 32 };
let opcode = if bits == 8 {
op_byte
} else if bits == 16 {
op_half
} else if bits == 32 {
op_word
} else {
op_dword
};
let mut load = MachineInstr::new(opcode).with_def(def_vr);
load.push_reg(ptr_vr);
load.push_imm(0);
load
}
pub fn lower_store_sized(&self, inst: &Value, op_byte: u32, op_half: u32, op_word: u32, op_dword: u32) -> MachineInstr {
let val_vr = self.get_operand_vreg(&inst.operands[0]);
let ptr_vr = self.get_operand_vreg(&inst.operands[1]);
let val_ty = &inst.operands[0].borrow().ty;
let bits = if val_ty.is_integer() { val_ty.integer_bit_width() } else { 32 };
let opcode = if bits == 8 {
op_byte
} else if bits == 16 {
op_half
} else if bits == 32 {
op_word
} else {
op_dword
};
let mut store = MachineInstr::new(opcode);
store.push_reg(val_vr);
store.push_reg(ptr_vr);
store.push_imm(0);
store
}
pub fn lower_switch(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if inst.operands.len() < 2 {
return instrs;
}
let val_vid = inst.operands[0].borrow().vid as usize;
let _default_label = inst.operands[1].borrow().name.clone();
if let Some(&val_vr) = self.vreg_map.get(&val_vid) {
for i in (2..inst.operands.len()).step_by(2) {
if i + 1 < inst.operands.len() {
let case_val = inst.operands[i].borrow();
let case_label = inst.operands[i + 1].borrow().name.clone();
let tmp_vr = 0xFFFF_FFF0 - i as u32;
let mut li = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(tmp_vr);
li.operands.push(MachineOperand::PhysReg(ZERO as u32));
li.push_imm(case_val.vid as i64);
instrs.push(li);
let mut beq = MachineInstr::new(RiscVOpcode::BEQ as u32);
beq.push_reg(val_vr);
beq.push_reg(tmp_vr);
beq.push_label(&case_label);
instrs.push(beq);
}
}
}
instrs
}
pub fn lower_indirect_br(&self, inst: &Value) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if let Some(addr) = inst.operands.first() {
let addr_vr = self.get_operand_vreg(addr);
let mut jalr = MachineInstr::new(RiscVOpcode::JALR as u32);
jalr.operands.push(MachineOperand::PhysReg(ZERO as u32));
jalr.push_reg(addr_vr);
jalr.push_imm(0);
instrs.push(jalr);
}
instrs
}
pub fn lower_fence(&self, _inst: &Value) -> MachineInstr {
let mut fence = MachineInstr::new(RiscVOpcode::FENCE as u32);
fence.operands.push(MachineOperand::Imm(0xF)); fence.operands.push(MachineOperand::Imm(0xF)); fence
}
pub fn lower_ptrtoint(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_inttoptr(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_addrspacecast(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_extractvalue(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_insertvalue(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
vec![self.emit_mov_reg_reg_def(def_vr, src_vr)]
}
pub fn lower_extractelement(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn lower_insertelement(&self, inst: &Value) -> Vec<MachineInstr> {
let def_vr = self.get_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]);
vec![self.emit_mov_reg_reg_def(def_vr, vec_vr)]
}
pub fn lower_shufflevector(&self, inst: &Value) -> Vec<MachineInstr> {
if inst.operands.len() < 2 {
return Vec::new();
}
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
vec![self.emit_mov_reg_reg_def(def_vr, src1_vr)]
}
pub fn lower_vaarg(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let ptr_vr = self.get_operand_vreg(&inst.operands[0]);
let opcode = if self.is_64bit {
RiscVOpcode::LD as u32
} else {
RiscVOpcode::LW as u32
};
let mut load = MachineInstr::new(opcode).with_def(def_vr);
load.push_reg(ptr_vr);
load.push_imm(0);
load
}
pub fn lower_freeze(&self, inst: &Value) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src_vr = self.get_operand_vreg(&inst.operands[0]);
let mut mv = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mv.push_reg(src_vr);
mv.push_imm(0);
mv
}
pub fn emit_lui(&self, def_vr: u32, imm: i64) -> MachineInstr {
let mut lui = MachineInstr::new(RiscVOpcode::LUI as u32).with_def(def_vr);
lui.push_imm((imm >> 12) & 0xFFFFF);
lui
}
pub fn emit_auipc(&self, def_vr: u32, imm: i64) -> MachineInstr {
let mut auipc = MachineInstr::new(RiscVOpcode::AUIPC as u32).with_def(def_vr);
auipc.push_imm((imm >> 12) & 0xFFFFF);
auipc
}
pub fn emit_li(&self, def_vr: u32, imm: i64) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
if Self::fits_in_12bit(imm) {
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
addi.operands.push(MachineOperand::PhysReg(ZERO as u32));
addi.push_imm(imm);
instrs.push(addi);
} else {
let upper = (imm + 0x800) as u64 & 0xFFFFF000;
let lower = imm - upper as i64;
let mut lui = MachineInstr::new(RiscVOpcode::LUI as u32).with_def(def_vr);
lui.push_imm(upper as i64 >> 12);
instrs.push(lui);
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
addi.push_reg(def_vr);
addi.push_imm(lower);
instrs.push(addi);
}
instrs
}
pub fn emit_la(&self, def_vr: u32, symbol: &str) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let mut auipc = MachineInstr::new(RiscVOpcode::AUIPC as u32).with_def(def_vr);
auipc.operands.push(MachineOperand::Global(format!("%pcrel_hi({})", symbol)));
instrs.push(auipc);
let mut addi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
addi.push_reg(def_vr);
addi.operands.push(MachineOperand::Global(format!("%pcrel_lo({})", symbol)));
instrs.push(addi);
instrs
}
pub fn emit_call_pseudo(&self, symbol: &str) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let tmp_vr = RA as u32;
let mut auipc = MachineInstr::new(RiscVOpcode::AUIPC as u32);
auipc.operands.push(MachineOperand::PhysReg(tmp_vr));
auipc.operands.push(MachineOperand::Global(format!("%pcrel_hi({})", symbol)));
instrs.push(auipc);
let mut jalr = MachineInstr::new(RiscVOpcode::JALR as u32);
jalr.operands.push(MachineOperand::PhysReg(tmp_vr));
jalr.push_reg(tmp_vr);
jalr.operands.push(MachineOperand::Global(format!("%pcrel_lo({})", symbol)));
instrs.push(jalr);
instrs
}
pub fn emit_tail_pseudo(&self, symbol: &str) -> Vec<MachineInstr> {
let mut instrs = Vec::new();
let tmp_vr = T1 as u32;
let mut auipc = MachineInstr::new(RiscVOpcode::AUIPC as u32);
auipc.operands.push(MachineOperand::PhysReg(tmp_vr));
auipc.operands.push(MachineOperand::Global(format!("%pcrel_hi({})", symbol)));
instrs.push(auipc);
let mut jalr = MachineInstr::new(RiscVOpcode::JALR as u32);
jalr.operands.push(MachineOperand::PhysReg(ZERO as u32));
jalr.push_reg(tmp_vr);
jalr.operands.push(MachineOperand::Global(format!("%pcrel_lo({})", symbol)));
instrs.push(jalr);
instrs
}
pub fn emit_nop(&self) -> MachineInstr {
let mut nop = MachineInstr::new(RiscVOpcode::ADDI as u32);
nop.operands.push(MachineOperand::PhysReg(ZERO as u32));
nop.operands.push(MachineOperand::PhysReg(ZERO as u32));
nop.push_imm(0);
nop
}
pub fn emit_mv(&self, dest: u32, src: u32) -> MachineInstr {
self.emit_mov_reg_reg(dest, src)
}
pub fn emit_not(&self, dest: u32, src: u32) -> MachineInstr {
let mut xori = MachineInstr::new(RiscVOpcode::XORI as u32);
xori.operands.push(MachineOperand::PhysReg(dest));
xori.push_reg(src);
xori.push_imm(-1);
xori
}
pub fn emit_neg(&self, dest: u32, src: u32) -> MachineInstr {
let mut sub = MachineInstr::new(RiscVOpcode::SUB as u32);
sub.operands.push(MachineOperand::PhysReg(dest));
sub.operands.push(MachineOperand::PhysReg(ZERO as u32));
sub.push_reg(src);
sub
}
pub fn emit_seqz(&self, dest: u32, src: u32) -> MachineInstr {
let mut sltiu = MachineInstr::new(RiscVOpcode::SLTIU as u32);
sltiu.operands.push(MachineOperand::PhysReg(dest));
sltiu.push_reg(src);
sltiu.push_imm(1);
sltiu
}
pub fn emit_snez(&self, dest: u32, src: u32) -> MachineInstr {
let mut sltu = MachineInstr::new(RiscVOpcode::SLTU as u32);
sltu.operands.push(MachineOperand::PhysReg(dest));
sltu.operands.push(MachineOperand::PhysReg(ZERO as u32));
sltu.push_reg(src);
sltu
}
pub fn emit_sltz(&self, dest: u32, src: u32) -> MachineInstr {
let mut slt = MachineInstr::new(RiscVOpcode::SLT as u32);
slt.operands.push(MachineOperand::PhysReg(dest));
slt.push_reg(src);
slt.operands.push(MachineOperand::PhysReg(ZERO as u32));
slt
}
pub fn emit_sgtz(&self, dest: u32, src: u32) -> MachineInstr {
let mut slt = MachineInstr::new(RiscVOpcode::SLT as u32);
slt.operands.push(MachineOperand::PhysReg(dest));
slt.operands.push(MachineOperand::PhysReg(ZERO as u32));
slt.push_reg(src);
slt
}
fn lower_three_reg_op(&self, inst: &Value, opcode: u32) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2_vr = self.get_operand_vreg(&inst.operands[1]);
let mut mi = MachineInstr::new(opcode).with_def(def_vr);
mi.push_reg(src1_vr);
mi.push_reg(src2_vr);
mi
}
fn lower_binary_op_with_imm(&self, inst: &Value, rr_opcode: u32, ri_opcode: u32) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2 = &inst.operands[1];
let src2_val = src2.borrow();
if let Some(imm_val) = Self::try_extract_constant(&src2_val) {
if Self::fits_in_12bit(imm_val) {
let mut mi = MachineInstr::new(ri_opcode).with_def(def_vr);
mi.push_reg(src1_vr);
mi.push_imm(imm_val);
return mi;
}
}
let src2_vr = self.get_operand_vreg(src2);
let mut mi = MachineInstr::new(rr_opcode).with_def(def_vr);
mi.push_reg(src1_vr);
mi.push_reg(src2_vr);
mi
}
fn lower_binary_op_with_shift_imm(&self, inst: &Value, rr_opcode: u32, ri_opcode: u32) -> MachineInstr {
let def_vr = self.get_vreg_by_vid(inst.vid as usize);
let src1_vr = self.get_operand_vreg(&inst.operands[0]);
let src2 = &inst.operands[1];
let src2_val = src2.borrow();
if let Some(shift_amt) = Self::try_extract_constant(&src2_val) {
let max_shift = if self.is_64bit { 63 } else { 31 };
if shift_amt >= 0 && shift_amt <= max_shift as i64 {
let mut mi = MachineInstr::new(ri_opcode).with_def(def_vr);
mi.push_reg(src1_vr);
mi.push_imm(shift_amt);
return mi;
}
}
let src2_vr = self.get_operand_vreg(src2);
let mut mi = MachineInstr::new(rr_opcode).with_def(def_vr);
mi.push_reg(src1_vr);
mi.push_reg(src2_vr);
mi
}
fn try_extract_constant(val: &Value) -> Option<i64> {
if val.is_constant() || val.subclass == crate::value::SubclassKind::ConstantInt {
return Some(val.vid as i64);
}
if val.name.starts_with("const_") {
if let Some(num_str) = val.name.strip_prefix("const_") {
if let Ok(v) = num_str.parse::<i64>() {
return Some(v);
}
}
}
None
}
fn get_vreg_by_vid(&self, vid: usize) -> VirtReg {
*self.vreg_map.get(&vid).unwrap_or(&(vid as u32))
}
fn get_operand_vreg(&self, val: &std::rc::Rc<std::cell::RefCell<Value>>) -> VirtReg {
let vid = val.borrow().vid as usize;
self.get_vreg_by_vid(vid)
}
pub fn emit_mov_reg_reg(&self, dest: u32, src: u32) -> MachineInstr {
let mut mi = MachineInstr::new(RiscVOpcode::ADDI as u32);
mi.operands.push(MachineOperand::PhysReg(dest));
mi.push_reg(src);
mi.push_imm(0);
mi
}
pub fn emit_mov_reg_reg_def(&self, def_vr: u32, src: u32) -> MachineInstr {
let mut mi = MachineInstr::new(RiscVOpcode::ADDI as u32).with_def(def_vr);
mi.push_reg(src);
mi.push_imm(0);
mi
}
pub fn emit_imm_arith(&self, opcode: u32, dest: u32, src: u32, imm: i64) -> MachineInstr {
let mut mi = MachineInstr::new(opcode);
mi.operands.push(MachineOperand::PhysReg(dest));
mi.push_reg(src);
mi.push_imm(imm);
mi
}
pub fn emit_rr_arith(&self, opcode: u32, dest: u32, src1: u32, src2: u32) -> MachineInstr {
let mut mi = MachineInstr::new(opcode);
mi.operands.push(MachineOperand::PhysReg(dest));
mi.push_reg(src1);
mi.push_reg(src2);
mi
}
pub fn emit_load_store(&self, opcode: u32, reg: u32, base: u32, offset: i64) -> MachineInstr {
let mut mi = MachineInstr::new(opcode);
mi.operands.push(MachineOperand::PhysReg(reg));
mi.push_reg(base);
mi.push_imm(offset);
mi
}
pub fn fits_in_12bit(value: i64) -> bool {
value >= -2048 && value <= 2047
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Type;
use crate::value::{valref, SubclassKind};
use std::rc::Rc;
fn build_add_ir() -> Value {
let i32_ty = Type::i32();
let mut func = Value::new(Type::void());
func.subclass = SubclassKind::Function;
func.name = "test_add".to_string();
let mut bb = Value::new(Type::label());
bb.subclass = SubclassKind::BasicBlock;
bb.name = "entry".to_string();
let a = valref(Value::new(i32_ty.clone()).named("a"));
let b = valref(Value::new(i32_ty.clone()).named("b"));
let mut add_inst = Value::new(i32_ty);
add_inst.subclass = SubclassKind::Instruction;
add_inst.set_opcode(Opcode::Add);
add_inst.name = "res".to_string();
add_inst.operands.push(Rc::clone(&a));
add_inst.operands.push(Rc::clone(&b));
add_inst.num_operands = 2;
let add_ref = valref(add_inst);
bb.operands.push(Rc::clone(&add_ref));
let mut ret = Value::new(Type::void());
ret.subclass = SubclassKind::Instruction;
ret.set_opcode(Opcode::Ret);
ret.num_operands = 0;
let ret_ref = valref(ret);
bb.operands.push(Rc::clone(&ret_ref));
let bb_ref = valref(bb);
func.successors.push(Rc::clone(&bb_ref));
func
}
fn build_branch_ir() -> Value {
let i1_ty = Type::i1();
let mut func = Value::new(Type::void());
func.subclass = SubclassKind::Function;
func.name = "test_br".to_string();
let mut entry = Value::new(Type::label());
entry.subclass = SubclassKind::BasicBlock;
entry.name = "entry".to_string();
let cond = valref(Value::new(i1_ty).named("cond"));
let true_bb = Value::new(Type::label());
let false_bb = Value::new(Type::label());
let true_bb_ref = valref(true_bb.named("if.then"));
let false_bb_ref = valref(false_bb.named("if.else"));
let mut br = Value::new(Type::void());
br.subclass = SubclassKind::Instruction;
br.set_opcode(Opcode::Br);
br.operands.push(Rc::clone(&cond));
br.operands.push(Rc::clone(&true_bb_ref));
br.operands.push(Rc::clone(&false_bb_ref));
br.num_operands = 3;
let br_ref = valref(br);
entry.operands.push(Rc::clone(&br_ref));
let entry_ref = valref(entry);
func.successors.push(Rc::clone(&entry_ref));
func.successors.push(Rc::clone(&true_bb_ref));
func.successors.push(Rc::clone(&false_bb_ref));
func
}
#[test]
fn test_new_rv64() {
let isel = RiscVInstructionSelector::new(true);
assert!(isel.is_64bit);
assert!(isel.vreg_map.is_empty());
}
#[test]
fn test_new_rv32() {
let isel = RiscVInstructionSelector::new(false);
assert!(!isel.is_64bit);
}
#[test]
fn test_lower_add() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Add);
inst.name = "res".to_string();
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_add(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADD as u32);
assert_eq!(mi.operands.len(), 2);
}
#[test]
fn test_lower_sub() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Sub);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_sub(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SUB as u32);
}
#[test]
fn test_lower_mul() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Mul);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_mul(&inst);
assert_eq!(mi.opcode, RiscVOpcode::MUL as u32);
}
#[test]
fn test_lower_sdiv() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::SDiv);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let instrs = isel.lower_sdiv(&inst);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, RiscVOpcode::DIV as u32);
}
#[test]
fn test_lower_udiv() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::UDiv);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let instrs = isel.lower_udiv(&inst);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, RiscVOpcode::DIVU as u32);
}
#[test]
fn test_lower_and() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::And);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_and(&inst);
assert_eq!(mi.opcode, RiscVOpcode::AND as u32);
}
#[test]
fn test_lower_or() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Or);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_or(&inst);
assert_eq!(mi.opcode, RiscVOpcode::OR as u32);
}
#[test]
fn test_lower_xor() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Xor);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_xor(&inst);
assert_eq!(mi.opcode, RiscVOpcode::XOR as u32);
}
#[test]
fn test_lower_shl() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Shl);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_shl(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SLL as u32);
}
#[test]
fn test_lower_lshr() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::LShr);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_lshr(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SRL as u32);
}
#[test]
fn test_lower_ashr() {
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::AShr);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_ashr(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SRA as u32);
}
#[test]
fn test_lower_icmp_has_sub_and_sltiu() {
let i32_ty = Type::i32();
let mut inst = Value::new(Type::i1());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::ICmp);
let a = valref(Value::new(i32_ty.clone()).named("a"));
let b = valref(Value::new(i32_ty).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let instrs = isel.lower_icmp(&inst);
assert_eq!(instrs.len(), 2);
assert_eq!(instrs[0].opcode, RiscVOpcode::SUB as u32);
assert_eq!(instrs[1].opcode, RiscVOpcode::SLTIU as u32);
}
#[test]
fn test_lower_ret_rv64() {
let mut inst = Value::new(Type::void());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Ret);
inst.num_operands = 0;
let isel = RiscVInstructionSelector::new(true);
let instrs = isel.lower_ret(&inst);
assert_eq!(instrs.len(), 1);
assert_eq!(instrs[0].opcode, RiscVOpcode::RET as u32);
}
#[test]
fn test_lower_ret_with_value() {
let mut inst = Value::new(Type::void());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Ret);
let val = valref(Value::new(Type::i32()).named("retval"));
inst.operands.push(Rc::clone(&val));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let instrs = isel.lower_ret(&inst);
assert!(instrs.len() >= 1);
assert_eq!(instrs.last().unwrap().opcode, RiscVOpcode::RET as u32);
}
#[test]
fn test_lower_alloca() {
let mut inst = Value::new(Type::pointer(0));
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Alloca);
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_alloca(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
assert!(matches!(&mi.operands[0], MachineOperand::PhysReg(r) if *r == SP as u32));
}
#[test]
fn test_lower_load_rv64() {
let mut inst = Value::new(Type::i32());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Load);
let ptr = valref(Value::new(Type::pointer(0)).named("ptr"));
inst.operands.push(Rc::clone(&ptr));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_load(&inst);
assert_eq!(mi.opcode, RiscVOpcode::LD as u32);
}
#[test]
fn test_lower_load_rv32() {
let mut inst = Value::new(Type::i32());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Load);
let ptr = valref(Value::new(Type::pointer(0)).named("ptr"));
inst.operands.push(Rc::clone(&ptr));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(false);
let mi = isel.lower_load(&inst);
assert_eq!(mi.opcode, RiscVOpcode::LW as u32);
}
#[test]
fn test_lower_store_rv64() {
let mut inst = Value::new(Type::void());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Store);
let val = valref(Value::new(Type::i32()).named("val"));
let ptr = valref(Value::new(Type::pointer(0)).named("ptr"));
inst.operands.push(Rc::clone(&val));
inst.operands.push(Rc::clone(&ptr));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_store(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SD as u32);
}
#[test]
fn test_lower_store_rv32() {
let mut inst = Value::new(Type::void());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Store);
let val = valref(Value::new(Type::i32()).named("val"));
let ptr = valref(Value::new(Type::pointer(0)).named("ptr"));
inst.operands.push(Rc::clone(&val));
inst.operands.push(Rc::clone(&ptr));
inst.num_operands = 2;
let isel = RiscVInstructionSelector::new(false);
let mi = isel.lower_store(&inst);
assert_eq!(mi.opcode, RiscVOpcode::SW as u32);
}
#[test]
fn test_lower_zext() {
let mut inst = Value::new(Type::i64());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::ZExt);
let val = valref(Value::new(Type::i32()).named("val"));
inst.operands.push(Rc::clone(&val));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_zext(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
}
#[test]
fn test_lower_sext() {
let mut inst = Value::new(Type::i64());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::SExt);
let val = valref(Value::new(Type::i32()).named("val"));
inst.operands.push(Rc::clone(&val));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_sext(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDIW as u32);
}
#[test]
fn test_lower_trunc() {
let mut inst = Value::new(Type::i32());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Trunc);
let val = valref(Value::new(Type::i64()).named("val"));
inst.operands.push(Rc::clone(&val));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_trunc(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
}
#[test]
fn test_lower_bitcast() {
let mut inst = Value::new(Type::i64());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::BitCast);
let val = valref(Value::new(Type::i64()).named("val"));
inst.operands.push(Rc::clone(&val));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_bitcast(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
}
#[test]
fn test_lower_gep() {
let mut inst = Value::new(Type::pointer(0));
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::GetElementPtr);
let base = valref(Value::new(Type::pointer(0)).named("base"));
inst.operands.push(Rc::clone(&base));
inst.num_operands = 1;
let isel = RiscVInstructionSelector::new(true);
let mi = isel.lower_gep(&inst);
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
}
#[test]
fn test_fits_in_12bit() {
assert!(RiscVInstructionSelector::fits_in_12bit(0));
assert!(RiscVInstructionSelector::fits_in_12bit(2047));
assert!(RiscVInstructionSelector::fits_in_12bit(-2048));
assert!(!RiscVInstructionSelector::fits_in_12bit(2048));
assert!(!RiscVInstructionSelector::fits_in_12bit(-2049));
assert!(!RiscVInstructionSelector::fits_in_12bit(4096));
}
#[test]
fn test_select_instruction_dispatch() {
let mut isel = RiscVInstructionSelector::new(true);
let i32_ty = Type::i32();
let mut inst = Value::new(i32_ty);
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Add);
let a = valref(Value::new(Type::i32()).named("a"));
let b = valref(Value::new(Type::i32()).named("b"));
inst.operands.push(Rc::clone(&a));
inst.operands.push(Rc::clone(&b));
inst.num_operands = 2;
let result = isel.select_instruction(&inst);
assert_eq!(result.len(), 1);
assert_eq!(result[0].opcode, RiscVOpcode::ADD as u32);
}
#[test]
fn test_select_instruction_unknown_returns_empty() {
let mut isel = RiscVInstructionSelector::new(true);
let mut inst = Value::new(Type::void());
inst.subclass = SubclassKind::Instruction;
inst.set_opcode(Opcode::Unreachable);
let result = isel.select_instruction(&inst);
assert!(result.is_empty());
}
}