pub const G0: u16 = 6000;
pub const G1: u16 = 6001;
pub const G2: u16 = 6002;
pub const G3: u16 = 6003;
pub const G4: u16 = 6004;
pub const G5: u16 = 6005;
pub const G6: u16 = 6006;
pub const G7: u16 = 6007;
pub const O0: u16 = 6008;
pub const O1: u16 = 6009;
pub const O2: u16 = 6010;
pub const O3: u16 = 6011;
pub const O4: u16 = 6012;
pub const O5: u16 = 6013;
pub const O6: u16 = 6014; pub const O7: u16 = 6015;
pub const L0: u16 = 6016;
pub const L1: u16 = 6017;
pub const L2: u16 = 6018;
pub const L3: u16 = 6019;
pub const L4: u16 = 6020;
pub const L5: u16 = 6021;
pub const L6: u16 = 6022;
pub const L7: u16 = 6023;
pub const I0: u16 = 6024;
pub const I1: u16 = 6025;
pub const I2: u16 = 6026;
pub const I3: u16 = 6027;
pub const I4: u16 = 6028;
pub const I5: u16 = 6029;
pub const I6: u16 = 6030; pub const I7: u16 = 6031;
pub const SP: u16 = O6;
pub const FP: u16 = I6;
pub const F0: u16 = 6032;
pub const F1: u16 = 6033;
pub const F2: u16 = 6034;
pub const F3: u16 = 6035;
pub const F4: u16 = 6036;
pub const F5: u16 = 6037;
pub const F6: u16 = 6038;
pub const F7: u16 = 6039;
pub const F8: u16 = 6040;
pub const F9: u16 = 6041;
pub const F10: u16 = 6042;
pub const F11: u16 = 6043;
pub const F12: u16 = 6044;
pub const F13: u16 = 6045;
pub const F14: u16 = 6046;
pub const F15: u16 = 6047;
pub const F16: u16 = 6048;
pub const F17: u16 = 6049;
pub const F18: u16 = 6050;
pub const F19: u16 = 6051;
pub const F20: u16 = 6052;
pub const F21: u16 = 6053;
pub const F22: u16 = 6054;
pub const F23: u16 = 6055;
pub const F24: u16 = 6056;
pub const F25: u16 = 6057;
pub const F26: u16 = 6058;
pub const F27: u16 = 6059;
pub const F28: u16 = 6060;
pub const F29: u16 = 6061;
pub const F30: u16 = 6062;
pub const F31: u16 = 6063;
pub const FSR: u16 = 6064;
pub const Y: u16 = 6065;
pub const CCR: u16 = 6066;
pub const SPARC_GPR_COUNT: usize = 32;
pub const SPARC_FPR_COUNT: usize = 32;
pub const SPARC_MAX_REG_ID: u16 = 6066;
pub const SPARC_GPR_BASE: u16 = 6000;
pub const SPARC_FPR_BASE: u16 = 6032;
pub const SPARC_SPECIAL_BASE: u16 = 6064;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SparcRegClass {
GPR,
FPR32,
FPR64,
Special,
}
impl std::fmt::Display for SparcRegClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SparcRegClass::GPR => write!(f, "GPR"),
SparcRegClass::FPR32 => write!(f, "FPR32"),
SparcRegClass::FPR64 => write!(f, "FPR64"),
SparcRegClass::Special => write!(f, "Special"),
}
}
}
pub struct SparcRegisterInfo;
impl SparcRegisterInfo {
pub fn get_asm_name(reg_id: u16) -> String {
match reg_id {
G0 => "%g0".into(),
G1 => "%g1".into(),
G2 => "%g2".into(),
G3 => "%g3".into(),
G4 => "%g4".into(),
G5 => "%g5".into(),
G6 => "%g6".into(),
G7 => "%g7".into(),
O0 => "%o0".into(),
O1 => "%o1".into(),
O2 => "%o2".into(),
O3 => "%o3".into(),
O4 => "%o4".into(),
O5 => "%o5".into(),
O6 => "%sp".into(),
O7 => "%o7".into(),
L0 => "%l0".into(),
L1 => "%l1".into(),
L2 => "%l2".into(),
L3 => "%l3".into(),
L4 => "%l4".into(),
L5 => "%l5".into(),
L6 => "%l6".into(),
L7 => "%l7".into(),
I0 => "%i0".into(),
I1 => "%i1".into(),
I2 => "%i2".into(),
I3 => "%i3".into(),
I4 => "%i4".into(),
I5 => "%i5".into(),
I6 => "%fp".into(),
I7 => "%i7".into(),
FSR => "%fsr".into(),
Y => "%y".into(),
CCR => "%ccr".into(),
_ if reg_id >= F0 && reg_id <= F31 => {
format!("%f{}", reg_id - F0)
}
_ => format!("%r{}", 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) -> SparcRegClass {
if reg_id >= SPARC_GPR_BASE && reg_id < SPARC_GPR_BASE + 32 {
SparcRegClass::GPR
} else if reg_id >= SPARC_SPECIAL_BASE && reg_id <= CCR {
SparcRegClass::Special
} else if reg_id >= SPARC_FPR_BASE && reg_id < SPARC_FPR_BASE + 32 {
SparcRegClass::FPR32
} else {
SparcRegClass::GPR
}
}
pub fn get_reg_width(reg_id: u16, is_64bit: bool) -> u32 {
match Self::get_reg_class(reg_id) {
SparcRegClass::GPR => {
if is_64bit {
64
} else {
32
}
}
SparcRegClass::FPR32 => 32,
SparcRegClass::FPR64 => 64,
SparcRegClass::Special => 32,
}
}
pub fn get_dwarf_num(reg_id: u16) -> i32 {
match reg_id {
_ if reg_id >= G0 && reg_id <= I7 => (reg_id - G0) as i32,
_ if reg_id >= F0 && reg_id <= F31 => 32 + (reg_id - F0) as i32,
FSR => -1,
Y => -1,
CCR => -1,
_ => -1,
}
}
pub fn is_callee_saved(reg_id: u16) -> bool {
matches!(
reg_id,
L0 | L1
| L2
| L3
| L4
| L5
| L6
| L7
| I0
| I1
| I2
| I3
| I4
| I5
| I6
| I7
| SP
| FP
| G1
| G2
| G3
| G4
| G5
| G6
| G7
) || (reg_id >= F20 && reg_id <= F31)
}
pub fn is_caller_saved(reg_id: u16) -> bool {
matches!(reg_id, G0 | O0 | O1 | O2 | O3 | O4 | O5 | O7) || (reg_id >= F0 && reg_id <= F19)
}
pub fn is_reserved(reg_id: u16) -> bool {
reg_id == G0 || reg_id == G5 || reg_id == G6 || reg_id == G7
}
pub fn get_allocatable_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 0..32 {
let r = SPARC_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![O0, O1, O2, O3, O4, O5]
}
pub fn get_fp_argument_regs(_is_64bit: bool) -> Vec<u16> {
vec![F0, F1, F2, F3, F4, F5, F6, F7]
}
pub fn get_return_regs() -> Vec<u16> {
vec![O0, O1]
}
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 {
I7
}
pub fn get_stack_pointer_reg() -> u16 {
SP
}
pub fn get_zero_reg() -> u16 {
G0
}
pub fn is_gpr(reg_id: u16) -> bool {
Self::get_reg_class(reg_id) == SparcRegClass::GPR
}
pub fn is_fpr(reg_id: u16) -> bool {
matches!(
Self::get_reg_class(reg_id),
SparcRegClass::FPR32 | SparcRegClass::FPR64
)
}
pub fn get_reg_index(reg_id: u16) -> u8 {
if reg_id >= SPARC_GPR_BASE && reg_id < SPARC_GPR_BASE + 32 {
(reg_id - SPARC_GPR_BASE) as u8
} else if reg_id >= SPARC_FPR_BASE && reg_id < SPARC_FPR_BASE + 32 {
(reg_id - SPARC_FPR_BASE) as u8
} else {
0
}
}
pub fn can_be_base_reg(reg_id: u16) -> bool {
Self::is_gpr(reg_id) && reg_id != G0
}
pub fn get_caller_saved_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 0..32 {
let r = SPARC_GPR_BASE + i as u16;
if Self::is_caller_saved(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 = SPARC_GPR_BASE + i as u16;
if Self::is_callee_saved(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!(SPARC_GPR_COUNT, 32);
assert_eq!(SPARC_FPR_COUNT, 32);
assert_eq!(SPARC_MAX_REG_ID, 6066);
}
#[test]
fn test_register_ids_unique() {
let gprs = vec![
G0, G1, G2, G3, G4, G5, G6, G7, O0, O1, O2, O3, O4, O5, O6, O7, L0, L1, L2, L3, L4, L5,
L6, L7, I0, I1, I2, I3, I4, I5, I6, I7,
];
let mut seen = std::collections::HashSet::new();
for r in &gprs {
assert!(seen.insert(*r), "Duplicate GPR ID: {}", r);
}
}
#[test]
fn test_fpr_ids_unique() {
let mut seen = std::collections::HashSet::new();
for i in 0..32 {
let r = F0 + i;
assert!(seen.insert(r), "Duplicate FPR ID: {}", r);
}
}
#[test]
fn test_abi_names() {
assert_eq!(SparcRegisterInfo::get_asm_name(G0), "%g0");
assert_eq!(SparcRegisterInfo::get_asm_name(O0), "%o0");
assert_eq!(SparcRegisterInfo::get_asm_name(O6), "%sp");
assert_eq!(SparcRegisterInfo::get_asm_name(L0), "%l0");
assert_eq!(SparcRegisterInfo::get_asm_name(I0), "%i0");
assert_eq!(SparcRegisterInfo::get_asm_name(I6), "%fp");
}
#[test]
fn test_get_reg_class() {
assert_eq!(SparcRegisterInfo::get_reg_class(G0), SparcRegClass::GPR);
assert_eq!(SparcRegisterInfo::get_reg_class(O0), SparcRegClass::GPR);
assert_eq!(SparcRegisterInfo::get_reg_class(F0), SparcRegClass::FPR32);
assert_eq!(
SparcRegisterInfo::get_reg_class(FSR),
SparcRegClass::Special
);
assert_eq!(SparcRegisterInfo::get_reg_class(Y), SparcRegClass::Special);
assert_eq!(
SparcRegisterInfo::get_reg_class(CCR),
SparcRegClass::Special
);
}
#[test]
fn test_get_reg_width() {
assert_eq!(SparcRegisterInfo::get_reg_width(G0, false), 32);
assert_eq!(SparcRegisterInfo::get_reg_width(G0, true), 64);
assert_eq!(SparcRegisterInfo::get_reg_width(F0, false), 32);
}
#[test]
fn test_get_dwarf_num() {
assert_eq!(SparcRegisterInfo::get_dwarf_num(G0), 0);
assert_eq!(SparcRegisterInfo::get_dwarf_num(O0), 8);
assert_eq!(SparcRegisterInfo::get_dwarf_num(F0), 32);
assert_eq!(SparcRegisterInfo::get_dwarf_num(F31), 63);
}
#[test]
fn test_is_callee_saved() {
assert!(SparcRegisterInfo::is_callee_saved(L0));
assert!(SparcRegisterInfo::is_callee_saved(FP));
assert!(!SparcRegisterInfo::is_callee_saved(O0));
assert!(!SparcRegisterInfo::is_callee_saved(G0));
}
#[test]
fn test_is_caller_saved() {
assert!(SparcRegisterInfo::is_caller_saved(O0));
assert!(SparcRegisterInfo::is_caller_saved(O7));
assert!(!SparcRegisterInfo::is_caller_saved(L0));
assert!(!SparcRegisterInfo::is_caller_saved(FP));
}
#[test]
fn test_is_reserved() {
assert!(SparcRegisterInfo::is_reserved(G0));
assert!(SparcRegisterInfo::is_reserved(G5));
assert!(SparcRegisterInfo::is_reserved(G6));
assert!(SparcRegisterInfo::is_reserved(G7));
assert!(!SparcRegisterInfo::is_reserved(G1));
assert!(!SparcRegisterInfo::is_reserved(O0));
}
#[test]
fn test_get_allocatable_gprs() {
let regs = SparcRegisterInfo::get_allocatable_gprs();
assert!(!regs.contains(&G0));
assert!(regs.contains(&G1));
assert!(regs.contains(&O0));
}
#[test]
fn test_get_allocatable_fprs() {
let regs = SparcRegisterInfo::get_allocatable_fprs();
assert_eq!(regs.len(), 32);
assert!(regs.contains(&F0));
assert!(regs.contains(&F31));
}
#[test]
fn test_get_argument_regs() {
let regs = SparcRegisterInfo::get_argument_regs();
assert_eq!(regs, vec![O0, O1, O2, O3, O4, O5]);
}
#[test]
fn test_get_return_regs() {
let regs = SparcRegisterInfo::get_return_regs();
assert_eq!(regs, vec![O0, O1]);
}
#[test]
fn test_special_regs() {
assert_eq!(SparcRegisterInfo::get_asm_name(FSR), "%fsr");
assert_eq!(SparcRegisterInfo::get_asm_name(Y), "%y");
assert_eq!(SparcRegisterInfo::get_asm_name(CCR), "%ccr");
}
#[test]
fn test_is_gpr_and_fpr() {
assert!(SparcRegisterInfo::is_gpr(G0));
assert!(SparcRegisterInfo::is_gpr(I7));
assert!(!SparcRegisterInfo::is_gpr(F0));
assert!(SparcRegisterInfo::is_fpr(F0));
assert!(!SparcRegisterInfo::is_fpr(G0));
}
#[test]
fn test_get_reg_index() {
assert_eq!(SparcRegisterInfo::get_reg_index(G0), 0);
assert_eq!(SparcRegisterInfo::get_reg_index(O0), 8);
assert_eq!(SparcRegisterInfo::get_reg_index(I7), 31);
assert_eq!(SparcRegisterInfo::get_reg_index(F0), 0);
assert_eq!(SparcRegisterInfo::get_reg_index(F31), 31);
}
#[test]
fn test_can_be_base_reg() {
assert!(!SparcRegisterInfo::can_be_base_reg(G0));
assert!(SparcRegisterInfo::can_be_base_reg(G1));
assert!(SparcRegisterInfo::can_be_base_reg(SP));
}
#[test]
fn test_reg_class_display() {
assert_eq!(format!("{}", SparcRegClass::GPR), "GPR");
assert_eq!(format!("{}", SparcRegClass::FPR32), "FPR32");
assert_eq!(format!("{}", SparcRegClass::FPR64), "FPR64");
assert_eq!(format!("{}", SparcRegClass::Special), "Special");
}
#[test]
fn test_caller_saved_gprs_count() {
let regs = SparcRegisterInfo::get_caller_saved_gprs();
assert!(regs.contains(&G0));
assert!(regs.contains(&O0));
assert!(regs.contains(&O7));
}
#[test]
fn test_callee_saved_gprs_count() {
let regs = SparcRegisterInfo::get_callee_saved_gprs();
assert!(regs.contains(&L0));
assert!(regs.contains(&FP));
assert!(regs.contains(&SP));
}
#[test]
fn test_caller_saved_fprs_count() {
let regs = SparcRegisterInfo::get_caller_saved_fprs();
assert_eq!(regs.len(), 20);
assert!(regs.contains(&F0));
assert!(regs.contains(&F19));
}
#[test]
fn test_callee_saved_fprs_count() {
let regs = SparcRegisterInfo::get_callee_saved_fprs();
assert_eq!(regs.len(), 12);
assert!(regs.contains(&F20));
assert!(regs.contains(&F31));
}
#[test]
fn test_all_gpr_ids_in_range() {
for i in 0..32 {
let r = SPARC_GPR_BASE + i;
assert!(r >= 6000 && r <= 6031);
}
}
#[test]
fn test_all_fpr_ids_in_range() {
for i in 0..32 {
let r = SPARC_FPR_BASE + i;
assert!(r >= 6032 && r <= 6063);
}
}
}