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;
pub const GPR_COUNT: usize = 32;
pub const GPR_LAST: u16 = 12031;
pub const PC: u16 = 12100;
pub const PSW: u16 = 12101;
pub const MCR: u16 = 12102;
pub const IR: u16 = 12103;
pub const LANAI_MAX_REG_ID: u16 = 12103;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LanaiRegClass {
GPR,
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"),
}
}
}
pub struct LanaiRegisterInfo;
impl LanaiRegisterInfo {
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),
}
}
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),
}
}
pub fn get_reg_class(reg_id: u16) -> LanaiRegClass {
if reg_id >= GPR_BASE && reg_id <= GPR_LAST {
LanaiRegClass::GPR
} else {
LanaiRegClass::SpecialReg
}
}
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
}
}
pub fn is_zero_reg(reg_id: u16) -> bool {
reg_id == R0
}
pub fn is_special(reg_id: u16) -> bool {
matches!(reg_id, PC | PSW | MCR | IR)
}
pub fn is_caller_saved(reg_id: u16) -> bool {
matches!(
reg_id,
R1 | R2 | R3 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | R15
)
}
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
)
}
pub fn get_return_address_reg() -> u16 {
RCA
}
pub fn get_stack_pointer_reg() -> u16 {
SP
}
pub fn get_return_value_reg() -> u16 {
RR1
}
pub fn get_zero_reg() -> u16 {
R0
}
pub fn get_program_counter() -> u16 {
RPC
}
pub fn get_allocatable_gprs() -> Vec<u16> {
(GPR_BASE..=GPR_LAST)
.filter(|&r| !matches!(r, R0 | R1 | R4 | R30 | R31))
.collect()
}
}
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));
}
}