llvm-native-core 0.1.10

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! PowerPC Instruction Selection — converts LLVM IR to PowerPC
//! machine instructions.
//!
//! Clean-room behavioral reconstruction from the Power ISA.
//! Zero LLVM source consultation.
//!
//! # Lowering patterns (canonical PowerPC mappings)
//!
//! | IR op       | PowerPC instruction(s)                   |
//! |-------------|------------------------------------------|
//! | `add`       | `ADD rd, ra, rb` / `ADDI rd, ra, imm`   |
//! | `sub`       | `SUBF rd, ra, rb`                        |
//! | `mul`       | `MULLW rd, ra, rb`                       |
//! | `and`       | `AND rd, ra, rb`                         |
//! | `or`        | `OR rd, ra, rb`                          |
//! | `xor`       | `XOR rd, ra, rb`                         |
//! | `load`      | `LWZ rd, offset(ra)` / `LD rd, offset(ra)`|
//! | `store`     | `STW rs, offset(ra)` / `STD rs, offset(ra)`|
//! | `call`      | `BL target`                              |
//! | `ret`       | `BLR` (`BCLR 20, 0`)                    |

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());
    }
}