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    /// Whether the allocator may put a value in one of these.
34    ///
35    /// True for every class a target means the allocator to use, which is nearly all of them.
36    /// False says the registers exist and are named and are not somewhere a value may be told to
37    /// live, so a virtual register of this class is a mistake at the point it was made rather than
38    /// a value the allocator has nowhere to put.
39    ///
40    /// The x87 stack is the case this exists for, and it is worth the sentence because it is not
41    /// the usual reason a register is unavailable. `rsp` is unavailable because it has a job;
42    /// `st0` is unavailable because the machine addresses it as a stack, so which register a name
43    /// means depends on how many values are on the stack at the time, and an allocator that hands
44    /// out a name has no way to say that. So nothing allocates from it, an eighty bit value lives
45    /// in a stack slot between one operation and the next, and the stack is empty on both sides of
46    /// every group of instructions that uses it. See `spec/10-backend.md` section 10.8, which says
47    /// what a group is and why nothing the allocator inserts can get into the middle of one, and
48    /// tamnd/rucc#540.
49    ///
50    /// A register in such a class can still be named, which is the whole reason the class is
51    /// described at all: a `long double` comes back from a call in `st0` and the convention has to
52    /// be able to say so.
53    pub allocatable: bool,
54}
55
56/// Which class a register or an operand belongs to.
57///
58/// A number into the file's classes rather than a name, because it is on every operand of every
59/// instruction and it is compared far more often than it is printed.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub struct RegClass(u8);
62
63impl RegClass {
64    /// The class with that number.
65    #[must_use]
66    pub const fn new(number: u8) -> Self {
67        Self(number)
68    }
69
70    /// Its number, which is what indexes the file.
71    #[must_use]
72    pub const fn number(self) -> u8 {
73        self.0
74    }
75}
76
77/// One physical register, as its number inside its class.
78///
79/// The class is not in here. An operand carries its class already, and a fixed-register
80/// constraint is a constraint on an operand, so repeating the class would be a second copy of
81/// something that can disagree with the first.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83pub struct PhysReg(u8);
84
85impl PhysReg {
86    /// The register with that number in its class.
87    #[must_use]
88    pub const fn new(number: u8) -> Self {
89        Self(number)
90    }
91
92    /// Its number inside its class.
93    #[must_use]
94    pub const fn number(self) -> u8 {
95        self.0
96    }
97}
98
99/// Every register a target has.
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub struct RegFile {
102    classes: &'static [ClassInfo],
103}
104
105impl RegFile {
106    /// The file of a target whose registers nothing has described yet.
107    ///
108    /// A target reaches 1.0 with a real one. Until it has one, the honest answer to what
109    /// registers it has is that nobody has written them down, and that is a file with no
110    /// classes in it rather than a panic or a plausible guess.
111    pub const EMPTY: Self = Self::new(&[]);
112
113    /// A file made of those classes, numbered in the order they are given.
114    #[must_use]
115    pub const fn new(classes: &'static [ClassInfo]) -> Self {
116        Self { classes }
117    }
118
119    /// Its classes, each with the number it is known by.
120    pub fn classes(&self) -> impl Iterator<Item = (RegClass, &'static ClassInfo)> + use<> {
121        self.classes.iter().enumerate().map(|(number, info)| (RegClass::new(number as u8), info))
122    }
123
124    /// What is in one class.
125    #[must_use]
126    pub fn class(&self, class: RegClass) -> Option<&'static ClassInfo> {
127        self.classes.get(usize::from(class.number()))
128    }
129
130    /// The class of that name, such as `gpr`.
131    #[must_use]
132    pub fn class_named(&self, name: &str) -> Option<RegClass> {
133        self.classes().find(|(_, info)| info.name == name).map(|(class, _)| class)
134    }
135
136    /// Whether the allocator may put a value in that class, which is [`ClassInfo::allocatable`].
137    ///
138    /// A class the file does not have is not one either, which is the same answer as a class
139    /// nothing allocates from and is the one that keeps a caller from having to say what it means
140    /// by a class number the target never gave out.
141    #[must_use]
142    pub fn allocatable(&self, class: RegClass) -> bool {
143        self.class(class).is_some_and(|info| info.allocatable)
144    }
145
146    /// How many registers are in a class, which is one past the largest number in it.
147    #[must_use]
148    pub fn len(&self, class: RegClass) -> usize {
149        self.class(class).map_or(0, |info| info.regs.len())
150    }
151
152    /// Whether the file has no classes at all, which is a target that has not described one.
153    #[must_use]
154    pub fn is_empty(&self) -> bool {
155        self.classes.is_empty()
156    }
157
158    /// What one register is called.
159    #[must_use]
160    pub fn name(&self, class: RegClass, reg: PhysReg) -> Option<&'static str> {
161        self.class(class)?.regs.get(usize::from(reg.number())).copied()
162    }
163
164    /// The register of that name, and the class it is in.
165    ///
166    /// The name is written without the sigil, so `rax` rather than `$rax`.
167    #[must_use]
168    pub fn reg_named(&self, name: &str) -> Option<(RegClass, PhysReg)> {
169        for (class, info) in self.classes() {
170            if let Some(number) = info.regs.iter().position(|&reg| reg == name) {
171                return Some((class, PhysReg::new(number as u8)));
172            }
173        }
174        None
175    }
176
177    /// A name this file gives to two registers, if it gives one to two.
178    ///
179    /// Reading a dump back needs every name to say which register it means, and a target that
180    /// breaks that produces text that cannot be parsed rather than an error at the point of the
181    /// mistake. So every target's own test asks this, which is why it is here and public.
182    #[must_use]
183    pub fn duplicate(&self) -> Option<&'static str> {
184        let mut seen: Vec<&'static str> = Vec::new();
185        for (_, info) in self.classes() {
186            for &reg in info.regs {
187                if seen.contains(&reg) {
188                    return Some(reg);
189                }
190                seen.push(reg);
191            }
192        }
193        None
194    }
195}
196
197/// The storage an address is counted from, on a machine that has more than one.
198///
199/// x86 keeps a thread's own block of words at a fixed place reached through a segment register,
200/// and that block is the only thing anything here uses one for. The stack protector's canary lives
201/// in it, which is why `%fs:40` is an address a compiler writes and `%fs` is not a register any
202/// program names. Every other address this compiler writes is in the flat segment and says nothing
203/// at all, which is what `None` is.
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub enum Segment {
206    /// `%fs`, which is where a thread's own block is on x86-64 under System V.
207    Fs,
208    /// `%gs`, which is where it is on x86-64 under Windows and inside a kernel.
209    Gs,
210}
211
212/// Where a target keeps the word a stack protector's canary is a copy of.
213///
214/// Not a register and not a symbol either, on the conventions here. The word is in the block a
215/// thread has to itself, which is reached through a segment register and no other way, so the only
216/// way to name it is a distance into that block. That is why `%fs:40` appears in every protected
217/// function glibc has ever linked and why no object file carries a relocation for it.
218///
219/// A convention that answers `None` is one this compiler has no protector for, and a command line
220/// that asks for one on such a target is told so rather than quietly given an unprotected frame.
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub struct Guard {
223    /// The storage the word is in.
224    pub segment: Segment,
225    /// How far into it the word is.
226    pub at: i32,
227    /// The function called when the copy in the frame no longer matches it, which does not come
228    /// back.
229    pub fail: &'static str,
230}
231
232/// Which registers a calling convention gives which job.
233///
234/// This is the second half of a target description and it is separate from [`RegFile`] because
235/// the two do not vary together. x86-64 has one register file and two conventions over it, and
236/// they disagree about nearly everything below: `rdi` is where the first argument arrives on
237/// SysV and a register a callee has to preserve on Windows, and a Windows caller reserves
238/// thirty two bytes below the call that a SysV caller does not.
239///
240/// The allocation order is here rather than on a class because it is a consequence of what a
241/// call clobbers. A value that does not live across a call belongs in a register the callee is
242/// free to destroy, because putting it in a preserved one costs a push and a pop in the
243/// prologue of whichever function ends up owning it.
244///
245/// Every register named here is a register of the file the same target describes, and each list
246/// is in the order the convention uses them, so the fourth integer argument is `int_args[3]` and
247/// nothing has to count.
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
249pub struct CallRegs {
250    /// The class the general purpose registers named here are in.
251    ///
252    /// A register is a number inside its class, so a list of them says nothing about which
253    /// registers they are without this. Everything else could get the class from the operand it
254    /// came off, and a frame cannot, because a saved register is not an operand of anything.
255    pub int_class: RegClass,
256    /// The class the vector registers named here are in.
257    pub sse_class: RegClass,
258    /// The general purpose registers integer arguments arrive in, in order.
259    pub int_args: &'static [PhysReg],
260    /// The vector registers floating point arguments arrive in, in order.
261    ///
262    /// Whether an argument's position counts against both lists or only against its own is
263    /// [`CallRegs::shared_positions`].
264    pub sse_args: &'static [PhysReg],
265    /// Whether an argument's position counts against both argument lists or only against its own.
266    ///
267    /// False on SysV, which counts each separately, so a `double` after six integers is still in
268    /// `xmm0`. True on Windows, which counts one position for both, so a `double` in the third
269    /// position is in `xmm2` and `r8` is skipped.
270    pub shared_positions: bool,
271    /// The general purpose registers an integer return value comes back in.
272    pub int_returns: &'static [PhysReg],
273    /// The vector registers a floating point return value comes back in.
274    pub sse_returns: &'static [PhysReg],
275    /// The x87 registers a `long double` comes back in, which is empty on a target whose
276    /// `long double` is a `double`.
277    pub x87_returns: &'static [PhysReg],
278    /// The general purpose registers a call leaves alone, so a value in one survives it.
279    pub int_saved: &'static [PhysReg],
280    /// The vector registers a call leaves alone, which is none of them on SysV.
281    pub sse_saved: &'static [PhysReg],
282    /// The general purpose registers the allocator may hand out, in the order it prefers them.
283    ///
284    /// The stack pointer is never in this list, and neither is the frame pointer, which a
285    /// target could allocate when nothing needs a frame and which nothing here does yet.
286    pub int_order: &'static [PhysReg],
287    /// The vector registers the allocator may hand out, in the order it prefers them.
288    pub sse_order: &'static [PhysReg],
289    /// The stack pointer.
290    pub stack_pointer: PhysReg,
291    /// The frame pointer, which is the register a prologue puts the old stack pointer in.
292    pub frame_pointer: PhysReg,
293    /// Where a variadic call says how many vector registers it passed arguments in, when the
294    /// convention makes it say.
295    ///
296    /// SysV puts the count in `al` and a variadic callee reads it to decide whether to save the
297    /// vector argument registers at all, which is what makes a call to `printf` with no
298    /// floating point argument cheap.
299    pub vector_count: Option<PhysReg>,
300    /// How many bytes below the stack pointer a leaf function may use without moving it.
301    ///
302    /// A hundred and twenty eight on SysV and nothing on Windows. It is nothing in kernel code
303    /// on either, because an interrupt handler runs on the interrupted stack and writes over
304    /// exactly this, which is what `-mno-red-zone` is for.
305    pub red_zone: u32,
306    /// How many bytes a caller reserves below the call for the callee to spill its register
307    /// arguments into, which is thirty two on Windows and nothing on SysV.
308    pub shadow: u32,
309    /// What the stack pointer has to be a multiple of at the instruction that makes a call.
310    ///
311    /// Sixteen on every convention here, and it is a real obligation rather than a preference,
312    /// because a callee is entitled to use an aligned vector store on its own frame and gets a
313    /// fault rather than a wrong answer when a caller got this wrong.
314    pub stack_align: u32,
315    /// How many bytes the call instruction itself pushes before the callee starts running.
316    ///
317    /// Eight on x86-64, where the return address is on the stack, and nothing on a machine that
318    /// leaves it in a register. It is what makes the stack pointer misaligned on entry by
319    /// exactly one word, which every frame layout has to undo.
320    pub return_address: u32,
321    /// How many bytes one general purpose register takes when it is saved on the stack.
322    pub word: u32,
323    /// What DWARF calls each register, one list per class in the order the file numbers the
324    /// classes, and inside a list in the order the class numbers its registers.
325    ///
326    /// The two numberings are a real difference and not a formality. On x86-64 the machine puts
327    /// `rcx` at one and DWARF puts `rdx` there, so a table written with the machine's numbers is
328    /// well formed and describes the wrong registers, which is a backtrace with plausible
329    /// nonsense in it rather than an error. Shorter than the file when the classes at the end are
330    /// ones DWARF has no column for, and empty on a target nobody has written this down for yet.
331    pub dwarf: &'static [&'static [u16]],
332    /// The column an unwind table files the return address under.
333    ///
334    /// Not a register on x86-64, where it is sixteen and `rip` is not a register anything can
335    /// name, and a real one on a machine that returns through a link register.
336    pub dwarf_return_address: u16,
337    /// Where the word a stack protector's canary is copied from lives, on a convention that has
338    /// somewhere to put one.
339    ///
340    /// Here rather than beside the frame instructions because it is a fact about the runtime the
341    /// code is linked against rather than about the machine. The two x86-64 conventions share
342    /// every instruction the check is made of and disagree about this.
343    pub guard: Option<Guard>,
344}
345
346impl CallRegs {
347    /// The number DWARF gives that register, or `None` for one it has no column for.
348    ///
349    /// The x87 stack is the case that answers `None` on x86-64, and it is not an omission: a
350    /// register whose name means whichever one is on top of the stack is not one a table can have
351    /// a column for. Nothing saves one across a call either, so nothing ever asks.
352    #[must_use]
353    pub fn dwarf(&self, class: RegClass, reg: PhysReg) -> Option<u16> {
354        self.dwarf.get(usize::from(class.number()))?.get(usize::from(reg.number())).copied()
355    }
356
357    /// Whether a call preserves that general purpose register.
358    #[must_use]
359    pub fn preserves_int(&self, reg: PhysReg) -> bool {
360        self.int_saved.contains(&reg)
361    }
362
363    /// Whether a call preserves that vector register.
364    #[must_use]
365    pub fn preserves_sse(&self, reg: PhysReg) -> bool {
366        self.sse_saved.contains(&reg)
367    }
368}
369
370/// Where one of the values a call passes is.
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum Where {
373    /// In that register.
374    Reg(PhysReg),
375    /// That many bytes up the argument area, which is where the stack pointer points at the
376    /// instruction that makes the call and is one word above the return address in the callee.
377    Stack(u32),
378}
379
380/// Where the values a call passes are, worked out one after another.
381///
382/// [`crate::abi::Call`] answers a different question: whether a value travels in registers at all
383/// and in how many, which is what decides the shape of a signature and is settled before the IR
384/// for a function exists. This answers the question after it. Given values in the order the
385/// signature holds them, it says which register each one is in and how far up the argument area
386/// the ones that got no register are. Both count registers, and they agree about how many fit
387/// because they read the same lists, but they run at opposite ends of the compiler and neither
388/// can be the other.
389///
390/// Ask about each value in the order the signature holds them. Asking out of order answers about
391/// a different signature, because where a value is depends on every value before it.
392#[derive(Debug, Clone)]
393pub struct Places<'a> {
394    regs: &'a CallRegs,
395    int: usize,
396    sse: usize,
397    stack: u32,
398}
399
400impl<'a> Places<'a> {
401    /// Where the first value is, for a call under that convention.
402    #[must_use]
403    pub fn new(regs: &'a CallRegs) -> Self {
404        Self { regs, int: 0, sse: 0, stack: regs.shadow }
405    }
406
407    /// Where the next value is, when it travels in a general purpose register.
408    pub fn integer(&mut self) -> Where {
409        match self.regs.int_args.get(self.position(false)) {
410            Some(&reg) => {
411                self.int += 1;
412                Where::Reg(reg)
413            }
414            None => self.on_stack(self.regs.word, self.regs.word),
415        }
416    }
417
418    /// Where the next value is, when it travels in a vector register.
419    pub fn float(&mut self) -> Where {
420        match self.regs.sse_args.get(self.position(true)) {
421            Some(&reg) => {
422                self.sse += 1;
423                Where::Reg(reg)
424            }
425            None => self.on_stack(self.regs.word, self.regs.word),
426        }
427    }
428
429    /// Where the next value is, when it travels in memory whatever is left.
430    ///
431    /// Every argument area is a run of whole words, so a value narrower than one still takes one
432    /// and a value that is not a whole number of them is rounded up. An alignment wider than a
433    /// word is respected, which is what a sixteen byte aligned structure passed by value needs.
434    pub fn on_stack(&mut self, size: u32, align: u32) -> Where {
435        let word = self.regs.word;
436        let at = self.stack.next_multiple_of(align.max(word));
437        self.stack = at.saturating_add(size.max(word).next_multiple_of(word));
438        Where::Stack(at)
439    }
440
441    /// How many bytes of argument area the values so far need, shadow space included.
442    #[must_use]
443    pub fn size(&self) -> u32 {
444        self.stack
445    }
446
447    /// How many general purpose argument registers the values so far took.
448    ///
449    /// What a variadic callee needs and nothing else does. `va_start` has to record how far into
450    /// each of the two register sequences the arguments the signature names got, because the first
451    /// argument it does not name is the one after them, and asking here is the only way to know
452    /// that is the same count the caller worked from.
453    #[must_use]
454    pub fn integers(&self) -> usize {
455        self.int
456    }
457
458    /// How many vector argument registers the values so far took.
459    #[must_use]
460    pub fn floats(&self) -> usize {
461        self.sse
462    }
463
464    /// The position the next value of a kind is at.
465    fn position(&self, sse: bool) -> usize {
466        if self.regs.shared_positions {
467            self.int + self.sse
468        } else if sse {
469            self.sse
470        } else {
471            self.int
472        }
473    }
474}
475
476impl fmt::Display for RegFile {
477    /// The file as a dump reads it, one class to a line.
478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479        for (_, info) in self.classes() {
480            writeln!(f, "class {} : i{} = {}", info.name, info.bits, info.regs.join(", "))?;
481        }
482        Ok(())
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489
490    static GPR: [&str; 3] = ["rax", "rcx", "rdx"];
491    static XMM: [&str; 2] = ["xmm0", "xmm1"];
492    static CLASSES: [ClassInfo; 2] = [
493        ClassInfo { name: "gpr", bits: 64, regs: &GPR, allocatable: true },
494        ClassInfo { name: "xmm", bits: 128, regs: &XMM, allocatable: true },
495    ];
496    static FILE: RegFile = RegFile::new(&CLASSES);
497
498    #[test]
499    fn a_class_is_found_by_its_name() {
500        let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
501        assert_eq!(FILE.len(gpr), 3);
502        assert_eq!(FILE.class(gpr).map(|info| info.bits), Some(64));
503        assert_eq!(FILE.class_named("vec"), None);
504    }
505
506    #[test]
507    fn a_register_is_found_by_its_name_and_names_itself_back() {
508        let (class, reg) = FILE.reg_named("xmm1").expect("the file has xmm1");
509        assert_eq!(FILE.class(class).map(|info| info.name), Some("xmm"));
510        assert_eq!(reg.number(), 1);
511        assert_eq!(FILE.name(class, reg), Some("xmm1"));
512        assert_eq!(FILE.reg_named("r15"), None);
513    }
514
515    #[test]
516    fn a_number_past_the_end_of_a_class_has_no_name() {
517        let gpr = FILE.class_named("gpr").expect("the file has a gpr class");
518        assert_eq!(FILE.name(gpr, PhysReg::new(3)), None);
519        assert_eq!(FILE.name(RegClass::new(7), PhysReg::new(0)), None);
520    }
521
522    #[test]
523    fn a_file_that_names_two_registers_alike_says_so() {
524        assert_eq!(FILE.duplicate(), None);
525        static BOTH: [ClassInfo; 2] = [
526            ClassInfo { name: "gpr", bits: 64, regs: &GPR, allocatable: true },
527            ClassInfo { name: "shadow", bits: 64, regs: &GPR, allocatable: true },
528        ];
529        assert_eq!(RegFile::new(&BOTH).duplicate(), Some("rax"));
530    }
531
532    #[test]
533    fn a_class_nothing_allocates_from_is_still_a_class_in_every_other_way() {
534        static WITH_STACK: [ClassInfo; 2] = [
535            ClassInfo { name: "gpr", bits: 64, regs: &GPR, allocatable: true },
536            ClassInfo { name: "x87", bits: 80, regs: &XMM, allocatable: false },
537        ];
538        let file = RegFile::new(&WITH_STACK);
539        let stack = file.class_named("x87").expect("the file has an x87 class");
540
541        assert!(!file.allocatable(stack));
542        assert!(file.allocatable(file.class_named("gpr").expect("the file has a gpr class")));
543
544        // Everything else about it works, which is the point of describing a class the allocator
545        // will not touch: the registers are counted, are named, and name themselves back.
546        assert_eq!(file.len(stack), 2);
547        assert_eq!(file.name(stack, PhysReg::new(1)), Some("xmm1"));
548        assert_eq!(file.reg_named("xmm1"), Some((stack, PhysReg::new(1))));
549    }
550
551    #[test]
552    fn a_class_the_file_does_not_have_is_not_one_to_allocate_from_either() {
553        assert!(!FILE.allocatable(RegClass::new(7)));
554    }
555
556    #[test]
557    fn the_file_prints_one_class_to_a_line() {
558        assert_eq!(
559            FILE.to_string(),
560            "class gpr : i64 = rax, rcx, rdx\nclass xmm : i128 = xmm0, xmm1\n"
561        );
562    }
563
564    /// Two integer registers, two vector registers and nothing else, so running out of them takes
565    /// three arguments rather than seven and the interesting case is the one being tested.
566    fn convention(shared: bool, shadow: u32) -> CallRegs {
567        static INT: [PhysReg; 2] = [PhysReg::new(0), PhysReg::new(1)];
568        static SSE: [PhysReg; 2] = [PhysReg::new(10), PhysReg::new(11)];
569        static NONE: [PhysReg; 0] = [];
570        CallRegs {
571            int_class: RegClass::new(0),
572            sse_class: RegClass::new(1),
573            int_args: &INT,
574            sse_args: &SSE,
575            shared_positions: shared,
576            int_returns: &INT,
577            sse_returns: &SSE,
578            x87_returns: &NONE,
579            int_saved: &NONE,
580            sse_saved: &NONE,
581            int_order: &INT,
582            sse_order: &SSE,
583            stack_pointer: PhysReg::new(4),
584            frame_pointer: PhysReg::new(5),
585            vector_count: None,
586            red_zone: 0,
587            shadow,
588            stack_align: 16,
589            return_address: 8,
590            word: 8,
591            // Empty, which is all a convention made up for a test of argument placement needs to
592            // say about a question it never asks.
593            dwarf: &[],
594            dwarf_return_address: 16,
595            guard: None,
596        }
597    }
598
599    #[test]
600    fn counting_each_kind_separately_leaves_the_first_vector_register_to_the_first_float() {
601        let regs = convention(false, 0);
602        let mut places = Places::new(&regs);
603        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
604        assert_eq!(places.integer(), Where::Reg(PhysReg::new(1)));
605        // Two integers went past, and a convention that counts separately has not spent a vector
606        // register on either of them.
607        assert_eq!(places.float(), Where::Reg(PhysReg::new(10)));
608        assert_eq!(places.size(), 0);
609    }
610
611    #[test]
612    fn counting_one_position_for_both_skips_the_register_the_other_kind_would_have_used() {
613        let regs = convention(true, 0);
614        let mut places = Places::new(&regs);
615        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
616        // The second position, so the second vector register, and the second integer register is
617        // spent whether anything is in it or not.
618        assert_eq!(places.float(), Where::Reg(PhysReg::new(11)));
619        assert_eq!(places.integer(), Where::Stack(0));
620    }
621
622    #[test]
623    fn running_out_of_one_kind_of_register_does_not_touch_the_other() {
624        let regs = convention(false, 0);
625        let mut places = Places::new(&regs);
626        assert_eq!(places.integer(), Where::Reg(PhysReg::new(0)));
627        assert_eq!(places.integer(), Where::Reg(PhysReg::new(1)));
628        assert_eq!(places.integer(), Where::Stack(0));
629        assert_eq!(places.float(), Where::Reg(PhysReg::new(10)));
630        assert_eq!(places.size(), 8);
631    }
632
633    #[test]
634    fn the_argument_area_starts_above_the_shadow_space_and_keeps_every_value_aligned() {
635        let regs = convention(false, 32);
636        let mut places = Places::new(&regs);
637        // A Windows caller reserves this whether it passes anything on the stack or not, which is
638        // why an empty area is thirty two bytes rather than none.
639        assert_eq!(places.size(), 32);
640        assert_eq!(places.on_stack(4, 4), Where::Stack(32));
641        // Sixteen byte alignment skips the word at 40, which is what a vector or an over-aligned
642        // structure passed by value asks for. The four byte value before it still took a whole
643        // word, which is why the skipped word is there to skip.
644        assert_eq!(places.on_stack(16, 16), Where::Stack(48));
645        assert_eq!(places.on_stack(8, 8), Where::Stack(64));
646        assert_eq!(places.size(), 72);
647    }
648}