Skip to main content

rucc_codegen/
frame.rs

1//! The frame: what a function's stack looks like while it runs.
2//!
3//! Design: `spec/10-backend.md` section 10.7.
4//!
5//! This is worked out after register allocation and not before, because the largest area in most
6//! frames is the spill slots and nothing knows how many of those there are until the allocator has
7//! finished running out of registers. It is worked out from the rewritten function rather than
8//! from the assignment alone, because the rewrite is what decides which scratch registers a reload
9//! uses, and a scratch register a call preserves is one the prologue has to save.
10//!
11//! # What is in one
12//!
13//! Section 10.7 lists the areas and this is the order they are in, from the stack pointer upward,
14//! which is the order of increasing address on every machine here.
15//!
16//! ```text
17//!   incoming stack arguments      the caller wrote these and they are above everything
18//!   return address                the call instruction pushed it, on a machine that does
19//!   saved frame pointer           when the function keeps one
20//!   saved general purpose regs    pushed, one word each
21//!   saved vector registers        stored rather than pushed, since no machine here pushes one
22//!   stack protector canary        when the function has one, above everything a local reaches
23//!   locals                        what an alloca becomes, widest alignment first
24//!   spill slots                   one for every value the allocator ran out of registers for
25//!   outgoing argument area        at the bottom, because a call reads its stack arguments from
26//!                                 the stack pointer upward
27//! ```
28//!
29//! Every offset reported here is from the stack pointer as it stands in the body of the function,
30//! which is after the prologue and before the epilogue. That is the one base register always
31//! available. A frame pointer is a second way to reach the same bytes and the prologue is what
32//! knows the distance between the two, so nothing here reports an offset from it. There are two
33//! exceptions and [`Frame::incoming`] is one of them, because the bytes it reports are the caller's
34//! rather than this function's, which is the one part of the picture a realigned frame loses sight
35//! of. It says which register it counted from. The other is a frame that grows, which is the next
36//! section and where the stack pointer stops being a base register at all.
37//!
38//! # Where the alignment comes from
39//!
40//! A call has to leave the stack pointer on a multiple of the convention's alignment, so a
41//! function's own frame is what puts it back: the call that reached this function pushed a return
42//! address and left the stack pointer one word off, and the prologue's pushes either fix that or
43//! make it worse depending on how many there are. The size the prologue subtracts is therefore not
44//! the size of the areas. It is whatever brings the stack pointer back to a multiple of the
45//! alignment given the pushes in front of it, which is the arithmetic in [`Frame::of`].
46//!
47//! # The red zone
48//!
49//! A leaf function may use the bytes below the stack pointer without moving it, which is what
50//! `red_zone` on a convention says and what makes a small leaf function's prologue and epilogue
51//! empty. Then the offsets are negative, which is why they are signed, and the areas are in the
52//! same order as ever, below the line rather than above it. Anything that calls, or is too big for
53//! the zone, or wants more alignment than the stack pointer has for free, moves the stack pointer.
54//!
55//! # Realignment
56//!
57//! A local wanting more alignment than a call leaves the stack pointer with cannot be placed by
58//! arithmetic, because nothing in the frame knows what the caller's stack pointer was a multiple
59//! of. The prologue has to force it, and forcing it destroys the only record of where the caller's
60//! stack was, so a realigned frame needs a frame pointer and the distance from the body's stack
61//! pointer to the incoming arguments stops being a constant. [`Frame::realign`] is where that is
62//! reported and it is why [`Frame::incoming`] answers from the frame pointer in such a frame and
63//! from the stack pointer in every other one.
64//!
65//! # Growing
66//!
67//! A variable length array is bytes the function takes off the stack pointer where the declaration
68//! stands, so in a function that has one the stack pointer is in a different place in the middle of
69//! the body than it was at the top of it. Every other offset in the frame was a distance from the
70//! stack pointer, and a distance from a register that moves is not a distance, so in a frame like
71//! this they are all distances from the frame pointer instead. That is what [`Layout::grows`] says
72//! and [`Frame::grows`] reports, and it is why such a frame keeps a frame pointer whatever the
73//! flags asked for, the same way a realigned one does and for a version of the same reason.
74//!
75//! Three other things follow from it. The red zone is gone, because the zone is the bytes below the
76//! stack pointer and the first thing an array like this does is move the stack pointer down over
77//! them. The frame asks for the convention's alignment even when nothing in it wanted that much, so
78//! that the stack pointer is on a multiple of it when the body starts and stays on one as each
79//! array rounds its own size up. And the bytes the array hands out start above the outgoing
80//! argument area rather than at the stack pointer, because that area stays at the bottom of the
81//! frame wherever the bottom has moved to, which is what [`Frame::below`] is for.
82//!
83//! An array asking for more alignment than that is not a realignment of the frame, and nothing
84//! here has to know about it. [`crate::expand::rounds`] asks for the alignment in extra bytes and
85//! hands out an address inside them, so the stack pointer moves by a multiple of the convention's
86//! alignment as it always did and the frame is an ordinary growing one.
87//!
88//! Realigning and growing together is the one combination that is not here. After the prologue has
89//! forced an alignment the distance from the frame pointer to the body's stack pointer is already
90//! not a constant, so there is no register left for the rest of the frame to be counted from, and
91//! what fixes that is a second pointer held for the purpose. The lowering refuses that pair rather
92//! than this guessing at it.
93//!
94//! # Late
95//!
96//! Where in the prologue the frame pointer is established is the platform's answer rather than this
97//! file's, and [`rucc_target::CallRegs::late_frame_pointer`] is where the reason for it is written
98//! down. On Windows it goes up after the frame has been taken rather than before, because the
99//! unwind record there cannot describe the other order, and that moves it: it holds a copy of the
100//! body's stack pointer rather than the address of the caller's copy of itself.
101//!
102//! Which is the easier of the two to lay out rather than the harder. Every offset here is from the
103//! body's stack pointer already, so in a frame like this the frame pointer holds exactly what those
104//! offsets are counted from, and a frame that grows needs no adjustment at all where the other
105//! order needs the whole frame and every push taken off. [`Frame::late`] is what says which it is.
106//!
107//! A realigned frame takes the late order too, the way clang lays one out for Windows. The early
108//! order forces the alignment after the pushes and before the frame, which leaves the pushes at a
109//! distance from the body's stack pointer that is not a constant, and the record has nothing to
110//! count them from. So the prologue does everything the record describes first, the pushes, the
111//! frame, the pointer and the vector saves, and only then rounds the stack pointer down. The record
112//! counts from the frame pointer and never sees the rounding. The body counts from the rounded
113//! stack pointer, and the epilogue puts the stack pointer back from the frame pointer before it
114//! does anything else. The vector saves go at the top of such a frame rather than the bottom,
115//! since the body's view of the frame moves down by up to the alignment and must not reach them.
116//! This was `tamnd/rucc#1422`.
117
118use rucc_mir::{Constraint, Func};
119use rucc_regalloc::Allocation;
120use rucc_regalloc::assign::Place;
121use rucc_target::{CallRegs, PhysReg, RegClass, RegFile};
122
123use crate::slots::{Cell, Slots};
124
125/// One register the prologue puts away in the frame, and where in the frame it goes.
126///
127/// A pushed register does not need one of these, because where it goes is wherever the stack
128/// pointer had reached, and the epilogue pops them back in the opposite order without having to
129/// know. A register that is stored rather than pushed does need one.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct Save {
132    /// The register.
133    pub reg: PhysReg,
134    /// Where it goes, from the stack pointer in the body of the function.
135    pub at: i32,
136}
137
138/// Where the arguments the caller passed on the stack are, and which register reaches them.
139///
140/// Two fields rather than one number because a realigned frame has no constant distance from its
141/// stack pointer to the caller's. Forcing the alignment threw that distance away, and the frame
142/// pointer is what still reaches the caller's stack afterwards, which is why a realigned frame is
143/// made to keep one. So there is always an answer, and which register it is counted from is part of
144/// it rather than something the reader is left to work out.
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub struct Incoming {
147    /// How far above that register the first argument passed on the stack is.
148    pub at: i32,
149    /// Whether the register is the frame pointer rather than the stack pointer.
150    pub through_frame_pointer: bool,
151}
152
153impl Incoming {
154    /// That far above the stack pointer as it stands in the body of the function, which is where
155    /// every other offset in a frame is from.
156    #[must_use]
157    pub fn from_stack(at: i32) -> Self {
158        Self { at, through_frame_pointer: false }
159    }
160
161    /// That far above the frame pointer, which is the only way a realigned frame reaches back.
162    #[must_use]
163    pub fn from_frame(at: i32) -> Self {
164        Self { at, through_frame_pointer: true }
165    }
166}
167
168/// A piece of memory the function needs for its own use, which is what an `alloca` becomes.
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub struct Local {
171    /// How many bytes of it there are.
172    pub size: u32,
173    /// What its address has to be a multiple of.
174    pub align: u32,
175}
176
177/// Everything about a function's frame that does not come out of its allocation.
178#[derive(Debug, Clone, Copy)]
179pub struct Layout<'a> {
180    /// Where the convention this function is compiled for puts things.
181    pub conv: &'a CallRegs,
182    /// The registers the target has, which is what says how wide a spill slot of a class is.
183    pub file: RegFile,
184    /// The memory the function asked for itself, in the order it wants it reported back.
185    pub locals: &'a [Local],
186    /// How many bytes the widest call in the function needs for arguments it passes on the stack.
187    pub outgoing: u32,
188    /// Whether the function calls nothing, which is what the alignment and the red zone turn on.
189    pub leaf: bool,
190    /// Whether the function keeps a frame pointer, which `-fno-omit-frame-pointer` asks for and
191    /// which a realigned or a dynamically grown frame requires whatever the flags say.
192    pub frame_pointer: bool,
193    /// Whether the function moves the stack pointer while it runs, which is what a variable length
194    /// array does and what the rest of the frame then has to be reached around.
195    ///
196    /// See `Growing` in the module documentation. A frame like this keeps a frame pointer, takes
197    /// its bytes rather than living in the red zone, and reports every offset in its body from the
198    /// frame pointer, because the stack pointer stops being somewhere a constant reaches from.
199    pub grows: bool,
200    /// Whether the red zone may be used at all, which `-mno-red-zone` and every kernel turns off.
201    pub red_zone: bool,
202    /// Whether the frame holds a stack protector's canary, which `-fstack-protector` and the
203    /// function's own attribute decide between them.
204    ///
205    /// A protected frame is never a leaf, whatever the function called, because the check at the
206    /// end of it calls when it fails. The caller sets `leaf` accordingly rather than this working
207    /// it out, so that there is one place a frame learns whether it owes an aligned stack pointer.
208    pub protect: bool,
209    /// Whether the function is written without a prologue or an epilogue, which
210    /// `__attribute__((naked))` asks for.
211    ///
212    /// A frame like this is empty and nothing is written around the body. No register is put away,
213    /// because the program said it would do that itself and the first thing one of these usually
214    /// does is read something the saving would have moved. No bytes are taken, because taking them
215    /// is the prologue's job and there is no prologue, which is why a naked function that wants any
216    /// is refused rather than given a frame nothing sets up. See [`Frame::of`].
217    pub naked: bool,
218    /// Whether the prologue puts the general purpose registers it saves on the stack two at a time,
219    /// which is what a machine with a pair instruction does and what [`crate::pipeline`] sets from
220    /// the target's `FrameInsts::pair`.
221    ///
222    /// On AArch64 a push moves the stack pointer sixteen bytes to keep it aligned, so a register
223    /// pushed alone wastes eight of them. Two in one `stp` fill the sixteen, which is how every
224    /// compiler for the machine saves `x19` to `x28`, and a count that is odd leaves the last one
225    /// alone.
226    pub pairs: bool,
227    /// Which locals and spill slots share their bytes with which, or `None` for a frame where
228    /// every one of them gets a run of its own.
229    ///
230    /// Worked out in [`crate::slots`], because what may share is a question about liveness and this
231    /// file is about arithmetic. `None` is the layout there was before that pass existed and is
232    /// what `-fstack-reuse=none` asks for.
233    pub share: Option<&'a Slots>,
234    /// How many bytes the prologue takes before anything else to home the argument registers into,
235    /// which is the area a variadic function on Windows on AArch64 makes for itself.
236    ///
237    /// It sits between the caller's stack and the frame record, so it moves every distance up to
238    /// the canonical frame address and none of the distances to the caller's arguments, which are
239    /// counted from the bottom of it. See [`Frame::home`].
240    pub home: u32,
241}
242
243impl<'a> Layout<'a> {
244    /// A layout for a function with nothing in it but what its allocation says: a leaf with no
245    /// locals and no calls, which is what every function is until the pieces that produce those
246    /// exist.
247    #[must_use]
248    pub fn new(conv: &'a CallRegs, file: RegFile) -> Self {
249        Self {
250            conv,
251            file,
252            locals: &[],
253            outgoing: 0,
254            leaf: true,
255            frame_pointer: false,
256            grows: false,
257            red_zone: true,
258            protect: false,
259            naked: false,
260            pairs: false,
261            share: None,
262            home: 0,
263        }
264    }
265}
266
267/// What a function's stack looks like while it runs.
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub struct Frame {
270    saved_int: Vec<PhysReg>,
271    saved_sse: Vec<Save>,
272    slots: Vec<i32>,
273    locals: Vec<i32>,
274    canary: Option<i32>,
275    outgoing: u32,
276    below: u32,
277    size: u32,
278    realign: Option<u32>,
279    incoming: Incoming,
280    frame_pointer: bool,
281    late: bool,
282    grows: bool,
283    naked: bool,
284    pairs: bool,
285    usage: u32,
286    home: u32,
287}
288
289impl Frame {
290    /// Works out the frame of a function the allocator has finished with.
291    ///
292    /// # Panics
293    ///
294    /// Panics on a frame of two gigabytes or more, which is a stack no machine here gives a
295    /// thread, and on a local whose alignment is not a power of two.
296    #[must_use]
297    pub fn of(func: &Func, allocation: &Allocation, layout: &Layout<'_>) -> Self {
298        let conv = layout.conv;
299        let word = conv.word;
300        // How far one push moves the stack pointer, which is the word on x86-64 and the whole
301        // alignment on AArch64. A machine where it is the whole alignment is one whose stack
302        // pointer is never allowed off it, and so a leaf there owes itself an aligned frame even
303        // though it owes nobody else one.
304        let push = conv.push;
305        let aligned = push % conv.stack_align == 0;
306        let (saved_int, vectors) = saved(func, allocation, layout);
307
308        // The vector registers are saved in the frame rather than pushed, because no machine here
309        // has an instruction that pushes one. Each takes the part of it a call keeps, which on
310        // AArch64 is the bottom eight bytes, and the whole register everywhere else.
311        let vector = conv.sse_kept.map_or_else(|| width(layout, conv.sse_class), u32::from);
312        let mut top = 0;
313        let mut align = word;
314        let mut saved_sse = Vec::with_capacity(vectors.len());
315        if !vectors.is_empty() {
316            align = align.max(vector);
317        }
318
319        // A frame that grows hands out the bytes above the outgoing area, and what makes that
320        // address usable for anything is the stack pointer being on a multiple of the convention's
321        // alignment when the body starts. Asking for that much here is what buys it: the area below
322        // is padded to `align` and the frame is rounded to land the stack pointer back on it.
323        if layout.grows {
324            align = align.max(conv.stack_align);
325        }
326
327        // One list rather than two, because a local and a spill slot that are never both wanted can
328        // be the same bytes and neither of them can share with something on the other list if the
329        // two lists are placed one after the other. See [`crate::slots`]. A layout that was handed
330        // no plan gets the one where nothing shares anything, which is the frame there was before
331        // that pass existed.
332        let apart;
333        let plan = match layout.share {
334            Some(plan) => plan,
335            None => {
336                apart = Slots::apart(layout.locals, &widths(layout, allocation));
337                &apart
338            }
339        };
340        // Normally they go at the bottom. In a frame that realigns with the pointer established
341        // late they go at the top instead, because the prologue stores them before it forces the
342        // alignment and the body counts from the stack pointer after it, which is anywhere up to
343        // the alignment lower. With the saves above everything the body reaches, the two never
344        // meet whichever way the rounding went. See `Late` above.
345        let wanted = plan.cells().iter().map(|cell| cell.align).max().unwrap_or(0);
346        let last = conv.late_frame_pointer && align.max(wanted) > conv.stack_align;
347        if !last {
348            for &reg in &vectors {
349                saved_sse.push(Save { reg, at: offset(top) });
350                top += vector;
351            }
352        }
353
354        let mut cells = Vec::with_capacity(plan.cells().len());
355        let mut order: Vec<usize> = (0..plan.cells().len()).collect();
356        // Widest alignment first, so that placing each one straight after the last never leaves a
357        // hole bigger than the alignment the next one asked for. Within one alignment, the cells
358        // that are a whole number of it go before the ones that are not, because a cell that ends
359        // part way through leaves a hole in front of the next cell that asked for the same
360        // alignment and none at all in front of a narrower one. A cell shared by a wide thing and
361        // a strict one is exactly how a size that is not a multiple of its own alignment arises,
362        // so without this a frame could come out larger for sharing than it was for not.
363        order.sort_by_key(|&cell| {
364            let Cell { size, align } = plan.cells()[cell];
365            (std::cmp::Reverse(align), size % align != 0)
366        });
367        cells.resize(plan.cells().len(), 0);
368        for cell in order {
369            let Cell { size, align: want } = plan.cells()[cell];
370            assert!(
371                want.is_power_of_two(),
372                "a local aligned to something that is not a power of 2"
373            );
374            align = align.max(want);
375            top = top.next_multiple_of(want);
376            cells[cell] = offset(top);
377            top += size;
378        }
379
380        // Read back out to the two lists the rest of the compiler asks its questions in. A cell
381        // several things share gives all of them the same offset, which is the whole point of it.
382        let placed = |cell: Option<usize>| cells[cell.expect("a plan covering every slot")];
383        let mut locals: Vec<i32> =
384            (0..layout.locals.len()).map(|local| placed(plan.local(local))).collect();
385        let mut slots: Vec<i32> = (0..allocation.assignment.slots().len())
386            .map(|slot| placed(plan.slot(u32::try_from(slot).expect("a frame"))))
387            .collect();
388
389        // Above everything the function can reach through a local, which is the whole point of it.
390        // A write that runs off the end of an array in this frame passes the canary before it
391        // reaches the saved registers and the return address, so the check at the end of the
392        // function sees a word that changed rather than a return that has already been taken.
393        let mut canary = None;
394        if layout.protect {
395            top = top.next_multiple_of(word);
396            canary = Some(offset(top));
397            top += word;
398        }
399        if last {
400            top = top.next_multiple_of(vector);
401            for &reg in &vectors {
402                saved_sse.push(Save { reg, at: offset(top) });
403                top += vector;
404            }
405        }
406
407        // A call reads its stack arguments from the stack pointer upward, so the outgoing area is
408        // at the bottom of the frame and its size is what shifts everything else.
409        let outgoing = if layout.leaf { 0 } else { layout.outgoing.max(conv.shadow) };
410        // Everything above it was placed as though it were not there, so moving it up by the size
411        // of the area is what would break its alignment. The area is padded to the widest
412        // alignment anything above it asked for, which costs at most that many bytes once and
413        // costs nothing at all in the usual frame, where the area is a multiple of it already.
414        // What the padding must not do is move the area itself: the callee reads its arguments
415        // from the stack pointer, so the bottom of the area is the stack pointer whatever is
416        // above it.
417        let shifted = outgoing.next_multiple_of(align);
418        let body = (top + shifted).next_multiple_of(word);
419
420        let realign = (align > conv.stack_align).then_some(align);
421        // Refused by [`crate::pipeline`] before anything gets here, because the two of them together
422        // want one register twice. See `Growing` above.
423        assert!(
424            !(layout.grows && realign.is_some()),
425            "a frame that grows and forces its alignment needs a second base register"
426        );
427        // Two frames keep one whatever the flags asked for, and each of them for its own version of
428        // the same reason: the prologue is about to leave the stack pointer somewhere no constant
429        // reaches the rest of the frame from, and the frame pointer is the register that still
430        // does. Forcing an alignment is one of the two and growing while the function runs is the
431        // other.
432        //
433        // A function that calls something on a machine whose call leaves the return address in a
434        // register is a third. The call writes over that register, so the prologue has to put it
435        // away, and it goes with the frame pointer as the one frame record the machine's unwinders
436        // and `__builtin_frame_address` expect to find.
437        let frame_pointer = layout.frame_pointer
438            || realign.is_some()
439            || layout.grows
440            || (!layout.leaf && conv.link.is_some());
441        // Where in the prologue the pointer is established, which is the platform's answer. See
442        // `Late` above.
443        let late = conv.late_frame_pointer;
444
445        // Where the stack pointer sits once the prologue has finished pushing: one return address
446        // short of aligned when the function starts, and one push further off for every push. The
447        // frame pointer is a push like any other here, which is why this is asked after the frames
448        // that keep one without being asked to have said so.
449        let pushed = u32::from(frame_pointer) + groups(saved_int.len(), layout.pairs);
450        let entry = wrap(conv.stack_align, conv.return_address);
451        let after = (entry + wrap(conv.stack_align, push * pushed)) % conv.stack_align;
452
453        // A frame that grows cannot be one of the free ones. The red zone is the bytes below the
454        // stack pointer, and the first thing a variable length array does is move the stack pointer
455        // down over them, so what was in the zone would be handed out twice.
456        let free = layout.leaf
457            && layout.red_zone
458            && realign.is_none()
459            && !layout.grows
460            && align <= word
461            && body <= conv.red_zone;
462        let size = match realign {
463            _ if free => 0,
464            // Once the prologue has forced the alignment, keeping the frame a multiple of it keeps
465            // everything in the frame aligned too. A late pointer forces it after the frame is
466            // taken, so the frame itself only has to land where any other frame does.
467            Some(to) if !late => body.next_multiple_of(to),
468            // A leaf owes nobody an aligned stack pointer, so it takes exactly what it uses.
469            None if layout.leaf && align <= word && !aligned => body,
470            // The smallest frame that lands the stack pointer back on a multiple of the alignment
471            // given where the pushes left it.
472            _ => body + (after + conv.stack_align - body % conv.stack_align) % conv.stack_align,
473        };
474
475        // With the stack pointer left where it was, the areas are the same areas in the same order
476        // and they are below it rather than above it.
477        //
478        // A frame that grows is counted from the frame pointer instead, which is the same areas in
479        // the same order with one more constant taken off: the prologue pushed the registers and
480        // then took the frame, so the body's stack pointer is that far below where the frame
481        // pointer was set. That distance is what a variable length array destroys and the frame
482        // pointer is what is left, which is why a growing frame keeps one.
483        //
484        // Unless the pointer is established late, where there is nothing to take off: the prologue
485        // points it at the stack pointer once the frame is whole, so the two hold the same address
486        // when the body starts and every distance from one is a distance from the other.
487        let mut shift = if free { -offset(body) } else { offset(shifted) };
488        if layout.grows && !late {
489            shift -= offset(size + push * groups(saved_int.len(), layout.pairs));
490        }
491        for at in slots
492            .iter_mut()
493            .chain(locals.iter_mut())
494            .chain(canary.iter_mut())
495            .chain(saved_sse.iter_mut().map(|save| &mut save.at))
496        {
497            *at += shift;
498        }
499
500        // What `-fstack-usage` reports, worked out here because this is the one place every term
501        // of it is in hand. See [`Frame::usage`] for what the number is. Every push comes before
502        // the prologue forces an alignment, so the pushes are what gets rounded up.
503        let pushes = conv.return_address + push * pushed;
504        let usage = realign.map_or(pushes, |to| pushes.next_multiple_of(to)) + size + layout.home;
505
506        Self {
507            saved_int,
508            saved_sse,
509            slots,
510            locals,
511            canary,
512            outgoing,
513            below: shifted,
514            size,
515            realign,
516            incoming: match () {
517                // A pointer established late holds what the body's stack pointer holds, so the
518                // caller's stack is the whole frame and every push above it, which is the same
519                // number a frame with no pointer counts from the stack pointer.
520                () if late && (layout.grows || realign.is_some()) => {
521                    Incoming::from_frame(offset(size + push * pushed + conv.return_address))
522                }
523                // The prologue saves the frame pointer before it does anything else and points it
524                // at where it saved it, so the caller's stack is one push for that and one return
525                // address above it, whatever the prologue did to the stack pointer afterwards.
526                () if realign.is_some() || layout.grows => {
527                    Incoming::from_frame(offset(push + conv.return_address))
528                }
529                () => Incoming::from_stack(offset(size + push * pushed + conv.return_address)),
530            },
531            frame_pointer,
532            late,
533            grows: layout.grows,
534            naked: layout.naked,
535            pairs: layout.pairs,
536            usage,
537            home: layout.home,
538        }
539    }
540
541    /// The general purpose registers the prologue pushes, in the order it pushes them.
542    ///
543    /// The frame pointer is not among them even when the convention calls it a saved register,
544    /// because a function that keeps one saves it as part of setting it up.
545    #[must_use]
546    pub fn saved_int(&self) -> &[PhysReg] {
547        &self.saved_int
548    }
549
550    /// The same registers as the pushes that put them on the stack, one or two to a push, in the
551    /// order the prologue makes them. See [`Layout::pairs`].
552    pub fn pushed_int(&self) -> std::slice::Chunks<'_, PhysReg> {
553        self.saved_int.chunks(if self.pairs { 2 } else { 1 })
554    }
555
556    /// The vector registers the prologue stores into the frame, and where each of them goes.
557    #[must_use]
558    pub fn saved_sse(&self) -> &[Save] {
559        &self.saved_sse
560    }
561
562    /// Where a spill slot is, from the stack pointer in the body of the function.
563    #[must_use]
564    pub fn slot(&self, slot: u32) -> Option<i32> {
565        self.slots.get(usize::try_from(slot).ok()?).copied()
566    }
567
568    /// Where a local is, from the stack pointer in the body of the function.
569    #[must_use]
570    pub fn local(&self, local: usize) -> Option<i32> {
571        self.locals.get(local).copied()
572    }
573
574    /// Where a local is, from the call frame address, which is what a debugger counts from.
575    ///
576    /// The call frame address is the stack pointer the caller held when it made the call, and
577    /// [`Frame::incoming`] is already the distance up to it, since the first argument passed on the
578    /// stack sits there. So the answer is one subtraction, and it is a negative number, because the
579    /// frame is below the address the call was made from.
580    ///
581    /// `None` in a frame whose alignment the prologue had to force, where there is no answer to
582    /// give. Rounding the stack pointer down throws away however far it was from where the caller
583    /// left it, so the distance from the body's stack pointer up to the call frame address is not a
584    /// constant in such a function, and the two numbers subtracted here are counted from different
585    /// registers on top of that. What the locals of such a function want is a location counted from
586    /// the frame pointer, which is a different expression from the one a frame base gives.
587    ///
588    /// `None` as well for a local this frame never placed.
589    #[must_use]
590    pub fn from_frame_base(&self, local: usize) -> Option<i32> {
591        if self.realign.is_some() {
592            return None;
593        }
594        Some(self.local(local)? - self.incoming.at)
595    }
596
597    /// Where a spill slot is, from the call frame address, which is what a debugger counts from.
598    ///
599    /// The same subtraction [`Frame::from_frame_base`] makes and `None` in the same function, for
600    /// the same reasons. It is the other half of the same question: a local the program named is
601    /// either in the part of the frame the front end asked for or in the part the allocator ran
602    /// out of registers into, and a debugger wants both counted from the same place.
603    #[must_use]
604    pub fn slot_from_frame_base(&self, slot: u32) -> Option<i32> {
605        if self.realign.is_some() {
606            return None;
607        }
608        Some(self.slot(slot)? - self.incoming.at)
609    }
610
611    /// Where the stack protector's canary is, from the stack pointer in the body of the function,
612    /// or `None` in a frame that has none.
613    #[must_use]
614    pub fn canary(&self) -> Option<i32> {
615        self.canary
616    }
617
618    /// How many bytes the prologue takes off the stack pointer, which is nothing for a function
619    /// small enough and quiet enough to live in the red zone.
620    #[must_use]
621    pub fn size(&self) -> u32 {
622        self.size
623    }
624
625    /// How many bytes at the bottom of the frame belong to the arguments of calls this function
626    /// makes, which is where the shadow space goes on Windows.
627    #[must_use]
628    pub fn outgoing(&self) -> u32 {
629        self.outgoing
630    }
631
632    /// How many bytes at the bottom of the frame nothing else may be placed in, which is that area
633    /// padded to the alignment everything above it asked for.
634    ///
635    /// What a variable length array has to step over. It takes its bytes off the stack pointer,
636    /// which leaves them at the bottom of the frame where the next call is going to write its
637    /// arguments, so the address it hands out is this far above the stack pointer rather than the
638    /// stack pointer itself.
639    #[must_use]
640    pub fn below(&self) -> u32 {
641        self.below
642    }
643
644    /// Whether the function moves the stack pointer while it runs.
645    ///
646    /// Every offset in the body of such a frame is from the frame pointer rather than from the
647    /// stack pointer, because a variable length array leaves the stack pointer somewhere no
648    /// constant reaches the rest of the frame from. See `Growing` in the module documentation.
649    #[must_use]
650    pub fn grows(&self) -> bool {
651        self.grows
652    }
653
654    /// What the prologue has to force the stack pointer to be a multiple of, when a local wants
655    /// more alignment than a call leaves it with.
656    #[must_use]
657    pub fn realign(&self) -> Option<u32> {
658        self.realign
659    }
660
661    /// Where the first argument the caller passed on the stack is, and which register reaches it.
662    ///
663    /// The only offset here that is not always from the stack pointer. A realigned frame counts
664    /// from the frame pointer instead, because forcing the alignment threw away however far the
665    /// caller's stack pointer was from where the prologue wanted it, and the frame pointer is what
666    /// reaches the caller's stack afterwards.
667    #[must_use]
668    pub fn incoming(&self) -> Incoming {
669        self.incoming
670    }
671
672    /// Whether the function keeps a frame pointer.
673    #[must_use]
674    pub fn frame_pointer(&self) -> bool {
675        self.frame_pointer
676    }
677
678    /// Whether nothing at all is to be written around the body, which `__attribute__((naked))`
679    /// asks for. See [`Layout::naked`].
680    ///
681    /// Such a frame is empty, since [`crate::pipeline`] refuses a naked function that wanted any
682    /// bytes rather than handing it a frame no prologue sets up. What is left for this to say is
683    /// that the prologue and the epilogue are not to be written, and the epilogue is the point:
684    /// the `ret` at the end of a function is written there, and a naked function ends where its
685    /// own text ends.
686    #[must_use]
687    pub fn naked(&self) -> bool {
688        self.naked
689    }
690
691    /// How many bytes the prologue takes before it saves anything, to home the argument registers
692    /// into, which is sixty four in a variadic function on Windows on AArch64 and nothing anywhere
693    /// else.
694    ///
695    /// The distance to the caller's arguments does not include it, because those are counted from
696    /// the bottom of this area: the homed registers are the first eight words of the list and what
697    /// the caller left on the stack follows them. What it does move is the canonical frame address,
698    /// which is that many bytes further above everything the prologue saves.
699    #[must_use]
700    pub fn home(&self) -> u32 {
701        self.home
702    }
703
704    /// Whether the prologue points the frame pointer at the frame after taking it rather than
705    /// before, which is [`rucc_target::CallRegs::late_frame_pointer`]. See `Late` in the module
706    /// documentation.
707    #[must_use]
708    pub fn late(&self) -> bool {
709        self.late
710    }
711
712    /// How many bytes of stack the function uses, not counting what a variable length array or an
713    /// `alloca` takes while it runs, which is the number `-fstack-usage` reports.
714    ///
715    /// Counted the way gcc counts its own frames, so that the two compilers' reports can be
716    /// compared function by function: the distance from the stack pointer the caller held just
717    /// before its call instruction down to where the prologue leaves the stack pointer. That is
718    /// the return address when the call pushes one, every register the prologue pushes, the frame
719    /// pointer among them, and the size the prologue subtracts, which already holds the saved
720    /// vector registers, the locals, the spill slots, the canary and the outgoing argument area.
721    /// Bytes a leaf keeps in the red zone are not counted, and gcc does not count them either.
722    ///
723    /// A frame whose alignment the prologue forces has the pushes rounded up to that alignment
724    /// before the rest is added, which is gcc's rule for the same frame. It is exact when the
725    /// caller's stack pointer happened to be aligned that far already, and otherwise it is off by
726    /// less than the alignment in one direction or the other, since how much the rounding throws
727    /// away depends on where the caller's stack pointer was.
728    ///
729    /// The two numbers agree on what they count and not always on the bytes: a function whose
730    /// locals rucc keeps in fewer or more slots than gcc does comes out smaller or larger by that
731    /// much. A frame that [`Frame::grows`] is reported as `dynamic` beside this, the same as gcc.
732    #[must_use]
733    pub fn usage(&self) -> u32 {
734        self.usage
735    }
736}
737
738/// The registers a call preserves that this function writes anyway, so the prologue has to put
739/// them back.
740///
741/// The rewritten function is what is read here rather than the assignment, because a spilled value
742/// is reloaded into a scratch register that no assignment mentions, and a scratch register the
743/// convention preserves is one this has to find.
744///
745/// Nothing at all in a naked function, which is the whole of what the attribute asks for. Such a
746/// function names `%rbx` and `%rbp` in its own text and means the registers rather than places to
747/// keep something, and putting them away in front of it would be the compiler answering a question
748/// the program did not ask. See [`Layout::naked`].
749fn saved(
750    func: &Func,
751    allocation: &Allocation,
752    layout: &Layout<'_>,
753) -> (Vec<PhysReg>, Vec<PhysReg>) {
754    if layout.naked {
755        return (Vec::new(), Vec::new());
756    }
757    let mut used: Vec<(RegClass, PhysReg)> = Vec::new();
758    let mut note = |class: RegClass, at: PhysReg| {
759        if !used.contains(&(class, at)) {
760            used.push((class, at));
761        }
762    };
763    for block in func.blocks() {
764        for inst in func.insts(block) {
765            for operand in &func[func[inst].operands] {
766                // Not the top of a register a call writes, which is nothing the caller counts on
767                // and nothing this function owes anybody. See [`Constraint::Above`].
768                if matches!(operand.constraint, Constraint::Above(_)) {
769                    continue;
770                }
771                if let Some(at) = operand.reg.phys() {
772                    note(operand.class, at);
773                }
774            }
775        }
776    }
777    for edit in &allocation.edits {
778        for place in [edit.mov.from, edit.mov.to] {
779            if let Place::Reg(at) = place {
780                note(edit.class, at);
781            }
782        }
783    }
784
785    let conv = layout.conv;
786    let wanted = |class: RegClass, at: PhysReg| used.contains(&(class, at));
787    // In the convention's order rather than the order the function happened to reach for them, so
788    // that two functions saving the same registers get the same prologue.
789    let kept = |at: PhysReg| !(layout.frame_pointer && at == conv.frame_pointer);
790    let mut saved_int: Vec<PhysReg> = conv
791        .int_saved
792        .iter()
793        .copied()
794        .filter(|&at| wanted(conv.int_class, at))
795        .filter(|&at| kept(at))
796        .collect();
797    // Each pair two registers next to each other, where the unwind codes can only name such a
798    // pair, by saving the register between two that are not. See `CallRegs::unwind_codes`.
799    if conv.unwind_codes && layout.pairs {
800        adjacent(&mut saved_int, conv.int_saved, kept);
801    }
802    let saved_sse =
803        conv.sse_saved.iter().copied().filter(|&at| wanted(conv.sse_class, at)).collect();
804    (saved_int, saved_sse)
805}
806
807/// Makes each pair of saved registers two that are next to each other in the convention's order,
808/// by saving the one after the first of a pair that is not. `kept` says which registers may be
809/// saved at all.
810fn adjacent(saved: &mut Vec<PhysReg>, order: &[PhysReg], kept: impl Fn(PhysReg) -> bool) {
811    let next = |at: PhysReg| {
812        let place = order.iter().position(|&reg| reg == at)?;
813        order.get(place + 1).copied().filter(|&reg| kept(reg))
814    };
815    let mut first = 0;
816    while first + 1 < saved.len() {
817        if let Some(want) = next(saved[first]).filter(|&want| want != saved[first + 1]) {
818            saved.insert(first + 1, want);
819        }
820        first += 2;
821    }
822}
823
824/// How many bytes a value of a class takes on the stack.
825///
826/// A power of two at least a word wide, because a slot is addressed and an address that is not a
827/// multiple of the size of the thing at it is a fault on some machines and slow on the rest. An
828/// eighty bit `long double` takes sixteen bytes for that reason, which is what every compiler
829/// does with one.
830fn width(layout: &Layout<'_>, class: RegClass) -> u32 {
831    let bits = layout.file.class(class).map_or(0, |info| info.bits);
832    bits.div_ceil(8).max(layout.conv.word).next_power_of_two()
833}
834
835/// How many bytes each of an allocation's spill slots takes on the stack.
836///
837/// The same question the width of one register class is, asked of a whole allocation at once, and
838/// public because [`crate::slots`] needs it to say how big a cell holding a spilled value has to
839/// be, which it has to know before there is a frame to ask.
840#[must_use]
841pub fn widths(layout: &Layout<'_>, allocation: &Allocation) -> Vec<u32> {
842    allocation.assignment.slots().iter().map(|&class| width(layout, class)).collect()
843}
844
845/// How many pushes put that many general purpose registers on the stack.
846fn groups(saved: usize, pairs: bool) -> u32 {
847    let pushes = if pairs { saved.div_ceil(2) } else { saved };
848    u32::try_from(pushes).expect("a frame")
849}
850
851/// How far past a multiple of an alignment a number is, counted the other way: what has to be
852/// added to it to reach the next one.
853fn wrap(align: u32, value: u32) -> u32 {
854    (align - value % align) % align
855}
856
857/// A distance in a frame, as the signed number every offset out of here is.
858fn offset(bytes: u32) -> i32 {
859    i32::try_from(bytes).expect("a frame under two gigabytes")
860}
861
862#[cfg(test)]
863mod tests {
864    use rucc_base::Interner;
865    use rucc_mir::{Opcode, Operand, Reg};
866    use rucc_regalloc::assign::Env;
867    use rucc_target::x86_64::{GPR, RBP, REGS, SYSV, WIN64, XMM};
868
869    use super::*;
870
871    /// An environment offering that many of the convention's registers, with everything after
872    /// them held back as scratch.
873    fn env(conv: &CallRegs, count: usize) -> Env {
874        Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
875    }
876
877    /// A function of that many values, every one of them written before any is read, allocated
878    /// with that many registers to hand out.
879    ///
880    /// Every value is live at the first read, so a count below the number of values is what puts
881    /// the function under enough pressure to spill, and each read wants one value so a reload
882    /// never needs more than one scratch register.
883    fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation) {
884        let mut names = Interner::new();
885        let mut func = Func::new(names.intern("f"));
886        let opcode = Opcode::new(names.intern("x64.nop"));
887        let block = func.create_block();
888        let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
889        for &reg in &regs {
890            func.build(block, opcode).def(reg, GPR).finish();
891        }
892        for &reg in &regs {
893            func.build(block, opcode).uses(reg, GPR).finish();
894        }
895        let allocation = rucc_regalloc::run(&mut func, &env(conv, count), "test", true);
896        (func, allocation)
897    }
898
899    /// What a list of registers is called, which is what an assertion reads.
900    fn named(regs: &[PhysReg]) -> Vec<&'static str> {
901        regs.iter().map(|&reg| REGS.name(GPR, reg).expect("a register")).collect()
902    }
903
904    #[test]
905    fn a_function_that_needs_nothing_of_the_stack_has_no_frame_at_all() {
906        let (func, allocation) = pressure(&SYSV, 2, 4);
907        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
908
909        assert_eq!(frame.size(), 0);
910        assert_eq!(named(frame.saved_int()), Vec::<&str>::new());
911        assert_eq!(frame.slot(0), None);
912        // Nothing between the stack pointer and the return address the call pushed.
913        assert_eq!(frame.incoming(), Incoming::from_stack(8));
914    }
915
916    #[test]
917    fn a_small_leaf_function_puts_its_spills_in_the_red_zone_and_moves_nothing() {
918        let (func, allocation) = pressure(&SYSV, 4, 2);
919        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
920
921        // Two registers for four values that are all live at once, so two are on the stack, and a
922        // leaf function small enough is entitled to the bytes below the stack pointer.
923        assert_eq!(frame.size(), 0);
924        assert_eq!((frame.slot(0), frame.slot(1)), (Some(-16), Some(-8)));
925        assert_eq!(frame.slot(2), None);
926        assert_eq!(frame.incoming(), Incoming::from_stack(8));
927    }
928
929    #[test]
930    fn a_leaf_function_told_it_has_no_red_zone_takes_the_bytes_instead() {
931        let (func, allocation) = pressure(&SYSV, 4, 2);
932        let base = Layout::new(&SYSV, REGS);
933        let frame = Frame::of(&func, &allocation, &Layout { red_zone: false, ..base });
934
935        assert_eq!(frame.size(), 16);
936        assert_eq!((frame.slot(0), frame.slot(1)), (Some(0), Some(8)));
937        assert_eq!(frame.incoming(), Incoming::from_stack(24));
938    }
939
940    #[test]
941    fn a_frame_too_big_for_the_red_zone_takes_the_bytes_whatever_else_is_true() {
942        let (func, allocation) = pressure(&SYSV, 40, 2);
943        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
944
945        // Thirty eight values on the stack is three hundred and four bytes, and the red zone is a
946        // hundred and twenty eight.
947        assert_eq!(frame.size(), 304);
948        assert_eq!(frame.slot(0), Some(0));
949        assert_eq!(frame.slot(37), Some(296));
950    }
951
952    #[test]
953    fn a_function_that_calls_something_leaves_the_stack_pointer_where_a_call_wants_it() {
954        let (func, allocation) = pressure(&SYSV, 4, 2);
955        let base = Layout::new(&SYSV, REGS);
956        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
957
958        // Sixteen bytes of spills, and the call that reached this function left the stack pointer
959        // eight bytes off, so the frame is eight bytes wider than the spills need and every call
960        // this function makes is correctly aligned.
961        assert_eq!(frame.size(), 24);
962        assert_eq!((frame.slot(0), frame.slot(1)), (Some(0), Some(8)));
963        assert_eq!(frame.incoming(), Incoming::from_stack(32));
964    }
965
966    #[test]
967    fn a_push_is_counted_in_the_alignment_the_frame_has_to_produce() {
968        let (func, allocation) = pressure(&SYSV, 12, 12);
969        let base = Layout::new(&SYSV, REGS);
970        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
971
972        // Twelve values reach into the preserved end of the allocation order, so three registers
973        // are pushed, and three pushes plus the return address is a multiple of sixteen already.
974        // The frame is empty and stays empty rather than being padded for the sake of it.
975        assert_eq!(named(frame.saved_int()), ["rbx", "r12", "r13"]);
976        assert_eq!(frame.size(), 0);
977        assert_eq!(frame.incoming(), Incoming::from_stack(32));
978    }
979
980    #[test]
981    fn the_registers_a_call_leaves_alone_are_saved_in_the_order_the_convention_lists_them() {
982        let (func, allocation) = pressure(&SYSV, 13, 13);
983        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
984
985        // Four of them now, in the convention's order rather than the order the allocator handed
986        // them out in, so that two functions saving the same registers get the same prologue.
987        assert_eq!(named(frame.saved_int()), ["rbx", "r12", "r13", "r14"]);
988    }
989
990    #[test]
991    fn a_function_that_keeps_a_frame_pointer_does_not_save_it_twice() {
992        let mut names = Interner::new();
993        let mut func = Func::new(names.intern("f"));
994        let opcode = Opcode::new(names.intern("x64.nop"));
995        let block = func.create_block();
996        // An instruction that names the frame pointer register outright, which is what a lowering
997        // rule for something that has to use it produces.
998        func.build(block, opcode).operand(Operand::write(Reg::physical(RBP), GPR)).finish();
999        let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4), "test", true);
1000        let base = Layout::new(&SYSV, REGS);
1001
1002        let kept = Frame::of(&func, &allocation, &Layout { frame_pointer: true, ..base });
1003        let dropped = Frame::of(&func, &allocation, &base);
1004
1005        // `rbp` is a register SysV preserves, so a function that leaves it alone saves it in the
1006        // ordinary way, and a function that keeps a frame pointer in it saves it as part of
1007        // setting the frame pointer up instead.
1008        assert_eq!(named(dropped.saved_int()), ["rbp"]);
1009        assert_eq!(named(kept.saved_int()), Vec::<&str>::new());
1010        assert!(kept.frame_pointer());
1011    }
1012
1013    #[test]
1014    fn locals_are_placed_widest_alignment_first_and_reported_in_the_order_they_arrived() {
1015        let (func, allocation) = pressure(&SYSV, 2, 4);
1016        let locals = [
1017            Local { size: 1, align: 1 },
1018            Local { size: 16, align: 16 },
1019            Local { size: 8, align: 8 },
1020        ];
1021        let base = Layout::new(&SYSV, REGS);
1022        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
1023
1024        // The sixteen byte one is placed first, so nothing is padded to reach it, and the one
1025        // byte one goes last where the padding after it costs nothing.
1026        assert_eq!((frame.local(1), frame.local(2), frame.local(0)), (Some(0), Some(16), Some(24)));
1027        assert_eq!(frame.local(3), None);
1028        // A local wanting sixteen byte alignment is more than the stack pointer has for free, so
1029        // the frame is taken rather than the red zone used, and it is padded to keep the local
1030        // where it was put.
1031        assert_eq!(frame.size(), 40);
1032        assert_eq!(frame.realign(), None);
1033    }
1034
1035    #[test]
1036    fn a_local_is_counted_from_the_call_frame_address_wherever_the_frame_was_put() {
1037        let (func, allocation) = pressure(&SYSV, 2, 4);
1038        let base = Layout::new(&SYSV, REGS);
1039
1040        let locals = [
1041            Local { size: 1, align: 1 },
1042            Local { size: 16, align: 16 },
1043            Local { size: 8, align: 8 },
1044        ];
1045        let taken = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
1046
1047        // The frame is forty bytes and the return address is eight more, so the call frame address
1048        // is forty eight above the stack pointer and every local is that much less than wherever
1049        // the layout put it. The one byte one is nearest, at the top of the frame.
1050        assert_eq!(taken.incoming(), Incoming::from_stack(48));
1051        assert_eq!(taken.from_frame_base(1), Some(-48));
1052        assert_eq!(taken.from_frame_base(2), Some(-32));
1053        assert_eq!(taken.from_frame_base(0), Some(-24));
1054        assert_eq!(taken.from_frame_base(3), None);
1055
1056        // And a leaf small enough to live in the red zone takes no frame at all, so its stack
1057        // pointer is still one return address below the call frame address and its local is below
1058        // that. The same subtraction answers both, which is the point of doing it this way.
1059        let one = [Local { size: 8, align: 8 }];
1060        let free = Frame::of(&func, &allocation, &Layout { locals: &one, ..base });
1061
1062        assert_eq!(free.size(), 0);
1063        assert_eq!(free.incoming(), Incoming::from_stack(8));
1064        assert_eq!(free.from_frame_base(0), Some(-16));
1065    }
1066
1067    #[test]
1068    fn a_realigned_frame_is_no_constant_distance_from_the_call_frame_address() {
1069        let (func, allocation) = pressure(&SYSV, 2, 4);
1070        let locals = [Local { size: 64, align: 32 }];
1071        let base = Layout::new(&SYSV, REGS);
1072        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
1073
1074        // The prologue rounds the stack pointer down to a multiple of thirty two, which throws
1075        // away however far the caller left it from there, so how far the local is below the call
1076        // frame address is a different number every time the function is called.
1077        assert_eq!(frame.realign(), Some(32));
1078        assert_eq!(frame.local(0), Some(0));
1079        assert_eq!(frame.from_frame_base(0), None);
1080    }
1081
1082    #[test]
1083    fn a_local_wanting_more_alignment_than_a_call_gives_makes_the_prologue_force_it() {
1084        let (func, allocation) = pressure(&SYSV, 2, 4);
1085        let locals = [Local { size: 64, align: 32 }];
1086        let base = Layout::new(&SYSV, REGS);
1087        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
1088
1089        assert_eq!(frame.realign(), Some(32));
1090        assert_eq!(frame.local(0), Some(0));
1091        assert_eq!(frame.size(), 64);
1092        // Forcing the alignment throws away how far the caller's stack pointer was from where the
1093        // prologue wanted it, so a frame pointer is needed and the caller's stack is reached
1094        // through it instead: one word for the saved frame pointer and one for the return address.
1095        assert!(frame.frame_pointer());
1096        assert_eq!(frame.incoming(), Incoming::from_frame(16));
1097    }
1098
1099    #[test]
1100    fn the_canary_is_above_every_byte_a_local_or_a_spill_reaches() {
1101        let (func, allocation) = pressure(&SYSV, 4, 2);
1102        let locals = [Local { size: 16, align: 16 }, Local { size: 8, align: 8 }];
1103        let base = Layout::new(&SYSV, REGS);
1104        let there = Layout { leaf: false, locals: &locals, protect: true, ..base };
1105        let frame = Frame::of(&func, &allocation, &there);
1106
1107        // Two spill slots at the bottom, then the two locals, then the canary above all four. That
1108        // order is the whole mechanism: a write that runs off the end of either local passes the
1109        // canary before it reaches the saved registers and the return address.
1110        let canary = frame.canary().expect("a protected frame has a slot");
1111        for below in [frame.slot(0), frame.slot(1), frame.local(0), frame.local(1)] {
1112            assert!(below.expect("a slot that was asked for") < canary);
1113        }
1114        assert_eq!(canary, 40);
1115        // Forty eight bytes of areas, and then the eight that put the stack pointer back where a
1116        // call wants it, because the arm the check fails on makes one.
1117        assert_eq!(frame.size(), 56);
1118        assert_eq!((frame.size() + SYSV.return_address) % SYSV.stack_align, 0);
1119    }
1120
1121    #[test]
1122    fn a_frame_with_no_protector_has_no_slot_for_a_canary() {
1123        let (func, allocation) = pressure(&SYSV, 2, 4);
1124        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
1125
1126        assert_eq!(frame.canary(), None);
1127    }
1128
1129    #[test]
1130    fn a_call_reads_its_stack_arguments_from_the_bottom_of_the_frame() {
1131        let (func, allocation) = pressure(&SYSV, 4, 2);
1132        let base = Layout::new(&SYSV, REGS);
1133        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, outgoing: 24, ..base });
1134
1135        // The outgoing area is at the stack pointer, because that is where the callee will look
1136        // for it, and the spills sit above it.
1137        assert_eq!(frame.outgoing(), 24);
1138        assert_eq!((frame.slot(0), frame.slot(1)), (Some(24), Some(32)));
1139        assert_eq!(frame.size(), 40);
1140    }
1141
1142    /// Moving everything up by the size of the outgoing area is what would break its alignment,
1143    /// so the area is padded to the widest alignment anything above it wanted. The area itself
1144    /// still starts at the stack pointer, because that is the one thing about it that is not this
1145    /// frame's to choose.
1146    #[test]
1147    fn what_is_above_the_outgoing_area_keeps_the_alignment_it_asked_for() {
1148        let (func, allocation) = pressure(&SYSV, 2, 4);
1149        let locals = [Local { size: 16, align: 16 }];
1150        let base = Layout::new(&SYSV, REGS);
1151        let there = Layout { leaf: false, outgoing: 8, locals: &locals, ..base };
1152        let frame = Frame::of(&func, &allocation, &there);
1153
1154        assert_eq!(frame.outgoing(), 8);
1155        assert_eq!(frame.local(0), Some(16));
1156        assert_eq!(frame.size(), 40);
1157        // A call leaves the stack pointer one return address short of aligned and nothing was
1158        // pushed on top of that, so the frame is what puts it back and the local lands aligned.
1159        assert_eq!((frame.size() + SYSV.return_address) % SYSV.stack_align, 0);
1160    }
1161
1162    #[test]
1163    fn a_windows_call_gets_the_thirty_two_bytes_below_it_even_when_it_passes_nothing() {
1164        let (func, allocation) = pressure(&WIN64, 2, 4);
1165        let base = Layout::new(&WIN64, REGS);
1166        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
1167
1168        // Windows has no red zone and every caller reserves thirty two bytes below the call for
1169        // the callee to spill its register arguments into.
1170        assert_eq!(frame.outgoing(), 32);
1171        assert_eq!(frame.size(), 40);
1172        assert_eq!(frame.incoming(), Incoming::from_stack(48));
1173    }
1174
1175    #[test]
1176    fn a_windows_frame_pointer_is_established_after_the_frame_rather_than_before_it() {
1177        let (func, allocation) = pressure(&WIN64, 4, 2);
1178        let base = Layout::new(&WIN64, REGS);
1179        let kept = Frame::of(&func, &allocation, &Layout { frame_pointer: true, ..base });
1180        let dropped = Frame::of(&func, &allocation, &base);
1181
1182        // The unwind record that platform reads cannot describe the other order, so the prologue
1183        // pushes, takes the frame and only then points the pointer at it. What that buys is that
1184        // the pointer holds what the stack pointer holds, so a frame with one and a frame without
1185        // one are the same frame with the same numbers in it.
1186        assert!(kept.frame_pointer());
1187        assert!(kept.late());
1188        assert!(!dropped.frame_pointer());
1189        assert_eq!(kept.size(), dropped.size());
1190        assert_eq!((kept.slot(0), kept.slot(1)), (dropped.slot(0), dropped.slot(1)));
1191        assert_eq!(kept.incoming(), Incoming::from_stack(dropped.incoming().at + 8));
1192    }
1193
1194    #[test]
1195    fn a_windows_frame_that_grows_keeps_the_numbers_it_had_and_changes_the_register() {
1196        let (func, allocation) = pressure(&WIN64, 4, 2);
1197        let base = Layout::new(&WIN64, REGS);
1198        let there = Layout { leaf: false, frame_pointer: true, ..base };
1199        let still = Frame::of(&func, &allocation, &there);
1200        let grown = Frame::of(&func, &allocation, &Layout { grows: true, ..there });
1201
1202        // A frame that grows keeps a pointer whatever the flags asked for, and on this platform
1203        // that pointer is established late, which means it is a copy of the stack pointer as the
1204        // body finds it. So every distance the frame had already worked out from the stack pointer
1205        // is the same distance from the pointer, and growing changes which register the offsets are
1206        // counted from and nothing else. That is the whole of why this frame needs no adjustment.
1207        assert!(grown.grows());
1208        assert!(grown.late());
1209        assert_eq!(grown.size(), still.size());
1210        assert_eq!(grown.outgoing(), still.outgoing());
1211        assert_eq!((grown.slot(0), grown.slot(1)), (still.slot(0), still.slot(1)));
1212        assert_eq!(grown.incoming(), Incoming::from_frame(still.incoming().at));
1213    }
1214
1215    #[test]
1216    fn a_realigned_frame_on_windows_takes_the_late_order_and_rounds_after_it() {
1217        let (func, allocation) = pressure(&WIN64, 2, 4);
1218        let locals = [Local { size: 64, align: 32 }];
1219        let base = Layout::new(&WIN64, REGS);
1220        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
1221
1222        // The pointer goes up after the frame as it does in any other frame here, and the rounding
1223        // comes after that, so the record describes the frame and never sees the rounding. The
1224        // caller's stack is the whole frame above the pointer, the pushes and the return address,
1225        // since the pointer holds where the stack pointer was before it was rounded.
1226        assert_eq!(frame.realign(), Some(32));
1227        assert!(frame.frame_pointer());
1228        assert!(frame.late());
1229        let pushes = 8 * (1 + u32::try_from(frame.saved_int().len()).unwrap()) + 8;
1230        assert_eq!(frame.incoming(), Incoming::from_frame(offset(frame.size() + pushes)));
1231        assert_eq!((frame.size() + pushes) % 16, 0);
1232        assert!(frame.local(0).unwrap() % 32 == 0);
1233    }
1234
1235    #[test]
1236    fn a_slot_is_as_wide_as_the_widest_thing_of_its_class() {
1237        let base = Layout::new(&SYSV, REGS);
1238
1239        assert_eq!(width(&base, GPR), 8);
1240        assert_eq!(width(&base, XMM), 16);
1241        // A long double is eighty bits and takes sixteen bytes, because an address has to be a
1242        // multiple of the size of what is at it.
1243        assert_eq!(width(&base, REGS.class_named("x87").expect("a class")), 16);
1244    }
1245
1246    /// A pair of saved registers that are not next to each other gets the one between, so every
1247    /// pair is one an ARM64 unwind code can name, and a last register left alone stays alone.
1248    #[test]
1249    fn saved_pairs_are_made_adjacent() {
1250        use rucc_target::aarch64::x;
1251        let order: Vec<PhysReg> = (19..=29).map(x).collect();
1252        let mut saved = vec![x(19), x(21), x(22), x(25), x(28)];
1253        adjacent(&mut saved, &order, |reg| reg != x(29));
1254        assert_eq!(saved, [x(19), x(20), x(21), x(22), x(25), x(26), x(28)]);
1255        let mut saved = vec![x(26), x(28)];
1256        adjacent(&mut saved, &order, |reg| reg != x(29));
1257        assert_eq!(saved, [x(26), x(27), x(28)]);
1258    }
1259}