use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClassInfo {
pub name: &'static str,
pub bits: u32,
pub regs: &'static [&'static str],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RegClass(u8);
impl RegClass {
#[must_use]
pub const fn new(number: u8) -> Self {
Self(number)
}
#[must_use]
pub const fn number(self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PhysReg(u8);
impl PhysReg {
#[must_use]
pub const fn new(number: u8) -> Self {
Self(number)
}
#[must_use]
pub const fn number(self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegFile {
classes: &'static [ClassInfo],
}
impl RegFile {
#[must_use]
pub const fn new(classes: &'static [ClassInfo]) -> Self {
Self { classes }
}
pub fn classes(&self) -> impl Iterator<Item = (RegClass, &'static ClassInfo)> + use<> {
self.classes.iter().enumerate().map(|(number, info)| (RegClass::new(number as u8), info))
}
#[must_use]
pub fn class(&self, class: RegClass) -> Option<&'static ClassInfo> {
self.classes.get(usize::from(class.number()))
}
#[must_use]
pub fn class_named(&self, name: &str) -> Option<RegClass> {
self.classes().find(|(_, info)| info.name == name).map(|(class, _)| class)
}
#[must_use]
pub fn len(&self, class: RegClass) -> usize {
self.class(class).map_or(0, |info| info.regs.len())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.classes.is_empty()
}
#[must_use]
pub fn name(&self, class: RegClass, reg: PhysReg) -> Option<&'static str> {
self.class(class)?.regs.get(usize::from(reg.number())).copied()
}
#[must_use]
pub fn reg_named(&self, name: &str) -> Option<(RegClass, PhysReg)> {
for (class, info) in self.classes() {
if let Some(number) = info.regs.iter().position(|®| reg == name) {
return Some((class, PhysReg::new(number as u8)));
}
}
None
}
#[must_use]
pub fn duplicate(&self) -> Option<&'static str> {
let mut seen: Vec<&'static str> = Vec::new();
for (_, info) in self.classes() {
for ® in info.regs {
if seen.contains(®) {
return Some(reg);
}
seen.push(reg);
}
}
None
}
}
impl fmt::Display for RegFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (_, info) in self.classes() {
writeln!(f, "class {} : i{} = {}", info.name, info.bits, info.regs.join(", "))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
static GPR: [&str; 3] = ["rax", "rcx", "rdx"];
static XMM: [&str; 2] = ["xmm0", "xmm1"];
static CLASSES: [ClassInfo; 2] = [
ClassInfo { name: "gpr", bits: 64, regs: &GPR },
ClassInfo { name: "xmm", bits: 128, regs: &XMM },
];
static FILE: RegFile = RegFile::new(&CLASSES);
#[test]
fn a_class_is_found_by_its_name() {
let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
assert_eq!(FILE.len(gpr), 3);
assert_eq!(FILE.class(gpr).map(|info| info.bits), Some(64));
assert_eq!(FILE.class_named("vec"), None);
}
#[test]
fn a_register_is_found_by_its_name_and_names_itself_back() {
let (class, reg) = FILE.reg_named("xmm1").expect("the file has xmm1");
assert_eq!(FILE.class(class).map(|info| info.name), Some("xmm"));
assert_eq!(reg.number(), 1);
assert_eq!(FILE.name(class, reg), Some("xmm1"));
assert_eq!(FILE.reg_named("r15"), None);
}
#[test]
fn a_number_past_the_end_of_a_class_has_no_name() {
let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
assert_eq!(FILE.name(gpr, PhysReg::new(3)), None);
assert_eq!(FILE.name(RegClass::new(7), PhysReg::new(0)), None);
}
#[test]
fn a_file_that_names_two_registers_alike_says_so() {
assert_eq!(FILE.duplicate(), None);
static BOTH: [ClassInfo; 2] = [
ClassInfo { name: "gpr", bits: 64, regs: &GPR },
ClassInfo { name: "shadow", bits: 64, regs: &GPR },
];
assert_eq!(RegFile::new(&BOTH).duplicate(), Some("rax"));
}
#[test]
fn the_file_prints_one_class_to_a_line() {
assert_eq!(
FILE.to_string(),
"class gpr : i64 = rax, rcx, rdx\nclass xmm : i128 = xmm0, xmm1\n"
);
}
}