pub const R0: u16 = 5000;
pub const R1: u16 = 5001;
pub const SP: u16 = 5001;
pub const R2: u16 = 5002;
pub const TOC: u16 = 5002;
pub const R3: u16 = 5003;
pub const R4: u16 = 5004;
pub const R5: u16 = 5005;
pub const R6: u16 = 5006;
pub const R7: u16 = 5007;
pub const R8: u16 = 5008;
pub const R9: u16 = 5009;
pub const R10: u16 = 5010;
pub const R11: u16 = 5011;
pub const R12: u16 = 5012;
pub const R13: u16 = 5013;
pub const R14: u16 = 5014;
pub const R15: u16 = 5015;
pub const R16: u16 = 5016;
pub const R17: u16 = 5017;
pub const R18: u16 = 5018;
pub const R19: u16 = 5019;
pub const R20: u16 = 5020;
pub const R21: u16 = 5021;
pub const R22: u16 = 5022;
pub const R23: u16 = 5023;
pub const R24: u16 = 5024;
pub const R25: u16 = 5025;
pub const R26: u16 = 5026;
pub const R27: u16 = 5027;
pub const R28: u16 = 5028;
pub const R29: u16 = 5029;
pub const R30: u16 = 5030;
pub const R31: u16 = 5031;
pub const F0: u16 = 5050;
pub const F1: u16 = 5051;
pub const F2: u16 = 5052;
pub const F3: u16 = 5053;
pub const F4: u16 = 5054;
pub const F5: u16 = 5055;
pub const F6: u16 = 5056;
pub const F7: u16 = 5057;
pub const F8: u16 = 5058;
pub const F9: u16 = 5059;
pub const F10: u16 = 5060;
pub const F11: u16 = 5061;
pub const F12: u16 = 5062;
pub const F13: u16 = 5063;
pub const F14: u16 = 5064;
pub const F15: u16 = 5065;
pub const F16: u16 = 5066;
pub const F17: u16 = 5067;
pub const F18: u16 = 5068;
pub const F19: u16 = 5069;
pub const F20: u16 = 5070;
pub const F21: u16 = 5071;
pub const F22: u16 = 5072;
pub const F23: u16 = 5073;
pub const F24: u16 = 5074;
pub const F25: u16 = 5075;
pub const F26: u16 = 5076;
pub const F27: u16 = 5077;
pub const F28: u16 = 5078;
pub const F29: u16 = 5079;
pub const F30: u16 = 5080;
pub const F31: u16 = 5081;
pub const LR: u16 = 5100;
pub const CTR: u16 = 5101;
pub const XER: u16 = 5102;
pub const CR0: u16 = 5110;
pub const CR1: u16 = 5111;
pub const CR2: u16 = 5112;
pub const CR3: u16 = 5113;
pub const CR4: u16 = 5114;
pub const CR5: u16 = 5115;
pub const CR6: u16 = 5116;
pub const CR7: u16 = 5117;
pub const VSCR: u16 = 5118;
pub const VRSAVE: u16 = 5119;
pub const V0: u16 = 5150;
pub const V1: u16 = 5151;
pub const V2: u16 = 5152;
pub const V3: u16 = 5153;
pub const V4: u16 = 5154;
pub const V5: u16 = 5155;
pub const V6: u16 = 5156;
pub const V7: u16 = 5157;
pub const V8: u16 = 5158;
pub const V9: u16 = 5159;
pub const V10: u16 = 5160;
pub const V11: u16 = 5161;
pub const V12: u16 = 5162;
pub const V13: u16 = 5163;
pub const V14: u16 = 5164;
pub const V15: u16 = 5165;
pub const V16: u16 = 5166;
pub const V17: u16 = 5167;
pub const V18: u16 = 5168;
pub const V19: u16 = 5169;
pub const V20: u16 = 5170;
pub const V21: u16 = 5171;
pub const V22: u16 = 5172;
pub const V23: u16 = 5173;
pub const V24: u16 = 5174;
pub const V25: u16 = 5175;
pub const V26: u16 = 5176;
pub const V27: u16 = 5177;
pub const V28: u16 = 5178;
pub const V29: u16 = 5179;
pub const V30: u16 = 5180;
pub const V31: u16 = 5181;
pub const VS0: u16 = 5200;
pub const VS32: u16 = 5232;
pub const VS63: u16 = 5263;
pub const PPC_GPR_COUNT: usize = 32;
pub const PPC_FPR_COUNT: usize = 32;
pub const PPC_VR_COUNT: usize = 32;
pub const PPC_VSR_COUNT: usize = 64;
pub const PPC_MAX_REG_ID: u16 = 5263;
pub const PPC_GPR_BASE: u16 = 5000;
pub const PPC_FPR_BASE: u16 = 5050;
pub const PPC_SPECIAL_BASE: u16 = 5100;
pub const PPC_VR_BASE: u16 = 5150;
pub const PPC_VSR_BASE: u16 = 5200;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PpcRegClass {
GPR,
FPR,
VR,
VSR,
CR,
Special,
}
impl std::fmt::Display for PpcRegClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PpcRegClass::GPR => write!(f, "GPR"),
PpcRegClass::FPR => write!(f, "FPR"),
PpcRegClass::VR => write!(f, "VR"),
PpcRegClass::VSR => write!(f, "VSR"),
PpcRegClass::CR => write!(f, "CR"),
PpcRegClass::Special => write!(f, "Special"),
}
}
}
pub struct PpcRegisterInfo;
impl PpcRegisterInfo {
pub fn get_asm_name(reg_id: u16) -> String {
if reg_id >= PPC_GPR_BASE && reg_id < PPC_GPR_BASE + 32 {
let idx = reg_id - PPC_GPR_BASE;
format!("r{}", idx)
} else if reg_id >= PPC_FPR_BASE && reg_id < PPC_FPR_BASE + 32 {
let idx = reg_id - PPC_FPR_BASE;
format!("f{}", idx)
} else if reg_id >= PPC_VR_BASE && reg_id < PPC_VR_BASE + 32 {
let idx = reg_id - PPC_VR_BASE;
format!("v{}", idx)
} else if reg_id >= PPC_VSR_BASE && reg_id < PPC_VSR_BASE + 64 {
let idx = reg_id - PPC_VSR_BASE;
format!("vs{}", idx)
} else {
match reg_id {
LR => "lr".into(),
CTR => "ctr".into(),
XER => "xer".into(),
CR0 => "cr0".into(),
CR1 => "cr1".into(),
CR2 => "cr2".into(),
CR3 => "cr3".into(),
CR4 => "cr4".into(),
CR5 => "cr5".into(),
CR6 => "cr6".into(),
CR7 => "cr7".into(),
_ => format!("r{}", reg_id),
}
}
}
pub fn get_reg_class(reg_id: u16) -> PpcRegClass {
if reg_id >= PPC_GPR_BASE && reg_id < PPC_GPR_BASE + 32 {
PpcRegClass::GPR
} else if reg_id >= PPC_FPR_BASE && reg_id < PPC_FPR_BASE + 32 {
PpcRegClass::FPR
} else if reg_id >= PPC_VR_BASE && reg_id < PPC_VR_BASE + 32 {
PpcRegClass::VR
} else if reg_id >= PPC_VSR_BASE && reg_id < PPC_VSR_BASE + 64 {
PpcRegClass::VSR
} else if reg_id >= CR0 && reg_id <= CR7 {
PpcRegClass::CR
} else {
PpcRegClass::Special
}
}
pub fn get_reg_width(reg_id: u16, is_64bit: bool) -> u32 {
match Self::get_reg_class(reg_id) {
PpcRegClass::GPR => {
if is_64bit {
64
} else {
32
}
}
PpcRegClass::FPR => 64,
PpcRegClass::VR => 128,
PpcRegClass::VSR => 128,
PpcRegClass::CR => 32,
PpcRegClass::Special => {
if is_64bit {
64
} else {
32
}
}
}
}
pub fn is_callee_saved(reg_id: u16) -> bool {
matches!(
reg_id,
R14 | R15
| R16
| R17
| R18
| R19
| R20
| R21
| R22
| R23
| R24
| R25
| R26
| R27
| R28
| R29
| R30
| R31
) || (reg_id >= F14 && reg_id <= F31)
}
pub fn is_caller_saved(reg_id: u16) -> bool {
matches!(
reg_id,
R0 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | R12
) || (reg_id >= F0 && reg_id <= F13)
}
pub fn is_reserved(reg_id: u16) -> bool {
reg_id == R0 || reg_id == R2 || reg_id == R13
}
pub fn get_allocatable_gprs() -> Vec<u16> {
let mut regs = Vec::new();
for i in 1..32 {
let r = PPC_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![R3, R4, R5, R6, R7, R8, R9, R10]
}
pub fn get_return_regs() -> Vec<u16> {
vec![R3]
}
pub fn get_frame_pointer_reg() -> u16 {
R31
}
pub fn get_return_address_reg() -> u16 {
LR
}
pub fn get_stack_pointer_reg() -> u16 {
SP
}
pub fn get_toc_register() -> u16 {
TOC
}
pub fn is_gpr(reg_id: u16) -> bool {
reg_id >= PPC_GPR_BASE && reg_id < PPC_GPR_BASE + 32
}
pub fn is_fpr(reg_id: u16) -> bool {
reg_id >= PPC_FPR_BASE && reg_id < PPC_FPR_BASE + 32
}
pub fn get_reg_index(reg_id: u16) -> Option<u8> {
if Self::is_gpr(reg_id) {
Some((reg_id - PPC_GPR_BASE) as u8)
} else if Self::is_fpr(reg_id) {
Some((reg_id - PPC_FPR_BASE) as u8)
} else if reg_id >= PPC_VR_BASE && reg_id < PPC_VR_BASE + 32 {
Some((reg_id - PPC_VR_BASE) as u8)
} else {
None
}
}
pub fn can_be_base_reg(reg_id: u16) -> bool {
Self::is_gpr(reg_id) && reg_id != R0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_count_constants() {
assert_eq!(PPC_GPR_COUNT, 32);
assert_eq!(PPC_FPR_COUNT, 32);
assert_eq!(PPC_VR_COUNT, 32);
}
#[test]
fn test_gpr_names() {
assert_eq!(PpcRegisterInfo::get_asm_name(R0), "r0");
assert_eq!(PpcRegisterInfo::get_asm_name(SP), "r1");
assert_eq!(PpcRegisterInfo::get_asm_name(R3), "r3");
assert_eq!(PpcRegisterInfo::get_asm_name(R31), "r31");
}
#[test]
fn test_fpr_names() {
assert_eq!(PpcRegisterInfo::get_asm_name(F0), "f0");
assert_eq!(PpcRegisterInfo::get_asm_name(F31), "f31");
}
#[test]
fn test_special_reg_names() {
assert_eq!(PpcRegisterInfo::get_asm_name(LR), "lr");
assert_eq!(PpcRegisterInfo::get_asm_name(CTR), "ctr");
assert_eq!(PpcRegisterInfo::get_asm_name(CR0), "cr0");
}
#[test]
fn test_get_reg_class() {
assert_eq!(PpcRegisterInfo::get_reg_class(R3), PpcRegClass::GPR);
assert_eq!(PpcRegisterInfo::get_reg_class(F0), PpcRegClass::FPR);
assert_eq!(PpcRegisterInfo::get_reg_class(LR), PpcRegClass::Special);
assert_eq!(PpcRegisterInfo::get_reg_class(CR0), PpcRegClass::CR);
assert_eq!(PpcRegisterInfo::get_reg_class(V0), PpcRegClass::VR);
}
#[test]
fn test_get_reg_width() {
assert_eq!(PpcRegisterInfo::get_reg_width(R3, false), 32);
assert_eq!(PpcRegisterInfo::get_reg_width(R3, true), 64);
assert_eq!(PpcRegisterInfo::get_reg_width(F0, false), 64);
assert_eq!(PpcRegisterInfo::get_reg_width(V0, false), 128);
}
#[test]
fn test_is_callee_saved() {
assert!(PpcRegisterInfo::is_callee_saved(R14));
assert!(PpcRegisterInfo::is_callee_saved(R31));
assert!(PpcRegisterInfo::is_callee_saved(F14));
assert!(!PpcRegisterInfo::is_callee_saved(R3));
}
#[test]
fn test_is_caller_saved() {
assert!(PpcRegisterInfo::is_caller_saved(R3));
assert!(PpcRegisterInfo::is_caller_saved(R12));
assert!(PpcRegisterInfo::is_caller_saved(F0));
assert!(!PpcRegisterInfo::is_caller_saved(R14));
}
#[test]
fn test_is_reserved() {
assert!(PpcRegisterInfo::is_reserved(R0));
assert!(PpcRegisterInfo::is_reserved(R2));
assert!(PpcRegisterInfo::is_reserved(R13));
assert!(!PpcRegisterInfo::is_reserved(R3));
}
#[test]
fn test_get_argument_regs() {
let regs = PpcRegisterInfo::get_argument_regs();
assert_eq!(regs.len(), 8);
assert_eq!(regs[0], R3);
assert_eq!(regs[7], R10);
}
#[test]
fn test_special_regs() {
assert_eq!(PpcRegisterInfo::get_frame_pointer_reg(), R31);
assert_eq!(PpcRegisterInfo::get_return_address_reg(), LR);
assert_eq!(PpcRegisterInfo::get_stack_pointer_reg(), SP);
assert_eq!(PpcRegisterInfo::get_toc_register(), TOC);
}
#[test]
fn test_is_gpr_and_fpr() {
assert!(PpcRegisterInfo::is_gpr(R0));
assert!(PpcRegisterInfo::is_gpr(R31));
assert!(!PpcRegisterInfo::is_gpr(F0));
assert!(PpcRegisterInfo::is_fpr(F0));
assert!(PpcRegisterInfo::is_fpr(F31));
assert!(!PpcRegisterInfo::is_fpr(R0));
}
#[test]
fn test_get_reg_index() {
assert_eq!(PpcRegisterInfo::get_reg_index(R0), Some(0));
assert_eq!(PpcRegisterInfo::get_reg_index(R31), Some(31));
assert_eq!(PpcRegisterInfo::get_reg_index(F0), Some(0));
assert_eq!(PpcRegisterInfo::get_reg_index(V0), Some(0));
assert_eq!(PpcRegisterInfo::get_reg_index(LR), None);
}
#[test]
fn test_can_be_base_reg() {
assert!(!PpcRegisterInfo::can_be_base_reg(R0));
assert!(PpcRegisterInfo::can_be_base_reg(SP));
assert!(PpcRegisterInfo::can_be_base_reg(R3));
assert!(!PpcRegisterInfo::can_be_base_reg(F0));
}
#[test]
fn test_reg_class_display() {
assert_eq!(PpcRegClass::GPR.to_string(), "GPR");
assert_eq!(PpcRegClass::FPR.to_string(), "FPR");
assert_eq!(PpcRegClass::VR.to_string(), "VR");
assert_eq!(PpcRegClass::Special.to_string(), "Special");
}
#[test]
fn test_vr_names() {
assert_eq!(PpcRegisterInfo::get_asm_name(V0), "v0");
assert_eq!(PpcRegisterInfo::get_asm_name(V31), "v31");
}
#[test]
fn test_vsr_names() {
assert_eq!(PpcRegisterInfo::get_asm_name(VS0), "vs0");
assert_eq!(PpcRegisterInfo::get_asm_name(VS32), "vs32");
}
}