1use std::fmt;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct ClassInfo {
27 pub name: &'static str,
29 pub bits: u32,
31 pub regs: &'static [&'static str],
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub struct RegClass(u8);
41
42impl RegClass {
43 #[must_use]
45 pub const fn new(number: u8) -> Self {
46 Self(number)
47 }
48
49 #[must_use]
51 pub const fn number(self) -> u8 {
52 self.0
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub struct PhysReg(u8);
63
64impl PhysReg {
65 #[must_use]
67 pub const fn new(number: u8) -> Self {
68 Self(number)
69 }
70
71 #[must_use]
73 pub const fn number(self) -> u8 {
74 self.0
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct RegFile {
81 classes: &'static [ClassInfo],
82}
83
84impl RegFile {
85 #[must_use]
87 pub const fn new(classes: &'static [ClassInfo]) -> Self {
88 Self { classes }
89 }
90
91 pub fn classes(&self) -> impl Iterator<Item = (RegClass, &'static ClassInfo)> + use<> {
93 self.classes.iter().enumerate().map(|(number, info)| (RegClass::new(number as u8), info))
94 }
95
96 #[must_use]
98 pub fn class(&self, class: RegClass) -> Option<&'static ClassInfo> {
99 self.classes.get(usize::from(class.number()))
100 }
101
102 #[must_use]
104 pub fn class_named(&self, name: &str) -> Option<RegClass> {
105 self.classes().find(|(_, info)| info.name == name).map(|(class, _)| class)
106 }
107
108 #[must_use]
110 pub fn len(&self, class: RegClass) -> usize {
111 self.class(class).map_or(0, |info| info.regs.len())
112 }
113
114 #[must_use]
116 pub fn is_empty(&self) -> bool {
117 self.classes.is_empty()
118 }
119
120 #[must_use]
122 pub fn name(&self, class: RegClass, reg: PhysReg) -> Option<&'static str> {
123 self.class(class)?.regs.get(usize::from(reg.number())).copied()
124 }
125
126 #[must_use]
130 pub fn reg_named(&self, name: &str) -> Option<(RegClass, PhysReg)> {
131 for (class, info) in self.classes() {
132 if let Some(number) = info.regs.iter().position(|®| reg == name) {
133 return Some((class, PhysReg::new(number as u8)));
134 }
135 }
136 None
137 }
138
139 #[must_use]
145 pub fn duplicate(&self) -> Option<&'static str> {
146 let mut seen: Vec<&'static str> = Vec::new();
147 for (_, info) in self.classes() {
148 for ® in info.regs {
149 if seen.contains(®) {
150 return Some(reg);
151 }
152 seen.push(reg);
153 }
154 }
155 None
156 }
157}
158
159impl fmt::Display for RegFile {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 for (_, info) in self.classes() {
163 writeln!(f, "class {} : i{} = {}", info.name, info.bits, info.regs.join(", "))?;
164 }
165 Ok(())
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 static GPR: [&str; 3] = ["rax", "rcx", "rdx"];
174 static XMM: [&str; 2] = ["xmm0", "xmm1"];
175 static CLASSES: [ClassInfo; 2] = [
176 ClassInfo { name: "gpr", bits: 64, regs: &GPR },
177 ClassInfo { name: "xmm", bits: 128, regs: &XMM },
178 ];
179 static FILE: RegFile = RegFile::new(&CLASSES);
180
181 #[test]
182 fn a_class_is_found_by_its_name() {
183 let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
184 assert_eq!(FILE.len(gpr), 3);
185 assert_eq!(FILE.class(gpr).map(|info| info.bits), Some(64));
186 assert_eq!(FILE.class_named("vec"), None);
187 }
188
189 #[test]
190 fn a_register_is_found_by_its_name_and_names_itself_back() {
191 let (class, reg) = FILE.reg_named("xmm1").expect("the file has xmm1");
192 assert_eq!(FILE.class(class).map(|info| info.name), Some("xmm"));
193 assert_eq!(reg.number(), 1);
194 assert_eq!(FILE.name(class, reg), Some("xmm1"));
195 assert_eq!(FILE.reg_named("r15"), None);
196 }
197
198 #[test]
199 fn a_number_past_the_end_of_a_class_has_no_name() {
200 let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
201 assert_eq!(FILE.name(gpr, PhysReg::new(3)), None);
202 assert_eq!(FILE.name(RegClass::new(7), PhysReg::new(0)), None);
203 }
204
205 #[test]
206 fn a_file_that_names_two_registers_alike_says_so() {
207 assert_eq!(FILE.duplicate(), None);
208 static BOTH: [ClassInfo; 2] = [
209 ClassInfo { name: "gpr", bits: 64, regs: &GPR },
210 ClassInfo { name: "shadow", bits: 64, regs: &GPR },
211 ];
212 assert_eq!(RegFile::new(&BOTH).duplicate(), Some("rax"));
213 }
214
215 #[test]
216 fn the_file_prints_one_class_to_a_line() {
217 assert_eq!(
218 FILE.to_string(),
219 "class gpr : i64 = rax, rcx, rdx\nclass xmm : i128 = xmm0, xmm1\n"
220 );
221 }
222}