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