llvm-native-core 0.1.10

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! Hexagon Instruction Selection — converts LLVM IR to Hexagon
//! machine instructions.
//!
//! Clean-room behavioral reconstruction from the Hexagon V5/V6
//! Programmer's Reference Manual. Zero LLVM source consultation.
//!
//! # Lowering patterns
//!
//! | IR op       | Hexagon instruction(s)                    |
//! |-------------|-------------------------------------------|
//! | `add`       | `ADD(Rd, Rs, Rt)`                        |
//! | `sub`       | `SUB(Rd, Rs, Rt)`                        |
//! | `mul`       | `MPY(Rd, Rs, Rt)`                        |
//! | `and`       | `AND(Rd, Rs, Rt)`                        |
//! | `or`        | `OR(Rd, Rs, Rt)`                         |
//! | `xor`       | `XOR(Rd, Rs, Rt)`                        |
//! | `shl`       | `LSL(Rd, Rs, Rt)`                        |
//! | `lshr`      | `LSR(Rd, Rs, Rt)`                        |
//! | `ashr`      | `ASR(Rd, Rs, Rt)`                        |
//! | `icmp eq`   | `CMPEQ(p0, Rs, Rt)` then IF p0 branch    |
//! | `br`        | `JUMP target`                            |
//! | `call`      | `CALL target`                            |
//! | `ret`       | `JUMPr lr`                               |
//! | `load`      | `LDW(Rd, Rs + #offset)`                  |
//! | `store`     | `STW(Rt, Rs + #offset)`                  |

use super::hexagon_instr_info::HexagonOpcode;
use super::hexagon_register_info::*;
use crate::codegen::*;
use crate::opcode::Opcode;
use crate::value::Value;
use std::collections::HashMap;

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

impl HexagonInstructionSelector {
    pub fn new(_is_64bit: bool) -> Self {
        HexagonInstructionSelector {
            is_64bit: false,
            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_three_reg(inst, HexagonOpcode::ADD as u32)],
            Opcode::Sub => vec![self.lower_three_reg(inst, HexagonOpcode::SUB as u32)],
            Opcode::Mul => vec![self.lower_three_reg(inst, HexagonOpcode::MPY as u32)],
            Opcode::And => vec![self.lower_three_reg(inst, HexagonOpcode::AND as u32)],
            Opcode::Or => vec![self.lower_three_reg(inst, HexagonOpcode::OR as u32)],
            Opcode::Xor => vec![self.lower_three_reg(inst, HexagonOpcode::XOR as u32)],
            Opcode::Shl => vec![self.lower_three_reg(inst, HexagonOpcode::LSL as u32)],
            Opcode::LShr => vec![self.lower_three_reg(inst, HexagonOpcode::LSR as u32)],
            Opcode::AShr => vec![self.lower_three_reg(inst, HexagonOpcode::ASR as u32)],
            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_move(inst)],
            Opcode::SExt => vec![self.lower_move(inst)],
            Opcode::Trunc => vec![self.lower_move(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_three_reg(&mut self, inst: &Value, opcode: u32) -> MachineInstr {
        let rs = self.get_vreg_for_operand(inst, 0);
        let rt = self.get_vreg_for_operand(inst, 1);
        let rd = self.get_or_create_vreg(inst);

        let mut mi = MachineInstr::new(opcode);
        mi.push_reg(rd);
        mi.push_reg(rs);
        mi.push_reg(rt);
        mi.def = Some(rd);
        mi
    }

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

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

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

        let mut cmp = MachineInstr::new(HexagonOpcode::CMPEQ as u32);
        cmp.push_reg(0); // p0 pred def
        cmp.push_reg(rs);
        cmp.push_reg(rt);

        let mut mux = MachineInstr::new(HexagonOpcode::MUX as u32);
        mux.push_reg(rd);
        mux.push_reg(0); // p0
        mux.push_imm(1);
        mux.push_imm(0);
        mux.def = Some(rd);

        vec![cmp, mux]
    }

    fn lower_br(&mut self, inst: &Value) -> MachineInstr {
        let mut mi = MachineInstr::new(HexagonOpcode::JUMP 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 {
        let mut mi = MachineInstr::new(HexagonOpcode::JUMPr as u32);
        mi.push_reg(LR as u32);
        mi
    }

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

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

    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(HexagonOpcode::LDW as u32);
        mi.push_reg(rd);
        mi.push_reg(base);
        mi.push_imm(0);
        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(HexagonOpcode::STW as u32);
        mi.push_reg(val);
        mi.push_reg(addr);
        mi.push_imm(0);
        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(HexagonOpcode::ADD as u32);
        mi.push_reg(rd);
        mi.push_reg(base);
        mi.push_reg(offset);
        mi.def = Some(rd);
        mi
    }
}