llvm-native-core 0.1.11

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
//! Lanai Register Information — 32 GPRs and 4 special registers
//! for the Google Myricom Lanai 32-bit RISC processor.
//!
//! Clean-room behavioral reconstruction from the Lanai ISA
//! specification. Zero LLVM source code consultation.
//!
//! Register conventions:
//! - r0: always reads as 0, writes are discarded
//! - r1: return address (default), aka rra
//! - r4: return address (rca) — caller return address
//! - r26: return value (rr1)
//! - r30: stack pointer (sp)
//! - r31: program counter (rpc)

// ============================================================================
// General Purpose Registers (base 12000)
// ============================================================================

pub const GPR_BASE: u16 = 12000;

pub const R0: u16 = 12000;
pub const R1: u16 = 12001;
pub const RRA: u16 = 12001;
pub const R2: u16 = 12002;
pub const R3: u16 = 12003;
pub const R4: u16 = 12004;
pub const RCA: u16 = 12004;
pub const R5: u16 = 12005;
pub const R6: u16 = 12006;
pub const R7: u16 = 12007;
pub const R8: u16 = 12008;
pub const R9: u16 = 12009;
pub const R10: u16 = 12010;
pub const R11: u16 = 12011;
pub const R12: u16 = 12012;
pub const R13: u16 = 12013;
pub const R14: u16 = 12014;
pub const R15: u16 = 12015;
pub const R16: u16 = 12016;
pub const R17: u16 = 12017;
pub const R18: u16 = 12018;
pub const R19: u16 = 12019;
pub const R20: u16 = 12020;
pub const R21: u16 = 12021;
pub const R22: u16 = 12022;
pub const R23: u16 = 12023;
pub const R24: u16 = 12024;
pub const R25: u16 = 12025;
pub const R26: u16 = 12026;
pub const RR1: u16 = 12026;
pub const R27: u16 = 12027;
pub const R28: u16 = 12028;
pub const R29: u16 = 12029;
pub const R30: u16 = 12030;
pub const SP: u16 = 12030;
pub const R31: u16 = 12031;
pub const RPC: u16 = 12031;

/// GPR count.
pub const GPR_COUNT: usize = 32;
/// Last GPR ID.
pub const GPR_LAST: u16 = 12031;

// ============================================================================
// Special Registers (base 12100)
// ============================================================================

/// Program Counter.
pub const PC: u16 = 12100;

/// Processor Status Word.
pub const PSW: u16 = 12101;

/// Memory Control Register.
pub const MCR: u16 = 12102;

/// Instruction Register.
pub const IR: u16 = 12103;

/// Maximum register ID.
pub const LANAI_MAX_REG_ID: u16 = 12103;

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

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LanaiRegClass {
    /// General Purpose Register.
    GPR,
    /// Special register (PC, PSW, MCR, IR).
    SpecialReg,
}

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

// ============================================================================
// LanaiRegisterInfo
// ============================================================================

/// Lanai register information provider.
pub struct LanaiRegisterInfo;

impl LanaiRegisterInfo {
    /// Get the assembly name for a register ID.
    pub fn get_asm_name(reg_id: u16) -> String {
        match reg_id {
            R0 => "r0".to_string(),
            R1 => "r1".to_string(),
            R2 => "r2".to_string(),
            R3 => "r3".to_string(),
            R4 => "r4".to_string(),
            R5 => "r5".to_string(),
            R6 => "r6".to_string(),
            R7 => "r7".to_string(),
            R8 => "r8".to_string(),
            R9 => "r9".to_string(),
            R10 => "r10".to_string(),
            R11 => "r11".to_string(),
            R12 => "r12".to_string(),
            R13 => "r13".to_string(),
            R14 => "r14".to_string(),
            R15 => "r15".to_string(),
            R16 => "r16".to_string(),
            R17 => "r17".to_string(),
            R18 => "r18".to_string(),
            R19 => "r19".to_string(),
            R20 => "r20".to_string(),
            R21 => "r21".to_string(),
            R22 => "r22".to_string(),
            R23 => "r23".to_string(),
            R24 => "r24".to_string(),
            R25 => "r25".to_string(),
            R26 => "r26".to_string(),
            R27 => "r27".to_string(),
            R28 => "r28".to_string(),
            R29 => "r29".to_string(),
            R30 => "r30".to_string(),
            R31 => "r31".to_string(),
            PC => "pc".to_string(),
            PSW => "psw".to_string(),
            MCR => "mcr".to_string(),
            IR => "ir".to_string(),
            id if id >= GPR_BASE && id <= GPR_LAST => {
                format!("r{}", id - GPR_BASE)
            }
            _ => format!("r{}", reg_id),
        }
    }

    /// Get the ABI name for a register.
    pub fn get_abi_name(reg_id: u16) -> String {
        match reg_id {
            R0 => "zero".to_string(),
            R1 => "rra".to_string(),
            R4 => "rca".to_string(),
            R26 => "rr1".to_string(),
            R30 => "sp".to_string(),
            R31 => "rpc".to_string(),
            _ if reg_id >= GPR_BASE && reg_id <= GPR_LAST => {
                format!("r{}", reg_id - GPR_BASE)
            }
            _ => Self::get_asm_name(reg_id),
        }
    }

    /// Get the register class for a register ID.
    pub fn get_reg_class(reg_id: u16) -> LanaiRegClass {
        if reg_id >= GPR_BASE && reg_id <= GPR_LAST {
            LanaiRegClass::GPR
        } else {
            LanaiRegClass::SpecialReg
        }
    }

    /// Get the register index within its class.
    pub fn get_reg_index(reg_id: u16) -> u16 {
        if reg_id >= GPR_BASE && reg_id <= GPR_LAST {
            reg_id - GPR_BASE
        } else {
            reg_id
        }
    }

    /// Check if register is the zero register.
    pub fn is_zero_reg(reg_id: u16) -> bool {
        reg_id == R0
    }

    /// Check if register is a special register.
    pub fn is_special(reg_id: u16) -> bool {
        matches!(reg_id, PC | PSW | MCR | IR)
    }

    /// Is this register caller-saved?
    pub fn is_caller_saved(reg_id: u16) -> bool {
        matches!(
            reg_id,
            R1 | R2 | R3 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | R15
        )
    }

    /// Is this register callee-saved?
    pub fn is_callee_saved(reg_id: u16) -> bool {
        matches!(
            reg_id,
            R16 | R17 | R18 | R19 | R20 | R21 | R22 | R23 | R24 | R25 | R26 | R27 | R28 | R29
        )
    }

    /// Get the return address register.
    pub fn get_return_address_reg() -> u16 {
        RCA
    }

    /// Get the stack pointer register.
    pub fn get_stack_pointer_reg() -> u16 {
        SP
    }

    /// Get the return value register.
    pub fn get_return_value_reg() -> u16 {
        RR1
    }

    /// Get the zero register.
    pub fn get_zero_reg() -> u16 {
        R0
    }

    /// Get the program counter register.
    pub fn get_program_counter() -> u16 {
        RPC
    }

    /// Get allocatable GPRs (excluding reserved: r0, r1, r4, r30, r31).
    pub fn get_allocatable_gprs() -> Vec<u16> {
        (GPR_BASE..=GPR_LAST)
            .filter(|&r| !matches!(r, R0 | R1 | R4 | R30 | R31))
            .collect()
    }
}

/// Helper: create a GPR ID from an index.
pub const fn gpr(n: u16) -> u16 {
    GPR_BASE + n
}

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

    #[test]
    fn test_gpr_ids() {
        assert_eq!(R0, 12000);
        assert_eq!(R31, 12031);
        assert_eq!(gpr(0), R0);
        assert_eq!(gpr(31), R31);
    }

    #[test]
    fn test_asm_names() {
        assert_eq!(LanaiRegisterInfo::get_asm_name(R0), "r0");
        assert_eq!(LanaiRegisterInfo::get_asm_name(SP), "r30");
        assert_eq!(LanaiRegisterInfo::get_asm_name(PC), "pc");
        assert_eq!(LanaiRegisterInfo::get_asm_name(PSW), "psw");
    }

    #[test]
    fn test_abi_names() {
        assert_eq!(LanaiRegisterInfo::get_abi_name(R0), "zero");
        assert_eq!(LanaiRegisterInfo::get_abi_name(R1), "rra");
        assert_eq!(LanaiRegisterInfo::get_abi_name(R4), "rca");
        assert_eq!(LanaiRegisterInfo::get_abi_name(SP), "sp");
    }

    #[test]
    fn test_reg_class() {
        assert_eq!(LanaiRegisterInfo::get_reg_class(R5), LanaiRegClass::GPR);
        assert_eq!(
            LanaiRegisterInfo::get_reg_class(PC),
            LanaiRegClass::SpecialReg
        );
    }

    #[test]
    fn test_zero_reg() {
        assert!(LanaiRegisterInfo::is_zero_reg(R0));
        assert!(!LanaiRegisterInfo::is_zero_reg(R1));
    }

    #[test]
    fn test_is_special() {
        assert!(LanaiRegisterInfo::is_special(PC));
        assert!(!LanaiRegisterInfo::is_special(R5));
    }

    #[test]
    fn test_caller_callee_saved() {
        assert!(LanaiRegisterInfo::is_caller_saved(R1));
        assert!(!LanaiRegisterInfo::is_caller_saved(R16));
        assert!(LanaiRegisterInfo::is_callee_saved(R16));
        assert!(!LanaiRegisterInfo::is_callee_saved(R1));
    }
}