llvm-native-core 0.1.12

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! Hexagon MC Encoder — instruction encoding for Qualcomm Hexagon
//! VLIW DSP.
//!
//! Encodes Hexagon instructions into little-endian byte sequences.
//! All instructions are 32 bits wide and organized in 4-slot VLIW
//! packets (up to 4 instructions per packet).
//!
//! # Instruction Encoding (32-bit)
//!
//! ```text
//! Major opcode(4) | Sub-opcode(4) | Reg fields | Immediate | Parse bits
//! ```
//!
//! Slot types: 0=ALU/MPY, 1=LD/ST, 2=J/JR, 3=XTYPE (vector)

use super::hexagon_instr_info::HexagonOpcode;
use crate::codegen::{MachineFunction, MachineInstr, MachineOperand};

fn u32_to_le_bytes(word: u32) -> Vec<u8> {
    word.to_le_bytes().to_vec()
}

pub struct HexagonMCEncoder {
    pub is_64bit: bool,
    pub output: Vec<u8>,
}

impl HexagonMCEncoder {
    pub fn new(_is_64bit: bool) -> Self {
        Self {
            is_64bit: false,
            output: Vec::with_capacity(256),
        }
    }

    pub fn encode_instruction(&mut self, mi: &MachineInstr) -> Vec<u8> {
        let bytes = self.instruction_to_bytes(mi);
        self.output.extend_from_slice(&bytes);
        bytes
    }

    pub fn encode_function(&mut self, mf: &MachineFunction) -> Vec<u8> {
        for block in &mf.blocks {
            for instr in &block.instructions {
                self.encode_instruction(instr);
            }
        }
        std::mem::take(&mut self.output)
    }

    fn instruction_to_bytes(&self, mi: &MachineInstr) -> Vec<u8> {
        let word = self.encode_by_opcode(mi);
        u32_to_le_bytes(word)
    }

    fn encode_by_opcode(&self, mi: &MachineInstr) -> u32 {
        match get_hexagon_opcode(mi.opcode) {
            HexagonOpcode::ADD => self.encode_alu(0x00, mi),
            HexagonOpcode::SUB => self.encode_alu(0x01, mi),
            HexagonOpcode::AND => self.encode_alu(0x02, mi),
            HexagonOpcode::OR => self.encode_alu(0x03, mi),
            HexagonOpcode::XOR => self.encode_alu(0x04, mi),
            HexagonOpcode::NEG => self.encode_alu_2r(0x05, mi),
            HexagonOpcode::NOT => self.encode_alu_2r(0x06, mi),
            HexagonOpcode::ABS => self.encode_alu_2r(0x07, mi),
            HexagonOpcode::LSL => self.encode_alu(0x10, mi),
            HexagonOpcode::LSR => self.encode_alu(0x11, mi),
            HexagonOpcode::ASR => self.encode_alu(0x12, mi),
            HexagonOpcode::MPY => self.encode_alu(0x20, mi),
            HexagonOpcode::MPYU => self.encode_alu(0x21, mi),
            HexagonOpcode::MPYI => self.encode_alu(0x22, mi),
            HexagonOpcode::LDW => self.encode_mem(0x30, mi),
            HexagonOpcode::LDB => self.encode_mem(0x31, mi),
            HexagonOpcode::LDH => self.encode_mem(0x32, mi),
            HexagonOpcode::LDD => self.encode_mem(0x33, mi),
            HexagonOpcode::STW => self.encode_mem(0x34, mi),
            HexagonOpcode::STB => self.encode_mem(0x35, mi),
            HexagonOpcode::STH => self.encode_mem(0x36, mi),
            HexagonOpcode::STD => self.encode_mem(0x37, mi),
            HexagonOpcode::CMPEQ => self.encode_cmp(0x40, mi),
            HexagonOpcode::CMPGT => self.encode_cmp(0x41, mi),
            HexagonOpcode::CMPGTU => self.encode_cmp(0x42, mi),
            HexagonOpcode::JUMP => self.encode_jump(0x50, mi),
            HexagonOpcode::JUMPR => self.encode_jumpr(0x51, mi),
            HexagonOpcode::JUMPr => self.encode_jumpr(0x52, mi),
            HexagonOpcode::CALL => self.encode_call(0x53, mi),
            HexagonOpcode::CALLR => self.encode_jumpr(0x54, mi),
            HexagonOpcode::MOV => self.encode_alu_2r(0x0F, mi),
            HexagonOpcode::NOP => 0x7C00,
            HexagonOpcode::LI => self.encode_li(mi),
            HexagonOpcode::VADD => self.encode_vec(0x60, mi),
            HexagonOpcode::VSUB => self.encode_vec(0x61, mi),
            HexagonOpcode::VMPY => self.encode_vec(0x62, mi),
            _ => 0x7C00, // NOP
        }
    }

    fn encode_alu(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let rd = Self::get_reg(mi, 0);
        let rs = Self::get_reg(mi, 1);
        let rt = Self::get_reg(mi, 2);
        (sub_op << 28) | (rd << 21) | (rs << 16) | (rt << 0)
    }

    fn encode_alu_2r(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let rd = Self::get_reg(mi, 0);
        let rs = Self::get_reg(mi, 1);
        (sub_op << 28) | (rd << 21) | (rs << 16)
    }

    fn encode_mem(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let rd = Self::get_reg(mi, 0);
        let rs = Self::get_reg(mi, 1);
        let offset: u32 = Self::get_imm(mi, 2) as u32 & 0xFFFF;
        (sub_op << 28) | (rd << 21) | (rs << 16) | offset
    }

    fn encode_cmp(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let pd = Self::get_reg(mi, 0);
        let rs = Self::get_reg(mi, 1);
        let rt = Self::get_reg(mi, 2);
        (sub_op << 28) | (pd << 23) | (rs << 16) | (rt << 0)
    }

    fn encode_jump(&self, sub_op: u32, _mi: &MachineInstr) -> u32 {
        (sub_op << 28)
    }

    fn encode_jumpr(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let rs = Self::get_reg(mi, 0);
        (sub_op << 28) | (rs << 16)
    }

    fn encode_call(&self, sub_op: u32, _mi: &MachineInstr) -> u32 {
        (sub_op << 28)
    }

    fn encode_li(&self, mi: &MachineInstr) -> u32 {
        let rd = Self::get_reg(mi, 0);
        let imm: u32 = Self::get_imm(mi, 1) as u32 & 0xFFFF;
        (0x0E << 28) | (rd << 21) | imm
    }

    fn encode_vec(&self, sub_op: u32, mi: &MachineInstr) -> u32 {
        let vd = Self::get_reg(mi, 0);
        let vs = Self::get_reg(mi, 1);
        let vt = Self::get_reg(mi, 2);
        (sub_op << 28) | (vd << 21) | (vs << 16) | (vt << 0)
    }

    fn get_reg(mi: &MachineInstr, index: usize) -> u32 {
        mi.operands
            .get(index)
            .map(|op| match op {
                MachineOperand::Reg(vr) => *vr & 0x1F,
                MachineOperand::PhysReg(pr) => *pr & 0x1F,
                _ => 0,
            })
            .unwrap_or(0) as u32
    }

    fn get_imm(mi: &MachineInstr, index: usize) -> i64 {
        mi.operands
            .get(index)
            .map(|op| match op {
                MachineOperand::Imm(imm) => *imm,
                _ => 0,
            })
            .unwrap_or(0)
    }
}

fn get_hexagon_opcode(opcode: u32) -> HexagonOpcode {
    match opcode {
        0 => HexagonOpcode::ADD,
        1 => HexagonOpcode::SUB,
        2 => HexagonOpcode::AND,
        3 => HexagonOpcode::OR,
        4 => HexagonOpcode::XOR,
        5 => HexagonOpcode::NEG,
        6 => HexagonOpcode::NOT,
        20 => HexagonOpcode::LSL,
        21 => HexagonOpcode::LSR,
        22 => HexagonOpcode::ASR,
        30 => HexagonOpcode::MPY,
        31 => HexagonOpcode::MPYU,
        32 => HexagonOpcode::MPYI,
        50 => HexagonOpcode::LDW,
        51 => HexagonOpcode::LDB,
        52 => HexagonOpcode::LDH,
        53 => HexagonOpcode::LDD,
        60 => HexagonOpcode::STW,
        61 => HexagonOpcode::STB,
        62 => HexagonOpcode::STH,
        63 => HexagonOpcode::STD,
        80 => HexagonOpcode::JUMP,
        81 => HexagonOpcode::JUMPR,
        82 => HexagonOpcode::JUMPr,
        83 => HexagonOpcode::CALL,
        84 => HexagonOpcode::CALLR,
        100 => HexagonOpcode::CMPEQ,
        101 => HexagonOpcode::CMPGT,
        102 => HexagonOpcode::CMPGTU,
        103 => HexagonOpcode::CMPLT,
        104 => HexagonOpcode::CMPLTU,
        120 => HexagonOpcode::VADD,
        121 => HexagonOpcode::VSUB,
        122 => HexagonOpcode::VMPY,
        140 => HexagonOpcode::NOP,
        141 => HexagonOpcode::LI,
        142 => HexagonOpcode::MOV,
        _ => HexagonOpcode::NOP,
    }
}