llvm-native-core 0.1.16

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! MSP430 Instruction Selection — converts LLVM IR to MSP430
//! machine instructions.
//!
//! Clean-room reconstruction from the MSP430x1xx Family User's Guide.
//! Zero LLVM source consultation.
//!
//! # Lowering patterns
//!
//! | IR op       | MSP430 instruction(s)            |
//! |-------------|----------------------------------|
//! | `add`       | `ADD src, dst`                   |
//! | `sub`       | `SUB src, dst`                   |
//! | `mul`       | Software routine (CALL #__mspabi_mpyi) |
//! | `and`       | `AND src, dst`                   |
//! | `or`        | `BIS src, dst`                   |
//! | `xor`       | `XOR src, dst`                   |
//! | `shl`       | `RLA dst` (repeated)             |
//! | `icmp eq`   | `CMP src, dst; JEQ label`        |
//! | `br`        | `JMP label` / `BR label`         |
//! | `call`      | `CALL func`                      |
//! | `ret`       | `RET`                            |
//! | `load`      | `MOV @addr, dst`                 |
//! | `store`     | `MOV src, addr`                  |

use super::msp430_instr_info::Msp430Opcode;
use super::msp430_register_info::*;
use crate::codegen::*;
use crate::opcode::Opcode;
use crate::value::Value;
use std::collections::HashMap;

pub struct Msp430InstructionSelector {
    pub vreg_map: HashMap<usize, VirtReg>,
    pub mbb: MachineBasicBlock,
    pub func_name: String,
}

impl Msp430InstructionSelector {
    pub fn new() -> Self {
        Msp430InstructionSelector {
            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_two_op(inst, Msp430Opcode::ADD as u32)],
            Opcode::Sub => vec![self.lower_two_op(inst, Msp430Opcode::SUB as u32)],
            Opcode::Mul => vec![self.lower_call_runtime(inst, "__mspabi_mpyi")],
            Opcode::And => vec![self.lower_two_op(inst, Msp430Opcode::AND as u32)],
            Opcode::Or => vec![self.lower_two_op(inst, Msp430Opcode::BIS as u32)],
            Opcode::Xor => vec![self.lower_two_op(inst, Msp430Opcode::XOR as u32)],
            Opcode::Shl => vec![self.lower_shl(inst)],
            Opcode::LShr => vec![self.lower_shift_r(inst)],
            Opcode::AShr => vec![self.lower_shift_ra(inst)],
            Opcode::ICmp => self.lower_icmp(inst),
            Opcode::Br => vec![self.lower_br(inst)],
            Opcode::Ret => vec![self.lower_ret(inst)],
            Opcode::Call => vec![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_mov(inst)],
            Opcode::SExt => vec![self.lower_mov(inst)],
            Opcode::Trunc => vec![self.lower_mov(inst)],
            Opcode::GetElementPtr => vec![self.lower_gep(inst)],
            _ => Vec::new(),
        }
    }

    fn get_or_create_vreg(&mut self, val: &Value) -> VirtReg {
        let vid_key = val.vid as usize;
        if let Some(vreg) = self.vreg_map.get(&vid_key) {
            return *vreg;
        }
        let vreg = self.vreg_map.len() as u32;
        self.vreg_map.insert(vid_key, vreg);
        vreg
    }

    fn get_vreg_for_operand(&mut self, inst: &Value, index: usize) -> VirtReg {
        if let Some(op) = inst.operands.get(index) {
            self.get_or_create_vreg(&op.borrow())
        } else {
            0
        }
    }

    fn lower_two_op(&mut self, inst: &Value, opcode: u32) -> MachineInstr {
        let src = self.get_vreg_for_operand(inst, 0);
        let dst = self.get_or_create_vreg(inst);

        let mut mi = MachineInstr::new(opcode);
        mi.push_reg(dst); // MSP430: first operand can be source, second is dest
        mi.push_reg(src);
        mi.def = Some(dst);
        mi
    }

    fn lower_mov(&mut self, inst: &Value) -> MachineInstr {
        let src = self.get_vreg_for_operand(inst, 0);
        let dst = self.get_or_create_vreg(inst);

        let mut mi = MachineInstr::new(Msp430Opcode::MOV as u32);
        mi.push_reg(dst);
        mi.push_reg(src);
        mi.def = Some(dst);
        mi
    }

    fn lower_shl(&mut self, inst: &Value) -> MachineInstr {
        let dst = self.get_or_create_vreg(inst);
        let mut mi = MachineInstr::new(Msp430Opcode::RLA as u32);
        mi.push_reg(dst);
        mi.def = Some(dst);
        mi
    }

    fn lower_shift_r(&mut self, _inst: &Value) -> MachineInstr {
        let mut mi = MachineInstr::new(Msp430Opcode::CLR as u32);
        mi
    }

    fn lower_shift_ra(&mut self, _inst: &Value) -> MachineInstr {
        let mut mi = MachineInstr::new(Msp430Opcode::RRA as u32);
        mi
    }

    fn lower_icmp(&mut self, inst: &Value) -> Vec<MachineInstr> {
        let src = self.get_vreg_for_operand(inst, 0);
        let dst_val = self.get_vreg_for_operand(inst, 1);
        let rd = self.get_or_create_vreg(inst);

        let mut cmp = MachineInstr::new(Msp430Opcode::CMP as u32);
        cmp.push_reg(dst_val);
        cmp.push_reg(src);

        let mut clr = MachineInstr::new(Msp430Opcode::CLR as u32);
        clr.push_reg(rd);
        clr.def = Some(rd);

        vec![cmp, clr]
    }

    fn lower_br(&mut self, inst: &Value) -> MachineInstr {
        let mut mi = MachineInstr::new(Msp430Opcode::JMP as u32);
        if let Some(dest) = inst.operands.get(0) {
            let bb = dest.borrow();
            mi.push_label(&bb.name);
        }
        mi
    }

    fn lower_ret(&mut self, _inst: &Value) -> MachineInstr {
        MachineInstr::new(Msp430Opcode::RET as u32)
    }

    fn lower_call(&mut self, inst: &Value) -> MachineInstr {
        let mut mi = MachineInstr::new(Msp430Opcode::CALL as u32);
        if let Some(callee) = inst.operands.get(0) {
            let func = callee.borrow();
            mi.push_label(&func.name);
        }
        mi
    }

    fn lower_call_runtime(&mut self, _inst: &Value, name: &str) -> MachineInstr {
        let mut mi = MachineInstr::new(Msp430Opcode::CALL as u32);
        mi.push_label(name);
        mi
    }

    fn lower_alloca(&mut self, inst: &Value) -> MachineInstr {
        let rd = self.get_or_create_vreg(inst);
        let mut mi = MachineInstr::new(Msp430Opcode::MOV as u32);
        mi.push_reg(rd);
        mi.push_reg(SP as u32);
        mi.def = Some(rd);
        mi
    }

    fn lower_load(&mut self, inst: &Value) -> MachineInstr {
        let rd = self.get_or_create_vreg(inst);
        let base = self.get_vreg_for_operand(inst, 0);

        let mut mi = MachineInstr::new(Msp430Opcode::MOV as u32);
        mi.push_reg(rd);
        mi.push_reg(base);
        mi.def = Some(rd);
        mi
    }

    fn lower_store(&mut self, inst: &Value) -> MachineInstr {
        let val = self.get_vreg_for_operand(inst, 0);
        let addr = self.get_vreg_for_operand(inst, 1);

        let mut mi = MachineInstr::new(Msp430Opcode::MOV as u32);
        mi.push_reg(val);
        mi.push_reg(addr);
        mi
    }

    fn lower_gep(&mut self, inst: &Value) -> MachineInstr {
        let base = self.get_vreg_for_operand(inst, 0);
        let offset = self.get_vreg_for_operand(inst, 1);
        let rd = self.get_or_create_vreg(inst);

        let mut mi = MachineInstr::new(Msp430Opcode::ADD as u32);
        mi.push_reg(rd);
        mi.push_reg(base);
        mi.push_reg(offset);
        mi.def = Some(rd);
        mi
    }
}