llvm-native-core 0.1.10

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! MSP430 Register Information — register definitions for the
//! Texas Instruments MSP430 16-bit microcontroller.
//!
//! Register categories:
//! - 16 General Purpose Registers: r0=PC, r1=SP, r2=SR/CG1, r3=CG2, r4-r15
//!
//! Clean-room reconstruction from the MSP430x1xx Family User's Guide.
//! Zero LLVM source code consultation.

// ============================================================================
// Register Identifiers — flat numbering scheme starting at 8000
// ============================================================================

// ============================================================================
// MSP430 Registers (8000–8015)
// ============================================================================

pub const R0: u16 = 8000; // PC (Program Counter)
pub const R1: u16 = 8001; // SP (Stack Pointer)
pub const R2: u16 = 8002; // SR/CG1 (Status Register / Constant Generator 1)
pub const R3: u16 = 8003; // CG2 (Constant Generator 2)
pub const R4: u16 = 8004;
pub const R5: u16 = 8005;
pub const R6: u16 = 8006;
pub const R7: u16 = 8007;
pub const R8: u16 = 8008;
pub const R9: u16 = 8009;
pub const R10: u16 = 8010;
pub const R11: u16 = 8011;
pub const R12: u16 = 8012;
pub const R13: u16 = 8013;
pub const R14: u16 = 8014;
pub const R15: u16 = 8015;

// Convenience aliases
pub const PC: u16 = R0;
pub const SP: u16 = R1;
pub const SR: u16 = R2;
pub const CG1: u16 = R2;
pub const CG2: u16 = R3;

// ============================================================================
// Register counts
// ============================================================================

pub const MSP430_GPR_COUNT: usize = 16;
pub const MSP430_MAX_REG_ID: u16 = 8015;
pub const MSP430_GPR_BASE: u16 = 8000;

// ============================================================================
// Register Class Enum
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Msp430RegClass {
    /// General Purpose Register (16-bit).
    GPR16,
}

impl std::fmt::Display for Msp430RegClass {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Msp430RegClass::GPR16 => write!(f, "GPR16"),
        }
    }
}

// ============================================================================
// Msp430RegisterInfo
// ============================================================================

pub struct Msp430RegisterInfo;

impl Msp430RegisterInfo {
    pub fn get_asm_name(reg_id: u16) -> String {
        match reg_id {
            R0 => "PC".into(),
            R1 => "SP".into(),
            R2 => "SR".into(),
            R3 => "CG2".into(),
            R4 => "R4".into(),
            R5 => "R5".into(),
            R6 => "R6".into(),
            R7 => "R7".into(),
            R8 => "R8".into(),
            R9 => "R9".into(),
            R10 => "R10".into(),
            R11 => "R11".into(),
            R12 => "R12".into(),
            R13 => "R13".into(),
            R14 => "R14".into(),
            R15 => "R15".into(),
            _ => format!("R{}", reg_id - MSP430_GPR_BASE),
        }
    }

    pub fn get_abi_name(reg_id: u16) -> String {
        Self::get_asm_name(reg_id)
    }

    pub fn get_reg_class(_reg_id: u16) -> Msp430RegClass {
        Msp430RegClass::GPR16
    }

    pub fn get_reg_width(_reg_id: u16, _is_64bit: bool) -> u32 {
        16
    }

    pub fn get_dwarf_num(reg_id: u16) -> i32 {
        match reg_id {
            _ if reg_id >= R0 && reg_id <= R15 => (reg_id - R0) as i32,
            _ => -1,
        }
    }

    pub fn is_callee_saved(reg_id: u16) -> bool {
        matches!(reg_id, R4 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | R1)
    }

    pub fn is_caller_saved(reg_id: u16) -> bool {
        matches!(reg_id, R12 | R13 | R14 | R15)
    }

    pub fn is_reserved(reg_id: u16) -> bool {
        reg_id == R0 || reg_id == R2 || reg_id == R3
    }

    pub fn get_allocatable_gprs() -> Vec<u16> {
        let mut regs = Vec::new();
        for i in 0..16 {
            let r = MSP430_GPR_BASE + i as u16;
            if !Self::is_reserved(r) {
                regs.push(r);
            }
        }
        regs
    }

    pub fn get_argument_regs() -> Vec<u16> {
        vec![R12, R13, R14, R15]
    }

    pub fn get_return_regs() -> Vec<u16> {
        vec![R12, R13]
    }

    pub fn get_frame_pointer_reg() -> u16 {
        R4
    }

    pub fn get_stack_pointer_reg() -> u16 {
        SP
    }

    pub fn is_gpr(reg_id: u16) -> bool {
        reg_id >= MSP430_GPR_BASE && reg_id < MSP430_GPR_BASE + 16
    }

    pub fn get_reg_index(reg_id: u16) -> u8 {
        if reg_id >= MSP430_GPR_BASE && reg_id < MSP430_GPR_BASE + 16 {
            (reg_id - MSP430_GPR_BASE) as u8
        } else {
            0
        }
    }

    pub fn can_be_base_reg(reg_id: u16) -> bool {
        Self::is_gpr(reg_id) && reg_id != R3
    }

    pub fn get_caller_saved_gprs() -> Vec<u16> {
        vec![R12, R13, R14, R15]
    }

    pub fn get_callee_saved_gprs() -> Vec<u16> {
        vec![R4, R5, R6, R7, R8, R9, R10, R11]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_register_count_constants() {
        assert_eq!(MSP430_GPR_COUNT, 16);
        assert_eq!(MSP430_MAX_REG_ID, 8015);
    }

    #[test]
    fn test_register_ids_unique() {
        let mut seen = std::collections::HashSet::new();
        for i in 0..16 {
            assert!(seen.insert(R0 + i));
        }
    }

    #[test]
    fn test_abi_names() {
        assert_eq!(Msp430RegisterInfo::get_asm_name(R0), "PC");
        assert_eq!(Msp430RegisterInfo::get_asm_name(R1), "SP");
        assert_eq!(Msp430RegisterInfo::get_asm_name(R2), "SR");
        assert_eq!(Msp430RegisterInfo::get_asm_name(R4), "R4");
        assert_eq!(Msp430RegisterInfo::get_asm_name(R15), "R15");
    }

    #[test]
    fn test_get_reg_width() {
        assert_eq!(Msp430RegisterInfo::get_reg_width(R0, false), 16);
        assert_eq!(Msp430RegisterInfo::get_reg_width(R15, true), 16);
    }

    #[test]
    fn test_is_callee_saved() {
        assert!(Msp430RegisterInfo::is_callee_saved(R4));
        assert!(Msp430RegisterInfo::is_callee_saved(R11));
        assert!(Msp430RegisterInfo::is_callee_saved(R1));
        assert!(!Msp430RegisterInfo::is_callee_saved(R12));
        assert!(!Msp430RegisterInfo::is_callee_saved(R15));
    }

    #[test]
    fn test_is_reserved() {
        assert!(Msp430RegisterInfo::is_reserved(R0));
        assert!(Msp430RegisterInfo::is_reserved(R2));
        assert!(Msp430RegisterInfo::is_reserved(R3));
        assert!(!Msp430RegisterInfo::is_reserved(R4));
        assert!(!Msp430RegisterInfo::is_reserved(R15));
    }

    #[test]
    fn test_get_argument_regs() {
        assert_eq!(
            Msp430RegisterInfo::get_argument_regs(),
            vec![R12, R13, R14, R15]
        );
    }

    #[test]
    fn test_get_return_regs() {
        assert_eq!(Msp430RegisterInfo::get_return_regs(), vec![R12, R13]);
    }

    #[test]
    fn test_get_reg_index() {
        assert_eq!(Msp430RegisterInfo::get_reg_index(R0), 0);
        assert_eq!(Msp430RegisterInfo::get_reg_index(R15), 15);
    }

    #[test]
    fn test_is_gpr() {
        assert!(Msp430RegisterInfo::is_gpr(R0));
        assert!(Msp430RegisterInfo::is_gpr(R15));
        assert!(!Msp430RegisterInfo::is_gpr(9000));
    }
}