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//! Realigning and growing together is the one combination that is not here. After the prologue has
84//! forced an alignment the distance from the frame pointer to the body's stack pointer is already
85//! not a constant, so there is no register left for the rest of the frame to be counted from, and
86//! what fixes that is a second pointer held for the purpose. The lowering refuses that pair rather
87//! than this guessing at it.
88//!
89//! # Late
90//!
91//! Where in the prologue the frame pointer is established is the platform's answer rather than this
92//! file's, and [`rucc_target::CallRegs::late_frame_pointer`] is where the reason for it is written
93//! down. On Windows it goes up after the frame has been taken rather than before, because the
94//! unwind record there cannot describe the other order, and that moves it: it holds a copy of the
95//! body's stack pointer rather than the address of the caller's copy of itself.
96//!
97//! Which is the easier of the two to lay out rather than the harder. Every offset here is from the
98//! body's stack pointer already, so in a frame like this the frame pointer holds exactly what those
99//! offsets are counted from, and a frame that grows needs no adjustment at all where the other
100//! order needs the whole frame and every push taken off. [`Frame::late`] is what says which it is.
101//!
102//! The realigned frame is the one that cannot have it whatever the platform says. There the
103//! prologue forces the alignment after the pushes, which leaves the pushes at a distance from the
104//! body's stack pointer that is not a constant, so a pointer established after all that gives the
105//! record nothing to count them from. Such a frame keeps the early order and is the one shape on
106//! Windows that still has no record, which is `tamnd/rucc#1422`.
107
108use rucc_mir::Func;
109use rucc_regalloc::Allocation;
110use rucc_regalloc::assign::Place;
111use rucc_target::{CallRegs, PhysReg, RegClass, RegFile};
112
113use crate::slots::{Cell, Slots};
114
115/// One register the prologue puts away in the frame, and where in the frame it goes.
116///
117/// A pushed register does not need one of these, because where it goes is wherever the stack
118/// pointer had reached, and the epilogue pops them back in the opposite order without having to
119/// know. A register that is stored rather than pushed does need one.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub struct Save {
122    /// The register.
123    pub reg: PhysReg,
124    /// Where it goes, from the stack pointer in the body of the function.
125    pub at: i32,
126}
127
128/// Where the arguments the caller passed on the stack are, and which register reaches them.
129///
130/// Two fields rather than one number because a realigned frame has no constant distance from its
131/// stack pointer to the caller's. Forcing the alignment threw that distance away, and the frame
132/// pointer is what still reaches the caller's stack afterwards, which is why a realigned frame is
133/// made to keep one. So there is always an answer, and which register it is counted from is part of
134/// it rather than something the reader is left to work out.
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub struct Incoming {
137    /// How far above that register the first argument passed on the stack is.
138    pub at: i32,
139    /// Whether the register is the frame pointer rather than the stack pointer.
140    pub through_frame_pointer: bool,
141}
142
143impl Incoming {
144    /// That far above the stack pointer as it stands in the body of the function, which is where
145    /// every other offset in a frame is from.
146    #[must_use]
147    pub fn from_stack(at: i32) -> Self {
148        Self { at, through_frame_pointer: false }
149    }
150
151    /// That far above the frame pointer, which is the only way a realigned frame reaches back.
152    #[must_use]
153    pub fn from_frame(at: i32) -> Self {
154        Self { at, through_frame_pointer: true }
155    }
156}
157
158/// A piece of memory the function needs for its own use, which is what an `alloca` becomes.
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub struct Local {
161    /// How many bytes of it there are.
162    pub size: u32,
163    /// What its address has to be a multiple of.
164    pub align: u32,
165}
166
167/// Everything about a function's frame that does not come out of its allocation.
168#[derive(Debug, Clone, Copy)]
169pub struct Layout<'a> {
170    /// Where the convention this function is compiled for puts things.
171    pub conv: &'a CallRegs,
172    /// The registers the target has, which is what says how wide a spill slot of a class is.
173    pub file: RegFile,
174    /// The memory the function asked for itself, in the order it wants it reported back.
175    pub locals: &'a [Local],
176    /// How many bytes the widest call in the function needs for arguments it passes on the stack.
177    pub outgoing: u32,
178    /// Whether the function calls nothing, which is what the alignment and the red zone turn on.
179    pub leaf: bool,
180    /// Whether the function keeps a frame pointer, which `-fno-omit-frame-pointer` asks for and
181    /// which a realigned or a dynamically grown frame requires whatever the flags say.
182    pub frame_pointer: bool,
183    /// Whether the function moves the stack pointer while it runs, which is what a variable length
184    /// array does and what the rest of the frame then has to be reached around.
185    ///
186    /// See `Growing` in the module documentation. A frame like this keeps a frame pointer, takes
187    /// its bytes rather than living in the red zone, and reports every offset in its body from the
188    /// frame pointer, because the stack pointer stops being somewhere a constant reaches from.
189    pub grows: bool,
190    /// Whether the red zone may be used at all, which `-mno-red-zone` and every kernel turns off.
191    pub red_zone: bool,
192    /// Whether the frame holds a stack protector's canary, which `-fstack-protector` and the
193    /// function's own attribute decide between them.
194    ///
195    /// A protected frame is never a leaf, whatever the function called, because the check at the
196    /// end of it calls when it fails. The caller sets `leaf` accordingly rather than this working
197    /// it out, so that there is one place a frame learns whether it owes an aligned stack pointer.
198    pub protect: bool,
199    /// Which locals and spill slots share their bytes with which, or `None` for a frame where
200    /// every one of them gets a run of its own.
201    ///
202    /// Worked out in [`crate::slots`], because what may share is a question about liveness and this
203    /// file is about arithmetic. `None` is the layout there was before that pass existed and is
204    /// what `-fstack-reuse=none` asks for.
205    pub share: Option<&'a Slots>,
206}
207
208impl<'a> Layout<'a> {
209    /// A layout for a function with nothing in it but what its allocation says: a leaf with no
210    /// locals and no calls, which is what every function is until the pieces that produce those
211    /// exist.
212    #[must_use]
213    pub fn new(conv: &'a CallRegs, file: RegFile) -> Self {
214        Self {
215            conv,
216            file,
217            locals: &[],
218            outgoing: 0,
219            leaf: true,
220            frame_pointer: false,
221            grows: false,
222            red_zone: true,
223            protect: false,
224            share: None,
225        }
226    }
227}
228
229/// What a function's stack looks like while it runs.
230#[derive(Debug, Clone, PartialEq, Eq)]
231pub struct Frame {
232    saved_int: Vec<PhysReg>,
233    saved_sse: Vec<Save>,
234    slots: Vec<i32>,
235    locals: Vec<i32>,
236    canary: Option<i32>,
237    outgoing: u32,
238    below: u32,
239    size: u32,
240    realign: Option<u32>,
241    incoming: Incoming,
242    frame_pointer: bool,
243    late: bool,
244    grows: bool,
245}
246
247impl Frame {
248    /// Works out the frame of a function the allocator has finished with.
249    ///
250    /// # Panics
251    ///
252    /// Panics on a frame of two gigabytes or more, which is a stack no machine here gives a
253    /// thread, and on a local whose alignment is not a power of two.
254    #[must_use]
255    pub fn of(func: &Func, allocation: &Allocation, layout: &Layout<'_>) -> Self {
256        let conv = layout.conv;
257        let word = conv.word;
258        let (saved_int, vectors) = saved(func, allocation, layout);
259
260        // The vector registers are saved in the frame rather than pushed, because no machine here
261        // has an instruction that pushes one.
262        let vector = width(layout, conv.sse_class);
263        let mut top = 0;
264        let mut align = word;
265        let mut saved_sse = Vec::with_capacity(vectors.len());
266        for reg in vectors {
267            align = align.max(vector);
268            saved_sse.push(Save { reg, at: offset(top) });
269            top += vector;
270        }
271
272        // A frame that grows hands out the bytes above the outgoing area, and what makes that
273        // address usable for anything is the stack pointer being on a multiple of the convention's
274        // alignment when the body starts. Asking for that much here is what buys it: the area below
275        // is padded to `align` and the frame is rounded to land the stack pointer back on it.
276        if layout.grows {
277            align = align.max(conv.stack_align);
278        }
279
280        // One list rather than two, because a local and a spill slot that are never both wanted can
281        // be the same bytes and neither of them can share with something on the other list if the
282        // two lists are placed one after the other. See [`crate::slots`]. A layout that was handed
283        // no plan gets the one where nothing shares anything, which is the frame there was before
284        // that pass existed.
285        let apart;
286        let plan = match layout.share {
287            Some(plan) => plan,
288            None => {
289                apart = Slots::apart(layout.locals, &widths(layout, allocation));
290                &apart
291            }
292        };
293        let mut cells = Vec::with_capacity(plan.cells().len());
294        let mut order: Vec<usize> = (0..plan.cells().len()).collect();
295        // Widest alignment first, so that placing each one straight after the last never leaves a
296        // hole bigger than the alignment the next one asked for. Within one alignment, the cells
297        // that are a whole number of it go before the ones that are not, because a cell that ends
298        // part way through leaves a hole in front of the next cell that asked for the same
299        // alignment and none at all in front of a narrower one. A cell shared by a wide thing and
300        // a strict one is exactly how a size that is not a multiple of its own alignment arises,
301        // so without this a frame could come out larger for sharing than it was for not.
302        order.sort_by_key(|&cell| {
303            let Cell { size, align } = plan.cells()[cell];
304            (std::cmp::Reverse(align), size % align != 0)
305        });
306        cells.resize(plan.cells().len(), 0);
307        for cell in order {
308            let Cell { size, align: want } = plan.cells()[cell];
309            assert!(
310                want.is_power_of_two(),
311                "a local aligned to something that is not a power of 2"
312            );
313            align = align.max(want);
314            top = top.next_multiple_of(want);
315            cells[cell] = offset(top);
316            top += size;
317        }
318
319        // Read back out to the two lists the rest of the compiler asks its questions in. A cell
320        // several things share gives all of them the same offset, which is the whole point of it.
321        let placed = |cell: Option<usize>| cells[cell.expect("a plan covering every slot")];
322        let mut locals: Vec<i32> =
323            (0..layout.locals.len()).map(|local| placed(plan.local(local))).collect();
324        let mut slots: Vec<i32> = (0..allocation.assignment.slots().len())
325            .map(|slot| placed(plan.slot(u32::try_from(slot).expect("a frame"))))
326            .collect();
327
328        // Above everything the function can reach through a local, which is the whole point of it.
329        // A write that runs off the end of an array in this frame passes the canary before it
330        // reaches the saved registers and the return address, so the check at the end of the
331        // function sees a word that changed rather than a return that has already been taken.
332        let mut canary = None;
333        if layout.protect {
334            top = top.next_multiple_of(word);
335            canary = Some(offset(top));
336            top += word;
337        }
338
339        // A call reads its stack arguments from the stack pointer upward, so the outgoing area is
340        // at the bottom of the frame and its size is what shifts everything else.
341        let outgoing = if layout.leaf { 0 } else { layout.outgoing.max(conv.shadow) };
342        // Everything above it was placed as though it were not there, so moving it up by the size
343        // of the area is what would break its alignment. The area is padded to the widest
344        // alignment anything above it asked for, which costs at most that many bytes once and
345        // costs nothing at all in the usual frame, where the area is a multiple of it already.
346        // What the padding must not do is move the area itself: the callee reads its arguments
347        // from the stack pointer, so the bottom of the area is the stack pointer whatever is
348        // above it.
349        let shifted = outgoing.next_multiple_of(align);
350        let body = (top + shifted).next_multiple_of(word);
351
352        let realign = (align > conv.stack_align).then_some(align);
353        // Refused by [`crate::pipeline`] before anything gets here, because the two of them together
354        // want one register twice. See `Growing` above.
355        assert!(
356            !(layout.grows && realign.is_some()),
357            "a frame that grows and forces its alignment needs a second base register"
358        );
359        // Two frames keep one whatever the flags asked for, and each of them for its own version of
360        // the same reason: the prologue is about to leave the stack pointer somewhere no constant
361        // reaches the rest of the frame from, and the frame pointer is the register that still
362        // does. Forcing an alignment is one of the two and growing while the function runs is the
363        // other.
364        let frame_pointer = layout.frame_pointer || realign.is_some() || layout.grows;
365        // Where in the prologue the pointer is established, which is the platform's answer except
366        // in the one frame that has an answer of its own. See `Late` above.
367        let late = conv.late_frame_pointer && realign.is_none();
368
369        // Where the stack pointer sits once the prologue has finished pushing: one return address
370        // short of aligned when the function starts, and one word further off for every push. The
371        // frame pointer is a push like any other here, which is why this is asked after the two
372        // frames that keep one without being asked to have said so.
373        let pushed = u32::from(frame_pointer) + u32::try_from(saved_int.len()).expect("a frame");
374        let entry = wrap(conv.stack_align, conv.return_address);
375        let after = (entry + wrap(conv.stack_align, word * pushed)) % conv.stack_align;
376
377        // A frame that grows cannot be one of the free ones. The red zone is the bytes below the
378        // stack pointer, and the first thing a variable length array does is move the stack pointer
379        // down over them, so what was in the zone would be handed out twice.
380        let free = layout.leaf
381            && layout.red_zone
382            && realign.is_none()
383            && !layout.grows
384            && align <= word
385            && body <= conv.red_zone;
386        let size = match realign {
387            _ if free => 0,
388            // Once the prologue has forced the alignment, keeping the frame a multiple of it keeps
389            // everything in the frame aligned too.
390            Some(to) => body.next_multiple_of(to),
391            // A leaf owes nobody an aligned stack pointer, so it takes exactly what it uses.
392            None if layout.leaf && align <= word => body,
393            // The smallest frame that lands the stack pointer back on a multiple of the alignment
394            // given where the pushes left it.
395            None => body + (after + conv.stack_align - body % conv.stack_align) % conv.stack_align,
396        };
397
398        // With the stack pointer left where it was, the areas are the same areas in the same order
399        // and they are below it rather than above it.
400        //
401        // A frame that grows is counted from the frame pointer instead, which is the same areas in
402        // the same order with one more constant taken off: the prologue pushed the registers and
403        // then took the frame, so the body's stack pointer is that far below where the frame
404        // pointer was set. That distance is what a variable length array destroys and the frame
405        // pointer is what is left, which is why a growing frame keeps one.
406        //
407        // Unless the pointer is established late, where there is nothing to take off: the prologue
408        // points it at the stack pointer once the frame is whole, so the two hold the same address
409        // when the body starts and every distance from one is a distance from the other.
410        let mut shift = if free { -offset(body) } else { offset(shifted) };
411        if layout.grows && !late {
412            shift -= offset(size) + offset(word) * i32::try_from(saved_int.len()).expect("a frame");
413        }
414        for at in slots
415            .iter_mut()
416            .chain(locals.iter_mut())
417            .chain(canary.iter_mut())
418            .chain(saved_sse.iter_mut().map(|save| &mut save.at))
419        {
420            *at += shift;
421        }
422
423        Self {
424            saved_int,
425            saved_sse,
426            slots,
427            locals,
428            canary,
429            outgoing,
430            below: shifted,
431            size,
432            realign,
433            incoming: match () {
434                // A pointer established late holds what the body's stack pointer holds, so the
435                // caller's stack is the whole frame and every push above it, which is the same
436                // number a frame with no pointer counts from the stack pointer.
437                () if late && layout.grows => {
438                    Incoming::from_frame(offset(size + word * pushed + conv.return_address))
439                }
440                // The prologue saves the frame pointer before it does anything else and points it
441                // at where it saved it, so the caller's stack is one word for that and one return
442                // address above it, whatever the prologue did to the stack pointer afterwards.
443                () if realign.is_some() || layout.grows => {
444                    Incoming::from_frame(offset(word + conv.return_address))
445                }
446                () => Incoming::from_stack(offset(size + word * pushed + conv.return_address)),
447            },
448            frame_pointer,
449            late,
450            grows: layout.grows,
451        }
452    }
453
454    /// The general purpose registers the prologue pushes, in the order it pushes them.
455    ///
456    /// The frame pointer is not among them even when the convention calls it a saved register,
457    /// because a function that keeps one saves it as part of setting it up.
458    #[must_use]
459    pub fn saved_int(&self) -> &[PhysReg] {
460        &self.saved_int
461    }
462
463    /// The vector registers the prologue stores into the frame, and where each of them goes.
464    #[must_use]
465    pub fn saved_sse(&self) -> &[Save] {
466        &self.saved_sse
467    }
468
469    /// Where a spill slot is, from the stack pointer in the body of the function.
470    #[must_use]
471    pub fn slot(&self, slot: u32) -> Option<i32> {
472        self.slots.get(usize::try_from(slot).ok()?).copied()
473    }
474
475    /// Where a local is, from the stack pointer in the body of the function.
476    #[must_use]
477    pub fn local(&self, local: usize) -> Option<i32> {
478        self.locals.get(local).copied()
479    }
480
481    /// Where the stack protector's canary is, from the stack pointer in the body of the function,
482    /// or `None` in a frame that has none.
483    #[must_use]
484    pub fn canary(&self) -> Option<i32> {
485        self.canary
486    }
487
488    /// How many bytes the prologue takes off the stack pointer, which is nothing for a function
489    /// small enough and quiet enough to live in the red zone.
490    #[must_use]
491    pub fn size(&self) -> u32 {
492        self.size
493    }
494
495    /// How many bytes at the bottom of the frame belong to the arguments of calls this function
496    /// makes, which is where the shadow space goes on Windows.
497    #[must_use]
498    pub fn outgoing(&self) -> u32 {
499        self.outgoing
500    }
501
502    /// How many bytes at the bottom of the frame nothing else may be placed in, which is that area
503    /// padded to the alignment everything above it asked for.
504    ///
505    /// What a variable length array has to step over. It takes its bytes off the stack pointer,
506    /// which leaves them at the bottom of the frame where the next call is going to write its
507    /// arguments, so the address it hands out is this far above the stack pointer rather than the
508    /// stack pointer itself.
509    #[must_use]
510    pub fn below(&self) -> u32 {
511        self.below
512    }
513
514    /// Whether the function moves the stack pointer while it runs.
515    ///
516    /// Every offset in the body of such a frame is from the frame pointer rather than from the
517    /// stack pointer, because a variable length array leaves the stack pointer somewhere no
518    /// constant reaches the rest of the frame from. See `Growing` in the module documentation.
519    #[must_use]
520    pub fn grows(&self) -> bool {
521        self.grows
522    }
523
524    /// What the prologue has to force the stack pointer to be a multiple of, when a local wants
525    /// more alignment than a call leaves it with.
526    #[must_use]
527    pub fn realign(&self) -> Option<u32> {
528        self.realign
529    }
530
531    /// Where the first argument the caller passed on the stack is, and which register reaches it.
532    ///
533    /// The only offset here that is not always from the stack pointer. A realigned frame counts
534    /// from the frame pointer instead, because forcing the alignment threw away however far the
535    /// caller's stack pointer was from where the prologue wanted it, and the frame pointer is what
536    /// reaches the caller's stack afterwards.
537    #[must_use]
538    pub fn incoming(&self) -> Incoming {
539        self.incoming
540    }
541
542    /// Whether the function keeps a frame pointer.
543    #[must_use]
544    pub fn frame_pointer(&self) -> bool {
545        self.frame_pointer
546    }
547
548    /// Whether the prologue points the frame pointer at the frame after taking it rather than
549    /// before, which is [`rucc_target::CallRegs::late_frame_pointer`] and the one frame that cannot
550    /// have it whatever the platform says. See `Late` in the module documentation.
551    #[must_use]
552    pub fn late(&self) -> bool {
553        self.late
554    }
555}
556
557/// The registers a call preserves that this function writes anyway, so the prologue has to put
558/// them back.
559///
560/// The rewritten function is what is read here rather than the assignment, because a spilled value
561/// is reloaded into a scratch register that no assignment mentions, and a scratch register the
562/// convention preserves is one this has to find.
563fn saved(
564    func: &Func,
565    allocation: &Allocation,
566    layout: &Layout<'_>,
567) -> (Vec<PhysReg>, Vec<PhysReg>) {
568    let mut used: Vec<(RegClass, PhysReg)> = Vec::new();
569    let mut note = |class: RegClass, at: PhysReg| {
570        if !used.contains(&(class, at)) {
571            used.push((class, at));
572        }
573    };
574    for block in func.blocks() {
575        for inst in func.insts(block) {
576            for operand in &func[func[inst].operands] {
577                if let Some(at) = operand.reg.phys() {
578                    note(operand.class, at);
579                }
580            }
581        }
582    }
583    for edit in &allocation.edits {
584        for place in [edit.mov.from, edit.mov.to] {
585            if let Place::Reg(at) = place {
586                note(edit.class, at);
587            }
588        }
589    }
590
591    let conv = layout.conv;
592    let wanted = |class: RegClass, at: PhysReg| used.contains(&(class, at));
593    // In the convention's order rather than the order the function happened to reach for them, so
594    // that two functions saving the same registers get the same prologue.
595    let saved_int = conv
596        .int_saved
597        .iter()
598        .copied()
599        .filter(|&at| wanted(conv.int_class, at))
600        .filter(|&at| !(layout.frame_pointer && at == conv.frame_pointer))
601        .collect();
602    let saved_sse =
603        conv.sse_saved.iter().copied().filter(|&at| wanted(conv.sse_class, at)).collect();
604    (saved_int, saved_sse)
605}
606
607/// How many bytes a value of a class takes on the stack.
608///
609/// A power of two at least a word wide, because a slot is addressed and an address that is not a
610/// multiple of the size of the thing at it is a fault on some machines and slow on the rest. An
611/// eighty bit `long double` takes sixteen bytes for that reason, which is what every compiler
612/// does with one.
613fn width(layout: &Layout<'_>, class: RegClass) -> u32 {
614    let bits = layout.file.class(class).map_or(0, |info| info.bits);
615    bits.div_ceil(8).max(layout.conv.word).next_power_of_two()
616}
617
618/// How many bytes each of an allocation's spill slots takes on the stack.
619///
620/// The same question the width of one register class is, asked of a whole allocation at once, and
621/// public because [`crate::slots`] needs it to say how big a cell holding a spilled value has to
622/// be, which it has to know before there is a frame to ask.
623#[must_use]
624pub fn widths(layout: &Layout<'_>, allocation: &Allocation) -> Vec<u32> {
625    allocation.assignment.slots().iter().map(|&class| width(layout, class)).collect()
626}
627
628/// How far past a multiple of an alignment a number is, counted the other way: what has to be
629/// added to it to reach the next one.
630fn wrap(align: u32, value: u32) -> u32 {
631    (align - value % align) % align
632}
633
634/// A distance in a frame, as the signed number every offset out of here is.
635fn offset(bytes: u32) -> i32 {
636    i32::try_from(bytes).expect("a frame under two gigabytes")
637}
638
639#[cfg(test)]
640mod tests {
641    use rucc_base::Interner;
642    use rucc_mir::{Opcode, Operand, Reg};
643    use rucc_regalloc::assign::Env;
644    use rucc_target::x86_64::{GPR, RBP, REGS, SYSV, WIN64, XMM};
645
646    use super::*;
647
648    /// An environment offering that many of the convention's registers, with everything after
649    /// them held back as scratch.
650    fn env(conv: &CallRegs, count: usize) -> Env {
651        Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
652    }
653
654    /// A function of that many values, every one of them written before any is read, allocated
655    /// with that many registers to hand out.
656    ///
657    /// Every value is live at the first read, so a count below the number of values is what puts
658    /// the function under enough pressure to spill, and each read wants one value so a reload
659    /// never needs more than one scratch register.
660    fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation) {
661        let mut names = Interner::new();
662        let mut func = Func::new(names.intern("f"));
663        let opcode = Opcode::new(names.intern("x64.nop"));
664        let block = func.create_block();
665        let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
666        for &reg in &regs {
667            func.build(block, opcode).def(reg, GPR).finish();
668        }
669        for &reg in &regs {
670            func.build(block, opcode).uses(reg, GPR).finish();
671        }
672        let allocation = rucc_regalloc::run(&mut func, &env(conv, count), "test", true);
673        (func, allocation)
674    }
675
676    /// What a list of registers is called, which is what an assertion reads.
677    fn named(regs: &[PhysReg]) -> Vec<&'static str> {
678        regs.iter().map(|&reg| REGS.name(GPR, reg).expect("a register")).collect()
679    }
680
681    #[test]
682    fn a_function_that_needs_nothing_of_the_stack_has_no_frame_at_all() {
683        let (func, allocation) = pressure(&SYSV, 2, 4);
684        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
685
686        assert_eq!(frame.size(), 0);
687        assert_eq!(named(frame.saved_int()), Vec::<&str>::new());
688        assert_eq!(frame.slot(0), None);
689        // Nothing between the stack pointer and the return address the call pushed.
690        assert_eq!(frame.incoming(), Incoming::from_stack(8));
691    }
692
693    #[test]
694    fn a_small_leaf_function_puts_its_spills_in_the_red_zone_and_moves_nothing() {
695        let (func, allocation) = pressure(&SYSV, 4, 2);
696        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
697
698        // Two registers for four values that are all live at once, so two are on the stack, and a
699        // leaf function small enough is entitled to the bytes below the stack pointer.
700        assert_eq!(frame.size(), 0);
701        assert_eq!((frame.slot(0), frame.slot(1)), (Some(-16), Some(-8)));
702        assert_eq!(frame.slot(2), None);
703        assert_eq!(frame.incoming(), Incoming::from_stack(8));
704    }
705
706    #[test]
707    fn a_leaf_function_told_it_has_no_red_zone_takes_the_bytes_instead() {
708        let (func, allocation) = pressure(&SYSV, 4, 2);
709        let base = Layout::new(&SYSV, REGS);
710        let frame = Frame::of(&func, &allocation, &Layout { red_zone: false, ..base });
711
712        assert_eq!(frame.size(), 16);
713        assert_eq!((frame.slot(0), frame.slot(1)), (Some(0), Some(8)));
714        assert_eq!(frame.incoming(), Incoming::from_stack(24));
715    }
716
717    #[test]
718    fn a_frame_too_big_for_the_red_zone_takes_the_bytes_whatever_else_is_true() {
719        let (func, allocation) = pressure(&SYSV, 40, 2);
720        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
721
722        // Thirty eight values on the stack is three hundred and four bytes, and the red zone is a
723        // hundred and twenty eight.
724        assert_eq!(frame.size(), 304);
725        assert_eq!(frame.slot(0), Some(0));
726        assert_eq!(frame.slot(37), Some(296));
727    }
728
729    #[test]
730    fn a_function_that_calls_something_leaves_the_stack_pointer_where_a_call_wants_it() {
731        let (func, allocation) = pressure(&SYSV, 4, 2);
732        let base = Layout::new(&SYSV, REGS);
733        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
734
735        // Sixteen bytes of spills, and the call that reached this function left the stack pointer
736        // eight bytes off, so the frame is eight bytes wider than the spills need and every call
737        // this function makes is correctly aligned.
738        assert_eq!(frame.size(), 24);
739        assert_eq!((frame.slot(0), frame.slot(1)), (Some(0), Some(8)));
740        assert_eq!(frame.incoming(), Incoming::from_stack(32));
741    }
742
743    #[test]
744    fn a_push_is_counted_in_the_alignment_the_frame_has_to_produce() {
745        let (func, allocation) = pressure(&SYSV, 12, 12);
746        let base = Layout::new(&SYSV, REGS);
747        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
748
749        // Twelve values reach into the preserved end of the allocation order, so three registers
750        // are pushed, and three pushes plus the return address is a multiple of sixteen already.
751        // The frame is empty and stays empty rather than being padded for the sake of it.
752        assert_eq!(named(frame.saved_int()), ["rbx", "r12", "r13"]);
753        assert_eq!(frame.size(), 0);
754        assert_eq!(frame.incoming(), Incoming::from_stack(32));
755    }
756
757    #[test]
758    fn the_registers_a_call_leaves_alone_are_saved_in_the_order_the_convention_lists_them() {
759        let (func, allocation) = pressure(&SYSV, 13, 13);
760        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
761
762        // Four of them now, in the convention's order rather than the order the allocator handed
763        // them out in, so that two functions saving the same registers get the same prologue.
764        assert_eq!(named(frame.saved_int()), ["rbx", "r12", "r13", "r14"]);
765    }
766
767    #[test]
768    fn a_function_that_keeps_a_frame_pointer_does_not_save_it_twice() {
769        let mut names = Interner::new();
770        let mut func = Func::new(names.intern("f"));
771        let opcode = Opcode::new(names.intern("x64.nop"));
772        let block = func.create_block();
773        // An instruction that names the frame pointer register outright, which is what a lowering
774        // rule for something that has to use it produces.
775        func.build(block, opcode).operand(Operand::write(Reg::physical(RBP), GPR)).finish();
776        let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4), "test", true);
777        let base = Layout::new(&SYSV, REGS);
778
779        let kept = Frame::of(&func, &allocation, &Layout { frame_pointer: true, ..base });
780        let dropped = Frame::of(&func, &allocation, &base);
781
782        // `rbp` is a register SysV preserves, so a function that leaves it alone saves it in the
783        // ordinary way, and a function that keeps a frame pointer in it saves it as part of
784        // setting the frame pointer up instead.
785        assert_eq!(named(dropped.saved_int()), ["rbp"]);
786        assert_eq!(named(kept.saved_int()), Vec::<&str>::new());
787        assert!(kept.frame_pointer());
788    }
789
790    #[test]
791    fn locals_are_placed_widest_alignment_first_and_reported_in_the_order_they_arrived() {
792        let (func, allocation) = pressure(&SYSV, 2, 4);
793        let locals = [
794            Local { size: 1, align: 1 },
795            Local { size: 16, align: 16 },
796            Local { size: 8, align: 8 },
797        ];
798        let base = Layout::new(&SYSV, REGS);
799        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
800
801        // The sixteen byte one is placed first, so nothing is padded to reach it, and the one
802        // byte one goes last where the padding after it costs nothing.
803        assert_eq!((frame.local(1), frame.local(2), frame.local(0)), (Some(0), Some(16), Some(24)));
804        assert_eq!(frame.local(3), None);
805        // A local wanting sixteen byte alignment is more than the stack pointer has for free, so
806        // the frame is taken rather than the red zone used, and it is padded to keep the local
807        // where it was put.
808        assert_eq!(frame.size(), 40);
809        assert_eq!(frame.realign(), None);
810    }
811
812    #[test]
813    fn a_local_wanting_more_alignment_than_a_call_gives_makes_the_prologue_force_it() {
814        let (func, allocation) = pressure(&SYSV, 2, 4);
815        let locals = [Local { size: 64, align: 32 }];
816        let base = Layout::new(&SYSV, REGS);
817        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
818
819        assert_eq!(frame.realign(), Some(32));
820        assert_eq!(frame.local(0), Some(0));
821        assert_eq!(frame.size(), 64);
822        // Forcing the alignment throws away how far the caller's stack pointer was from where the
823        // prologue wanted it, so a frame pointer is needed and the caller's stack is reached
824        // through it instead: one word for the saved frame pointer and one for the return address.
825        assert!(frame.frame_pointer());
826        assert_eq!(frame.incoming(), Incoming::from_frame(16));
827    }
828
829    #[test]
830    fn the_canary_is_above_every_byte_a_local_or_a_spill_reaches() {
831        let (func, allocation) = pressure(&SYSV, 4, 2);
832        let locals = [Local { size: 16, align: 16 }, Local { size: 8, align: 8 }];
833        let base = Layout::new(&SYSV, REGS);
834        let there = Layout { leaf: false, locals: &locals, protect: true, ..base };
835        let frame = Frame::of(&func, &allocation, &there);
836
837        // Two spill slots at the bottom, then the two locals, then the canary above all four. That
838        // order is the whole mechanism: a write that runs off the end of either local passes the
839        // canary before it reaches the saved registers and the return address.
840        let canary = frame.canary().expect("a protected frame has a slot");
841        for below in [frame.slot(0), frame.slot(1), frame.local(0), frame.local(1)] {
842            assert!(below.expect("a slot that was asked for") < canary);
843        }
844        assert_eq!(canary, 40);
845        // Forty eight bytes of areas, and then the eight that put the stack pointer back where a
846        // call wants it, because the arm the check fails on makes one.
847        assert_eq!(frame.size(), 56);
848        assert_eq!((frame.size() + SYSV.return_address) % SYSV.stack_align, 0);
849    }
850
851    #[test]
852    fn a_frame_with_no_protector_has_no_slot_for_a_canary() {
853        let (func, allocation) = pressure(&SYSV, 2, 4);
854        let frame = Frame::of(&func, &allocation, &Layout::new(&SYSV, REGS));
855
856        assert_eq!(frame.canary(), None);
857    }
858
859    #[test]
860    fn a_call_reads_its_stack_arguments_from_the_bottom_of_the_frame() {
861        let (func, allocation) = pressure(&SYSV, 4, 2);
862        let base = Layout::new(&SYSV, REGS);
863        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, outgoing: 24, ..base });
864
865        // The outgoing area is at the stack pointer, because that is where the callee will look
866        // for it, and the spills sit above it.
867        assert_eq!(frame.outgoing(), 24);
868        assert_eq!((frame.slot(0), frame.slot(1)), (Some(24), Some(32)));
869        assert_eq!(frame.size(), 40);
870    }
871
872    /// Moving everything up by the size of the outgoing area is what would break its alignment,
873    /// so the area is padded to the widest alignment anything above it wanted. The area itself
874    /// still starts at the stack pointer, because that is the one thing about it that is not this
875    /// frame's to choose.
876    #[test]
877    fn what_is_above_the_outgoing_area_keeps_the_alignment_it_asked_for() {
878        let (func, allocation) = pressure(&SYSV, 2, 4);
879        let locals = [Local { size: 16, align: 16 }];
880        let base = Layout::new(&SYSV, REGS);
881        let there = Layout { leaf: false, outgoing: 8, locals: &locals, ..base };
882        let frame = Frame::of(&func, &allocation, &there);
883
884        assert_eq!(frame.outgoing(), 8);
885        assert_eq!(frame.local(0), Some(16));
886        assert_eq!(frame.size(), 40);
887        // A call leaves the stack pointer one return address short of aligned and nothing was
888        // pushed on top of that, so the frame is what puts it back and the local lands aligned.
889        assert_eq!((frame.size() + SYSV.return_address) % SYSV.stack_align, 0);
890    }
891
892    #[test]
893    fn a_windows_call_gets_the_thirty_two_bytes_below_it_even_when_it_passes_nothing() {
894        let (func, allocation) = pressure(&WIN64, 2, 4);
895        let base = Layout::new(&WIN64, REGS);
896        let frame = Frame::of(&func, &allocation, &Layout { leaf: false, ..base });
897
898        // Windows has no red zone and every caller reserves thirty two bytes below the call for
899        // the callee to spill its register arguments into.
900        assert_eq!(frame.outgoing(), 32);
901        assert_eq!(frame.size(), 40);
902        assert_eq!(frame.incoming(), Incoming::from_stack(48));
903    }
904
905    #[test]
906    fn a_windows_frame_pointer_is_established_after_the_frame_rather_than_before_it() {
907        let (func, allocation) = pressure(&WIN64, 4, 2);
908        let base = Layout::new(&WIN64, REGS);
909        let kept = Frame::of(&func, &allocation, &Layout { frame_pointer: true, ..base });
910        let dropped = Frame::of(&func, &allocation, &base);
911
912        // The unwind record that platform reads cannot describe the other order, so the prologue
913        // pushes, takes the frame and only then points the pointer at it. What that buys is that
914        // the pointer holds what the stack pointer holds, so a frame with one and a frame without
915        // one are the same frame with the same numbers in it.
916        assert!(kept.frame_pointer());
917        assert!(kept.late());
918        assert!(!dropped.frame_pointer());
919        assert_eq!(kept.size(), dropped.size());
920        assert_eq!((kept.slot(0), kept.slot(1)), (dropped.slot(0), dropped.slot(1)));
921        assert_eq!(kept.incoming(), Incoming::from_stack(dropped.incoming().at + 8));
922    }
923
924    #[test]
925    fn a_windows_frame_that_grows_keeps_the_numbers_it_had_and_changes_the_register() {
926        let (func, allocation) = pressure(&WIN64, 4, 2);
927        let base = Layout::new(&WIN64, REGS);
928        let there = Layout { leaf: false, frame_pointer: true, ..base };
929        let still = Frame::of(&func, &allocation, &there);
930        let grown = Frame::of(&func, &allocation, &Layout { grows: true, ..there });
931
932        // A frame that grows keeps a pointer whatever the flags asked for, and on this platform
933        // that pointer is established late, which means it is a copy of the stack pointer as the
934        // body finds it. So every distance the frame had already worked out from the stack pointer
935        // is the same distance from the pointer, and growing changes which register the offsets are
936        // counted from and nothing else. That is the whole of why this frame needs no adjustment.
937        assert!(grown.grows());
938        assert!(grown.late());
939        assert_eq!(grown.size(), still.size());
940        assert_eq!(grown.outgoing(), still.outgoing());
941        assert_eq!((grown.slot(0), grown.slot(1)), (still.slot(0), still.slot(1)));
942        assert_eq!(grown.incoming(), Incoming::from_frame(still.incoming().at));
943    }
944
945    #[test]
946    fn a_realigned_frame_on_windows_keeps_the_early_order_it_has_no_choice_about() {
947        let (func, allocation) = pressure(&WIN64, 2, 4);
948        let locals = [Local { size: 64, align: 32 }];
949        let base = Layout::new(&WIN64, REGS);
950        let frame = Frame::of(&func, &allocation, &Layout { locals: &locals, ..base });
951
952        // Forcing the alignment leaves the pushes at no constant distance from anything, so the
953        // pointer has to go up before the mask and the platform's answer does not apply. Such a
954        // frame is described by nothing and the assembler refuses it by name, which is
955        // `tamnd/rucc#1422`.
956        assert_eq!(frame.realign(), Some(32));
957        assert!(frame.frame_pointer());
958        assert!(!frame.late());
959        assert_eq!(frame.incoming(), Incoming::from_frame(16));
960    }
961
962    #[test]
963    fn a_slot_is_as_wide_as_the_widest_thing_of_its_class() {
964        let base = Layout::new(&SYSV, REGS);
965
966        assert_eq!(width(&base, GPR), 8);
967        assert_eq!(width(&base, XMM), 16);
968        // A long double is eighty bits and takes sixteen bytes, because an address has to be a
969        // multiple of the size of what is at it.
970        assert_eq!(width(&base, REGS.class_named("x87").expect("a class")), 16);
971    }
972}