Skip to main content

rucc_codegen/
finish.rs

1//! The prologue, the epilogue, and the moves the allocator asked for.
2//!
3//! Design: `spec/10-backend.md` sections 10.4 and 10.7.
4//!
5//! [`crate::frame`] works out what a function's stack looks like and writes nothing. This is what
6//! writes it. Three things are still missing from a function the allocator has finished with, and
7//! all three of them are instructions no lowering rule chose:
8//!
9//! ```text
10//!   the prologue     takes the frame the layout worked out, and puts away the registers a call
11//!                    leaves alone that this function writes anyway
12//!   the moves        every spill, every reload and every copy the allocator handed back as an
13//!                    edit, in the place it said and in the order it said
14//!   the epilogue     gives the frame back and puts the registers back, at the end of every block
15//!                    the function returns from
16//! ```
17//!
18//! There is a fourth thing and it is not an instruction but a number. The lowering wrote an
19//! instruction for every `alloca` that computes the address of the memory it asked for, and could
20//! not write how far into the frame that memory is, because when it ran there was no frame. So
21//! the displacement of each of those is filled in here, out of the same [`Frame`] everything else
22//! here reads, and off the same stack pointer every other offset in it is from.
23//!
24//! There is a fifth thing on a command line that asked for the stack to be touched a page at a
25//! time, and it is the only one of them that is written into the middle of a block rather than at
26//! one end of the function. A variable length array moves the stack pointer by a number that is not
27//! known until the declaration runs, so walking it a page at a time is a loop written around the one
28//! instruction the lowering left, and that turns the block the declaration was in into four.
29//!
30//! The loads that read the arguments the caller passed on the stack are waiting on the same number
31//! and on one more. Those bytes are the caller's rather than this function's, and a frame that had
32//! to force its own alignment cannot say how far away the caller's stack pointer was, so it reaches
33//! back through the frame pointer instead. Which register a load reads through is therefore settled
34//! here too, and it is the only base register in a finished function that was not settled by
35//! whoever wrote the instruction.
36//!
37//! After this the function is one an encoder can read: every register is physical, every offset
38//! into the frame is a constant, and the stack pointer is where the convention says it should be
39//! at every instruction that could look.
40//!
41//! # Why the moves go in first
42//!
43//! Every offset the frame reports is from the stack pointer as it stands in the body of the
44//! function. A spill written before the prologue exists would be written in front of the
45//! instruction it belongs to and behind nothing, which is where the prologue then goes, so the
46//! prologue ends up in front of it and the offsets stay true. Writing them the other way round
47//! would put the first reload above the instruction that takes the frame, and it would read from
48//! an address that is one frame out.
49//!
50//! # Where a return is
51//!
52//! A block that goes nowhere is a block the function leaves from. Mostly that is a return, and
53//! the other kind is a block ending in `unreachable`, which is a point the front end says control
54//! does not arrive at and which the lowering writes no instruction for. Both want the same thing
55//! here. A return wants the epilogue because that is what a return is once the frame is known,
56//! and an unreachable block wants it because the alternative is a function whose last instruction
57//! falls into whatever the assembler put after it, which is worse than an epilogue nothing runs.
58//! So the epilogue goes at the end of every block with an empty successor list, and there may be
59//! several, because nothing here insists a function has one exit.
60//!
61//! # What is target-specific here
62//!
63//! The names, and only the names. Which instruction pushes a register and which one moves the
64//! stack pointer is [`rucc_target::FrameInsts`], which the target says and this reads, so what
65//! is written below is the shape of a prologue rather than any particular machine's. That is
66//! `spec/10-backend.md` section 10.8 as it applies to the one pass that would otherwise be full
67//! of `x64.` by hand.
68
69use rucc_base::Interner;
70use rucc_base::hash::Map;
71use rucc_diag::Span;
72use rucc_mir::{Block, BlockCall, CfiOp, Func, Inst, Mem, Opcode, Operand, Patch, Reg, Role};
73use rucc_regalloc::Allocation;
74use rucc_regalloc::assign::Place;
75use rucc_regalloc::rewrite::{At, Edit};
76use rucc_target::{BranchInsts, CallRegs, Chkstk, FrameInsts, Guard, PhysReg, Probe, RegClass};
77
78use crate::frame::Frame;
79use crate::lower::Stack;
80
81/// What the stack protector's check needs beyond the frame, in a function that has one.
82///
83/// Three things that come from three places, which is why they arrive together rather than being
84/// looked up here. Where the word the canary is copied from lives is a fact about the runtime the
85/// code is linked against. What a branch on a register is is a fact about the machine. And the two
86/// registers are neither: they are the ones the allocator was told to hold back, which is a
87/// decision about the allocator, and they are free at a return for exactly that reason.
88#[derive(Debug, Clone, Copy)]
89pub struct Protect<'a> {
90    /// Where the word the canary is a copy of lives, and what to call when the copy has changed.
91    pub guard: &'a Guard,
92    /// What a branch on a register is, which is what the check ends its block with.
93    pub branch: &'a BranchInsts,
94    /// The two registers the check may use, which are two the allocator never handed out.
95    pub scratch: [PhysReg; 2],
96}
97
98/// What a function that takes its stack a page at a time needs beyond the frame.
99///
100/// What `-fstack-clash-protection` asks for, and the same three kinds of thing [`Protect`] is:
101/// one fact about the platform, one about the machine, and two registers that are neither. See
102/// [`rucc_target::Probe`] for what the sequence is defending against.
103///
104/// Read in two places, because a function has two ways of moving its stack pointer and the flag is
105/// about both of them. The prologue takes the frame the layout worked out, and a variable length
106/// array takes however many bytes its declaration asked for while the function runs. The same three
107/// things answer both.
108#[derive(Debug, Clone, Copy)]
109pub struct Probing<'a> {
110    /// What touches a page and how far apart the pages are.
111    pub probe: &'a Probe,
112    /// What a branch on a register is, which is what the loop under a large frame ends with.
113    pub branch: &'a BranchInsts,
114    /// The two registers the sequence may use, which are two the allocator never handed out.
115    pub scratch: [PhysReg; 2],
116}
117
118/// What a profiler's hook at the top of a function is, in a function that has one.
119///
120/// What `-pg` asks for. See [`rucc_target::Trace`] for why there are two of these and what each of
121/// them lets the hook see. Only the name survives to here, because by this point the flag has been
122/// read against the target and a prologue that has the name has everything it needs.
123#[derive(Debug, Clone, Copy)]
124pub struct Tracing {
125    /// What is called, which is a routine the runtime provides and not one the program wrote.
126    pub name: &'static str,
127    /// Whether the call goes in front of the prologue rather than once the frame is taken.
128    pub early: bool,
129}
130
131/// The room at the top of a function for something to be written over later, in a function that
132/// was promised any.
133///
134/// What `-fpatchable-function-entry=` asks for. The room is a run of the shortest instruction the
135/// machine has that does nothing, and what makes it worth reserving is that it is never run for
136/// long: a tracer or a live patcher writes a jump or a call over it once the program is up, and
137/// what it needs from the compiler is a known address and a known number of bytes.
138///
139/// Two counts because the room can be on either side of the function's own label. Only the half
140/// after it is written here, since the stream starts at the label and there is nowhere in it to put
141/// the other half; the half in front is carried through so that whatever lays the function down can
142/// lay that many bytes ahead of the symbol.
143#[derive(Debug, Clone, Copy)]
144pub struct Padding {
145    /// What the instruction that does nothing is called on this target.
146    pub name: &'static str,
147    /// How many of them go in front of the function's own label.
148    pub before: u32,
149    /// How many go after it.
150    pub after: u32,
151}
152
153/// What the convention this function is compiled for says a frame is.
154///
155/// Seven answers to the one question, which is why they travel together: where it puts things,
156/// which instructions build one, whether this function's carries a protector, whether it is taken a
157/// page at a time, whether the function opens with a landing pad, whether it calls a profiler on
158/// the way in, and how much room it opens with for a patcher. The last five are the only ones about
159/// this function rather than about every function on the target, and they are here because what
160/// they need is the other two and nothing else.
161#[derive(Debug, Clone, Copy)]
162pub struct Convention<'a> {
163    /// Where the convention puts things.
164    pub regs: &'a CallRegs,
165    /// The instructions a prologue, an epilogue, a spill and a reload are made of on it.
166    pub insts: &'a FrameInsts,
167    /// What this function's stack protector needs, or `None` in a function with none.
168    pub protect: Option<Protect<'a>>,
169    /// What this function's probing prologue needs, or `None` when the frame is taken in one
170    /// subtraction, which is what a command line that did not ask asks for.
171    pub probe: Option<Probing<'a>>,
172    /// What says an indirect branch may arrive at the top of this function, or `None` when the
173    /// command line did not ask for one and on a target that has no such instruction.
174    ///
175    /// See [`rucc_target::FrameInsts::landing`]. A name rather than a flag because the flag has
176    /// already been read against the target by the time this is built, and because a prologue that
177    /// has the name has everything it needs.
178    pub landing: Option<&'static str>,
179    /// What this function's call to a profiler is, or `None` in one that makes none, which is every
180    /// function on a command line that did not ask.
181    pub trace: Option<Tracing>,
182    /// What room this function opens with for a patcher, or `None` in one that was promised none,
183    /// which is every function on a command line that did not ask.
184    pub pad: Option<Padding>,
185}
186
187/// The furthest below the frame pointer an ARM64 Windows unwind code can say the stack pointer
188/// is, which is 255 eights.
189const MOST_ADD_FP: i32 = 2040;
190
191impl<'a> Convention<'a> {
192    /// That convention, for a function with no stack protector, no probing, no landing pad, no
193    /// call to a profiler and no room for a patcher, which is most of them.
194    #[must_use]
195    pub fn new(regs: &'a CallRegs, insts: &'a FrameInsts) -> Self {
196        Self { regs, insts, protect: None, probe: None, landing: None, trace: None, pad: None }
197    }
198}
199
200/// Which instruction each of the allocator's moves became.
201///
202/// A spill and a copy are both a `mov` once they are written, and so is an instruction the lowering
203/// wrote that happens to move the same register to the same address. Telling them apart afterwards
204/// by looking at them is guesswork, and a pass that guesses wrong about a store to a volatile
205/// variable deletes a read the program insisted on. So what the allocator asked for is recorded as
206/// it is written, and a later pass that is only allowed to touch the allocator's own moves has the
207/// list rather than a heuristic. See [`crate::copies`], which is the one pass that reads this.
208///
209/// It is kept by instruction number rather than in a hash map, because that pass asks about every
210/// instruction in the function and the numbers are dense.
211#[derive(Debug, Default)]
212pub struct Moves(Vec<Option<Edit>>);
213
214impl Moves {
215    /// What the allocator asked for at this instruction, or `None` at an instruction that is not
216    /// one of its moves.
217    #[must_use]
218    pub fn at(&self, inst: Inst) -> Option<Edit> {
219        self.0.get(inst.index()).copied().flatten()
220    }
221
222    /// Records that this instruction is what that move came to.
223    pub fn record(&mut self, inst: Inst, edit: Edit) {
224        if self.0.len() <= inst.index() {
225            self.0.resize(inst.index() + 1, None);
226        }
227        self.0[inst.index()] = Some(edit);
228    }
229}
230
231/// Writes the moves, the prologue and the epilogue into a function the allocator has finished
232/// with.
233///
234/// Hands back which instruction each of the allocator's moves became, for the one pass that is
235/// allowed to take one of them out again.
236///
237/// # Panics
238///
239/// Panics on a function with no blocks in it, on a frame whose slots or locals the allocation and
240/// the lowering do not match, and on a move of a class the target did not say how to move. All of
241/// them are the caller handing it a frame and a function that were not worked out from each other.
242pub fn finish(
243    func: &mut Func,
244    allocation: &Allocation,
245    frame: &Frame,
246    stack: &Stack,
247    convention: Convention<'_>,
248    names: &mut Interner,
249) -> Moves {
250    let Convention { regs: conv, insts, protect, probe, landing, trace, pad } = convention;
251    let entry = func.entry().expect("a function with a block in it");
252
253    // Before anything is written, because these are instructions the lowering already put in the
254    // function and every one of them is somewhere the prologue is about to go in front of, which
255    // is what makes an offset from the stack pointer the right thing to write into them. In a
256    // frame that grows it is an offset from the frame pointer instead, so the base register is
257    // rewritten the way an incoming argument's is, and for a version of the same reason.
258    //
259    // Added rather than assigned. The instruction named here is the `lea` the lowering wrote, or
260    // whatever [`crate::fold`] folded that `lea` into, and a reader that took it brought a
261    // displacement of its own: the address of a local is where the object starts and reading a
262    // field of it is some way past that. Assigning would throw the field offset away and read the
263    // front of the object every time.
264    for &(inst, local) in &stack.addresses {
265        let at = frame.local(local).expect("a local the frame was worked out from");
266        let mem = func[inst].mem.expect("the address of a local is an address");
267        func[mem].disp += at;
268        if frame.grows() {
269            rebase(func, inst, conv.frame_pointer);
270        }
271    }
272
273    // The bytes a variable length array takes are already off the stack pointer by the time one of
274    // these runs, so what is left to write is how far above the new stack pointer the array starts,
275    // which is however much of the bottom of the frame belongs to the arguments of a call. That
276    // area stays at the bottom wherever the bottom has moved to. Added rather than assigned for the
277    // reason the loop above is: one of these folds into its readers like any other address, and a
278    // reader that took it brought a displacement of its own.
279    for &inst in &stack.dynamic {
280        let mem = func[inst].mem.expect("the address of a growable local is an address");
281        func[mem].disp += offset(frame.below());
282    }
283
284    // The same, one area further up, and through the frame pointer when that is what reaches it.
285    // These are in the entry block ahead of everything, so the prologue still goes in front of
286    // them, which is what makes both registers hold what these offsets are counted from.
287    let incoming = frame.incoming();
288    for &(inst, up) in &stack.arguments {
289        let mem = func[inst].mem.expect("an argument read out of memory is read from an address");
290        func[mem].disp += incoming.at + offset(up);
291        if incoming.through_frame_pointer {
292            rebase(func, inst, conv.frame_pointer);
293        }
294    }
295
296    // Every offset the frame reports is from this one register, which is the stack pointer in an
297    // ordinary frame and the frame pointer in one that moves the stack pointer while it runs.
298    let base = if frame.grows() { conv.frame_pointer } else { conv.stack_pointer };
299    let mut writer = Writer { func, conv, insts, names, base, ahead: None };
300
301    let mut cursors: Map<At, Inst> = Map::default();
302    let mut moves = Moves::default();
303    for edit in &allocation.edits {
304        let inst = writer.mov(edit, frame);
305        writer.put(&mut cursors, edit.at, inst);
306        moves.record(inst, *edit);
307    }
308
309    // Before the epilogues, because this is what turns one block into four and the last of the four
310    // is the one the function goes on to return from. A block that went nowhere before a variable
311    // length array was walked in the middle of it is not the block that goes nowhere afterwards, and
312    // an epilogue written into the wrong one of them gives the frame back before the body has run.
313    if let Some(probing) = probe {
314        for &took in &stack.grown {
315            writer.walk(took, probing);
316        }
317    }
318
319    // Almost nothing of either in a naked function, which is the whole of what the attribute asks
320    // for and the only place in this file that knows the word. The frame is empty by the time one
321    // gets here, since [`crate::pipeline`] refuses a naked function that wanted bytes, so what is
322    // left to leave out is the part of the prologue that is written on somebody's instruction
323    // rather than on the frame's: the room a patcher was promised and the profiler's hook, both of
324    // which are a call or a run of bytes in front of a body that said it is the whole of the
325    // function. The protector and the probe are already off, the first because the pipeline turned
326    // it off and the second because it only fires on a frame there is none of.
327    //
328    // The landing pad stays. It is not the compiler adding something to the function, it is the
329    // address of the function being made one an indirect branch may arrive at, and gcc writes it
330    // in a naked function too.
331    //
332    // The epilogue is the one that matters. It is what writes the `ret`, and a naked function ends
333    // where its own text ends, which for micropython's `nlr_push` is a jump somewhere else and for
334    // everything else is the `ud2` the lowering put there.
335    let bare = frame.naked();
336    let prologue = writer.prologue(
337        frame,
338        protect,
339        probe.filter(|_| !bare),
340        landing,
341        trace.filter(|_| !bare),
342        pad.filter(|_| !bare),
343    );
344    for &inst in prologue.iter().rev() {
345        writer.func.prepend_inst(entry, inst);
346    }
347    let returns: Vec<Block> = if bare {
348        Vec::new()
349    } else {
350        writer.func.blocks().filter(|&block| writer.func[block].succs.is_empty()).collect()
351    };
352    for block in returns {
353        // The check goes in front of the epilogue and takes the return with it. What is left in
354        // the block the function used to return from is the check, and the block the epilogue then
355        // goes in is the arm the canary was unchanged on.
356        let block = match protect {
357            Some(protect) => writer.check(block, frame, protect),
358            None => block,
359        };
360        let epilogue = writer.epilogue(frame);
361        for inst in epilogue {
362            writer.func.append_inst(block, inst);
363        }
364    }
365
366    // Last of everything, because the blocks a probing prologue made have to come in front of the
367    // block the function used to begin with and the ones the protector's check makes are made
368    // after that. Nothing has been laid out yet: `crate::layout` runs after this and puts every
369    // block in its own order, and all this decides is which block the function is entered at.
370    if let Some(ahead) = writer.ahead {
371        let rest: Vec<Block> =
372            writer.func.blocks().filter(|block| !ahead.contains(block)).collect();
373        let order: Vec<Block> = ahead.into_iter().chain(rest).collect();
374        writer.func.set_block_order(&order);
375    }
376    moves
377}
378
379/// Points every access to the frame that its instruction cannot carry the offset of at a scratch
380/// register holding most of the address.
381///
382/// Nothing to do on x86, where every offset a frame has fits in the instruction. An AArch64 load
383/// reaches a few kilobytes up from the stack pointer and `add` reaches four, so a local deep in a
384/// large frame is written as `add x16, sp, #4096` and then the access four thousand and some bytes
385/// closer, which is what gcc writes. The part left in the instruction is the low bits when the
386/// instruction can carry those and nothing when it cannot, which is the unaligned offset a scaled
387/// load refuses.
388///
389/// After the allocator's moves have been cleaned up rather than here in [`finish`], because that
390/// pass reads what each scratch register holds between one move and the next and an address
391/// written into one in the middle is not a move it knows about. The scratch register is one the
392/// instruction does not read, and one of the two always is, since an access through the stack
393/// pointer has no base register of its own to have been reloaded into a scratch.
394pub fn far(
395    func: &mut Func,
396    insts: &FrameInsts,
397    conv: &CallRegs,
398    scratch: &[PhysReg],
399    names: &mut Interner,
400) {
401    let Some(reaches) = insts.reaches else { return };
402    let lea = Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.lea)));
403    let frame = [conv.stack_pointer, conv.frame_pointer];
404    let all: Vec<Inst> = func.blocks().flat_map(|block| func.insts(block)).collect();
405    for inst in all {
406        let Some(mem) = func[inst].mem else { continue };
407        let amode = func[mem];
408        let plain = amode.index.is_none()
409            && amode.symbol.is_none()
410            && amode.block.is_none()
411            && amode.table.is_none()
412            && amode.segment.is_none();
413        let Some(at) = amode.base.filter(|_| plain) else { continue };
414        let operands = func[inst].operands;
415        let Some(from) = func[operands][usize::from(at)].reg.phys() else { continue };
416        let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(insts.prefix) else {
417            continue;
418        };
419        if !frame.contains(&from) || reaches(name, amode.disp) {
420            continue;
421        }
422        let read = |reg: PhysReg| {
423            func[operands].iter().any(|op| op.role != Role::Def && op.reg.phys() == Some(reg))
424        };
425        let written = |reg: PhysReg| {
426            func[operands].iter().any(|op| op.role == Role::Def && op.reg.phys() == Some(reg))
427        };
428        // A scratch register the instruction writes is free for the address, and so is one
429        // nothing reads again. The cleanup keeps a value in scratch from one instruction to the
430        // next, so a register this instruction does not read can still be holding one. Taking
431        // that register put the frame address in `x16` just before a store read the value it had.
432        let free = |reg: PhysReg| !read(reg) && (written(reg) || !live_after(func, inst, reg));
433        let loaded = || {
434            func[operands]
435                .iter()
436                .filter(|op| op.role == Role::Def && op.class == conv.int_class)
437                .filter_map(|op| op.reg.phys())
438                .find(|&reg| !read(reg) && !frame.contains(&reg))
439        };
440        // With neither free, one the instruction does not read is pushed around it and popped
441        // back after, which moves the stack pointer and so every offset counted from it. That is
442        // a store of one scratch register while the other is still wanted, which is rare. The
443        // unwind table is not told, so a backtrace taken on one of those few instructions in a
444        // function with no frame pointer is sixteen bytes off.
445        let (into, saved) = match scratch.iter().copied().find(|&reg| free(reg)).or_else(loaded) {
446            Some(reg) => (reg, false),
447            None => match scratch.iter().copied().find(|&reg| !read(reg)) {
448                Some(reg) => (reg, true),
449                None => continue,
450            },
451        };
452        let moved = if saved && from == conv.stack_pointer { offset(conv.push) } else { 0 };
453        let disp = amode.disp + moved;
454        let sign = disp.signum();
455        let mut steps: Vec<i32> =
456            insts.steps(disp.unsigned_abs()).into_iter().map(|step| offset(step) * sign).collect();
457        let last = steps.last().copied().unwrap_or(0);
458        let keep = if steps.len() > 1 && reaches(name, last) {
459            steps.pop();
460            last
461        } else {
462            0
463        };
464        let class = conv.int_class;
465        if saved {
466            let push = Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.push)));
467            let pop = Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.pop)));
468            let push = func.build_loose(push).uses(Reg::physical(into), class).finish();
469            func.insert_before(inst, push);
470            let pop = func.build_loose(pop).def(Reg::physical(into), class).finish();
471            func.insert_after(inst, pop);
472        }
473        let mut base = from;
474        for step in steps {
475            let address = func
476                .build_loose(lea)
477                .def(Reg::physical(into), class)
478                .mem(Mem::at(Operand::read(Reg::physical(base), class)).plus(step))
479                .finish();
480            func.insert_before(inst, address);
481            base = into;
482        }
483        func[operands][usize::from(at)].reg = Reg::physical(into);
484        func[mem].disp = keep;
485    }
486}
487
488/// Whether something after that instruction in its block reads the register before anything
489/// writes it again.
490///
491/// Only the block, because a scratch register is not live into another one. The cleanup follows
492/// what one holds from a move to its reader and starts again at the top of every block.
493fn live_after(func: &Func, inst: Inst, reg: PhysReg) -> bool {
494    let Some(block) = func.block_of(inst) else { return true };
495    for next in func.insts(block).skip_while(|&at| at != inst).skip(1) {
496        let operands = func[next].operands;
497        let holds = |def: bool| {
498            func[operands].iter().any(|op| op.role.is_def() == def && op.reg.phys() == Some(reg))
499        };
500        if holds(false) {
501            return true;
502        }
503        if holds(true) {
504            return false;
505        }
506    }
507    false
508}
509
510/// How many pages a probing prologue touches one after another before it writes a loop instead.
511///
512/// Three, which is what gcc unrolls to. The loop is four instructions however many pages it walks
513/// and a page written out is two, so three is the last size at which the straight line is no
514/// longer than the loop, and the straight line has no branch in it and needs no register.
515const UNROLLED: u32 = 3;
516
517/// One function having its frame written into it.
518/// Points an address the lowering left counted from the stack pointer at another register.
519///
520/// The base register is an operand of the instruction and the addressing mode holds where in the
521/// operand vector it is, so the register is changed there and not in the mode.
522fn rebase(func: &mut Func, inst: Inst, to: PhysReg) {
523    let mem = func[inst].mem.expect("an address");
524    let at = func[mem].base.expect("an address the lowering wrote a base register into");
525    let operands = func[inst].operands;
526    func[operands][usize::from(at)].reg = Reg::physical(to);
527}
528
529struct Writer<'a> {
530    func: &'a mut Func,
531    conv: &'a CallRegs,
532    insts: &'a FrameInsts,
533    names: &'a mut Interner,
534    /// Which register every offset into the frame is counted from, which is the stack pointer
535    /// unless the function moves it while it runs. See `Growing` in [`crate::frame`].
536    base: PhysReg,
537    /// The blocks a probing prologue made, which go in front of the one the function began with.
538    ///
539    /// Empty in every function whose frame is taken in one subtraction, which is every function
540    /// on a command line that did not ask for the stack to be touched a page at a time and most
541    /// of them on one that did. See [`Writer::pages`].
542    ahead: Option<[Block; 2]>,
543}
544
545impl Writer<'_> {
546    /// The instructions the prologue is, in the order they run.
547    ///
548    /// The order is the one the epilogue undoes and it is not free. The frame pointer is saved
549    /// before anything else, so that it points at a fixed place whatever else happens. The
550    /// registers are pushed before the alignment is forced, so that the epilogue can find them
551    /// again from the frame pointer, since after the alignment is forced nothing else can. And the
552    /// vector registers are stored last, because until the frame has been taken there is nowhere
553    /// to store them.
554    ///
555    /// Where the pointer is pointed at the frame is the one part of that order the platform gets a
556    /// say in. Windows wants it after the frame has been taken rather than before, because the
557    /// unwind record it reads has no way to describe the other order, so on that target the move
558    /// goes between the frame and the vector stores instead. See `Late` in [`crate::frame`].
559    ///
560    /// The landing pad is in front of all of it, because the address it makes reachable is the
561    /// address of the function and the address of the function is where the first instruction is.
562    /// It has to be written here rather than after the fact, since a probing prologue moves the
563    /// instructions written so far into a block of its own and the pad has to move with them.
564    ///
565    /// The room a patcher was promised goes after the pad, because a patcher wants somewhere it can
566    /// write a call that happens before anything else, and the pad is the one instruction that has
567    /// to come first for a reason of its own.
568    ///
569    /// A profiler's hook goes next, or at the end when it is the kind that reads the frame pointer.
570    /// The early one is in front of everything the frame does for a reason of its own: what makes
571    /// it worth replacing while the program runs is that the stack at that instruction is exactly
572    /// what a call leaves, and a prologue that had already run would have changed it.
573    fn prologue(
574        &mut self,
575        frame: &Frame,
576        protect: Option<Protect<'_>>,
577        probe: Option<Probing<'_>>,
578        landing: Option<&'static str>,
579        trace: Option<Tracing>,
580        pad: Option<Padding>,
581    ) -> Vec<Inst> {
582        let sp = self.conv.stack_pointer;
583        let fp = self.conv.frame_pointer;
584        let int = self.conv.int_class;
585        let sse = self.conv.sse_class;
586        let word = offset(self.conv.word);
587        let push = offset(self.conv.push);
588        let mut out = Vec::new();
589        // What the prologue wrote before it had described anything, which is what decides whether
590        // there is a rule to remember at the end of it. Neither of these moves a register or takes
591        // a frame, so a function whose whole prologue is one of them has no rows and must not be
592        // given a pair of them that cancel out.
593        let mut quiet = Vec::new();
594        if let Some(name) = landing {
595            let opcode = self.opcode(name);
596            let inst = self.func.build_loose(opcode).finish();
597            out.push(inst);
598            quiet.push(inst);
599        }
600        // After the pad and in front of everything else, which is where gcc puts it. The pad is the
601        // function's first instruction because the address an indirect branch may arrive at is the
602        // address of the function, and the room comes next because what gets written over it is a
603        // call and the point of that call is that it happens before the function has done anything.
604        //
605        // Nothing is described for any of it. A byte that does nothing does not move the stack
606        // pointer, and what a patcher writes over it later is its own problem rather than this
607        // function's: the rules here say what this function did, and it did nothing.
608        if let Some(pad) = pad {
609            let opcode = self.opcode(pad.name);
610            let mut first = None;
611            for _ in 0..pad.after {
612                let inst = self.func.build_loose(opcode).finish();
613                out.push(inst);
614                quiet.push(inst);
615                first.get_or_insert(inst);
616            }
617            self.func.patch = Some(Patch { before: pad.before, pad: opcode, after: first });
618        }
619        // Nothing is described for it and nothing needs to be: the call pushes a return address and
620        // the hook pops it, so the frame is the same on both sides, and the hook preserves every
621        // register because it is written in assembly for exactly this. That is also why the
622        // allocator, which ran before any of this, never saw the call and did not have to.
623        if let Some(trace) = trace.filter(|trace| trace.early) {
624            let inst = self.hook(trace);
625            out.push(inst);
626            quiet.push(inst);
627        }
628        // How far the stack pointer is below the canonical frame address, and whether the address
629        // is still counted from the stack pointer at all. It starts at the return address the
630        // call itself pushed, which is the rule the CIE already states, so the first row here is
631        // the first thing this function does on top of that.
632        let mut below = offset(self.conv.return_address);
633        let mut from_sp = true;
634        // The home area of a variadic function on Windows on AArch64, before the frame record, so
635        // that the argument registers the body stores into it end directly below the arguments the
636        // caller left on the stack. Nothing is saved into it here, which the body does, so the one
637        // row is the distance.
638        if frame.home() > 0 {
639            let sub = self.opcode(self.insts.sub);
640            let inst = self.arith(sub, i64::from(frame.home()));
641            out.push(inst);
642            below += offset(frame.home());
643            self.row(inst, CfiOp::DefCfaOffset(below));
644        }
645        if frame.frame_pointer() {
646            let inst = self.push_frame();
647            out.push(inst);
648            below += push;
649            self.row(inst, CfiOp::DefCfaOffset(below));
650            self.saved(inst, int, fp, -below);
651            // The frame record, where the return address a call left in a register goes on with
652            // the frame pointer and sits the word above it.
653            if let Some(link) = self.record() {
654                self.saved(inst, int, link, word - below);
655            }
656            // Straight away unless the platform wants it after the frame, where the same two
657            // instructions go at the bottom of this function instead. See `Late` in
658            // [`crate::frame`].
659            if !frame.late() {
660                let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
661                let inst = self.two(mov, fp, sp);
662                out.push(inst);
663                let number = self.dwarf(int, fp);
664                self.row(inst, CfiOp::DefCfaRegister(number));
665                from_sp = false;
666            }
667        }
668        for regs in frame.pushed_int() {
669            let inst = self.push_int(regs);
670            out.push(inst);
671            below += push;
672            if from_sp {
673                self.row(inst, CfiOp::DefCfaOffset(below));
674            }
675            // The first of a pair at the lower address and the second a word above it, which is
676            // what the pair instruction does and where [`Self::push_frame`] puts its two as well.
677            for (&reg, above) in regs.iter().zip([0, offset(self.conv.word)]) {
678                self.saved(inst, int, reg, above - below);
679            }
680        }
681        // Where the frame pointer is again, for an unwinder that can only get past what comes
682        // next from there. See `CallRegs::unwind_codes`. Before the stack is aligned, which moves
683        // it by an amount nothing can say, and at the end of the prologue of a body that grows the
684        // frame, unless the frame is too far below the frame pointer for the code to say that, and
685        // then here, with the rest of the prologue left to the body.
686        let pushed = push * (self.pushes(frame) - i32::from(frame.frame_pointer()));
687        let restate = self.conv.unwind_codes && frame.frame_pointer() && !frame.late();
688        let whole = pushed + offset(frame.size());
689        let grows = restate && frame.grows() && frame.realign().is_none() && whole > 0;
690        let (early, end) = match grows {
691            true if whole <= MOST_ADD_FP => (false, true),
692            true => (pushed > 0, false),
693            false => (restate && frame.realign().is_some() && pushed > 0, false),
694        };
695        if early {
696            let lea = self.opcode(self.insts.lea);
697            out.push(self.address(lea, fp, sp, pushed));
698        }
699        if let Some(to) = frame.realign().filter(|_| !frame.late()) {
700            // Nothing is written for this and nothing can be. After it the stack pointer is a
701            // rounded-down version of where it was rather than a fixed distance from it, which is
702            // exactly what a rule cannot say. It is also why a frame that realigns is a frame
703            // with a frame pointer: by here the address is already counted from that instead.
704            assert!(!from_sp, "a frame that forces its own alignment has a frame pointer");
705            let and = self.opcode(self.insts.align);
706            out.push(self.arith(and, -i64::from(to)));
707        }
708        if frame.size() > 0 {
709            self.take(&mut out, frame.size(), &mut below, from_sp, probe);
710        }
711        // The other half of the pair above, for the platform whose record counts everything from
712        // where the stack pointer ends the prologue. Here it is that register the pointer is a copy
713        // of, so what the row says is the whole distance rather than that nothing has changed, and
714        // the frame is described before it rather than after, which is the whole of what the record
715        // could not say about the early order.
716        if frame.late() && frame.frame_pointer() {
717            let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
718            let inst = self.two(mov, fp, sp);
719            out.push(inst);
720            let number = self.dwarf(int, fp);
721            self.row(inst, CfiOp::DefCfa { reg: number, offset: below });
722        }
723        for save in frame.saved_sse() {
724            let inst = self.save_sse(save.reg, save.at);
725            out.push(inst);
726            // Where it went is an offset from whichever register the frame counts from, and the
727            // address is a constant above that register, so the two make one constant. In an
728            // ordinary frame that register is the stack pointer and the constant is `below`. In one
729            // that grows it is the frame pointer, which the address has been counted from since the
730            // prologue pointed it at where it saved the caller's copy, so the constant is the two
731            // words above it and nothing the prologue did afterwards changes it. Unless the pointer
732            // went up late, where it holds what the stack pointer holds and the constant is `below`
733            // again, which is why the question is about both. A realigned frame has no such constant
734            // at all and the rule is left out rather than guessed. On SysV that costs nothing, since
735            // it preserves no vector register for a prologue to save. On Windows it would be a save
736            // with no row, and what stops that reaching an object file is that a realigned frame
737            // there takes the late order, where the saves happen before the alignment is forced
738            // and `below` is still the constant. See `Late` in [`crate::frame`].
739            if frame.realign().is_none() || frame.late() {
740                let above = if frame.grows() && !frame.late() {
741                    push + offset(self.conv.return_address)
742                } else {
743                    below
744                };
745                self.saved(inst, sse, save.reg, save.at - above);
746            }
747        }
748        // The alignment a late frame forces, once everything the record describes is done. No row,
749        // for the reason the early order has none, and none is needed: the address is counted from
750        // the frame pointer by now. The body counts from the stack pointer this leaves.
751        if end {
752            let lea = self.opcode(self.insts.lea);
753            out.push(self.address(lea, fp, sp, whole));
754        }
755        if let Some(to) = frame.realign().filter(|_| frame.late()) {
756            let and = self.opcode(self.insts.align);
757            out.push(self.arith(and, -i64::from(to)));
758        }
759        // Before the canary and after the frame, which is where gcc puts it. The hook reads the
760        // frame pointer to find out who called this function, so it has to run once there is one,
761        // and it is a call, so it has to run before anything the function is keeping in the frame
762        // could be read back.
763        if let Some(trace) = trace.filter(|trace| !trace.early) {
764            let inst = self.hook(trace);
765            out.push(inst);
766        }
767        // Last of everything, because it writes into the frame and there is no frame to write into
768        // until the stack pointer has moved. Nothing is described for either instruction: they
769        // write a slot rather than save a register, and no unwinder wants to put a canary back.
770        if let Some(protect) = protect {
771            let at = frame.canary().expect("a protected function has a slot for its canary");
772            let [into, _] = protect.scratch;
773            out.push(self.read_guard(into, protect.guard));
774            out.push(self.store(self.conv.int_class, into, at));
775        }
776        // The rules the body runs under, kept so that each epilogue can put them back rather than
777        // leaving the next block reading whatever the last one ended on. See `epilogue`.
778        //
779        // Nothing is kept in a function whose whole prologue is the pieces that describe nothing.
780        // See `quiet` above.
781        if let Some(&last) = out.last() {
782            if !quiet.contains(&last) {
783                self.row(last, CfiOp::RememberState);
784            }
785        }
786        out
787    }
788
789    /// The call to a profiler's hook.
790    ///
791    /// No arguments and no result. Which function is being entered is not passed, because the hook
792    /// reads its own return address to find out, and that is the whole reason the call is written
793    /// rather than something cheaper.
794    fn hook(&mut self, trace: Tracing) -> Inst {
795        let call = self.opcode(self.insts.call);
796        let symbol = self.names.intern(trace.name);
797        self.func.build_loose(call).symbol(symbol).finish()
798    }
799
800    /// Takes the frame, which is one subtraction unless the command line asked for the stack to be
801    /// touched a page at a time.
802    ///
803    /// `below` is how far the canonical frame address is above the stack pointer, and it comes
804    /// back as what it is once the frame has been taken.
805    fn take(
806        &mut self,
807        out: &mut Vec<Inst>,
808        size: u32,
809        below: &mut i32,
810        from_sp: bool,
811        probe: Option<Probing<'_>>,
812    ) {
813        // In front of everything else, because a platform with a routine for this has it for every
814        // frame rather than for the ones a flag was passed about, and because the routine does the
815        // whole of what the walk below would have done. See [`rucc_target::Chkstk`].
816        let page = self.insts.probe.map_or(u32::MAX, |probe| probe.interval);
817        if let Some(chkstk) = self.conv.chkstk.filter(|_| size > page) {
818            let inst = self.reach(out, chkstk, size);
819            *below += offset(size);
820            if from_sp {
821                self.row(inst, CfiOp::DefCfaOffset(*below));
822            }
823            return;
824        }
825        let Some(probing) = probe.filter(|probing| size > probing.probe.interval) else {
826            for step in self.insts.steps(size) {
827                let inst = self.sub(step);
828                out.push(inst);
829                *below += offset(step);
830                if from_sp {
831                    self.row(inst, CfiOp::DefCfaOffset(*below));
832                }
833            }
834            return;
835        };
836        // Every step but the last is a whole page and is followed by a touch, and the last is
837        // whatever is left over, which is between one byte and one whole page. So the stack
838        // pointer never moves further than a page without something being written where it landed,
839        // and the unmapped page an operating system leaves below a stack cannot be stepped over.
840        //
841        // That is why the count is worked out from one less than the size. A frame that is an
842        // exact number of pages gets one fewer touch than it has pages, and the step left over is
843        // a whole page, which is a step that lands on the next page boundary rather than past it.
844        // gcc touches that last page as well, so this is one instruction shorter on a frame whose
845        // size is a multiple of the page and the same everywhere else.
846        let interval = probing.probe.interval;
847        let pages = (size - 1) / interval;
848        let rest = size - pages * interval;
849        let mut walked = false;
850        if pages <= UNROLLED {
851            for _ in 0..pages {
852                let inst = self.sub(interval);
853                out.push(inst);
854                *below += offset(interval);
855                if from_sp {
856                    self.row(inst, CfiOp::DefCfaOffset(*below));
857                }
858                let touch = self.touch(probing.probe);
859                out.push(touch);
860            }
861        } else {
862            self.pages(out, pages, below, from_sp, probing);
863            walked = from_sp;
864        }
865        let inst = self.sub(rest);
866        out.push(inst);
867        *below += offset(rest);
868        if from_sp {
869            // A loop leaves the address counted from the register the stack pointer was compared
870            // against, since that is the one thing in it that holds still. This is where it goes
871            // back to being counted from the stack pointer, and it is written behind this
872            // instruction rather than behind the branch because a row is written behind an
873            // instruction and the branch is not one that survives [`crate::layout`].
874            let op = if walked {
875                let number = self.dwarf(self.conv.int_class, self.conv.stack_pointer);
876                CfiOp::DefCfa { reg: number, offset: *below }
877            } else {
878                CfiOp::DefCfaOffset(*below)
879            };
880            self.row(inst, op);
881        }
882    }
883
884    /// Reaches the pages of a frame by calling the routine this platform has for it, and then takes
885    /// the frame.
886    ///
887    /// Three instructions, and the third is the one that moves anything:
888    ///
889    /// ```text
890    ///   the size into a register    which register is the platform's answer rather than ours
891    ///   the call                    touches every page from here down to that many bytes below
892    ///   the subtraction             takes the frame, of the register the size is still in
893    /// ```
894    ///
895    /// The routine comes back having moved nothing, which is what makes the third instruction
896    /// necessary and is also what makes it a subtraction of a register rather than of the constant
897    /// written again. Writing the constant twice would be the same number of bytes of code and one
898    /// more place for the two to disagree.
899    ///
900    /// Nothing is described to the unwinder for the first two. The call pushes a return address and
901    /// the routine pops it, so the frame is the same on both sides of it, which is the same argument
902    /// the profiler's hook makes a few lines above. The row goes behind the subtraction, where the
903    /// stack pointer has actually moved.
904    ///
905    /// No register here has to be asked about. The size goes in one the convention passes no
906    /// argument in, which is what lets the platform name it at all, and the routine destroys two
907    /// that are exactly the two the allocator was told to hold back. A prologue is also the one
908    /// place in a function where the only live values are the ones that arrived in the convention's
909    /// own registers.
910    fn reach(&mut self, out: &mut Vec<Inst>, chkstk: Chkstk, size: u32) -> Inst {
911        let class = self.conv.int_class;
912        let sp = Reg::physical(self.conv.stack_pointer);
913        let count = Reg::physical(chkstk.size);
914
915        // ARM64 counts the size in sixteens, which the frame always is a whole number of, and
916        // takes the frame with the register shifted back by the same amount.
917        let units = size >> chkstk.shift;
918        let imm = self.opcode(self.insts.imm);
919        // A machine that writes a constant sixteen bits at a time writes the low half and then
920        // puts the high half in above it, which is a frame of a megabyte or more on ARM64.
921        let (low, high) = match self.insts.insert {
922            Some(insert) if units > 0xffff => (units & 0xffff, Some((insert, units >> 16))),
923            _ => (units, None),
924        };
925        let inst = self.func.build_loose(imm).def(count, class).imm(i64::from(low)).finish();
926        out.push(inst);
927        if let Some((insert, high)) = high {
928            let insert = self.opcode(insert);
929            let inst = self
930                .func
931                .build_loose(insert)
932                .def(count, class)
933                .uses(count, class)
934                .imm(i64::from(high))
935                .finish();
936            out.push(inst);
937        }
938        let call = self.opcode(self.insts.call);
939        let symbol = self.names.intern(chkstk.name);
940        let inst = self.func.build_loose(call).symbol(symbol).finish();
941        out.push(inst);
942        let scaled = self.insts.scaled.filter(|_| chkstk.shift > 0);
943        let grow = self.opcode(scaled.unwrap_or(self.insts.grow));
944        let inst =
945            self.func.build_loose(grow).def(sp, class).uses(sp, class).uses(count, class).finish();
946        out.push(inst);
947        inst
948    }
949
950    /// The loop that takes a frame too large for the touches to be written one after another.
951    ///
952    /// Three blocks, and the first two are new and go in front of the one the function began with:
953    ///
954    /// ```text
955    ///   what the function is entered at   everything the prologue did before this, and then the
956    ///                                     address the stack pointer is walking down to
957    ///   the loop                          one page, the touch, and the question of whether the
958    ///                                     stack pointer has got there yet
959    ///   what the function began with      the rest of the prologue, and then the body
960    /// ```
961    ///
962    /// The instructions the prologue has written so far move into the first of them, because a
963    /// block is entered at the top and they have to run before the loop does. Nothing is laid out
964    /// here: which block comes first in memory is [`crate::layout`]'s answer, and all this decides
965    /// is which one the function is entered at.
966    fn pages(
967        &mut self,
968        out: &mut Vec<Inst>,
969        pages: u32,
970        below: &mut i32,
971        from_sp: bool,
972        probing: Probing<'_>,
973    ) {
974        let class = self.conv.int_class;
975        let sp = self.conv.stack_pointer;
976        let all = offset(pages * probing.probe.interval);
977        let [limit, byte] = probing.scratch;
978
979        let head = self.func.create_block();
980        for &inst in out.iter() {
981            self.func.append_inst(head, inst);
982        }
983        out.clear();
984        // Where the stack pointer is walking down to, worked out before it starts moving. A loop
985        // that counted down instead would need somewhere to keep the count, and this is somewhere
986        // to keep it that the comparison can read without arithmetic.
987        let lea = self.opcode(self.insts.lea);
988        let inst = self.address(lea, limit, sp, -all);
989        self.func.append_inst(head, inst);
990        if from_sp {
991            // The address is counted from that register for as long as the loop runs, and it has
992            // to be: the stack pointer moves once an iteration, so no fixed distance from it is
993            // true twice, and this register was written so that one distance is.
994            let number = self.dwarf(class, limit);
995            self.row(inst, CfiOp::DefCfa { reg: number, offset: *below + all });
996        }
997
998        let body = self.func.create_block();
999        *self.func.succs_mut(head) = vec![BlockCall::to(body)];
1000        let inst = self.sub(probing.probe.interval);
1001        self.func.append_inst(body, inst);
1002        let touch = self.touch(probing.probe);
1003        self.func.append_inst(body, touch);
1004        let differ = self.opcode(self.insts.differ);
1005        let inst = self
1006            .func
1007            .build_loose(differ)
1008            .def(Reg::physical(byte), class)
1009            .uses(Reg::physical(sp), class)
1010            .uses(Reg::physical(limit), class)
1011            .finish();
1012        self.func.append_inst(body, inst);
1013        let cond = Opcode::new(
1014            self.names.intern(&format!("{}{}", probing.branch.prefix, probing.branch.cond)),
1015        );
1016        let inst = self.func.build_loose(cond).uses(Reg::physical(byte), class).finish();
1017        self.func.append_inst(body, inst);
1018        // The first arm is the one taken when the condition held, and the condition is that the
1019        // stack pointer and the address it is walking down to still differ, so the first arm is
1020        // another page.
1021        let began = self.func.entry().expect("a function with a block in it");
1022        *self.func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(began)];
1023        *below += all;
1024        self.ahead = Some([head, body]);
1025    }
1026
1027    /// Walks the pages a variable length array takes, at the declaration that takes them.
1028    ///
1029    /// The prologue's own pages are counted when it is written, so it can step down to an address
1030    /// it worked out in advance and stop when it gets there. A declaration in the body cannot: how
1031    /// many bytes it asked for arrives in a register, so where it is going is arithmetic rather than
1032    /// a constant, and how many pages that is is a number nothing has. What is written instead is a
1033    /// loop that steps a page and asks whether it has arrived yet, which is the same walk with the
1034    /// count taken out of it.
1035    ///
1036    /// The one instruction the lowering wrote becomes four blocks:
1037    ///
1038    /// ```text
1039    ///   what the block was          everything it did before the declaration, and then where the
1040    ///                               stack pointer is going, worked out before it starts moving
1041    ///   the step                    one page, and whether the stack pointer is still above there
1042    ///   the page it stepped onto    the touch, and round again
1043    ///   the rest of the block       the stack pointer put where it was going, and then the body
1044    /// ```
1045    ///
1046    /// The touch is behind the question rather than in front of it, so the only page ever written
1047    /// is one the array reaches. The last step down is a whole page whatever is left, which puts the
1048    /// stack pointer at or past the end of the array, and the block that follows puts it back on the
1049    /// end. Nothing is touched there and nothing has to be: that is a move of less than a page from
1050    /// a page this loop has already been to, which is the whole of what a guard page asks.
1051    ///
1052    /// Nothing is described to the unwinder for any of it. A function with a variable length array
1053    /// in it keeps a frame pointer, because its own stack pointer is not a fixed distance from
1054    /// anything, and by here the frame is already counted from that register rather than from the
1055    /// stack pointer. So the rule that was true before the walk is still true after it.
1056    fn walk(&mut self, took: Inst, probing: Probing<'_>) {
1057        let class = self.conv.int_class;
1058        let sp = self.conv.stack_pointer;
1059        let span = self.func.span(took);
1060        let block = self.func.block_of(took).expect("an instruction the lowering put in a block");
1061
1062        // Which register the bytes arrived in, and which two the walk may use. The bytes may be in
1063        // one of the two, because a reload the rewriter wrote is written into one of them, and a
1064        // value that arrived that way is read by the one instruction it was written in front of and
1065        // is dead after it. So the limit goes in whichever of the pair the bytes are not in, and the
1066        // other one is free from the moment the limit has been worked out.
1067        let operands = self.func[took].operands;
1068        let bytes = self.func[operands][2].reg.phys().expect("a register the allocator settled");
1069        let [first, second] = probing.scratch;
1070        let (limit, flag) = if bytes == first { (second, first) } else { (first, second) };
1071
1072        let tail: Vec<Inst> = {
1073            let mut rest = self.func.insts(block).skip_while(|&inst| inst != took);
1074            rest.next();
1075            rest.collect()
1076        };
1077        let step = self.func.create_block();
1078        let onto = self.func.create_block();
1079        let done = self.func.create_block();
1080
1081        let mov = self.opcode(self.insts.moves(class).expect("a class the target can move").mov);
1082        let inst = self.two(mov, sp, limit);
1083        self.func.append_inst(done, inst);
1084        for inst in tail {
1085            self.func.remove_inst(inst);
1086            self.func.append_inst(done, inst);
1087        }
1088        let succs = std::mem::take(self.func.succs_mut(block));
1089        *self.func.succs_mut(done) = succs;
1090
1091        // The subtraction the lowering wrote is what the loop is instead of, so it goes. What is
1092        // left in the block it was in is where the stack pointer is walking down to.
1093        self.func.remove_inst(took);
1094        let inst = self.two(mov, limit, sp);
1095        self.func.append_inst(block, inst);
1096        let grow = self.opcode(self.insts.grow);
1097        let inst = self
1098            .func
1099            .build_loose(grow)
1100            .at(span)
1101            .def(Reg::physical(limit), class)
1102            .uses(Reg::physical(limit), class)
1103            .uses(Reg::physical(bytes), class)
1104            .finish();
1105        self.func.append_inst(block, inst);
1106        *self.func.succs_mut(block) = vec![BlockCall::to(step)];
1107
1108        let inst = self.sub(probing.probe.interval);
1109        self.func.append_inst(step, inst);
1110        let above = self.opcode(self.insts.above);
1111        let inst = self
1112            .func
1113            .build_loose(above)
1114            .def(Reg::physical(flag), class)
1115            .uses(Reg::physical(sp), class)
1116            .uses(Reg::physical(limit), class)
1117            .finish();
1118        self.func.append_inst(step, inst);
1119        let cond = Opcode::new(
1120            self.names.intern(&format!("{}{}", probing.branch.prefix, probing.branch.cond)),
1121        );
1122        let inst = self.func.build_loose(cond).uses(Reg::physical(flag), class).finish();
1123        self.func.append_inst(step, inst);
1124        // The first arm is the one taken when the condition held, and the condition is that the
1125        // stack pointer is still above where the array ends, so the first arm is the page it has
1126        // just stepped onto being written and another time round.
1127        *self.func.succs_mut(step) = vec![BlockCall::to(onto), BlockCall::to(done)];
1128
1129        let touch = self.touch(probing.probe);
1130        self.func.append_inst(onto, touch);
1131        *self.func.succs_mut(onto) = vec![BlockCall::to(step)];
1132    }
1133
1134    /// Writes the page the stack pointer is on without changing what is there.
1135    fn touch(&mut self, probe: &Probe) -> Inst {
1136        let opcode = self.opcode(probe.inst);
1137        let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
1138        self.func.build_loose(opcode).imm(0).mem(Mem::at(base)).finish()
1139    }
1140
1141    /// Takes that many bytes off the stack pointer.
1142    fn sub(&mut self, bytes: u32) -> Inst {
1143        let sub = self.opcode(self.insts.sub);
1144        self.arith(sub, i64::from(bytes))
1145    }
1146
1147    /// The stack protector's check, written at the end of a block the function returns from.
1148    ///
1149    /// Gives back the block the epilogue goes in, which is a new one: the check has to be the last
1150    /// thing the old block does, and what follows it is one of two arms rather than the return.
1151    ///
1152    /// ```text
1153    ///   block that returned      reload the slot, read the word again, compare, branch
1154    ///   the arm it changed on    call the function that does not come back, and nothing after
1155    ///   the arm it did not       the epilogue, which the caller writes into what this gives back
1156    /// ```
1157    ///
1158    /// The two registers are the ones the allocator was told to hold back, so nothing here has to
1159    /// ask what is live: a scratch register holds nothing at the end of a block, because the only
1160    /// thing that writes one is a move the rewriter put in and every one of those is read by the
1161    /// instruction it was put in front of.
1162    fn check(&mut self, block: Block, frame: &Frame, protect: Protect<'_>) -> Block {
1163        let class = self.conv.int_class;
1164        let at = frame.canary().expect("a protected function has a slot for its canary");
1165        let [ours, theirs] = protect.scratch;
1166
1167        let inst = self.load(class, ours, at);
1168        self.func.append_inst(block, inst);
1169        let inst = self.read_guard(theirs, protect.guard);
1170        self.func.append_inst(block, inst);
1171        let differ = self.opcode(self.insts.differ);
1172        let inst = self
1173            .func
1174            .build_loose(differ)
1175            .def(Reg::physical(theirs), class)
1176            .uses(Reg::physical(ours), class)
1177            .uses(Reg::physical(theirs), class)
1178            .finish();
1179        self.func.append_inst(block, inst);
1180
1181        let failed = self.func.create_block();
1182        let ok = self.func.create_block();
1183        let cond = Opcode::new(
1184            self.names.intern(&format!("{}{}", protect.branch.prefix, protect.branch.cond)),
1185        );
1186        let inst = self.func.build_loose(cond).uses(Reg::physical(theirs), class).finish();
1187        self.func.append_inst(block, inst);
1188        // The first arm is the one taken when the condition held, and the condition is that the
1189        // two words differ, so the first arm is the one the canary was overwritten on.
1190        *self.func.succs_mut(block) = vec![BlockCall::to(failed), BlockCall::to(ok)];
1191
1192        let call = self.opcode(self.insts.call);
1193        let symbol = self.names.intern(protect.guard.fail);
1194        self.func.build(failed, call).symbol(symbol).finish();
1195        ok
1196    }
1197
1198    /// Reads the word the canary is a copy of into a register.
1199    ///
1200    /// The address is a constant and names no register at all, because where the block a thread
1201    /// has to itself begins is something only the machine knows and the segment register is what
1202    /// holds it.
1203    fn read_guard(&mut self, into: PhysReg, guard: &Guard) -> Inst {
1204        let class = self.conv.int_class;
1205        let load = self.opcode(self.insts.moves(class).expect("a class to load").load);
1206        self.func
1207            .build_loose(load)
1208            .def(Reg::physical(into), class)
1209            .mem(Mem::in_segment(guard.segment, guard.at))
1210            .finish()
1211    }
1212
1213    /// The instructions the epilogue is, in the order they run.
1214    ///
1215    /// The vector registers are read back while the stack pointer is still where the body left it,
1216    /// because that is what their offsets are from. Then the stack pointer goes back to the last
1217    /// register the prologue pushed, which is arithmetic when the prologue knew how far it had
1218    /// moved and a read of the frame pointer when it did not.
1219    fn epilogue(&mut self, frame: &Frame) -> Vec<Inst> {
1220        let sp = self.conv.stack_pointer;
1221        let fp = self.conv.frame_pointer;
1222        let int = self.conv.int_class;
1223        let sse = self.conv.sse_class;
1224        let push = self.conv.push;
1225        let described = !self.func.cfi.is_empty();
1226        let mut out = Vec::new();
1227        // Where the body left things, which is where every epilogue starts from.
1228        let mut below = offset(self.conv.return_address)
1229            + offset(frame.home())
1230            + offset(push) * self.pushes(frame)
1231            + offset(frame.size());
1232        let from_sp = !frame.frame_pointer();
1233        // A late frame that forced its alignment has the stack pointer somewhere below where the
1234        // prologue left it, and the frame pointer holds where that was. Putting it back first makes
1235        // the rest of this the epilogue of any other late frame.
1236        if frame.late() && frame.realign().is_some() {
1237            let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
1238            out.push(self.two(mov, sp, fp));
1239        }
1240        for save in frame.saved_sse() {
1241            let inst = self.restore_sse(save.reg, save.at);
1242            out.push(inst);
1243            if frame.realign().is_none() || frame.late() {
1244                self.restored(inst, sse, save.reg);
1245            }
1246        }
1247        let pushed = u32::try_from(frame.pushed_int().len()).expect("a frame");
1248        if frame.frame_pointer() {
1249            // No row for either of these. The address is counted from the frame pointer here and
1250            // this is what moves the stack pointer rather than the frame pointer, so the rule that
1251            // was true before it is still true after it.
1252            //
1253            // Where the pointer is decides how far back this has to go. The early order left it one
1254            // push above the first push, so the pops start that many pushes below it. The late one
1255            // left it where the body's stack pointer was, so they start the whole frame above it,
1256            // and in both cases the distance is a constant even in a frame that grew while it ran,
1257            // which is why this is written rather than an addition to the stack pointer.
1258            let back = if frame.late() { offset(frame.size()) } else { -offset(push * pushed) };
1259            if back == 0 {
1260                let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
1261                out.push(self.two(mov, sp, fp));
1262            } else {
1263                let lea = self.opcode(self.insts.lea);
1264                out.push(self.address(lea, sp, fp, back));
1265            }
1266        } else if frame.size() > 0 {
1267            let add = self.opcode(self.insts.add);
1268            for step in self.insts.steps(frame.size()) {
1269                let inst = self.arith(add, i64::from(step));
1270                out.push(inst);
1271                below -= offset(step);
1272                self.row(inst, CfiOp::DefCfaOffset(below));
1273            }
1274        }
1275        for regs in frame.pushed_int().rev() {
1276            let inst = self.pop_int(regs);
1277            out.push(inst);
1278            for &reg in regs {
1279                self.restored(inst, int, reg);
1280            }
1281            below -= offset(push);
1282            if from_sp {
1283                self.row(inst, CfiOp::DefCfaOffset(below));
1284            }
1285        }
1286        if frame.frame_pointer() {
1287            let inst = self.pop_frame();
1288            out.push(inst);
1289            self.restored(inst, int, fp);
1290            if let Some(link) = self.record() {
1291                self.restored(inst, int, link);
1292            }
1293            // The frame pointer holds the caller's value again, so the address goes back to being
1294            // counted from the stack pointer, which by now is at the return address.
1295            let number = self.dwarf(int, sp);
1296            let at = offset(self.conv.return_address) + offset(frame.home());
1297            self.row(inst, CfiOp::DefCfa { reg: number, offset: at });
1298        }
1299        // And the home area last, which is the first thing the prologue took.
1300        if frame.home() > 0 {
1301            let add = self.opcode(self.insts.add);
1302            let inst = self.arith(add, i64::from(frame.home()));
1303            out.push(inst);
1304            self.row(inst, CfiOp::DefCfaOffset(offset(self.conv.return_address)));
1305        }
1306        let ret = self.opcode(self.insts.ret);
1307        let inst = self.func.build_loose(ret).finish();
1308        out.push(inst);
1309        // These take effect at the address just past the return, which is where the next block
1310        // begins, and the next block is body again. Popping the body's rules and pushing them
1311        // straight back leaves the stack one deep however many blocks the function returns from,
1312        // which is what makes one remembering in the prologue enough for all of them.
1313        if described {
1314            self.row(inst, CfiOp::RestoreState);
1315            self.row(inst, CfiOp::RememberState);
1316        }
1317        // And where all of it came from, which is the closing brace. Nothing here has a span of its
1318        // own: an epilogue is the frame going back the way it came and no expression in the source
1319        // asked for any of it, so without this the bytes are covered by whatever the last statement
1320        // of the body was. That is the hole the prologue used to have, at the other end, and gcc
1321        // fills it the same way it fills the other one, with the brace. A function whose body this
1322        // does not know is left alone and keeps covering those bytes with the last row before them.
1323        let closing = ending(self.func.declared);
1324        if !closing.is_dummy() {
1325            for &inst in &out {
1326                self.func.set_span(inst, closing);
1327            }
1328        }
1329        out
1330    }
1331
1332    /// How many pushes the prologue made, the frame pointer's included, which on a machine that
1333    /// pushes two at a time is fewer than the registers they saved.
1334    fn pushes(&self, frame: &Frame) -> i32 {
1335        let saved = i32::try_from(frame.pushed_int().len()).expect("a frame");
1336        saved + i32::from(frame.frame_pointer())
1337    }
1338
1339    /// One row of the unwind table, taking effect after that instruction.
1340    fn row(&mut self, inst: Inst, op: CfiOp) {
1341        self.func.cfi.push((inst, op));
1342    }
1343
1344    /// A row saying the caller's copy of that register is that far from the canonical frame
1345    /// address, which is below it and so is negative.
1346    fn saved(&mut self, inst: Inst, class: RegClass, reg: PhysReg, from_cfa: i32) {
1347        let number = self.dwarf(class, reg);
1348        self.row(inst, CfiOp::Offset { reg: number, offset: from_cfa });
1349    }
1350
1351    /// A row saying that register holds what the caller left in it again.
1352    fn restored(&mut self, inst: Inst, class: RegClass, reg: PhysReg) {
1353        let number = self.dwarf(class, reg);
1354        self.row(inst, CfiOp::Restore(number));
1355    }
1356
1357    /// What an unwind table calls that register.
1358    fn dwarf(&self, class: RegClass, reg: PhysReg) -> u16 {
1359        self.conv.dwarf(class, reg).expect("a register a frame saves is one the table can name")
1360    }
1361
1362    /// One edit as the instruction that makes it true.
1363    fn mov(&mut self, edit: &Edit, frame: &Frame) -> Inst {
1364        let moves = self.insts.moves(edit.class).expect("a class the target says how to move");
1365        match (edit.mov.to, edit.mov.from) {
1366            (Place::Reg(to), Place::Reg(from)) => {
1367                let mov = self.opcode(moves.mov);
1368                self.func
1369                    .build_loose(mov)
1370                    .def(Reg::physical(to), edit.class)
1371                    .uses(Reg::physical(from), edit.class)
1372                    .finish()
1373            }
1374            (Place::Reg(to), Place::Slot(slot)) => {
1375                let at = self.slot(frame, slot);
1376                self.load(edit.class, to, at)
1377            }
1378            (Place::Slot(slot), Place::Reg(from)) => {
1379                let at = self.slot(frame, slot);
1380                self.store(edit.class, from, at)
1381            }
1382            // The allocator expands this into two moves through a register of its own, because a
1383            // machine that could do it in one is not a machine any of this is written for.
1384            (Place::Slot(_), Place::Slot(_)) => {
1385                unreachable!("a move from one stack slot straight into another")
1386            }
1387        }
1388    }
1389
1390    /// Puts an instruction where an edit says it goes, after whatever earlier edits went there.
1391    ///
1392    /// The edits at one place are in the order they have to be made in, so each one goes behind
1393    /// the last, and the first of them is what the place itself means.
1394    fn put(&mut self, cursors: &mut Map<At, Inst>, at: At, inst: Inst) {
1395        if let Some(cursor) = cursors.get_mut(&at) {
1396            self.func.insert_after(*cursor, inst);
1397            *cursor = inst;
1398            return;
1399        }
1400        match at {
1401            At::Before(before) => self.func.insert_before(before, inst),
1402            At::After(after) => self.func.insert_after(after, inst),
1403            At::StartOf(block) => self.func.prepend_inst(block, inst),
1404            // Behind everything in the block. A block the allocator puts an edge's moves at the
1405            // end of is one with a single edge out of it, and an edge like that is not an
1406            // instruction here: [`crate::layout`] writes the jump it becomes after this has run.
1407            // So the last instruction is an ordinary one, which may still be waiting on moves of
1408            // its own that have to be made before the edge's are.
1409            At::EndOf(block) => self.func.append_inst(block, inst),
1410        }
1411        cursors.insert(at, inst);
1412    }
1413
1414    /// Where a spill slot is, from the stack pointer in the body of the function.
1415    fn slot(&self, frame: &Frame, slot: u32) -> i32 {
1416        frame.slot(slot).expect("a slot the frame was worked out from")
1417    }
1418
1419    /// Reads a register out of the frame.
1420    fn load(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
1421        let load = self.insts.moves(class).expect("a class to load").load;
1422        self.load_as(load, class, reg, at)
1423    }
1424
1425    /// Reads a register out of the frame with that load.
1426    fn load_as(&mut self, load: &str, class: RegClass, reg: PhysReg, at: i32) -> Inst {
1427        let load = self.opcode(load);
1428        let base = Operand::read(Reg::physical(self.base), self.conv.int_class);
1429        self.func
1430            .build_loose(load)
1431            .def(Reg::physical(reg), class)
1432            .mem(Mem::at(base).plus(at))
1433            .finish()
1434    }
1435
1436    /// Writes a register into the frame.
1437    fn store(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
1438        let store = self.insts.moves(class).expect("a class to store").store;
1439        self.store_as(store, class, reg, at)
1440    }
1441
1442    /// The part of a vector register the prologue owes the caller, into the frame. The whole of it
1443    /// unless the convention keeps only part, and then that part and no more. See
1444    /// [`rucc_target::Kept`].
1445    fn save_sse(&mut self, reg: PhysReg, at: i32) -> Inst {
1446        let sse = self.conv.sse_class;
1447        match self.insts.kept.filter(|_| self.conv.sse_kept.is_some()) {
1448            Some(kept) => self.store_as(kept.store, sse, reg, at),
1449            None => self.store(sse, reg, at),
1450        }
1451    }
1452
1453    /// What [`Self::save_sse`] wrote, back out of the frame.
1454    fn restore_sse(&mut self, reg: PhysReg, at: i32) -> Inst {
1455        let sse = self.conv.sse_class;
1456        match self.insts.kept.filter(|_| self.conv.sse_kept.is_some()) {
1457            Some(kept) => self.load_as(kept.load, sse, reg, at),
1458            None => self.load(sse, reg, at),
1459        }
1460    }
1461
1462    /// Writes a register into the frame with that store.
1463    fn store_as(&mut self, store: &str, class: RegClass, reg: PhysReg, at: i32) -> Inst {
1464        let store = self.opcode(store);
1465        let base = Operand::read(Reg::physical(self.base), self.conv.int_class);
1466        self.func
1467            .build_loose(store)
1468            .uses(Reg::physical(reg), class)
1469            .mem(Mem::at(base).plus(at))
1470            .finish()
1471    }
1472
1473    /// Puts one general purpose register on the stack, or two with the pair instruction. See
1474    /// [`crate::frame::Layout::pairs`].
1475    fn push_int(&mut self, regs: &[PhysReg]) -> Inst {
1476        let &[first, second] = regs else { return self.push(regs[0]) };
1477        let pair = self.insts.pair.expect("a frame that pairs is on a machine that can");
1478        let push = self.opcode(pair.push);
1479        let class = self.conv.int_class;
1480        self.func
1481            .build_loose(push)
1482            .uses(Reg::physical(first), class)
1483            .uses(Reg::physical(second), class)
1484            .finish()
1485    }
1486
1487    /// Takes back what [`Self::push_int`] put on the stack.
1488    fn pop_int(&mut self, regs: &[PhysReg]) -> Inst {
1489        let &[first, second] = regs else { return self.pop(regs[0]) };
1490        let pair = self.insts.pair.expect("a frame that pairs is on a machine that can");
1491        let pop = self.opcode(pair.pop);
1492        let class = self.conv.int_class;
1493        self.func
1494            .build_loose(pop)
1495            .def(Reg::physical(first), class)
1496            .def(Reg::physical(second), class)
1497            .finish()
1498    }
1499
1500    /// Puts a general purpose register on the stack.
1501    fn push(&mut self, reg: PhysReg) -> Inst {
1502        let push = self.opcode(self.insts.push);
1503        self.func.build_loose(push).uses(Reg::physical(reg), self.conv.int_class).finish()
1504    }
1505
1506    /// Takes a general purpose register back off the stack.
1507    fn pop(&mut self, reg: PhysReg) -> Inst {
1508        let pop = self.opcode(self.insts.pop);
1509        self.func.build_loose(pop).def(Reg::physical(reg), self.conv.int_class).finish()
1510    }
1511
1512    /// The register that goes on the stack with the frame pointer, which is the one a call leaves
1513    /// the return address in on a machine that pushes the two together, and nothing anywhere else.
1514    fn record(&self) -> Option<PhysReg> {
1515        self.conv.link.filter(|_| self.insts.pair.is_some())
1516    }
1517
1518    /// Puts the caller's frame pointer on the stack, together with the return address on a machine
1519    /// that keeps it in a register. The frame pointer goes at the lower address, so the pointer set
1520    /// to it straight after names the caller's copy and the return address is the word above.
1521    fn push_frame(&mut self) -> Inst {
1522        let fp = self.conv.frame_pointer;
1523        let (Some(link), Some(pair)) = (self.record(), self.insts.pair) else {
1524            return self.push(fp);
1525        };
1526        let push = self.opcode(pair.push);
1527        let class = self.conv.int_class;
1528        self.func
1529            .build_loose(push)
1530            .uses(Reg::physical(fp), class)
1531            .uses(Reg::physical(link), class)
1532            .finish()
1533    }
1534
1535    /// Takes back what [`Self::push_frame`] put on the stack.
1536    fn pop_frame(&mut self) -> Inst {
1537        let fp = self.conv.frame_pointer;
1538        let (Some(link), Some(pair)) = (self.record(), self.insts.pair) else {
1539            return self.pop(fp);
1540        };
1541        let pop = self.opcode(pair.pop);
1542        let class = self.conv.int_class;
1543        self.func
1544            .build_loose(pop)
1545            .def(Reg::physical(fp), class)
1546            .def(Reg::physical(link), class)
1547            .finish()
1548    }
1549
1550    /// One general purpose register written with another.
1551    fn two(&mut self, opcode: Opcode, to: PhysReg, from: PhysReg) -> Inst {
1552        let class = self.conv.int_class;
1553        self.func
1554            .build_loose(opcode)
1555            .def(Reg::physical(to), class)
1556            .uses(Reg::physical(from), class)
1557            .finish()
1558    }
1559
1560    /// Two-address arithmetic on the stack pointer, which reads it and writes it back.
1561    fn arith(&mut self, opcode: Opcode, value: i64) -> Inst {
1562        let class = self.conv.int_class;
1563        let sp = Reg::physical(self.conv.stack_pointer);
1564        self.func.build_loose(opcode).def(sp, class).uses(sp, class).imm(value).finish()
1565    }
1566
1567    /// One register written with an address rather than with what is at it.
1568    fn address(&mut self, opcode: Opcode, to: PhysReg, base: PhysReg, disp: i32) -> Inst {
1569        let class = self.conv.int_class;
1570        let base = Operand::read(Reg::physical(base), class);
1571        self.func
1572            .build_loose(opcode)
1573            .def(Reg::physical(to), class)
1574            .mem(Mem::at(base).plus(disp))
1575            .finish()
1576    }
1577
1578    /// The opcode of that name, in the machine IR's spelling, which is the target's prefix and
1579    /// then the name the target gave.
1580    fn opcode(&mut self, name: &str) -> Opcode {
1581        Opcode::new(self.names.intern(&format!("{}{name}", self.insts.prefix)))
1582    }
1583}
1584
1585/// A distance in a frame, as the signed number every offset is.
1586fn offset(bytes: u32) -> i32 {
1587    i32::try_from(bytes).expect("a frame under two gigabytes")
1588}
1589
1590/// The last character of a span, which for the span of a function body is its closing brace.
1591///
1592/// A span runs from the first byte to one past the last, so the brace is the byte before the end
1593/// rather than the end. [`Span::DUMMY`] for a function that came from no C source, which is what
1594/// the tests and the IR parser build, and for the empty span that cannot have a last character.
1595fn ending(body: Span) -> Span {
1596    if body.is_dummy() || body.hi <= body.lo {
1597        return Span::DUMMY;
1598    }
1599    Span::new(body.hi - 1, body.hi)
1600}
1601
1602#[cfg(test)]
1603mod tests {
1604    use rucc_base::Interner;
1605    use rucc_mir::{BlockCall, print_func};
1606    use rucc_regalloc::assign::Env;
1607    use rucc_target::x86_64::{
1608        BRANCH, FRAME, GPR, PROBE, R10, R11, RAX, REGS, SYSV, WIN64, XMM, xmm,
1609    };
1610
1611    use super::*;
1612    use crate::frame::{Layout, Local};
1613
1614    /// The closing brace of a body is the last character of its span and not the end of it, since a
1615    /// span runs to one past what it covers. A function that came from no source has no brace and
1616    /// asks for no row, which is what keeps the epilogue of one the IR parser built covered by the
1617    /// row before it rather than by a position in a file that is not there.
1618    #[test]
1619    fn the_end_of_a_body_is_its_closing_brace_and_not_one_past_it() {
1620        assert_eq!(ending(Span::new(10, 40)), Span::new(39, 40));
1621        assert_eq!(ending(Span::DUMMY), Span::DUMMY);
1622        assert_eq!(ending(Span::new(7, 7)), Span::DUMMY);
1623    }
1624
1625    /// An environment offering that many of the convention's registers, with everything after
1626    /// them held back as scratch.
1627    fn env(conv: &CallRegs, count: usize) -> Env {
1628        Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
1629    }
1630
1631    /// A function of that many values, every one written before any is read, allocated with that
1632    /// many registers to hand out. The same shape the frame layout's own tests are written
1633    /// against, so that a frame here is one that has already been checked there.
1634    fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation, Interner) {
1635        let mut names = Interner::new();
1636        let mut func = Func::new(names.intern("f"));
1637        let opcode = Opcode::new(names.intern("x64.nop"));
1638        let block = func.create_block();
1639        let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
1640        for &reg in &regs {
1641            func.build(block, opcode).def(reg, GPR).finish();
1642        }
1643        for &reg in &regs {
1644            func.build(block, opcode).uses(reg, GPR).finish();
1645        }
1646        let allocation = rucc_regalloc::run(&mut func, &env(conv, count), "test", true);
1647        (func, allocation, names)
1648    }
1649
1650    /// The function with its frame written into it, as the lines a dump would show.
1651    fn written(
1652        func: &mut Func,
1653        allocation: &Allocation,
1654        layout: &Layout<'_>,
1655        names: &mut Interner,
1656    ) -> Vec<String> {
1657        with_protector(func, allocation, layout, None, names)
1658    }
1659
1660    /// The same, for a function the caller has decided is protected or is not.
1661    fn with_protector(
1662        func: &mut Func,
1663        allocation: &Allocation,
1664        layout: &Layout<'_>,
1665        protect: Option<Protect<'_>>,
1666        names: &mut Interner,
1667    ) -> Vec<String> {
1668        let convention = Convention { protect, ..Convention::new(layout.conv, &FRAME) };
1669        under(func, allocation, layout, &Stack::default(), convention, names)
1670    }
1671
1672    /// The same, for a function whose frame the caller has decided is taken a page at a time.
1673    fn with_probing(
1674        func: &mut Func,
1675        allocation: &Allocation,
1676        layout: &Layout<'_>,
1677        probe: Option<Probing<'_>>,
1678        names: &mut Interner,
1679    ) -> Vec<String> {
1680        let convention = Convention { probe, ..Convention::new(layout.conv, &FRAME) };
1681        under(func, allocation, layout, &Stack::default(), convention, names)
1682    }
1683
1684    /// A function whose one block takes a run of bytes off the stack pointer, which is what the
1685    /// lowering writes for a variable length array, with the count already in the register given.
1686    fn growing(count: PhysReg) -> (Func, Allocation, Interner, Stack) {
1687        let mut names = Interner::new();
1688        let mut func = Func::new(names.intern("f"));
1689        let block = func.create_block();
1690        let sp = Reg::physical(SYSV.stack_pointer);
1691        let grow = Opcode::new(names.intern("x64.sub_rr_64"));
1692        let took = func
1693            .build(block, grow)
1694            .def(sp, GPR)
1695            .uses(sp, GPR)
1696            .uses(Reg::physical(count), GPR)
1697            .finish();
1698        let nop = Opcode::new(names.intern("x64.nop"));
1699        func.build(block, nop).finish();
1700        let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4), "test", true);
1701        (func, allocation, names, Stack { grown: vec![took], ..Stack::default() })
1702    }
1703
1704    /// The function with its frame written into it under that convention.
1705    fn under(
1706        func: &mut Func,
1707        allocation: &Allocation,
1708        layout: &Layout<'_>,
1709        stack: &Stack,
1710        convention: Convention<'_>,
1711        names: &mut Interner,
1712    ) -> Vec<String> {
1713        let frame = Frame::of(func, allocation, layout);
1714        finish(func, allocation, &frame, stack, convention, names);
1715        print_func(func, names, &REGS)
1716            .lines()
1717            .filter(|line| !line.is_empty())
1718            .map(|line| line.trim().to_string())
1719            .collect()
1720    }
1721
1722    /// Just the lines the frame put in, which is every line that is not the function it was
1723    /// given and not the shape of the dump around it.
1724    fn added(lines: &[String]) -> Vec<&str> {
1725        lines
1726            .iter()
1727            .map(String::as_str)
1728            .filter(|line| !line.contains("x64.nop"))
1729            .filter(|line| !line.starts_with("mfunc") && !line.starts_with("block") && *line != "}")
1730            .collect()
1731    }
1732
1733    #[test]
1734    fn a_function_that_needs_no_frame_is_given_a_return_and_nothing_else() {
1735        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1736        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
1737
1738        // Two values and four registers, so nothing is spilled, nothing is saved and the stack
1739        // pointer never moves. A prologue of nothing is the right prologue for that.
1740        assert_eq!(added(&lines), ["x64.ret"]);
1741    }
1742
1743    /// The bytes that give a frame back are filed under the closing brace, which is where a
1744    /// debugger says a function ends and which nothing in an epilogue could say for itself.
1745    #[test]
1746    fn an_epilogue_is_filed_under_the_closing_brace_of_the_body() {
1747        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1748        func.declared = Span::new(100, 140);
1749        let base = Layout::new(&SYSV, REGS);
1750        let layout = Layout { red_zone: false, ..base };
1751        written(&mut func, &allocation, &layout, &mut names);
1752
1753        // Everything from the first instruction of the epilogue to the return, and nothing above
1754        // it: the body's own instructions keep the spans they arrived with, which here is none.
1755        let ends: Vec<Span> = func
1756            .blocks()
1757            .flat_map(|block| func.insts(block).collect::<Vec<_>>())
1758            .map(|inst| func.span(inst))
1759            .filter(|span| !span.is_dummy())
1760            .collect();
1761        assert!(!ends.is_empty(), "an epilogue was written");
1762        assert!(ends.iter().all(|&span| span == Span::new(139, 140)), "{ends:?}");
1763    }
1764
1765    #[test]
1766    fn a_spill_is_a_store_and_a_reload_is_a_load() {
1767        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1768        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
1769
1770        // Two registers for four values, so two of them go to the stack. The store goes behind the
1771        // instruction that wrote the value and the load in front of the one that wants it, both at
1772        // the offsets the frame gave, which are below the stack pointer because a small leaf
1773        // function is entitled to the red zone.
1774        assert_eq!(
1775            lines,
1776            [
1777                "mfunc @f {",
1778                "block0:",
1779                "$rax = x64.nop",
1780                "$rcx = x64.nop",
1781                "$rdx = x64.nop",
1782                "x64.mov_mr_64 $rdx, [$rsp - 16]",
1783                "$rdx = x64.nop",
1784                "x64.mov_mr_64 $rdx, [$rsp - 8]",
1785                "x64.nop $rax",
1786                "x64.nop $rcx",
1787                "$rdx = x64.mov_rm_64 [$rsp - 16]",
1788                "x64.nop $rdx",
1789                "$rdx = x64.mov_rm_64 [$rsp - 8]",
1790                "x64.nop $rdx",
1791                "x64.ret",
1792                "}",
1793            ]
1794        );
1795    }
1796
1797    #[test]
1798    fn the_frame_the_prologue_takes_is_the_frame_the_epilogue_gives_back() {
1799        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1800        let base = Layout::new(&SYSV, REGS);
1801        let layout = Layout { red_zone: false, ..base };
1802        let lines = written(&mut func, &allocation, &layout, &mut names);
1803
1804        // The same function told it may not use the red zone takes sixteen bytes instead, and
1805        // every offset moves above the stack pointer to match.
1806        assert_eq!(
1807            added(&lines),
1808            [
1809                "$rsp = x64.sub_ri_64 $rsp, 16",
1810                "x64.mov_mr_64 $rdx, [$rsp]",
1811                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1812                "$rdx = x64.mov_rm_64 [$rsp]",
1813                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1814                "$rsp = x64.add_ri_64 $rsp, 16",
1815                "x64.ret",
1816            ]
1817        );
1818    }
1819
1820    #[test]
1821    fn the_registers_the_prologue_pushes_come_back_in_the_opposite_order() {
1822        let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
1823        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
1824
1825        // Four registers a call leaves alone, pushed in the convention's order and popped in the
1826        // other one, which is the only order that gets each of them its own value back.
1827        assert_eq!(
1828            added(&lines),
1829            [
1830                "x64.push_64 $rbx",
1831                "x64.push_64 $r12",
1832                "x64.push_64 $r13",
1833                "x64.push_64 $r14",
1834                "$r14 = x64.pop_64",
1835                "$r13 = x64.pop_64",
1836                "$r12 = x64.pop_64",
1837                "$rbx = x64.pop_64",
1838                "x64.ret",
1839            ]
1840        );
1841    }
1842
1843    #[test]
1844    fn a_function_that_keeps_a_frame_pointer_sets_it_up_and_leaves_by_it() {
1845        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1846        let base = Layout::new(&SYSV, REGS);
1847        let layout = Layout { frame_pointer: true, red_zone: false, ..base };
1848        let lines = written(&mut func, &allocation, &layout, &mut names);
1849
1850        // The frame pointer is saved before anything else and points at where it was saved, so the
1851        // epilogue reaches the stack pointer through it rather than by counting the frame back.
1852        assert_eq!(
1853            added(&lines),
1854            [
1855                "x64.push_64 $rbp",
1856                "$rbp = x64.mov_rr_64 $rsp",
1857                "$rsp = x64.sub_ri_64 $rsp, 16",
1858                "x64.mov_mr_64 $rdx, [$rsp]",
1859                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1860                "$rdx = x64.mov_rm_64 [$rsp]",
1861                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1862                "$rsp = x64.mov_rr_64 $rbp",
1863                "$rbp = x64.pop_64",
1864                "x64.ret",
1865            ]
1866        );
1867    }
1868
1869    #[test]
1870    fn a_realigned_frame_forces_the_alignment_after_it_has_pushed_what_it_saves() {
1871        let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
1872        let locals = [Local { size: 64, align: 32 }];
1873        let base = Layout::new(&SYSV, REGS);
1874        let layout = Layout { locals: &locals, ..base };
1875        let lines = written(&mut func, &allocation, &layout, &mut names);
1876
1877        // Forcing the alignment throws away how far the stack pointer had moved, so the registers
1878        // are pushed before it happens and the epilogue counts back from the frame pointer to find
1879        // them. The frame pointer is required here whatever the flags said.
1880        assert_eq!(
1881            added(&lines),
1882            [
1883                "x64.push_64 $rbp",
1884                "$rbp = x64.mov_rr_64 $rsp",
1885                "x64.push_64 $rbx",
1886                "x64.push_64 $r12",
1887                "x64.push_64 $r13",
1888                "x64.push_64 $r14",
1889                "$rsp = x64.and_ri_64 $rsp, -32",
1890                "$rsp = x64.sub_ri_64 $rsp, 64",
1891                "$rsp = x64.lea_64 [$rbp - 32]",
1892                "$r14 = x64.pop_64",
1893                "$r13 = x64.pop_64",
1894                "$r12 = x64.pop_64",
1895                "$rbx = x64.pop_64",
1896                "$rbp = x64.pop_64",
1897                "x64.ret",
1898            ]
1899        );
1900    }
1901
1902    #[test]
1903    fn every_block_the_function_returns_from_gets_an_epilogue() {
1904        let mut names = Interner::new();
1905        let mut func = Func::new(names.intern("f"));
1906        let opcode = Opcode::new(names.intern("x64.nop"));
1907        let head = func.create_block();
1908        let left = func.create_block();
1909        let right = func.create_block();
1910        func.build(head, opcode).finish();
1911        *func.succs_mut(head) = vec![BlockCall::to(left), BlockCall::to(right)];
1912        func.build(left, opcode).finish();
1913        func.build(right, opcode).finish();
1914        let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4), "test", true);
1915        let base = Layout::new(&SYSV, REGS);
1916        let layout = Layout { leaf: false, ..base };
1917        let lines = written(&mut func, &allocation, &layout, &mut names);
1918
1919        // Both ways out get the frame given back, and the block that goes somewhere gets nothing,
1920        // because a block with an edge out of it is not a block anything returns from.
1921        assert_eq!(
1922            lines,
1923            [
1924                "mfunc @f {",
1925                "block0:",
1926                "$rsp = x64.sub_ri_64 $rsp, 8",
1927                "x64.nop block1, block2",
1928                "block1:",
1929                "x64.nop",
1930                "$rsp = x64.add_ri_64 $rsp, 8",
1931                "x64.ret",
1932                "block2:",
1933                "x64.nop",
1934                "$rsp = x64.add_ri_64 $rsp, 8",
1935                "x64.ret",
1936                "}",
1937            ]
1938        );
1939    }
1940
1941    #[test]
1942    fn a_protected_function_writes_the_canary_last_and_checks_it_before_it_returns() {
1943        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1944        let base = Layout::new(&SYSV, REGS);
1945        let layout = Layout { leaf: false, protect: true, ..base };
1946        let guard = SYSV.guard.as_ref().expect("this convention has somewhere to keep the word");
1947        // The two the real pipeline holds back, which are held back in the environment above too:
1948        // it hands out the first two of the convention's order and keeps everything after them.
1949        let protect = Protect { guard, branch: &BRANCH, scratch: [R10, R11] };
1950        let lines = with_protector(&mut func, &allocation, &layout, Some(protect), &mut names);
1951
1952        // The read of the word and the store into the slot come after the stack pointer has moved,
1953        // because there is no slot to store into until it has. The check is the last thing the
1954        // block that returned does and the epilogue is on the arm the canary was unchanged on, so
1955        // a function whose canary changed never gives its frame back and never returns.
1956        assert_eq!(
1957            added(&lines),
1958            [
1959                "$rsp = x64.sub_ri_64 $rsp, 24",
1960                "$r10 = x64.mov_rm_64 [fs:40]",
1961                "x64.mov_mr_64 $r10, [$rsp + 16]",
1962                "x64.mov_mr_64 $rdx, [$rsp]",
1963                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1964                "$rdx = x64.mov_rm_64 [$rsp]",
1965                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1966                "$r10 = x64.mov_rm_64 [$rsp + 16]",
1967                "$r11 = x64.mov_rm_64 [fs:40]",
1968                "$r11 = x64.cmp_set_ne_64 $r10, $r11",
1969                "x64.br_cond_8 $r11, block1, block2",
1970                "x64.call @__stack_chk_fail",
1971                "$rsp = x64.add_ri_64 $rsp, 24",
1972                "x64.ret",
1973            ]
1974        );
1975    }
1976
1977    #[test]
1978    fn a_frame_that_fits_in_one_page_is_taken_in_one_subtraction_even_when_pages_are_touched() {
1979        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1980        let locals = [Local { size: 4088, align: 16 }];
1981        let base = Layout::new(&SYSV, REGS);
1982        let layout = Layout { leaf: false, locals: &locals, ..base };
1983        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
1984        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
1985
1986        // A frame of one page cannot step over the page below it, because the far end of it is the
1987        // near end of that page and anything written there is written to a page that is there. So
1988        // the flag costs such a function nothing, which is most functions.
1989        assert_eq!(
1990            added(&lines),
1991            ["$rsp = x64.sub_ri_64 $rsp, 4088", "$rsp = x64.add_ri_64 $rsp, 4088", "x64.ret",]
1992        );
1993    }
1994
1995    #[test]
1996    fn a_probing_prologue_touches_every_page_of_a_frame_a_few_pages_deep() {
1997        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1998        let locals = [Local { size: 9000, align: 16 }];
1999        let base = Layout::new(&SYSV, REGS);
2000        let layout = Layout { leaf: false, locals: &locals, ..base };
2001        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
2002        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
2003
2004        // A page of the stack pointer's own, then the touch that says the page is there, and only
2005        // then the next one, which is the whole of the defence: nothing here ever moves the stack
2006        // pointer further than one page without writing where it landed. The last subtraction is
2007        // the remainder and is smaller than a page, so it needs no touch of its own, and it exists
2008        // in every frame because the count of pages is taken off one less than the size.
2009        assert_eq!(
2010            added(&lines),
2011            [
2012                "$rsp = x64.sub_ri_64 $rsp, 4096",
2013                "x64.or_mi_8 [$rsp], 0",
2014                "$rsp = x64.sub_ri_64 $rsp, 4096",
2015                "x64.or_mi_8 [$rsp], 0",
2016                "$rsp = x64.sub_ri_64 $rsp, 808",
2017                "$rsp = x64.add_ri_64 $rsp, 9000",
2018                "x64.ret",
2019            ]
2020        );
2021    }
2022
2023    #[test]
2024    fn a_variable_length_array_walks_its_pages_where_the_declaration_stands() {
2025        let (mut func, allocation, mut names, stack) = growing(RAX);
2026        let base = Layout::new(&SYSV, REGS);
2027        let layout = Layout { leaf: false, grows: true, ..base };
2028        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
2029        let convention = Convention { probe: Some(probing), ..Convention::new(&SYSV, &FRAME) };
2030        let lines = under(&mut func, &allocation, &layout, &stack, convention, &mut names);
2031
2032        // The whole listing, because what the walk is cannot be read off the instructions alone.
2033        // The one subtraction the lowering wrote is gone and four blocks stand where its block was:
2034        // where the stack pointer is going, the step, the page the step landed on, and the rest of
2035        // what the block was doing with the stack pointer put back where it was going.
2036        assert_eq!(
2037            lines,
2038            [
2039                "mfunc @f {",
2040                "block0:",
2041                "x64.push_64 $rbp",
2042                "$rbp = x64.mov_rr_64 $rsp",
2043                "$r10 = x64.mov_rr_64 $rsp",
2044                "$r10 = x64.sub_rr_64 $r10, $rax, block1",
2045                "block1:",
2046                "$rsp = x64.sub_ri_64 $rsp, 4096",
2047                "$r11 = x64.cmp_set_a_64 $rsp, $r10",
2048                "x64.br_cond_8 $r11, block2, block3",
2049                "block2:",
2050                "x64.or_mi_8 [$rsp], 0, block1",
2051                "block3:",
2052                "$rsp = x64.mov_rr_64 $r10",
2053                "x64.nop",
2054                "$rsp = x64.mov_rr_64 $rbp",
2055                "$rbp = x64.pop_64",
2056                "x64.ret",
2057                "}",
2058            ]
2059        );
2060    }
2061
2062    #[test]
2063    fn the_walk_keeps_the_register_the_count_arrived_in() {
2064        let (mut func, allocation, mut names, stack) = growing(R10);
2065        let base = Layout::new(&SYSV, REGS);
2066        let layout = Layout { leaf: false, grows: true, ..base };
2067        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
2068        let convention = Convention { probe: Some(probing), ..Convention::new(&SYSV, &FRAME) };
2069        let lines = under(&mut func, &allocation, &layout, &stack, convention, &mut names);
2070
2071        // The count is in the first of the two registers the walk was given, which is where a
2072        // reload the rewriter wrote would have put it, so the limit goes in the other one and the
2073        // comparison writes the first one back only once the count has been read for the last time.
2074        let added = added(&lines);
2075        assert!(added.contains(&"$r11 = x64.mov_rr_64 $rsp"), "{added:?}");
2076        assert!(added.contains(&"$r11 = x64.sub_rr_64 $r11, $r10, block1"), "{added:?}");
2077        assert!(added.contains(&"$r10 = x64.cmp_set_a_64 $rsp, $r11"), "{added:?}");
2078    }
2079
2080    #[test]
2081    fn a_variable_length_array_takes_its_bytes_in_one_subtraction_when_nothing_asked() {
2082        let (mut func, allocation, mut names, stack) = growing(RAX);
2083        let base = Layout::new(&SYSV, REGS);
2084        let layout = Layout { leaf: false, grows: true, ..base };
2085        let convention = Convention::new(&SYSV, &FRAME);
2086        let lines = under(&mut func, &allocation, &layout, &stack, convention, &mut names);
2087
2088        // The instruction the lowering wrote, where it wrote it, and one block still.
2089        assert!(lines.contains(&"$rsp = x64.sub_rr_64 $rsp, $rax".to_owned()), "{lines:?}");
2090        assert_eq!(lines.iter().filter(|line| line.starts_with("block")).count(), 1, "{lines:?}");
2091    }
2092
2093    #[test]
2094    fn a_probing_prologue_deeper_than_that_walks_the_pages_in_a_loop() {
2095        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
2096        let locals = [Local { size: 100_000, align: 16 }];
2097        let base = Layout::new(&SYSV, REGS);
2098        let layout = Layout { leaf: false, locals: &locals, ..base };
2099        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
2100        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
2101
2102        // Twenty-four pages, which is more than a straight line is worth, so the prologue works out
2103        // where it is going first and then walks there. The whole listing rather than the added
2104        // lines, because what matters as much as the instructions is that the two blocks the walk
2105        // is made of come in front of the block the function began with: the body the allocator
2106        // filled is block2 here and it was block0 before this ran.
2107        assert_eq!(
2108            lines,
2109            [
2110                "mfunc @f {",
2111                "block0:",
2112                "$r10 = x64.lea_64 [$rsp - 98304], block1",
2113                "block1:",
2114                "$rsp = x64.sub_ri_64 $rsp, 4096",
2115                "x64.or_mi_8 [$rsp], 0",
2116                "$r11 = x64.cmp_set_ne_64 $rsp, $r10",
2117                "x64.br_cond_8 $r11, block1, block2",
2118                "block2:",
2119                "$rsp = x64.sub_ri_64 $rsp, 1704",
2120                "$rax = x64.nop",
2121                "$rcx = x64.nop",
2122                "x64.nop $rax",
2123                "x64.nop $rcx",
2124                "$rsp = x64.add_ri_64 $rsp, 100008",
2125                "x64.ret",
2126                "}",
2127            ]
2128        );
2129    }
2130
2131    #[test]
2132    fn a_large_frame_on_a_platform_with_a_routine_for_its_pages_calls_the_routine() {
2133        let (mut func, allocation, mut names) = pressure(&WIN64, 2, 4);
2134        let locals = [Local { size: 100_000, align: 16 }];
2135        let base = Layout::new(&WIN64, REGS);
2136        let layout = Layout { leaf: false, locals: &locals, ..base };
2137        let lines = written(&mut func, &allocation, &layout, &mut names);
2138
2139        // Nothing asked for this on the command line, which is the point: Windows commits a stack
2140        // by having the pages touched in order, so a frame this size has to reach them whatever the
2141        // flags said. The size goes in the register the platform names, the routine touches every
2142        // page down to there, and the frame is taken afterwards, because the routine comes back
2143        // having moved nothing. What the epilogue gives back is what the register was given, which
2144        // is the one thing worth tying together here.
2145        let added = added(&lines);
2146        assert_eq!(added.len(), 5, "{added:?}");
2147        let size = added[0].strip_prefix("$rax = x64.mov_ri_64 ").expect("a size in a register");
2148        assert_eq!(added[1], "x64.call @__chkstk");
2149        assert_eq!(added[2], "$rsp = x64.sub_rr_64 $rsp, $rax");
2150        assert_eq!(added[3], format!("$rsp = x64.add_ri_64 $rsp, {size}"));
2151        assert_eq!(added[4], "x64.ret");
2152    }
2153
2154    #[test]
2155    fn a_frame_of_one_page_calls_nothing_on_that_platform_either() {
2156        let (mut func, allocation, mut names) = pressure(&WIN64, 2, 4);
2157        let locals = [Local { size: 4000, align: 16 }];
2158        let base = Layout::new(&WIN64, REGS);
2159        let layout = Layout { leaf: false, locals: &locals, ..base };
2160        let lines = written(&mut func, &allocation, &layout, &mut names);
2161
2162        // The same reason a frame of one page is taken in one subtraction under the flag. The far
2163        // end of such a frame is inside the page below the stack pointer, and touching that page is
2164        // what the function does on its way to using the frame at all, so there is nothing for a
2165        // routine to do and a call to it would be a call in every function that declares an array.
2166        let added = added(&lines);
2167        assert!(added.iter().all(|line| !line.contains("chkstk")), "{added:?}");
2168        assert_eq!(added.len(), 3, "{added:?}");
2169    }
2170
2171    #[test]
2172    fn a_windows_prologue_points_its_frame_pointer_at_the_frame_once_the_frame_is_whole() {
2173        let (mut func, allocation, mut names) = pressure(&WIN64, 4, 2);
2174        let base = Layout::new(&WIN64, REGS);
2175        let layout = Layout { frame_pointer: true, ..base };
2176        let lines = written(&mut func, &allocation, &layout, &mut names);
2177
2178        // The other order, which is what every other platform here writes, has no unwind record on
2179        // this one: the record counts its slots from where the stack pointer ends the prologue and
2180        // gets there by taking a constant off the frame pointer, so a register pushed after the
2181        // pointer was established sits below the place the record counts from. Pushing first and
2182        // pointing last is the order that has a record, and it leaves the pointer holding a copy of
2183        // the stack pointer, so the spills stay where they were and the epilogue counts the frame
2184        // back off the pointer rather than moving the pointer into the stack pointer.
2185        assert_eq!(
2186            added(&lines),
2187            [
2188                "x64.push_64 $rbp",
2189                "$rsp = x64.sub_ri_64 $rsp, 16",
2190                "$rbp = x64.mov_rr_64 $rsp",
2191                "x64.mov_mr_64 $rdx, [$rsp]",
2192                "x64.mov_mr_64 $rdx, [$rsp + 8]",
2193                "$rdx = x64.mov_rm_64 [$rsp]",
2194                "$rdx = x64.mov_rm_64 [$rsp + 8]",
2195                "$rsp = x64.lea_64 [$rbp + 16]",
2196                "$rbp = x64.pop_64",
2197                "x64.ret",
2198            ]
2199        );
2200    }
2201
2202    #[test]
2203    fn a_windows_prologue_that_saves_registers_too_pushes_all_of_them_before_the_frame() {
2204        let (mut func, allocation, mut names) = pressure(&WIN64, 9, 8);
2205        let base = Layout::new(&WIN64, REGS);
2206        let layout = Layout { leaf: false, frame_pointer: true, ..base };
2207        let lines = written(&mut func, &allocation, &layout, &mut names);
2208
2209        // The shape that made the order necessary. All three pushes are above the frame, so every
2210        // one of them has a row the record can write, and the pointer is the last thing the
2211        // prologue does. Forty eight bytes is the thirty two every Windows caller reserves below a
2212        // call, eight for the one value that did not fit in a register, and eight that put the
2213        // stack pointer back where a call wants it given three pushes and the return address.
2214        assert_eq!(
2215            added(&lines),
2216            [
2217                "x64.push_64 $rbp",
2218                "x64.push_64 $rbx",
2219                "x64.push_64 $rsi",
2220                "$rsp = x64.sub_ri_64 $rsp, 48",
2221                "$rbp = x64.mov_rr_64 $rsp",
2222                "x64.mov_mr_64 $rsi, [$rsp + 32]",
2223                "$rsi = x64.mov_rm_64 [$rsp + 32]",
2224                "$rsp = x64.lea_64 [$rbp + 48]",
2225                "$rsi = x64.pop_64",
2226                "$rbx = x64.pop_64",
2227                "$rbp = x64.pop_64",
2228                "x64.ret",
2229            ]
2230        );
2231    }
2232
2233    #[test]
2234    fn a_realigned_windows_frame_forces_the_alignment_after_the_prologue() {
2235        let (mut func, allocation, mut names) = pressure(&WIN64, 9, 8);
2236        let locals = [Local { size: 64, align: 32 }];
2237        let base = Layout::new(&WIN64, REGS);
2238        let layout = Layout { leaf: false, locals: &locals, ..base };
2239        let lines = written(&mut func, &allocation, &layout, &mut names);
2240
2241        // The late order the record can describe, and the rounding after all of it, which is
2242        // clang's shape for the same frame. The epilogue puts the stack pointer back from the frame
2243        // pointer before it does anything else, and from there it is any other late epilogue.
2244        assert_eq!(
2245            added(&lines),
2246            [
2247                "x64.push_64 $rbp",
2248                "x64.push_64 $rbx",
2249                "x64.push_64 $rsi",
2250                "$rsp = x64.sub_ri_64 $rsp, 112",
2251                "$rbp = x64.mov_rr_64 $rsp",
2252                "$rsp = x64.and_ri_64 $rsp, -32",
2253                "x64.mov_mr_64 $rsi, [$rsp + 96]",
2254                "$rsi = x64.mov_rm_64 [$rsp + 96]",
2255                "$rsp = x64.mov_rr_64 $rbp",
2256                "$rsp = x64.lea_64 [$rbp + 112]",
2257                "$rsi = x64.pop_64",
2258                "$rbx = x64.pop_64",
2259                "$rbp = x64.pop_64",
2260                "x64.ret",
2261            ]
2262        );
2263    }
2264
2265    #[test]
2266    fn a_vector_register_a_windows_call_preserves_is_stored_and_read_back() {
2267        let mut names = Interner::new();
2268        let mut func = Func::new(names.intern("f"));
2269        let opcode = Opcode::new(names.intern("x64.nop"));
2270        let block = func.create_block();
2271        // An instruction that writes one of the vector registers Windows preserves, which is what
2272        // a rule for something that has to use it produces.
2273        func.build(block, opcode).operand(Operand::write(Reg::physical(xmm(6)), XMM)).finish();
2274        let allocation = rucc_regalloc::run(&mut func, &env(&WIN64, 4), "test", true);
2275        let lines = written(&mut func, &allocation, &Layout::new(&WIN64, REGS), &mut names);
2276
2277        // No machine here pushes a vector register, so it is stored into the frame rather than
2278        // pushed, and the frame has to be taken before there is anywhere to put it.
2279        assert_eq!(
2280            added(&lines),
2281            [
2282                "$rsp = x64.sub_ri_64 $rsp, 24",
2283                "x64.movaps_mr $xmm6, [$rsp]",
2284                "$xmm6 = x64.movaps_rm [$rsp]",
2285                "$rsp = x64.add_ri_64 $rsp, 24",
2286                "x64.ret",
2287            ]
2288        );
2289    }
2290
2291    /// A reload from deep in the frame takes the scratch register it loads into for the address,
2292    /// and not the other one, which the store after it reads. This is the pair the cleanup leaves
2293    /// when it keeps a value in `x16`, and taking `x16` stored the frame address in its place.
2294    #[test]
2295    fn a_far_access_leaves_alone_the_scratch_register_read_after_it() {
2296        use rucc_target::aarch64::{self, AAPCS64, X16, X17};
2297        let mut names = Interner::new();
2298        let mut func = Func::new(names.intern("f"));
2299        let block = func.create_block();
2300        let gpr = aarch64::GPR;
2301        let sp = Mem::at(Operand::read(Reg::physical(AAPCS64.stack_pointer), gpr)).plus(40_000);
2302        let load = Opcode::new(names.intern("a64.ldr_64"));
2303        let store = Opcode::new(names.intern("a64.str_64"));
2304        let reload = func.build(block, load).def(Reg::physical(X17), gpr).mem(sp).finish();
2305        let into = Mem::at(Operand::read(Reg::physical(X17), gpr));
2306        func.build(block, store).uses(Reg::physical(X16), gpr).mem(into).finish();
2307
2308        far(&mut func, &aarch64::FRAME, &AAPCS64, &[X16, X17], &mut names);
2309        let insts: Vec<Inst> = func.insts(block).collect();
2310        assert!(insts.len() > 2, "the offset is out of reach and wants an address");
2311        for &inst in &insts[..insts.iter().position(|&at| at == reload).expect("still there")] {
2312            let written = func[func[inst].operands].iter().filter(|op| op.role.is_def());
2313            assert!(written.map(|op| op.reg.phys()).all(|reg| reg == Some(X17)));
2314        }
2315    }
2316
2317    /// A store of one scratch register deep in the frame while the other is still wanted after it
2318    /// has neither to build the address in, so the other goes on the stack for the length of the
2319    /// store, and the offset grows by the sixteen bytes the push moved the stack pointer.
2320    #[test]
2321    fn a_far_store_with_no_free_scratch_register_saves_one_around_itself() {
2322        use rucc_target::aarch64::{self, AAPCS64, X16, X17};
2323        let mut names = Interner::new();
2324        let mut func = Func::new(names.intern("f"));
2325        let block = func.create_block();
2326        let gpr = aarch64::GPR;
2327        let sp = Mem::at(Operand::read(Reg::physical(AAPCS64.stack_pointer), gpr)).plus(40_000);
2328        let store = Opcode::new(names.intern("a64.str_64"));
2329        let spill = func.build(block, store).uses(Reg::physical(X16), gpr).mem(sp).finish();
2330        let into = Mem::at(Operand::read(Reg::physical(AAPCS64.stack_pointer), gpr));
2331        func.build(block, store).uses(Reg::physical(X17), gpr).mem(into).finish();
2332
2333        far(&mut func, &aarch64::FRAME, &AAPCS64, &[X16, X17], &mut names);
2334        let text = print_func(&func, &names, &aarch64::REGS);
2335        let lines: Vec<&str> =
2336            text.lines().map(str::trim).filter(|line| line.contains("a64.")).collect();
2337        assert!(lines[0].starts_with("a64.push_64 $x17"), "{text}");
2338        assert!(lines.last().unwrap().starts_with("a64.str_64 $x17"), "{text}");
2339        assert!(lines[lines.len() - 2].contains("a64.pop_64"), "{text}");
2340        let spilled = func.insts(block).position(|at| at == spill).expect("still there");
2341        let base = func[func[spill].mem.expect("an address")].base.expect("a base");
2342        assert_eq!(func[func[spill].operands][usize::from(base)].reg, Reg::physical(X17), "{text}");
2343        let whole: i32 = func
2344            .insts(block)
2345            .take(spilled)
2346            .filter_map(|at| func[at].mem.map(|mem| func[mem].disp))
2347            .sum::<i32>()
2348            + func[func[spill].mem.expect("an address")].disp;
2349        assert_eq!(whole, 40_016, "{text}");
2350    }
2351}