Skip to main content

rucc_target/
regs.rs

1//! The register file: what registers a target has, and what classes they fall into.
2//!
3//! Design: `spec/10-backend.md` section 10.8.
4//!
5//! A register file is data rather than code, which is the same claim the rest of this crate
6//! makes and the one `M10` puts a number on. A class is a set of registers that an operand of
7//! that class may be assigned to, and a physical register is its number inside its class, so
8//! the allocator works in dense small integers and only the printer and the parser ever deal in
9//! names.
10//!
11//! The file lives here rather than in `rucc-mir` because more than one thing reads it. The
12//! machine IR needs it to print, the allocator needs the set it may assign from, and the ABI
13//! description needs to name the registers arguments arrive in. All three are above this crate,
14//! and the alternative is the register file living in whichever of them happens to be lowest,
15//! which is how a layering ends up describing itself as historical.
16//!
17//! Names are unique across the whole file, not merely inside a class. That is what lets a
18//! register be written `$rax` in a dump rather than `$gpr.0`, and it is a real constraint on a
19//! target that gives one register two classes: it has to say which class it is in, or use two
20//! names. [`RegFile::duplicate`] is what a target's own test asks to find out.
21
22use std::fmt;
23
24/// One class of registers, and the registers in it.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct ClassInfo {
27    /// What the class is called in a dump, such as `gpr`.
28    pub name: &'static str,
29    /// How wide one of its registers is, in bits.
30    pub bits: u32,
31    /// The registers, in the order their numbers run, without the sigil a dump writes.
32    pub regs: &'static [&'static str],
33}
34
35/// Which class a register or an operand belongs to.
36///
37/// A number into the file's classes rather than a name, because it is on every operand of every
38/// instruction and it is compared far more often than it is printed.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub struct RegClass(u8);
41
42impl RegClass {
43    /// The class with that number.
44    #[must_use]
45    pub const fn new(number: u8) -> Self {
46        Self(number)
47    }
48
49    /// Its number, which is what indexes the file.
50    #[must_use]
51    pub const fn number(self) -> u8 {
52        self.0
53    }
54}
55
56/// One physical register, as its number inside its class.
57///
58/// The class is not in here. An operand carries its class already, and a fixed-register
59/// constraint is a constraint on an operand, so repeating the class would be a second copy of
60/// something that can disagree with the first.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub struct PhysReg(u8);
63
64impl PhysReg {
65    /// The register with that number in its class.
66    #[must_use]
67    pub const fn new(number: u8) -> Self {
68        Self(number)
69    }
70
71    /// Its number inside its class.
72    #[must_use]
73    pub const fn number(self) -> u8 {
74        self.0
75    }
76}
77
78/// Every register a target has.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct RegFile {
81    classes: &'static [ClassInfo],
82}
83
84impl RegFile {
85    /// The file of a target whose registers nothing has described yet.
86    ///
87    /// A target reaches 1.0 with a real one. Until it has one, the honest answer to what
88    /// registers it has is that nobody has written them down, and that is a file with no
89    /// classes in it rather than a panic or a plausible guess.
90    pub const EMPTY: Self = Self::new(&[]);
91
92    /// A file made of those classes, numbered in the order they are given.
93    #[must_use]
94    pub const fn new(classes: &'static [ClassInfo]) -> Self {
95        Self { classes }
96    }
97
98    /// Its classes, each with the number it is known by.
99    pub fn classes(&self) -> impl Iterator<Item = (RegClass, &'static ClassInfo)> + use<> {
100        self.classes.iter().enumerate().map(|(number, info)| (RegClass::new(number as u8), info))
101    }
102
103    /// What is in one class.
104    #[must_use]
105    pub fn class(&self, class: RegClass) -> Option<&'static ClassInfo> {
106        self.classes.get(usize::from(class.number()))
107    }
108
109    /// The class of that name, such as `gpr`.
110    #[must_use]
111    pub fn class_named(&self, name: &str) -> Option<RegClass> {
112        self.classes().find(|(_, info)| info.name == name).map(|(class, _)| class)
113    }
114
115    /// How many registers are in a class, which is one past the largest number in it.
116    #[must_use]
117    pub fn len(&self, class: RegClass) -> usize {
118        self.class(class).map_or(0, |info| info.regs.len())
119    }
120
121    /// Whether the file has no classes at all, which is a target that has not described one.
122    #[must_use]
123    pub fn is_empty(&self) -> bool {
124        self.classes.is_empty()
125    }
126
127    /// What one register is called.
128    #[must_use]
129    pub fn name(&self, class: RegClass, reg: PhysReg) -> Option<&'static str> {
130        self.class(class)?.regs.get(usize::from(reg.number())).copied()
131    }
132
133    /// The register of that name, and the class it is in.
134    ///
135    /// The name is written without the sigil, so `rax` rather than `$rax`.
136    #[must_use]
137    pub fn reg_named(&self, name: &str) -> Option<(RegClass, PhysReg)> {
138        for (class, info) in self.classes() {
139            if let Some(number) = info.regs.iter().position(|&reg| reg == name) {
140                return Some((class, PhysReg::new(number as u8)));
141            }
142        }
143        None
144    }
145
146    /// A name this file gives to two registers, if it gives one to two.
147    ///
148    /// Reading a dump back needs every name to say which register it means, and a target that
149    /// breaks that produces text that cannot be parsed rather than an error at the point of the
150    /// mistake. So every target's own test asks this, which is why it is here and public.
151    #[must_use]
152    pub fn duplicate(&self) -> Option<&'static str> {
153        let mut seen: Vec<&'static str> = Vec::new();
154        for (_, info) in self.classes() {
155            for &reg in info.regs {
156                if seen.contains(&reg) {
157                    return Some(reg);
158                }
159                seen.push(reg);
160            }
161        }
162        None
163    }
164}
165
166/// Which registers a calling convention gives which job.
167///
168/// This is the second half of a target description and it is separate from [`RegFile`] because
169/// the two do not vary together. x86-64 has one register file and two conventions over it, and
170/// they disagree about nearly everything below: `rdi` is where the first argument arrives on
171/// SysV and a register a callee has to preserve on Windows, and a Windows caller reserves
172/// thirty two bytes below the call that a SysV caller does not.
173///
174/// The allocation order is here rather than on a class because it is a consequence of what a
175/// call clobbers. A value that does not live across a call belongs in a register the callee is
176/// free to destroy, because putting it in a preserved one costs a push and a pop in the
177/// prologue of whichever function ends up owning it.
178///
179/// Every register named here is a register of the file the same target describes, and each list
180/// is in the order the convention uses them, so the fourth integer argument is `int_args[3]` and
181/// nothing has to count.
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub struct CallRegs {
184    /// The class the general purpose registers named here are in.
185    ///
186    /// A register is a number inside its class, so a list of them says nothing about which
187    /// registers they are without this. Everything else could get the class from the operand it
188    /// came off, and a frame cannot, because a saved register is not an operand of anything.
189    pub int_class: RegClass,
190    /// The class the vector registers named here are in.
191    pub sse_class: RegClass,
192    /// The general purpose registers integer arguments arrive in, in order.
193    pub int_args: &'static [PhysReg],
194    /// The vector registers floating point arguments arrive in, in order.
195    ///
196    /// Whether an argument's position counts against both lists or only against its own is
197    /// [`CallRegs::shared_positions`].
198    pub sse_args: &'static [PhysReg],
199    /// Whether an argument's position counts against both argument lists or only against its own.
200    ///
201    /// False on SysV, which counts each separately, so a `double` after six integers is still in
202    /// `xmm0`. True on Windows, which counts one position for both, so a `double` in the third
203    /// position is in `xmm2` and `r8` is skipped.
204    pub shared_positions: bool,
205    /// The general purpose registers an integer return value comes back in.
206    pub int_returns: &'static [PhysReg],
207    /// The vector registers a floating point return value comes back in.
208    pub sse_returns: &'static [PhysReg],
209    /// The x87 registers a `long double` comes back in, which is empty on a target whose
210    /// `long double` is a `double`.
211    pub x87_returns: &'static [PhysReg],
212    /// The general purpose registers a call leaves alone, so a value in one survives it.
213    pub int_saved: &'static [PhysReg],
214    /// The vector registers a call leaves alone, which is none of them on SysV.
215    pub sse_saved: &'static [PhysReg],
216    /// The general purpose registers the allocator may hand out, in the order it prefers them.
217    ///
218    /// The stack pointer is never in this list, and neither is the frame pointer, which a
219    /// target could allocate when nothing needs a frame and which nothing here does yet.
220    pub int_order: &'static [PhysReg],
221    /// The vector registers the allocator may hand out, in the order it prefers them.
222    pub sse_order: &'static [PhysReg],
223    /// The stack pointer.
224    pub stack_pointer: PhysReg,
225    /// The frame pointer, which is the register a prologue puts the old stack pointer in.
226    pub frame_pointer: PhysReg,
227    /// Where a variadic call says how many vector registers it passed arguments in, when the
228    /// convention makes it say.
229    ///
230    /// SysV puts the count in `al` and a variadic callee reads it to decide whether to save the
231    /// vector argument registers at all, which is what makes a call to `printf` with no
232    /// floating point argument cheap.
233    pub vector_count: Option<PhysReg>,
234    /// How many bytes below the stack pointer a leaf function may use without moving it.
235    ///
236    /// A hundred and twenty eight on SysV and nothing on Windows. It is nothing in kernel code
237    /// on either, because an interrupt handler runs on the interrupted stack and writes over
238    /// exactly this, which is what `-mno-red-zone` is for.
239    pub red_zone: u32,
240    /// How many bytes a caller reserves below the call for the callee to spill its register
241    /// arguments into, which is thirty two on Windows and nothing on SysV.
242    pub shadow: u32,
243    /// What the stack pointer has to be a multiple of at the instruction that makes a call.
244    ///
245    /// Sixteen on every convention here, and it is a real obligation rather than a preference,
246    /// because a callee is entitled to use an aligned vector store on its own frame and gets a
247    /// fault rather than a wrong answer when a caller got this wrong.
248    pub stack_align: u32,
249    /// How many bytes the call instruction itself pushes before the callee starts running.
250    ///
251    /// Eight on x86-64, where the return address is on the stack, and nothing on a machine that
252    /// leaves it in a register. It is what makes the stack pointer misaligned on entry by
253    /// exactly one word, which every frame layout has to undo.
254    pub return_address: u32,
255    /// How many bytes one general purpose register takes when it is saved on the stack.
256    pub word: u32,
257}
258
259impl CallRegs {
260    /// Whether a call preserves that general purpose register.
261    #[must_use]
262    pub fn preserves_int(&self, reg: PhysReg) -> bool {
263        self.int_saved.contains(&reg)
264    }
265
266    /// Whether a call preserves that vector register.
267    #[must_use]
268    pub fn preserves_sse(&self, reg: PhysReg) -> bool {
269        self.sse_saved.contains(&reg)
270    }
271}
272
273/// Where one of the values a call passes is.
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum Where {
276    /// In that register.
277    Reg(PhysReg),
278    /// That many bytes up the argument area, which is where the stack pointer points at the
279    /// instruction that makes the call and is one word above the return address in the callee.
280    Stack(u32),
281}
282
283/// Where the values a call passes are, worked out one after another.
284///
285/// [`crate::abi::Call`] answers a different question: whether a value travels in registers at all
286/// and in how many, which is what decides the shape of a signature and is settled before the IR
287/// for a function exists. This answers the question after it. Given values in the order the
288/// signature holds them, it says which register each one is in and how far up the argument area
289/// the ones that got no register are. Both count registers, and they agree about how many fit
290/// because they read the same lists, but they run at opposite ends of the compiler and neither
291/// can be the other.
292///
293/// Ask about each value in the order the signature holds them. Asking out of order answers about
294/// a different signature, because where a value is depends on every value before it.
295#[derive(Debug, Clone)]
296pub struct Places<'a> {
297    regs: &'a CallRegs,
298    int: usize,
299    sse: usize,
300    stack: u32,
301}
302
303impl<'a> Places<'a> {
304    /// Where the first value is, for a call under that convention.
305    #[must_use]
306    pub fn new(regs: &'a CallRegs) -> Self {
307        Self { regs, int: 0, sse: 0, stack: regs.shadow }
308    }
309
310    /// Where the next value is, when it travels in a general purpose register.
311    pub fn integer(&mut self) -> Where {
312        match self.regs.int_args.get(self.position(false)) {
313            Some(&reg) => {
314                self.int += 1;
315                Where::Reg(reg)
316            }
317            None => self.on_stack(self.regs.word, self.regs.word),
318        }
319    }
320
321    /// Where the next value is, when it travels in a vector register.
322    pub fn float(&mut self) -> Where {
323        match self.regs.sse_args.get(self.position(true)) {
324            Some(&reg) => {
325                self.sse += 1;
326                Where::Reg(reg)
327            }
328            None => self.on_stack(self.regs.word, self.regs.word),
329        }
330    }
331
332    /// Where the next value is, when it travels in memory whatever is left.
333    ///
334    /// Every argument area is a run of whole words, so a value narrower than one still takes one
335    /// and a value that is not a whole number of them is rounded up. An alignment wider than a
336    /// word is respected, which is what a sixteen byte aligned structure passed by value needs.
337    pub fn on_stack(&mut self, size: u32, align: u32) -> Where {
338        let word = self.regs.word;
339        let at = self.stack.next_multiple_of(align.max(word));
340        self.stack = at.saturating_add(size.max(word).next_multiple_of(word));
341        Where::Stack(at)
342    }
343
344    /// How many bytes of argument area the values so far need, shadow space included.
345    #[must_use]
346    pub fn size(&self) -> u32 {
347        self.stack
348    }
349
350    /// How many general purpose argument registers the values so far took.
351    ///
352    /// What a variadic callee needs and nothing else does. `va_start` has to record how far into
353    /// each of the two register sequences the arguments the signature names got, because the first
354    /// argument it does not name is the one after them, and asking here is the only way to know
355    /// that is the same count the caller worked from.
356    #[must_use]
357    pub fn integers(&self) -> usize {
358        self.int
359    }
360
361    /// How many vector argument registers the values so far took.
362    #[must_use]
363    pub fn floats(&self) -> usize {
364        self.sse
365    }
366
367    /// The position the next value of a kind is at.
368    fn position(&self, sse: bool) -> usize {
369        if self.regs.shared_positions {
370            self.int + self.sse
371        } else if sse {
372            self.sse
373        } else {
374            self.int
375        }
376    }
377}
378
379impl fmt::Display for RegFile {
380    /// The file as a dump reads it, one class to a line.
381    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382        for (_, info) in self.classes() {
383            writeln!(f, "class {} : i{} = {}", info.name, info.bits, info.regs.join(", "))?;
384        }
385        Ok(())
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    static GPR: [&str; 3] = ["rax", "rcx", "rdx"];
394    static XMM: [&str; 2] = ["xmm0", "xmm1"];
395    static CLASSES: [ClassInfo; 2] = [
396        ClassInfo { name: "gpr", bits: 64, regs: &GPR },
397        ClassInfo { name: "xmm", bits: 128, regs: &XMM },
398    ];
399    static FILE: RegFile = RegFile::new(&CLASSES);
400
401    #[test]
402    fn a_class_is_found_by_its_name() {
403        let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
404        assert_eq!(FILE.len(gpr), 3);
405        assert_eq!(FILE.class(gpr).map(|info| info.bits), Some(64));
406        assert_eq!(FILE.class_named("vec"), None);
407    }
408
409    #[test]
410    fn a_register_is_found_by_its_name_and_names_itself_back() {
411        let (class, reg) = FILE.reg_named("xmm1").expect("the file has xmm1");
412        assert_eq!(FILE.class(class).map(|info| info.name), Some("xmm"));
413        assert_eq!(reg.number(), 1);
414        assert_eq!(FILE.name(class, reg), Some("xmm1"));
415        assert_eq!(FILE.reg_named("r15"), None);
416    }
417
418    #[test]
419    fn a_number_past_the_end_of_a_class_has_no_name() {
420        let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
421        assert_eq!(FILE.name(gpr, PhysReg::new(3)), None);
422        assert_eq!(FILE.name(RegClass::new(7), PhysReg::new(0)), None);
423    }
424
425    #[test]
426    fn a_file_that_names_two_registers_alike_says_so() {
427        assert_eq!(FILE.duplicate(), None);
428        static BOTH: [ClassInfo; 2] = [
429            ClassInfo { name: "gpr", bits: 64, regs: &GPR },
430            ClassInfo { name: "shadow", bits: 64, regs: &GPR },
431        ];
432        assert_eq!(RegFile::new(&BOTH).duplicate(), Some("rax"));
433    }
434
435    #[test]
436    fn the_file_prints_one_class_to_a_line() {
437        assert_eq!(
438            FILE.to_string(),
439            "class gpr : i64 = rax, rcx, rdx\nclass xmm : i128 = xmm0, xmm1\n"
440        );
441    }
442
443    /// Two integer registers, two vector registers and nothing else, so running out of them takes
444    /// three arguments rather than seven and the interesting case is the one being tested.
445    fn convention(shared: bool, shadow: u32) -> CallRegs {
446        static INT: [PhysReg; 2] = [PhysReg::new(0), PhysReg::new(1)];
447        static SSE: [PhysReg; 2] = [PhysReg::new(10), PhysReg::new(11)];
448        static NONE: [PhysReg; 0] = [];
449        CallRegs {
450            int_class: RegClass::new(0),
451            sse_class: RegClass::new(1),
452            int_args: &INT,
453            sse_args: &SSE,
454            shared_positions: shared,
455            int_returns: &INT,
456            sse_returns: &SSE,
457            x87_returns: &NONE,
458            int_saved: &NONE,
459            sse_saved: &NONE,
460            int_order: &INT,
461            sse_order: &SSE,
462            stack_pointer: PhysReg::new(4),
463            frame_pointer: PhysReg::new(5),
464            vector_count: None,
465            red_zone: 0,
466            shadow,
467            stack_align: 16,
468            return_address: 8,
469            word: 8,
470        }
471    }
472
473    #[test]
474    fn counting_each_kind_separately_leaves_the_first_vector_register_to_the_first_float() {
475        let regs = convention(false, 0);
476        let mut places = Places::new(&regs);
477        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
478        assert_eq!(places.integer(), Where::Reg(PhysReg::new(1)));
479        // Two integers went past, and a convention that counts separately has not spent a vector
480        // register on either of them.
481        assert_eq!(places.float(), Where::Reg(PhysReg::new(10)));
482        assert_eq!(places.size(), 0);
483    }
484
485    #[test]
486    fn counting_one_position_for_both_skips_the_register_the_other_kind_would_have_used() {
487        let regs = convention(true, 0);
488        let mut places = Places::new(&regs);
489        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
490        // The second position, so the second vector register, and the second integer register is
491        // spent whether anything is in it or not.
492        assert_eq!(places.float(), Where::Reg(PhysReg::new(11)));
493        assert_eq!(places.integer(), Where::Stack(0));
494    }
495
496    #[test]
497    fn running_out_of_one_kind_of_register_does_not_touch_the_other() {
498        let regs = convention(false, 0);
499        let mut places = Places::new(&regs);
500        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
501        assert_eq!(places.integer(), Where::Reg(PhysReg::new(1)));
502        assert_eq!(places.integer(), Where::Stack(0));
503        assert_eq!(places.float(), Where::Reg(PhysReg::new(10)));
504        assert_eq!(places.size(), 8);
505    }
506
507    #[test]
508    fn the_argument_area_starts_above_the_shadow_space_and_keeps_every_value_aligned() {
509        let regs = convention(false, 32);
510        let mut places = Places::new(&regs);
511        // A Windows caller reserves this whether it passes anything on the stack or not, which is
512        // why an empty area is thirty two bytes rather than none.
513        assert_eq!(places.size(), 32);
514        assert_eq!(places.on_stack(4, 4), Where::Stack(32));
515        // Sixteen byte alignment skips the word at 40, which is what a vector or an over-aligned
516        // structure passed by value asks for. The four byte value before it still took a whole
517        // word, which is why the skipped word is there to skip.
518        assert_eq!(places.on_stack(16, 16), Where::Stack(48));
519        assert_eq!(places.on_stack(8, 8), Where::Stack(64));
520        assert_eq!(places.size(), 72);
521    }
522}