pub const ZERO: u16 = 4000;
pub const AT: u16 = 4001;
pub const V0: u16 = 4002;
pub const V1: u16 = 4003;
pub const A0: u16 = 4004;
pub const A1: u16 = 4005;
pub const A2: u16 = 4006;
pub const A3: u16 = 4007;
pub const T0: u16 = 4008;
pub const T1: u16 = 4009;
pub const T2: u16 = 4010;
pub const T3: u16 = 4011;
pub const T4: u16 = 4012;
pub const T5: u16 = 4013;
pub const T6: u16 = 4014;
pub const T7: u16 = 4015;
pub const S0: u16 = 4016;
pub const S1: u16 = 4017;
pub const S2: u16 = 4018;
pub const S3: u16 = 4019;
pub const S4: u16 = 4020;
pub const S5: u16 = 4021;
pub const S6: u16 = 4022;
pub const S7: u16 = 4023;
pub const T8: u16 = 4024;
pub const T9: u16 = 4025;
pub const K0: u16 = 4026;
pub const K1: u16 = 4027;
pub const GP: u16 = 4028;
pub const SP: u16 = 4029;
pub const FP: u16 = 4030;
pub const RA: u16 = 4031;
pub const HI: u16 = 4032;
pub const LO: u16 = 4033;
pub const PC: u16 = 4034;
pub const F0: u16 = 4050;
pub const F1: u16 = 4051;
pub const F2: u16 = 4052;
pub const F3: u16 = 4053;
pub const F4: u16 = 4054;
pub const F5: u16 = 4055;
pub const F6: u16 = 4056;
pub const F7: u16 = 4057;
pub const F8: u16 = 4058;
pub const F9: u16 = 4059;
pub const F10: u16 = 4060;
pub const F11: u16 = 4061;
pub const F12: u16 = 4062;
pub const F13: u16 = 4063;
pub const F14: u16 = 4064;
pub const F15: u16 = 4065;
pub const F16: u16 = 4066;
pub const F17: u16 = 4067;
pub const F18: u16 = 4068;
pub const F19: u16 = 4069;
pub const F20: u16 = 4070;
pub const F21: u16 = 4071;
pub const F22: u16 = 4072;
pub const F23: u16 = 4073;
pub const F24: u16 = 4074;
pub const F25: u16 = 4075;
pub const F26: u16 = 4076;
pub const F27: u16 = 4077;
pub const F28: u16 = 4078;
pub const F29: u16 = 4079;
pub const F30: u16 = 4080;
pub const F31: u16 = 4081;
pub const MIPS_GPR_COUNT: usize = 32;
pub const MIPS_FPR_COUNT: usize = 32;
pub const MIPS_MAX_REG_ID: u16 = 4081;
pub const MIPS_GPR_BASE: u16 = 4000;
pub const MIPS_SPECIAL_BASE: u16 = 4032;
pub const MIPS_FPR_BASE: u16 = 4050;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MipsRegClass {
GPR,
FPR32,
FPR64,
Special,
}
impl std::fmt::Display for MipsRegClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MipsRegClass::GPR => write!(f, "GPR"),
MipsRegClass::FPR32 => write!(f, "FPR32"),
MipsRegClass::FPR64 => write!(f, "FPR64"),
MipsRegClass::Special => write!(f, "Special"),
}
}
}
pub struct MipsRegisterInfo;
impl MipsRegisterInfo {
pub fn get_asm_name(reg_id: u16) -> String {
match reg_id {
ZERO => "$zero".into(),
AT => "$at".into(),
V0 => "$v0".into(),
V1 => "$v1".into(),
A0 => "$a0".into(),
A1 => "$a1".into(),
A2 => "$a2".into(),
A3 => "$a3".into(),
T0 => "$t0".into(),
T1 => "$t1".into(),
T2 => "$t2".into(),
T3 => "$t3".into(),
T4 => "$t4".into(),
T5 => "$t5".into(),
T6 => "$t6".into(),
T7 => "$t7".into(),
S0 => "$s0".into(),
S1 => "$s1".into(),
S2 => "$s2".into(),
S3 => "$s3".into(),
S4 => "$s4".into(),
S5 => "$s5".into(),
S6 => "$s6".into(),
S7 => "$s7".into(),
T8 => "$t8".into(),
T9 => "$t9".into(),
K0 => "$k0".into(),
K1 => "$k1".into(),
GP => "$gp".into(),
SP => "$sp".into(),
FP => "$fp".into(),
RA => "$ra".into(),
HI => "$hi".into(),
LO => "$lo".into(),
PC => "$pc".into(),
_ if reg_id >= F0 && reg_id <= F31 => {
format!("$f{}", reg_id - F0)
}
_ => format!("${}", reg_id),
}
}
pub fn get_abi_name(reg_id: u16) -> String {
Self::get_asm_name(reg_id)
}
pub fn get_reg_class(reg_id: u16) -> MipsRegClass {
if reg_id >= MIPS_GPR_BASE && reg_id < MIPS_GPR_BASE + 32 {
MipsRegClass::GPR
} else if reg_id >= HI && reg_id <= PC {
MipsRegClass::Special
} else if reg_id >= MIPS_FPR_BASE && reg_id < MIPS_FPR_BASE + 32 {
MipsRegClass::FPR32
} else {
MipsRegClass::GPR
}
}
pub fn get_reg_width(reg_id: u16, is_64bit: bool) -> u32 {
match Self::get_reg_class(reg_id) {
MipsRegClass::GPR => {
if is_64bit {
64
} else {
32
}
}
MipsRegClass::FPR32 => 32,
MipsRegClass::FPR64 => 64,
MipsRegClass::Special => {
if is_64bit {
64
} else {
32
}
}
}
}
pub fn get_dwarf_num(reg_id: u16) -> i32 {
match reg_id {
_ if reg_id >= ZERO && reg_id <= RA => (reg_id - ZERO) as i32,
_ if reg_id >= F0 && reg_id <= F31 => 32 + (reg_id - F0) as i32,
HI => -1,
LO => -1,
PC => -1,
_ => -1,
}
}
pub fn is_callee_saved(reg_id: u16) -> bool {
matches!(
reg_id,
S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | FP | GP | SP | RA
) || (reg_id >= F20 && reg_id <= F31)
}
pub fn is_caller_saved(reg_id: u16) -> bool {
matches!(
reg_id,
AT | V0
| V1
| A0
| A1
| A2
| A3
| T0
| T1
| T2
| T3
| T4
| T5
| T6
| T7
| T8
| T9
| K0
| K1
) || (reg_id >= F0 && reg_id <= F19)
}
pub fn is_reserved(reg_id: u16) -> bool {
reg_id == ZERO || reg_id == AT || reg_id == K0 || reg_id == K1
}
pub fn get_allocatable_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 1..32 {
let r = MIPS_GPR_BASE + i as u16;
if !Self::is_reserved(r) {
regs.push(r);
}
}
regs
}
pub fn get_allocatable_fprs() -> Vec<u16> {
(F0..=F31).collect()
}
pub fn get_argument_regs() -> Vec<u16> {
vec![A0, A1, A2, A3]
}
pub fn get_fp_argument_regs(_is_64bit: bool) -> Vec<u16> {
vec![F12, F13, F14, F15, F16, F17, F18, F19]
}
pub fn get_return_regs() -> Vec<u16> {
vec![V0, V1]
}
pub fn get_fp_return_regs() -> Vec<u16> {
vec![F0, F1]
}
pub fn get_frame_pointer_reg() -> u16 {
FP
}
pub fn get_return_address_reg() -> u16 {
RA
}
pub fn get_stack_pointer_reg() -> u16 {
SP
}
pub fn get_global_pointer_reg() -> u16 {
GP
}
pub fn get_zero_reg() -> u16 {
ZERO
}
pub fn is_gpr(reg_id: u16) -> bool {
reg_id >= MIPS_GPR_BASE && reg_id < MIPS_GPR_BASE + 32
}
pub fn is_fpr(reg_id: u16) -> bool {
reg_id >= MIPS_FPR_BASE && reg_id < MIPS_FPR_BASE + 32
}
pub fn get_reg_index(reg_id: u16) -> Option<u8> {
if Self::is_gpr(reg_id) {
Some((reg_id - MIPS_GPR_BASE) as u8)
} else if Self::is_fpr(reg_id) {
Some((reg_id - MIPS_FPR_BASE) as u8)
} else {
None
}
}
pub fn can_be_base_reg(reg_id: u16) -> bool {
Self::is_gpr(reg_id) && reg_id != ZERO
}
pub fn get_caller_saved_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 0..32 {
let r = MIPS_GPR_BASE + i as u16;
if i != 0 && Self::is_caller_saved(r) && !Self::is_reserved(r) {
regs.push(r);
}
}
regs
}
pub fn get_callee_saved_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 0..32 {
let r = MIPS_GPR_BASE + i as u16;
if Self::is_callee_saved(r) && !Self::is_reserved(r) {
regs.push(r);
}
}
regs
}
pub fn get_caller_saved_fprs() -> Vec<u16> {
(F0..=F19).collect()
}
pub fn get_callee_saved_fprs() -> Vec<u16> {
(F20..=F31).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_count_constants() {
assert_eq!(MIPS_GPR_COUNT, 32);
assert_eq!(MIPS_FPR_COUNT, 32);
assert_eq!(MIPS_MAX_REG_ID, 4081);
assert_eq!(MIPS_GPR_BASE, 4000);
assert_eq!(MIPS_FPR_BASE, 4050);
}
#[test]
fn test_register_ids_unique() {
let all_regs = [
ZERO, AT, V0, V1, A0, A1, A2, A3, T0, T1, T2, T3, T4, T5, T6, T7, S0, S1, S2, S3, S4,
S5, S6, S7, T8, T9, K0, K1, GP, SP, FP, RA, HI, LO, PC,
];
let mut seen = std::collections::HashSet::new();
for &r in &all_regs {
assert!(seen.insert(r), "Duplicate register ID: {}", r);
}
}
#[test]
fn test_fpr_ids_unique() {
let fprs: Vec<u16> = (F0..=F31).collect();
let mut seen = std::collections::HashSet::new();
for &r in &fprs {
assert!(seen.insert(r), "Duplicate FPR ID: {}", r);
}
assert_eq!(fprs.len(), 32);
}
#[test]
fn test_abi_names() {
assert_eq!(MipsRegisterInfo::get_asm_name(ZERO), "$zero");
assert_eq!(MipsRegisterInfo::get_asm_name(SP), "$sp");
assert_eq!(MipsRegisterInfo::get_asm_name(RA), "$ra");
assert_eq!(MipsRegisterInfo::get_asm_name(FP), "$fp");
assert_eq!(MipsRegisterInfo::get_asm_name(GP), "$gp");
assert_eq!(MipsRegisterInfo::get_asm_name(V0), "$v0");
assert_eq!(MipsRegisterInfo::get_asm_name(A0), "$a0");
assert_eq!(MipsRegisterInfo::get_asm_name(T0), "$t0");
assert_eq!(MipsRegisterInfo::get_asm_name(S0), "$s0");
}
#[test]
fn test_fpr_names() {
assert_eq!(MipsRegisterInfo::get_asm_name(F0), "$f0");
assert_eq!(MipsRegisterInfo::get_asm_name(F12), "$f12");
assert_eq!(MipsRegisterInfo::get_asm_name(F31), "$f31");
}
#[test]
fn test_special_reg_names() {
assert_eq!(MipsRegisterInfo::get_asm_name(HI), "$hi");
assert_eq!(MipsRegisterInfo::get_asm_name(LO), "$lo");
assert_eq!(MipsRegisterInfo::get_asm_name(PC), "$pc");
}
#[test]
fn test_get_reg_class() {
assert_eq!(MipsRegisterInfo::get_reg_class(ZERO), MipsRegClass::GPR);
assert_eq!(MipsRegisterInfo::get_reg_class(RA), MipsRegClass::GPR);
assert_eq!(MipsRegisterInfo::get_reg_class(HI), MipsRegClass::Special);
assert_eq!(MipsRegisterInfo::get_reg_class(LO), MipsRegClass::Special);
assert_eq!(MipsRegisterInfo::get_reg_class(F0), MipsRegClass::FPR32);
assert_eq!(MipsRegisterInfo::get_reg_class(F31), MipsRegClass::FPR32);
}
#[test]
fn test_get_reg_width() {
assert_eq!(MipsRegisterInfo::get_reg_width(V0, false), 32);
assert_eq!(MipsRegisterInfo::get_reg_width(V0, true), 64);
assert_eq!(MipsRegisterInfo::get_reg_width(F0, false), 32);
assert_eq!(MipsRegisterInfo::get_reg_width(HI, true), 64);
}
#[test]
fn test_get_dwarf_num() {
assert_eq!(MipsRegisterInfo::get_dwarf_num(ZERO), 0);
assert_eq!(MipsRegisterInfo::get_dwarf_num(AT), 1);
assert_eq!(MipsRegisterInfo::get_dwarf_num(RA), 31);
assert_eq!(MipsRegisterInfo::get_dwarf_num(F0), 32);
assert_eq!(MipsRegisterInfo::get_dwarf_num(F31), 63);
assert_eq!(MipsRegisterInfo::get_dwarf_num(HI), -1);
}
#[test]
fn test_is_callee_saved() {
assert!(MipsRegisterInfo::is_callee_saved(S0));
assert!(MipsRegisterInfo::is_callee_saved(S7));
assert!(MipsRegisterInfo::is_callee_saved(FP));
assert!(MipsRegisterInfo::is_callee_saved(RA));
assert!(MipsRegisterInfo::is_callee_saved(F20));
assert!(!MipsRegisterInfo::is_callee_saved(T0));
assert!(!MipsRegisterInfo::is_callee_saved(A0));
}
#[test]
fn test_is_caller_saved() {
assert!(MipsRegisterInfo::is_caller_saved(T0));
assert!(MipsRegisterInfo::is_caller_saved(A0));
assert!(MipsRegisterInfo::is_caller_saved(V0));
assert!(MipsRegisterInfo::is_caller_saved(F0));
assert!(!MipsRegisterInfo::is_caller_saved(S0));
}
#[test]
fn test_is_reserved() {
assert!(MipsRegisterInfo::is_reserved(ZERO));
assert!(MipsRegisterInfo::is_reserved(K0));
assert!(MipsRegisterInfo::is_reserved(K1));
assert!(!MipsRegisterInfo::is_reserved(V0));
assert!(!MipsRegisterInfo::is_reserved(SP));
}
#[test]
fn test_get_allocatable_gprs() {
let regs = MipsRegisterInfo::get_allocatable_gprs();
assert!(regs.len() >= 25);
assert!(!regs.contains(&ZERO));
assert!(!regs.contains(&AT));
assert!(!regs.contains(&K0));
assert!(!regs.contains(&K1));
}
#[test]
fn test_get_allocatable_fprs() {
let regs = MipsRegisterInfo::get_allocatable_fprs();
assert_eq!(regs.len(), 32);
}
#[test]
fn test_get_argument_regs() {
let regs = MipsRegisterInfo::get_argument_regs();
assert_eq!(regs, vec![A0, A1, A2, A3]);
}
#[test]
fn test_get_fp_argument_regs() {
let regs = MipsRegisterInfo::get_fp_argument_regs(false);
assert_eq!(regs.len(), 8);
assert!(regs.contains(&F12));
assert!(regs.contains(&F19));
}
#[test]
fn test_get_return_regs() {
let regs = MipsRegisterInfo::get_return_regs();
assert_eq!(regs, vec![V0, V1]);
}
#[test]
fn test_get_fp_return_regs() {
let regs = MipsRegisterInfo::get_fp_return_regs();
assert_eq!(regs, vec![F0, F1]);
}
#[test]
fn test_special_regs() {
assert_eq!(MipsRegisterInfo::get_frame_pointer_reg(), FP);
assert_eq!(MipsRegisterInfo::get_return_address_reg(), RA);
assert_eq!(MipsRegisterInfo::get_stack_pointer_reg(), SP);
assert_eq!(MipsRegisterInfo::get_global_pointer_reg(), GP);
assert_eq!(MipsRegisterInfo::get_zero_reg(), ZERO);
}
#[test]
fn test_is_gpr_and_fpr() {
assert!(MipsRegisterInfo::is_gpr(ZERO));
assert!(MipsRegisterInfo::is_gpr(RA));
assert!(!MipsRegisterInfo::is_gpr(F0));
assert!(!MipsRegisterInfo::is_gpr(HI));
assert!(MipsRegisterInfo::is_fpr(F0));
assert!(MipsRegisterInfo::is_fpr(F31));
assert!(!MipsRegisterInfo::is_fpr(ZERO));
}
#[test]
fn test_get_reg_index() {
assert_eq!(MipsRegisterInfo::get_reg_index(ZERO), Some(0));
assert_eq!(MipsRegisterInfo::get_reg_index(AT), Some(1));
assert_eq!(MipsRegisterInfo::get_reg_index(RA), Some(31));
assert_eq!(MipsRegisterInfo::get_reg_index(F0), Some(0));
assert_eq!(MipsRegisterInfo::get_reg_index(F31), Some(31));
assert_eq!(MipsRegisterInfo::get_reg_index(HI), None);
}
#[test]
fn test_can_be_base_reg() {
assert!(!MipsRegisterInfo::can_be_base_reg(ZERO));
assert!(MipsRegisterInfo::can_be_base_reg(SP));
assert!(MipsRegisterInfo::can_be_base_reg(T0));
assert!(!MipsRegisterInfo::can_be_base_reg(F0));
}
#[test]
fn test_reg_class_display() {
assert_eq!(MipsRegClass::GPR.to_string(), "GPR");
assert_eq!(MipsRegClass::FPR32.to_string(), "FPR32");
assert_eq!(MipsRegClass::FPR64.to_string(), "FPR64");
assert_eq!(MipsRegClass::Special.to_string(), "Special");
}
#[test]
fn test_caller_saved_gprs_count() {
let regs = MipsRegisterInfo::get_caller_saved_gprs();
assert!(regs.len() >= 13); }
#[test]
fn test_callee_saved_gprs_count() {
let regs = MipsRegisterInfo::get_callee_saved_gprs();
assert!(regs.len() >= 8); assert!(regs.contains(&S0));
assert!(regs.contains(&S7));
}
#[test]
fn test_caller_saved_fprs_count() {
let regs = MipsRegisterInfo::get_caller_saved_fprs();
assert_eq!(regs.len(), 20); }
#[test]
fn test_callee_saved_fprs_count() {
let regs = MipsRegisterInfo::get_callee_saved_fprs();
assert_eq!(regs.len(), 12); }
#[test]
fn test_all_gpr_ids_in_range() {
for i in 0..32 {
let r = MIPS_GPR_BASE + i as u16;
assert!(r >= MIPS_GPR_BASE && r < MIPS_GPR_BASE + 32);
}
}
#[test]
fn test_all_fpr_ids_in_range() {
for i in 0..32 {
let r = MIPS_FPR_BASE + i as u16;
assert!(r >= MIPS_FPR_BASE && r < MIPS_FPR_BASE + 32);
}
}
}