use super::ppc_instr_info::PpcOpcode;
use super::ppc_register_info::*;
use crate::codegen::*;
use crate::opcode::Opcode;
use crate::value::Value;
use std::collections::HashMap;
pub struct PpcInstructionSelector {
pub is_64bit: bool,
pub vreg_map: HashMap<usize, VirtReg>,
pub mbb: MachineBasicBlock,
pub func_name: String,
}
impl PpcInstructionSelector {
pub fn new(is_64bit: bool) -> Self {
PpcInstructionSelector {
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 => vec![self.lower_sdiv(inst)],
Opcode::UDiv => vec![self.lower_udiv(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::ICmp => self.lower_icmp(inst),
Opcode::Br => self.lower_br(inst),
Opcode::Ret => self.lower_ret(inst),
Opcode::Call => self.lower_call(inst),
Opcode::Alloca => vec![self.lower_alloca(inst)],
Opcode::Load => vec![self.lower_load(inst)],
Opcode::Store => vec![self.lower_store(inst)],
Opcode::ZExt => vec![self.lower_zext(inst)],
Opcode::GetElementPtr => vec![self.lower_gep(inst)],
_ => Vec::new(),
}
}
pub fn lower_add(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::ADD as u32)
}
pub fn lower_sub(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::SUBF as u32)
}
pub fn lower_mul(&self, inst: &Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, PpcOpcode::MULLD as u32)
} else {
self.lower_three_reg_op(inst, PpcOpcode::MULLW as u32)
}
}
pub fn lower_sdiv(&self, inst: &Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, PpcOpcode::DIVD as u32)
} else {
self.lower_three_reg_op(inst, PpcOpcode::DIVW as u32)
}
}
pub fn lower_udiv(&self, inst: &Value) -> MachineInstr {
if self.is_64bit {
self.lower_three_reg_op(inst, PpcOpcode::DIVD as u32)
} else {
self.lower_three_reg_op(inst, PpcOpcode::DIVW as u32)
}
}
pub fn lower_and(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::AND as u32)
}
pub fn lower_or(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::OR as u32)
}
pub fn lower_xor(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::XOR as u32)
}
pub fn lower_shl(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::SLW as u32)
}
pub fn lower_lshr(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::SRW as u32)
}
pub fn lower_ashr(&self, inst: &Value) -> MachineInstr {
self.lower_three_reg_op(inst, PpcOpcode::SRAW as u32)
}
pub fn lower_icmp(&self, inst: &Value) -> Vec<MachineInstr> {
let rs: u32 = self.get_vreg_for_operand(inst, 0);
let rt: u32 = self.get_vreg_for_operand(inst, 1);
let rd: u32 = self.get_or_create_vreg(inst);
let mut cmp_instr = MachineInstr::new(PpcOpcode::CMPW as u32);
cmp_instr.push_reg(CR0 as u32);
cmp_instr.push_reg(rs);
cmp_instr.push_reg(rt);
vec![cmp_instr]
}
pub fn lower_br(&self, inst: &Value) -> Vec<MachineInstr> {
if inst.successors.len() >= 2 {
let cond = self.get_vreg_for_operand(inst, 0);
let mut bc_instr = MachineInstr::new(PpcOpcode::BC as u32);
bc_instr.push_reg(CR0 as u32);
bc_instr.push_imm(0);
vec![bc_instr]
} else if inst.successors.len() == 1 {
let mut b_instr = MachineInstr::new(PpcOpcode::B as u32);
b_instr.push_imm(0);
vec![b_instr]
} else {
Vec::new()
}
}
pub fn lower_ret(&self, _inst: &Value) -> Vec<MachineInstr> {
let mut blr = MachineInstr::new(PpcOpcode::BCLR as u32);
blr.push_reg(CR0 as u32);
vec![blr]
}
pub fn lower_call(&self, inst: &Value) -> Vec<MachineInstr> {
let callee_name = if !inst.operands.is_empty() {
let callee_ref = &inst.operands[0];
let callee = callee_ref.borrow();
callee.name.clone()
} else {
String::new()
};
let mut bl_instr = MachineInstr::new(PpcOpcode::BL as u32);
if !callee_name.is_empty() {
bl_instr.push_label(&callee_name);
}
bl_instr.push_imm(0);
vec![bl_instr]
}
pub fn lower_load(&self, inst: &Value) -> MachineInstr {
let rt: u32 = self.get_or_create_vreg(inst);
let ra: u32 = self.get_vreg_for_operand(inst, 0);
if self.is_64bit {
let mut ld = MachineInstr::new(PpcOpcode::LD as u32);
ld.push_reg(rt);
ld.push_reg(ra);
ld.push_imm(0);
ld.def = Some(rt);
ld
} else {
let mut lwz = MachineInstr::new(PpcOpcode::LWZ as u32);
lwz.push_reg(rt);
lwz.push_reg(ra);
lwz.push_imm(0);
lwz.def = Some(rt);
lwz
}
}
pub fn lower_store(&self, inst: &Value) -> MachineInstr {
let rs: u32 = self.get_vreg_for_operand(inst, 0);
let ra: u32 = self.get_vreg_for_operand(inst, 1);
if self.is_64bit {
let mut std = MachineInstr::new(PpcOpcode::STD as u32);
std.push_reg(rs);
std.push_reg(ra);
std.push_imm(0);
std
} else {
let mut stw = MachineInstr::new(PpcOpcode::STW as u32);
stw.push_reg(rs);
stw.push_reg(ra);
stw.push_imm(0);
stw
}
}
pub fn lower_alloca(&self, inst: &Value) -> MachineInstr {
let rd: u32 = self.get_or_create_vreg(inst);
let mut addi = MachineInstr::new(PpcOpcode::ADDI as u32);
addi.push_reg(rd);
addi.push_reg(SP as u32);
addi.push_imm(0);
addi.def = Some(rd);
addi
}
pub fn lower_zext(&self, inst: &Value) -> MachineInstr {
let rs: u32 = self.get_vreg_for_operand(inst, 0);
let rd: u32 = self.get_or_create_vreg(inst);
let mut mr = MachineInstr::new(PpcOpcode::MR as u32);
mr.push_reg(rd);
mr.push_reg(rs);
mr.def = Some(rd);
mr
}
pub fn lower_gep(&self, inst: &Value) -> MachineInstr {
let base: u32 = self.get_vreg_for_operand(inst, 0);
let rd: u32 = self.get_or_create_vreg(inst);
let mut add = MachineInstr::new(PpcOpcode::ADD as u32);
add.push_reg(rd);
add.push_reg(base);
add.push_reg(R0 as u32);
add.def = Some(rd);
add
}
fn lower_three_reg_op(&self, inst: &Value, opcode: u32) -> MachineInstr {
let rd: u32 = self.get_or_create_vreg(inst);
let ra: u32 = self.get_vreg_for_operand(inst, 0);
let mut mi = MachineInstr::new(opcode);
mi.push_reg(rd);
mi.push_reg(ra);
if inst.operands.len() >= 2 {
let rb: u32 = self.get_vreg_for_operand(inst, 1);
mi.push_reg(rb);
} else {
mi.push_reg(R0 as u32);
}
mi.def = Some(rd);
mi
}
fn get_or_create_vreg(&self, inst: &Value) -> VirtReg {
*self.vreg_map.get(&(inst.vid as usize)).unwrap_or(&0)
}
fn get_vreg_for_operand(&self, inst: &Value, idx: usize) -> VirtReg {
if idx >= inst.operands.len() {
return 0;
}
let op_ref = &inst.operands[idx];
let op = op_ref.borrow();
*self.vreg_map.get(&(op.vid as usize)).unwrap_or(&0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_selector() {
let sel = PpcInstructionSelector::new(false);
assert!(!sel.is_64bit);
assert!(sel.vreg_map.is_empty());
}
}