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//! The loads that read the arguments the caller passed on the stack are waiting on the same number
25//! and on one more. Those bytes are the caller's rather than this function's, and a frame that had
26//! to force its own alignment cannot say how far away the caller's stack pointer was, so it reaches
27//! back through the frame pointer instead. Which register a load reads through is therefore settled
28//! here too, and it is the only base register in a finished function that was not settled by
29//! whoever wrote the instruction.
30//!
31//! After this the function is one an encoder can read: every register is physical, every offset
32//! into the frame is a constant, and the stack pointer is where the convention says it should be
33//! at every instruction that could look.
34//!
35//! # Why the moves go in first
36//!
37//! Every offset the frame reports is from the stack pointer as it stands in the body of the
38//! function. A spill written before the prologue exists would be written in front of the
39//! instruction it belongs to and behind nothing, which is where the prologue then goes, so the
40//! prologue ends up in front of it and the offsets stay true. Writing them the other way round
41//! would put the first reload above the instruction that takes the frame, and it would read from
42//! an address that is one frame out.
43//!
44//! # Where a return is
45//!
46//! A block that goes nowhere is a block the function leaves from. Mostly that is a return, and
47//! the other kind is a block ending in `unreachable`, which is a point the front end says control
48//! does not arrive at and which the lowering writes no instruction for. Both want the same thing
49//! here. A return wants the epilogue because that is what a return is once the frame is known,
50//! and an unreachable block wants it because the alternative is a function whose last instruction
51//! falls into whatever the assembler put after it, which is worse than an epilogue nothing runs.
52//! So the epilogue goes at the end of every block with an empty successor list, and there may be
53//! several, because nothing here insists a function has one exit.
54//!
55//! # What is target-specific here
56//!
57//! The names, and only the names. Which instruction pushes a register and which one moves the
58//! stack pointer is [`rucc_target::FrameInsts`], which the target says and this reads, so what
59//! is written below is the shape of a prologue rather than any particular machine's. That is
60//! `spec/10-backend.md` section 10.8 as it applies to the one pass that would otherwise be full
61//! of `x64.` by hand.
62
63use rucc_base::Interner;
64use rucc_mir::{Block, BlockCall, CfiOp, Func, Inst, Mem, Opcode, Operand, Reg};
65use rucc_regalloc::Allocation;
66use rucc_regalloc::assign::Place;
67use rucc_regalloc::rewrite::{At, Edit};
68use rucc_target::{BranchInsts, CallRegs, FrameInsts, Guard, PhysReg, Probe, RegClass};
69
70use crate::frame::Frame;
71use crate::lower::Stack;
72
73/// What the stack protector's check needs beyond the frame, in a function that has one.
74///
75/// Three things that come from three places, which is why they arrive together rather than being
76/// looked up here. Where the word the canary is copied from lives is a fact about the runtime the
77/// code is linked against. What a branch on a register is is a fact about the machine. And the two
78/// registers are neither: they are the ones the allocator was told to hold back, which is a
79/// decision about the allocator, and they are free at a return for exactly that reason.
80#[derive(Debug, Clone, Copy)]
81pub struct Protect<'a> {
82    /// Where the word the canary is a copy of lives, and what to call when the copy has changed.
83    pub guard: &'a Guard,
84    /// What a branch on a register is, which is what the check ends its block with.
85    pub branch: &'a BranchInsts,
86    /// The two registers the check may use, which are two the allocator never handed out.
87    pub scratch: [PhysReg; 2],
88}
89
90/// What a prologue that takes its frame a page at a time needs beyond the frame.
91///
92/// What `-fstack-clash-protection` asks for, and the same three kinds of thing [`Protect`] is:
93/// one fact about the platform, one about the machine, and two registers that are neither. See
94/// [`rucc_target::Probe`] for what the sequence is defending against.
95#[derive(Debug, Clone, Copy)]
96pub struct Probing<'a> {
97    /// What touches a page and how far apart the pages are.
98    pub probe: &'a Probe,
99    /// What a branch on a register is, which is what the loop under a large frame ends with.
100    pub branch: &'a BranchInsts,
101    /// The two registers the sequence may use, which are two the allocator never handed out.
102    pub scratch: [PhysReg; 2],
103}
104
105/// What a profiler's hook at the top of a function is, in a function that has one.
106///
107/// What `-pg` asks for. See [`rucc_target::Trace`] for why there are two of these and what each of
108/// them lets the hook see. Only the name survives to here, because by this point the flag has been
109/// read against the target and a prologue that has the name has everything it needs.
110#[derive(Debug, Clone, Copy)]
111pub struct Tracing {
112    /// What is called, which is a routine the runtime provides and not one the program wrote.
113    pub name: &'static str,
114    /// Whether the call goes in front of the prologue rather than once the frame is taken.
115    pub early: bool,
116}
117
118/// What the convention this function is compiled for says a frame is.
119///
120/// Six answers to the one question, which is why they travel together: where it puts things, which
121/// instructions build one, whether this function's carries a protector, whether it is taken a page
122/// at a time, whether the function opens with a landing pad, and whether it calls a profiler on the
123/// way in. The last four are the only ones about this function rather than about every function on
124/// the target, and they are here because what they need is the other two and nothing else.
125#[derive(Debug, Clone, Copy)]
126pub struct Convention<'a> {
127    /// Where the convention puts things.
128    pub regs: &'a CallRegs,
129    /// The instructions a prologue, an epilogue, a spill and a reload are made of on it.
130    pub insts: &'a FrameInsts,
131    /// What this function's stack protector needs, or `None` in a function with none.
132    pub protect: Option<Protect<'a>>,
133    /// What this function's probing prologue needs, or `None` when the frame is taken in one
134    /// subtraction, which is what a command line that did not ask asks for.
135    pub probe: Option<Probing<'a>>,
136    /// What says an indirect branch may arrive at the top of this function, or `None` when the
137    /// command line did not ask for one and on a target that has no such instruction.
138    ///
139    /// See [`rucc_target::FrameInsts::landing`]. A name rather than a flag because the flag has
140    /// already been read against the target by the time this is built, and because a prologue that
141    /// has the name has everything it needs.
142    pub landing: Option<&'static str>,
143    /// What this function's call to a profiler is, or `None` in one that makes none, which is every
144    /// function on a command line that did not ask.
145    pub trace: Option<Tracing>,
146}
147
148impl<'a> Convention<'a> {
149    /// That convention, for a function with no stack protector, no probing, no landing pad and no
150    /// call to a profiler, which is most of them.
151    #[must_use]
152    pub fn new(regs: &'a CallRegs, insts: &'a FrameInsts) -> Self {
153        Self { regs, insts, protect: None, probe: None, landing: None, trace: None }
154    }
155}
156
157/// Writes the moves, the prologue and the epilogue into a function the allocator has finished
158/// with.
159///
160/// # Panics
161///
162/// Panics on a function with no blocks in it, on a frame whose slots or locals the allocation and
163/// the lowering do not match, and on a move of a class the target did not say how to move. All of
164/// them are the caller handing it a frame and a function that were not worked out from each other.
165pub fn finish(
166    func: &mut Func,
167    allocation: &Allocation,
168    frame: &Frame,
169    stack: &Stack,
170    convention: Convention<'_>,
171    names: &mut Interner,
172) {
173    let Convention { regs: conv, insts, protect, probe, landing, trace } = convention;
174    let entry = func.entry().expect("a function with a block in it");
175    let returns: Vec<Block> = func.blocks().filter(|&block| func[block].succs.is_empty()).collect();
176
177    // Before anything is written, because these are instructions the lowering already put in the
178    // function and every one of them is somewhere the prologue is about to go in front of, which
179    // is what makes an offset from the stack pointer the right thing to write into them.
180    for &(inst, local) in &stack.addresses {
181        let at = frame.local(local).expect("a local the frame was worked out from");
182        let mem = func[inst].mem.expect("the address of a local is an address");
183        func[mem].disp = at;
184    }
185
186    // The same, one area further up, and through the frame pointer when that is what reaches it.
187    // These are in the entry block ahead of everything, so the prologue still goes in front of
188    // them, which is what makes both registers hold what these offsets are counted from.
189    let incoming = frame.incoming();
190    for &(inst, up) in &stack.arguments {
191        let mem = func[inst].mem.expect("an argument read out of memory is read from an address");
192        func[mem].disp = incoming.at + offset(up);
193        if incoming.through_frame_pointer {
194            // The base register is an operand of the instruction and the addressing mode holds
195            // where in the operand vector it is, so the register is changed there and not here.
196            let at = func[mem].base.expect("an address the lowering wrote a base register into");
197            let operands = func[inst].operands;
198            func[operands][usize::from(at)].reg = Reg::physical(conv.frame_pointer);
199        }
200    }
201
202    let mut writer = Writer { func, conv, insts, names, ahead: None };
203
204    let mut cursors: Vec<(At, Inst)> = Vec::new();
205    for edit in &allocation.edits {
206        let inst = writer.mov(edit, frame);
207        writer.put(&mut cursors, edit.at, inst);
208    }
209
210    let prologue = writer.prologue(frame, protect, probe, landing, trace);
211    for &inst in prologue.iter().rev() {
212        writer.func.prepend_inst(entry, inst);
213    }
214    for block in returns {
215        // The check goes in front of the epilogue and takes the return with it. What is left in
216        // the block the function used to return from is the check, and the block the epilogue then
217        // goes in is the arm the canary was unchanged on.
218        let block = match protect {
219            Some(protect) => writer.check(block, frame, protect),
220            None => block,
221        };
222        let epilogue = writer.epilogue(frame);
223        for inst in epilogue {
224            writer.func.append_inst(block, inst);
225        }
226    }
227
228    // Last of everything, because the blocks a probing prologue made have to come in front of the
229    // block the function used to begin with and the ones the protector's check makes are made
230    // after that. Nothing has been laid out yet: `crate::layout` runs after this and puts every
231    // block in its own order, and all this decides is which block the function is entered at.
232    if let Some(ahead) = writer.ahead {
233        let rest: Vec<Block> =
234            writer.func.blocks().filter(|block| !ahead.contains(block)).collect();
235        let order: Vec<Block> = ahead.into_iter().chain(rest).collect();
236        writer.func.set_block_order(&order);
237    }
238}
239
240/// How many pages a probing prologue touches one after another before it writes a loop instead.
241///
242/// Three, which is what gcc unrolls to. The loop is four instructions however many pages it walks
243/// and a page written out is two, so three is the last size at which the straight line is no
244/// longer than the loop, and the straight line has no branch in it and needs no register.
245const UNROLLED: u32 = 3;
246
247/// One function having its frame written into it.
248struct Writer<'a> {
249    func: &'a mut Func,
250    conv: &'a CallRegs,
251    insts: &'a FrameInsts,
252    names: &'a mut Interner,
253    /// The blocks a probing prologue made, which go in front of the one the function began with.
254    ///
255    /// Empty in every function whose frame is taken in one subtraction, which is every function
256    /// on a command line that did not ask for the stack to be touched a page at a time and most
257    /// of them on one that did. See [`Writer::pages`].
258    ahead: Option<[Block; 2]>,
259}
260
261impl Writer<'_> {
262    /// The instructions the prologue is, in the order they run.
263    ///
264    /// The order is the one the epilogue undoes and it is not free. The frame pointer is saved
265    /// before anything else, so that it points at a fixed place whatever else happens. The
266    /// registers are pushed before the alignment is forced, so that the epilogue can find them
267    /// again from the frame pointer, since after the alignment is forced nothing else can. And the
268    /// vector registers are stored last, because until the frame has been taken there is nowhere
269    /// to store them.
270    ///
271    /// The landing pad is in front of all of it, because the address it makes reachable is the
272    /// address of the function and the address of the function is where the first instruction is.
273    /// It has to be written here rather than after the fact, since a probing prologue moves the
274    /// instructions written so far into a block of its own and the pad has to move with them.
275    ///
276    /// A profiler's hook goes next, or at the end when it is the kind that reads the frame pointer.
277    /// The early one is in front of everything the frame does for a reason of its own: what makes
278    /// it worth replacing while the program runs is that the stack at that instruction is exactly
279    /// what a call leaves, and a prologue that had already run would have changed it.
280    fn prologue(
281        &mut self,
282        frame: &Frame,
283        protect: Option<Protect<'_>>,
284        probe: Option<Probing<'_>>,
285        landing: Option<&'static str>,
286        trace: Option<Tracing>,
287    ) -> Vec<Inst> {
288        let sp = self.conv.stack_pointer;
289        let fp = self.conv.frame_pointer;
290        let int = self.conv.int_class;
291        let sse = self.conv.sse_class;
292        let word = offset(self.conv.word);
293        let mut out = Vec::new();
294        // What the prologue wrote before it had described anything, which is what decides whether
295        // there is a rule to remember at the end of it. Neither of these moves a register or takes
296        // a frame, so a function whose whole prologue is one of them has no rows and must not be
297        // given a pair of them that cancel out.
298        let mut quiet = Vec::new();
299        if let Some(name) = landing {
300            let opcode = self.opcode(name);
301            let inst = self.func.build_loose(opcode).finish();
302            out.push(inst);
303            quiet.push(inst);
304        }
305        // Nothing is described for it and nothing needs to be: the call pushes a return address and
306        // the hook pops it, so the frame is the same on both sides, and the hook preserves every
307        // register because it is written in assembly for exactly this. That is also why the
308        // allocator, which ran before any of this, never saw the call and did not have to.
309        if let Some(trace) = trace.filter(|trace| trace.early) {
310            let inst = self.hook(trace);
311            out.push(inst);
312            quiet.push(inst);
313        }
314        // How far the stack pointer is below the canonical frame address, and whether the address
315        // is still counted from the stack pointer at all. It starts at the return address the
316        // call itself pushed, which is the rule the CIE already states, so the first row here is
317        // the first thing this function does on top of that.
318        let mut below = offset(self.conv.return_address);
319        let mut from_sp = true;
320        if frame.frame_pointer() {
321            let inst = self.push(fp);
322            out.push(inst);
323            below += word;
324            self.row(inst, CfiOp::DefCfaOffset(below));
325            self.saved(inst, int, fp, -below);
326            let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
327            let inst = self.two(mov, fp, sp);
328            out.push(inst);
329            let number = self.dwarf(int, fp);
330            self.row(inst, CfiOp::DefCfaRegister(number));
331            from_sp = false;
332        }
333        for &reg in frame.saved_int() {
334            let inst = self.push(reg);
335            out.push(inst);
336            below += word;
337            if from_sp {
338                self.row(inst, CfiOp::DefCfaOffset(below));
339            }
340            self.saved(inst, int, reg, -below);
341        }
342        if let Some(to) = frame.realign() {
343            // Nothing is written for this and nothing can be. After it the stack pointer is a
344            // rounded-down version of where it was rather than a fixed distance from it, which is
345            // exactly what a rule cannot say. It is also why a frame that realigns is a frame
346            // with a frame pointer: by here the address is already counted from that instead.
347            assert!(!from_sp, "a frame that forces its own alignment has a frame pointer");
348            let and = self.opcode(self.insts.align);
349            out.push(self.arith(and, -i64::from(to)));
350        }
351        if frame.size() > 0 {
352            self.take(&mut out, frame.size(), &mut below, from_sp, probe);
353        }
354        for save in frame.saved_sse() {
355            let inst = self.store(sse, save.reg, save.at);
356            out.push(inst);
357            // Where it went is an offset from the stack pointer in the body, and the address is
358            // `below` above that, so the two make one constant. Unless the frame realigned, in
359            // which case there is no such constant and the rule is left out rather than guessed;
360            // the one convention that realigns and the one that preserves a vector register are
361            // not the same convention, so nothing reaches this today.
362            if frame.realign().is_none() {
363                self.saved(inst, sse, save.reg, save.at - below);
364            }
365        }
366        // Before the canary and after the frame, which is where gcc puts it. The hook reads the
367        // frame pointer to find out who called this function, so it has to run once there is one,
368        // and it is a call, so it has to run before anything the function is keeping in the frame
369        // could be read back.
370        if let Some(trace) = trace.filter(|trace| !trace.early) {
371            let inst = self.hook(trace);
372            out.push(inst);
373        }
374        // Last of everything, because it writes into the frame and there is no frame to write into
375        // until the stack pointer has moved. Nothing is described for either instruction: they
376        // write a slot rather than save a register, and no unwinder wants to put a canary back.
377        if let Some(protect) = protect {
378            let at = frame.canary().expect("a protected function has a slot for its canary");
379            let [into, _] = protect.scratch;
380            out.push(self.read_guard(into, protect.guard));
381            out.push(self.store(self.conv.int_class, into, at));
382        }
383        // The rules the body runs under, kept so that each epilogue can put them back rather than
384        // leaving the next block reading whatever the last one ended on. See `epilogue`.
385        //
386        // Nothing is kept in a function whose whole prologue is the pieces that describe nothing.
387        // See `quiet` above.
388        if let Some(&last) = out.last() {
389            if !quiet.contains(&last) {
390                self.row(last, CfiOp::RememberState);
391            }
392        }
393        out
394    }
395
396    /// The call to a profiler's hook.
397    ///
398    /// No arguments and no result. Which function is being entered is not passed, because the hook
399    /// reads its own return address to find out, and that is the whole reason the call is written
400    /// rather than something cheaper.
401    fn hook(&mut self, trace: Tracing) -> Inst {
402        let call = self.opcode(self.insts.call);
403        let symbol = self.names.intern(trace.name);
404        self.func.build_loose(call).symbol(symbol).finish()
405    }
406
407    /// Takes the frame, which is one subtraction unless the command line asked for the stack to be
408    /// touched a page at a time.
409    ///
410    /// `below` is how far the canonical frame address is above the stack pointer, and it comes
411    /// back as what it is once the frame has been taken.
412    fn take(
413        &mut self,
414        out: &mut Vec<Inst>,
415        size: u32,
416        below: &mut i32,
417        from_sp: bool,
418        probe: Option<Probing<'_>>,
419    ) {
420        let Some(probing) = probe.filter(|probing| size > probing.probe.interval) else {
421            let inst = self.sub(size);
422            out.push(inst);
423            *below += offset(size);
424            if from_sp {
425                self.row(inst, CfiOp::DefCfaOffset(*below));
426            }
427            return;
428        };
429        // Every step but the last is a whole page and is followed by a touch, and the last is
430        // whatever is left over, which is between one byte and one whole page. So the stack
431        // pointer never moves further than a page without something being written where it landed,
432        // and the unmapped page an operating system leaves below a stack cannot be stepped over.
433        //
434        // That is why the count is worked out from one less than the size. A frame that is an
435        // exact number of pages gets one fewer touch than it has pages, and the step left over is
436        // a whole page, which is a step that lands on the next page boundary rather than past it.
437        // gcc touches that last page as well, so this is one instruction shorter on a frame whose
438        // size is a multiple of the page and the same everywhere else.
439        let interval = probing.probe.interval;
440        let pages = (size - 1) / interval;
441        let rest = size - pages * interval;
442        let mut walked = false;
443        if pages <= UNROLLED {
444            for _ in 0..pages {
445                let inst = self.sub(interval);
446                out.push(inst);
447                *below += offset(interval);
448                if from_sp {
449                    self.row(inst, CfiOp::DefCfaOffset(*below));
450                }
451                let touch = self.touch(probing.probe);
452                out.push(touch);
453            }
454        } else {
455            self.pages(out, pages, below, from_sp, probing);
456            walked = from_sp;
457        }
458        let inst = self.sub(rest);
459        out.push(inst);
460        *below += offset(rest);
461        if from_sp {
462            // A loop leaves the address counted from the register the stack pointer was compared
463            // against, since that is the one thing in it that holds still. This is where it goes
464            // back to being counted from the stack pointer, and it is written behind this
465            // instruction rather than behind the branch because a row is written behind an
466            // instruction and the branch is not one that survives [`crate::layout`].
467            let op = if walked {
468                let number = self.dwarf(self.conv.int_class, self.conv.stack_pointer);
469                CfiOp::DefCfa { reg: number, offset: *below }
470            } else {
471                CfiOp::DefCfaOffset(*below)
472            };
473            self.row(inst, op);
474        }
475    }
476
477    /// The loop that takes a frame too large for the touches to be written one after another.
478    ///
479    /// Three blocks, and the first two are new and go in front of the one the function began with:
480    ///
481    /// ```text
482    ///   what the function is entered at   everything the prologue did before this, and then the
483    ///                                     address the stack pointer is walking down to
484    ///   the loop                          one page, the touch, and the question of whether the
485    ///                                     stack pointer has got there yet
486    ///   what the function began with      the rest of the prologue, and then the body
487    /// ```
488    ///
489    /// The instructions the prologue has written so far move into the first of them, because a
490    /// block is entered at the top and they have to run before the loop does. Nothing is laid out
491    /// here: which block comes first in memory is [`crate::layout`]'s answer, and all this decides
492    /// is which one the function is entered at.
493    fn pages(
494        &mut self,
495        out: &mut Vec<Inst>,
496        pages: u32,
497        below: &mut i32,
498        from_sp: bool,
499        probing: Probing<'_>,
500    ) {
501        let class = self.conv.int_class;
502        let sp = self.conv.stack_pointer;
503        let all = offset(pages * probing.probe.interval);
504        let [limit, byte] = probing.scratch;
505
506        let head = self.func.create_block();
507        for &inst in out.iter() {
508            self.func.append_inst(head, inst);
509        }
510        out.clear();
511        // Where the stack pointer is walking down to, worked out before it starts moving. A loop
512        // that counted down instead would need somewhere to keep the count, and this is somewhere
513        // to keep it that the comparison can read without arithmetic.
514        let lea = self.opcode(self.insts.lea);
515        let inst = self.address(lea, limit, sp, -all);
516        self.func.append_inst(head, inst);
517        if from_sp {
518            // The address is counted from that register for as long as the loop runs, and it has
519            // to be: the stack pointer moves once an iteration, so no fixed distance from it is
520            // true twice, and this register was written so that one distance is.
521            let number = self.dwarf(class, limit);
522            self.row(inst, CfiOp::DefCfa { reg: number, offset: *below + all });
523        }
524
525        let body = self.func.create_block();
526        *self.func.succs_mut(head) = vec![BlockCall::to(body)];
527        let inst = self.sub(probing.probe.interval);
528        self.func.append_inst(body, inst);
529        let touch = self.touch(probing.probe);
530        self.func.append_inst(body, touch);
531        let differ = self.opcode(self.insts.differ);
532        let inst = self
533            .func
534            .build_loose(differ)
535            .def(Reg::physical(byte), class)
536            .uses(Reg::physical(sp), class)
537            .uses(Reg::physical(limit), class)
538            .finish();
539        self.func.append_inst(body, inst);
540        let cond = Opcode::new(
541            self.names.intern(&format!("{}{}", probing.branch.prefix, probing.branch.cond)),
542        );
543        let inst = self.func.build_loose(cond).uses(Reg::physical(byte), class).finish();
544        self.func.append_inst(body, inst);
545        // The first arm is the one taken when the condition held, and the condition is that the
546        // stack pointer and the address it is walking down to still differ, so the first arm is
547        // another page.
548        let began = self.func.entry().expect("a function with a block in it");
549        *self.func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(began)];
550        *below += all;
551        self.ahead = Some([head, body]);
552    }
553
554    /// Writes the page the stack pointer is on without changing what is there.
555    fn touch(&mut self, probe: &Probe) -> Inst {
556        let opcode = self.opcode(probe.inst);
557        let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
558        self.func.build_loose(opcode).imm(0).mem(Mem::at(base)).finish()
559    }
560
561    /// Takes that many bytes off the stack pointer.
562    fn sub(&mut self, bytes: u32) -> Inst {
563        let sub = self.opcode(self.insts.sub);
564        self.arith(sub, i64::from(bytes))
565    }
566
567    /// The stack protector's check, written at the end of a block the function returns from.
568    ///
569    /// Gives back the block the epilogue goes in, which is a new one: the check has to be the last
570    /// thing the old block does, and what follows it is one of two arms rather than the return.
571    ///
572    /// ```text
573    ///   block that returned      reload the slot, read the word again, compare, branch
574    ///   the arm it changed on    call the function that does not come back, and nothing after
575    ///   the arm it did not       the epilogue, which the caller writes into what this gives back
576    /// ```
577    ///
578    /// The two registers are the ones the allocator was told to hold back, so nothing here has to
579    /// ask what is live: a scratch register holds nothing at the end of a block, because the only
580    /// thing that writes one is a move the rewriter put in and every one of those is read by the
581    /// instruction it was put in front of.
582    fn check(&mut self, block: Block, frame: &Frame, protect: Protect<'_>) -> Block {
583        let class = self.conv.int_class;
584        let at = frame.canary().expect("a protected function has a slot for its canary");
585        let [ours, theirs] = protect.scratch;
586
587        let inst = self.load(class, ours, at);
588        self.func.append_inst(block, inst);
589        let inst = self.read_guard(theirs, protect.guard);
590        self.func.append_inst(block, inst);
591        let differ = self.opcode(self.insts.differ);
592        let inst = self
593            .func
594            .build_loose(differ)
595            .def(Reg::physical(theirs), class)
596            .uses(Reg::physical(ours), class)
597            .uses(Reg::physical(theirs), class)
598            .finish();
599        self.func.append_inst(block, inst);
600
601        let failed = self.func.create_block();
602        let ok = self.func.create_block();
603        let cond = Opcode::new(
604            self.names.intern(&format!("{}{}", protect.branch.prefix, protect.branch.cond)),
605        );
606        let inst = self.func.build_loose(cond).uses(Reg::physical(theirs), class).finish();
607        self.func.append_inst(block, inst);
608        // The first arm is the one taken when the condition held, and the condition is that the
609        // two words differ, so the first arm is the one the canary was overwritten on.
610        *self.func.succs_mut(block) = vec![BlockCall::to(failed), BlockCall::to(ok)];
611
612        let call = self.opcode(self.insts.call);
613        let symbol = self.names.intern(protect.guard.fail);
614        self.func.build(failed, call).symbol(symbol).finish();
615        ok
616    }
617
618    /// Reads the word the canary is a copy of into a register.
619    ///
620    /// The address is a constant and names no register at all, because where the block a thread
621    /// has to itself begins is something only the machine knows and the segment register is what
622    /// holds it.
623    fn read_guard(&mut self, into: PhysReg, guard: &Guard) -> Inst {
624        let class = self.conv.int_class;
625        let load = self.opcode(self.insts.moves(class).expect("a class to load").load);
626        self.func
627            .build_loose(load)
628            .def(Reg::physical(into), class)
629            .mem(Mem::in_segment(guard.segment, guard.at))
630            .finish()
631    }
632
633    /// The instructions the epilogue is, in the order they run.
634    ///
635    /// The vector registers are read back while the stack pointer is still where the body left it,
636    /// because that is what their offsets are from. Then the stack pointer goes back to the last
637    /// register the prologue pushed, which is arithmetic when the prologue knew how far it had
638    /// moved and a read of the frame pointer when it did not.
639    fn epilogue(&mut self, frame: &Frame) -> Vec<Inst> {
640        let sp = self.conv.stack_pointer;
641        let fp = self.conv.frame_pointer;
642        let int = self.conv.int_class;
643        let sse = self.conv.sse_class;
644        let word = self.conv.word;
645        let described = !self.func.cfi.is_empty();
646        let mut out = Vec::new();
647        // Where the body left things, which is where every epilogue starts from.
648        let mut below = offset(self.conv.return_address)
649            + offset(word) * self.pushes(frame)
650            + offset(frame.size());
651        let from_sp = !frame.frame_pointer();
652        for save in frame.saved_sse() {
653            let inst = self.load(sse, save.reg, save.at);
654            out.push(inst);
655            if frame.realign().is_none() {
656                self.restored(inst, sse, save.reg);
657            }
658        }
659        let pushed = u32::try_from(frame.saved_int().len()).expect("a frame");
660        if frame.frame_pointer() {
661            // No row for either of these. The address is counted from the frame pointer here and
662            // this is what moves the stack pointer rather than the frame pointer, so the rule that
663            // was true before it is still true after it.
664            if pushed == 0 {
665                let mov = self.opcode(self.insts.moves(int).expect("a move").mov);
666                out.push(self.two(mov, sp, fp));
667            } else {
668                let lea = self.opcode(self.insts.lea);
669                let back = -offset(word * pushed);
670                out.push(self.address(lea, sp, fp, back));
671            }
672        } else if frame.size() > 0 {
673            let add = self.opcode(self.insts.add);
674            let inst = self.arith(add, i64::from(frame.size()));
675            out.push(inst);
676            below -= offset(frame.size());
677            self.row(inst, CfiOp::DefCfaOffset(below));
678        }
679        for &reg in frame.saved_int().iter().rev() {
680            let inst = self.pop(reg);
681            out.push(inst);
682            self.restored(inst, int, reg);
683            below -= offset(word);
684            if from_sp {
685                self.row(inst, CfiOp::DefCfaOffset(below));
686            }
687        }
688        if frame.frame_pointer() {
689            let inst = self.pop(fp);
690            out.push(inst);
691            self.restored(inst, int, fp);
692            // The frame pointer holds the caller's value again, so the address goes back to being
693            // counted from the stack pointer, which by now is at the return address.
694            let number = self.dwarf(int, sp);
695            self.row(inst, CfiOp::DefCfa { reg: number, offset: offset(self.conv.return_address) });
696        }
697        let ret = self.opcode(self.insts.ret);
698        let inst = self.func.build_loose(ret).finish();
699        out.push(inst);
700        // These take effect at the address just past the return, which is where the next block
701        // begins, and the next block is body again. Popping the body's rules and pushing them
702        // straight back leaves the stack one deep however many blocks the function returns from,
703        // which is what makes one remembering in the prologue enough for all of them.
704        if described {
705            self.row(inst, CfiOp::RestoreState);
706            self.row(inst, CfiOp::RememberState);
707        }
708        out
709    }
710
711    /// How many general purpose registers the prologue put on the stack, the frame pointer
712    /// included.
713    fn pushes(&self, frame: &Frame) -> i32 {
714        let saved = i32::try_from(frame.saved_int().len()).expect("a frame");
715        saved + i32::from(frame.frame_pointer())
716    }
717
718    /// One row of the unwind table, taking effect after that instruction.
719    fn row(&mut self, inst: Inst, op: CfiOp) {
720        self.func.cfi.push((inst, op));
721    }
722
723    /// A row saying the caller's copy of that register is that far from the canonical frame
724    /// address, which is below it and so is negative.
725    fn saved(&mut self, inst: Inst, class: RegClass, reg: PhysReg, from_cfa: i32) {
726        let number = self.dwarf(class, reg);
727        self.row(inst, CfiOp::Offset { reg: number, offset: from_cfa });
728    }
729
730    /// A row saying that register holds what the caller left in it again.
731    fn restored(&mut self, inst: Inst, class: RegClass, reg: PhysReg) {
732        let number = self.dwarf(class, reg);
733        self.row(inst, CfiOp::Restore(number));
734    }
735
736    /// What an unwind table calls that register.
737    fn dwarf(&self, class: RegClass, reg: PhysReg) -> u16 {
738        self.conv.dwarf(class, reg).expect("a register a frame saves is one the table can name")
739    }
740
741    /// One edit as the instruction that makes it true.
742    fn mov(&mut self, edit: &Edit, frame: &Frame) -> Inst {
743        let moves = self.insts.moves(edit.class).expect("a class the target says how to move");
744        match (edit.mov.to, edit.mov.from) {
745            (Place::Reg(to), Place::Reg(from)) => {
746                let mov = self.opcode(moves.mov);
747                self.func
748                    .build_loose(mov)
749                    .def(Reg::physical(to), edit.class)
750                    .uses(Reg::physical(from), edit.class)
751                    .finish()
752            }
753            (Place::Reg(to), Place::Slot(slot)) => {
754                let at = self.slot(frame, slot);
755                self.load(edit.class, to, at)
756            }
757            (Place::Slot(slot), Place::Reg(from)) => {
758                let at = self.slot(frame, slot);
759                self.store(edit.class, from, at)
760            }
761            // The allocator expands this into two moves through a register of its own, because a
762            // machine that could do it in one is not a machine any of this is written for.
763            (Place::Slot(_), Place::Slot(_)) => {
764                unreachable!("a move from one stack slot straight into another")
765            }
766        }
767    }
768
769    /// Puts an instruction where an edit says it goes, after whatever earlier edits went there.
770    ///
771    /// The edits at one place are in the order they have to be made in, so each one goes behind
772    /// the last, and the first of them is what the place itself means.
773    fn put(&mut self, cursors: &mut Vec<(At, Inst)>, at: At, inst: Inst) {
774        if let Some(cursor) = cursors.iter_mut().find(|(place, _)| *place == at) {
775            self.func.insert_after(cursor.1, inst);
776            cursor.1 = inst;
777            return;
778        }
779        match at {
780            At::Before(before) => self.func.insert_before(before, inst),
781            At::After(after) => self.func.insert_after(after, inst),
782            At::StartOf(block) => self.func.prepend_inst(block, inst),
783            // Behind everything in the block. A block the allocator puts an edge's moves at the
784            // end of is one with a single edge out of it, and an edge like that is not an
785            // instruction here: [`crate::layout`] writes the jump it becomes after this has run.
786            // So the last instruction is an ordinary one, which may still be waiting on moves of
787            // its own that have to be made before the edge's are.
788            At::EndOf(block) => self.func.append_inst(block, inst),
789        }
790        cursors.push((at, inst));
791    }
792
793    /// Where a spill slot is, from the stack pointer in the body of the function.
794    fn slot(&self, frame: &Frame, slot: u32) -> i32 {
795        frame.slot(slot).expect("a slot the frame was worked out from")
796    }
797
798    /// Reads a register out of the frame.
799    fn load(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
800        let load = self.opcode(self.insts.moves(class).expect("a class to load").load);
801        let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
802        self.func
803            .build_loose(load)
804            .def(Reg::physical(reg), class)
805            .mem(Mem::at(base).plus(at))
806            .finish()
807    }
808
809    /// Writes a register into the frame.
810    fn store(&mut self, class: RegClass, reg: PhysReg, at: i32) -> Inst {
811        let store = self.opcode(self.insts.moves(class).expect("a class to store").store);
812        let base = Operand::read(Reg::physical(self.conv.stack_pointer), self.conv.int_class);
813        self.func
814            .build_loose(store)
815            .uses(Reg::physical(reg), class)
816            .mem(Mem::at(base).plus(at))
817            .finish()
818    }
819
820    /// Puts a general purpose register on the stack.
821    fn push(&mut self, reg: PhysReg) -> Inst {
822        let push = self.opcode(self.insts.push);
823        self.func.build_loose(push).uses(Reg::physical(reg), self.conv.int_class).finish()
824    }
825
826    /// Takes a general purpose register back off the stack.
827    fn pop(&mut self, reg: PhysReg) -> Inst {
828        let pop = self.opcode(self.insts.pop);
829        self.func.build_loose(pop).def(Reg::physical(reg), self.conv.int_class).finish()
830    }
831
832    /// One general purpose register written with another.
833    fn two(&mut self, opcode: Opcode, to: PhysReg, from: PhysReg) -> Inst {
834        let class = self.conv.int_class;
835        self.func
836            .build_loose(opcode)
837            .def(Reg::physical(to), class)
838            .uses(Reg::physical(from), class)
839            .finish()
840    }
841
842    /// Two-address arithmetic on the stack pointer, which reads it and writes it back.
843    fn arith(&mut self, opcode: Opcode, value: i64) -> Inst {
844        let class = self.conv.int_class;
845        let sp = Reg::physical(self.conv.stack_pointer);
846        self.func.build_loose(opcode).def(sp, class).uses(sp, class).imm(value).finish()
847    }
848
849    /// One register written with an address rather than with what is at it.
850    fn address(&mut self, opcode: Opcode, to: PhysReg, base: PhysReg, disp: i32) -> Inst {
851        let class = self.conv.int_class;
852        let base = Operand::read(Reg::physical(base), class);
853        self.func
854            .build_loose(opcode)
855            .def(Reg::physical(to), class)
856            .mem(Mem::at(base).plus(disp))
857            .finish()
858    }
859
860    /// The opcode of that name, in the machine IR's spelling, which is the target's prefix and
861    /// then the name the target gave.
862    fn opcode(&mut self, name: &str) -> Opcode {
863        Opcode::new(self.names.intern(&format!("{}{name}", self.insts.prefix)))
864    }
865}
866
867/// A distance in a frame, as the signed number every offset is.
868fn offset(bytes: u32) -> i32 {
869    i32::try_from(bytes).expect("a frame under two gigabytes")
870}
871
872#[cfg(test)]
873mod tests {
874    use rucc_base::Interner;
875    use rucc_mir::{BlockCall, print_func};
876    use rucc_regalloc::assign::Env;
877    use rucc_target::x86_64::{BRANCH, FRAME, GPR, PROBE, R10, R11, REGS, SYSV, WIN64, XMM, xmm};
878
879    use super::*;
880    use crate::frame::{Layout, Local};
881
882    /// An environment offering that many of the convention's registers, with everything after
883    /// them held back as scratch.
884    fn env(conv: &CallRegs, count: usize) -> Env {
885        Env::new().with(GPR, &conv.int_order[..count], &conv.int_order[count..])
886    }
887
888    /// A function of that many values, every one written before any is read, allocated with that
889    /// many registers to hand out. The same shape the frame layout's own tests are written
890    /// against, so that a frame here is one that has already been checked there.
891    fn pressure(conv: &CallRegs, values: usize, count: usize) -> (Func, Allocation, Interner) {
892        let mut names = Interner::new();
893        let mut func = Func::new(names.intern("f"));
894        let opcode = Opcode::new(names.intern("x64.nop"));
895        let block = func.create_block();
896        let regs: Vec<Reg> = (0..values).map(|_| func.new_vreg(GPR)).collect();
897        for &reg in &regs {
898            func.build(block, opcode).def(reg, GPR).finish();
899        }
900        for &reg in &regs {
901            func.build(block, opcode).uses(reg, GPR).finish();
902        }
903        let allocation = rucc_regalloc::run(&mut func, &env(conv, count), "test");
904        (func, allocation, names)
905    }
906
907    /// The function with its frame written into it, as the lines a dump would show.
908    fn written(
909        func: &mut Func,
910        allocation: &Allocation,
911        layout: &Layout<'_>,
912        names: &mut Interner,
913    ) -> Vec<String> {
914        with_protector(func, allocation, layout, None, names)
915    }
916
917    /// The same, for a function the caller has decided is protected or is not.
918    fn with_protector(
919        func: &mut Func,
920        allocation: &Allocation,
921        layout: &Layout<'_>,
922        protect: Option<Protect<'_>>,
923        names: &mut Interner,
924    ) -> Vec<String> {
925        let convention = Convention { protect, ..Convention::new(layout.conv, &FRAME) };
926        under(func, allocation, layout, convention, names)
927    }
928
929    /// The same, for a function whose frame the caller has decided is taken a page at a time.
930    fn with_probing(
931        func: &mut Func,
932        allocation: &Allocation,
933        layout: &Layout<'_>,
934        probe: Option<Probing<'_>>,
935        names: &mut Interner,
936    ) -> Vec<String> {
937        let convention = Convention { probe, ..Convention::new(layout.conv, &FRAME) };
938        under(func, allocation, layout, convention, names)
939    }
940
941    /// The function with its frame written into it under that convention.
942    fn under(
943        func: &mut Func,
944        allocation: &Allocation,
945        layout: &Layout<'_>,
946        convention: Convention<'_>,
947        names: &mut Interner,
948    ) -> Vec<String> {
949        let frame = Frame::of(func, allocation, layout);
950        finish(func, allocation, &frame, &Stack::default(), convention, names);
951        print_func(func, names, &REGS)
952            .lines()
953            .filter(|line| !line.is_empty())
954            .map(|line| line.trim().to_string())
955            .collect()
956    }
957
958    /// Just the lines the frame put in, which is every line that is not the function it was
959    /// given and not the shape of the dump around it.
960    fn added(lines: &[String]) -> Vec<&str> {
961        lines
962            .iter()
963            .map(String::as_str)
964            .filter(|line| !line.contains("x64.nop"))
965            .filter(|line| !line.starts_with("mfunc") && !line.starts_with("block") && *line != "}")
966            .collect()
967    }
968
969    #[test]
970    fn a_function_that_needs_no_frame_is_given_a_return_and_nothing_else() {
971        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
972        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
973
974        // Two values and four registers, so nothing is spilled, nothing is saved and the stack
975        // pointer never moves. A prologue of nothing is the right prologue for that.
976        assert_eq!(added(&lines), ["x64.ret"]);
977    }
978
979    #[test]
980    fn a_spill_is_a_store_and_a_reload_is_a_load() {
981        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
982        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
983
984        // Two registers for four values, so two of them go to the stack. The store goes behind the
985        // instruction that wrote the value and the load in front of the one that wants it, both at
986        // the offsets the frame gave, which are below the stack pointer because a small leaf
987        // function is entitled to the red zone.
988        assert_eq!(
989            lines,
990            [
991                "mfunc @f {",
992                "block0:",
993                "$rax = x64.nop",
994                "$rcx = x64.nop",
995                "$rdx = x64.nop",
996                "x64.mov_mr_64 $rdx, [$rsp - 16]",
997                "$rdx = x64.nop",
998                "x64.mov_mr_64 $rdx, [$rsp - 8]",
999                "x64.nop $rax",
1000                "x64.nop $rcx",
1001                "$rdx = x64.mov_rm_64 [$rsp - 16]",
1002                "x64.nop $rdx",
1003                "$rdx = x64.mov_rm_64 [$rsp - 8]",
1004                "x64.nop $rdx",
1005                "x64.ret",
1006                "}",
1007            ]
1008        );
1009    }
1010
1011    #[test]
1012    fn the_frame_the_prologue_takes_is_the_frame_the_epilogue_gives_back() {
1013        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1014        let base = Layout::new(&SYSV, REGS);
1015        let layout = Layout { red_zone: false, ..base };
1016        let lines = written(&mut func, &allocation, &layout, &mut names);
1017
1018        // The same function told it may not use the red zone takes sixteen bytes instead, and
1019        // every offset moves above the stack pointer to match.
1020        assert_eq!(
1021            added(&lines),
1022            [
1023                "$rsp = x64.sub_ri_64 $rsp, 16",
1024                "x64.mov_mr_64 $rdx, [$rsp]",
1025                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1026                "$rdx = x64.mov_rm_64 [$rsp]",
1027                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1028                "$rsp = x64.add_ri_64 $rsp, 16",
1029                "x64.ret",
1030            ]
1031        );
1032    }
1033
1034    #[test]
1035    fn the_registers_the_prologue_pushes_come_back_in_the_opposite_order() {
1036        let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
1037        let lines = written(&mut func, &allocation, &Layout::new(&SYSV, REGS), &mut names);
1038
1039        // Four registers a call leaves alone, pushed in the convention's order and popped in the
1040        // other one, which is the only order that gets each of them its own value back.
1041        assert_eq!(
1042            added(&lines),
1043            [
1044                "x64.push_64 $rbx",
1045                "x64.push_64 $r12",
1046                "x64.push_64 $r13",
1047                "x64.push_64 $r14",
1048                "$r14 = x64.pop_64",
1049                "$r13 = x64.pop_64",
1050                "$r12 = x64.pop_64",
1051                "$rbx = x64.pop_64",
1052                "x64.ret",
1053            ]
1054        );
1055    }
1056
1057    #[test]
1058    fn a_function_that_keeps_a_frame_pointer_sets_it_up_and_leaves_by_it() {
1059        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1060        let base = Layout::new(&SYSV, REGS);
1061        let layout = Layout { frame_pointer: true, red_zone: false, ..base };
1062        let lines = written(&mut func, &allocation, &layout, &mut names);
1063
1064        // The frame pointer is saved before anything else and points at where it was saved, so the
1065        // epilogue reaches the stack pointer through it rather than by counting the frame back.
1066        assert_eq!(
1067            added(&lines),
1068            [
1069                "x64.push_64 $rbp",
1070                "$rbp = x64.mov_rr_64 $rsp",
1071                "$rsp = x64.sub_ri_64 $rsp, 16",
1072                "x64.mov_mr_64 $rdx, [$rsp]",
1073                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1074                "$rdx = x64.mov_rm_64 [$rsp]",
1075                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1076                "$rsp = x64.mov_rr_64 $rbp",
1077                "$rbp = x64.pop_64",
1078                "x64.ret",
1079            ]
1080        );
1081    }
1082
1083    #[test]
1084    fn a_realigned_frame_forces_the_alignment_after_it_has_pushed_what_it_saves() {
1085        let (mut func, allocation, mut names) = pressure(&SYSV, 13, 13);
1086        let locals = [Local { size: 64, align: 32 }];
1087        let base = Layout::new(&SYSV, REGS);
1088        let layout = Layout { locals: &locals, ..base };
1089        let lines = written(&mut func, &allocation, &layout, &mut names);
1090
1091        // Forcing the alignment throws away how far the stack pointer had moved, so the registers
1092        // are pushed before it happens and the epilogue counts back from the frame pointer to find
1093        // them. The frame pointer is required here whatever the flags said.
1094        assert_eq!(
1095            added(&lines),
1096            [
1097                "x64.push_64 $rbp",
1098                "$rbp = x64.mov_rr_64 $rsp",
1099                "x64.push_64 $rbx",
1100                "x64.push_64 $r12",
1101                "x64.push_64 $r13",
1102                "x64.push_64 $r14",
1103                "$rsp = x64.and_ri_64 $rsp, -32",
1104                "$rsp = x64.sub_ri_64 $rsp, 64",
1105                "$rsp = x64.lea_64 [$rbp - 32]",
1106                "$r14 = x64.pop_64",
1107                "$r13 = x64.pop_64",
1108                "$r12 = x64.pop_64",
1109                "$rbx = x64.pop_64",
1110                "$rbp = x64.pop_64",
1111                "x64.ret",
1112            ]
1113        );
1114    }
1115
1116    #[test]
1117    fn every_block_the_function_returns_from_gets_an_epilogue() {
1118        let mut names = Interner::new();
1119        let mut func = Func::new(names.intern("f"));
1120        let opcode = Opcode::new(names.intern("x64.nop"));
1121        let head = func.create_block();
1122        let left = func.create_block();
1123        let right = func.create_block();
1124        func.build(head, opcode).finish();
1125        *func.succs_mut(head) = vec![BlockCall::to(left), BlockCall::to(right)];
1126        func.build(left, opcode).finish();
1127        func.build(right, opcode).finish();
1128        let allocation = rucc_regalloc::run(&mut func, &env(&SYSV, 4), "test");
1129        let base = Layout::new(&SYSV, REGS);
1130        let layout = Layout { leaf: false, ..base };
1131        let lines = written(&mut func, &allocation, &layout, &mut names);
1132
1133        // Both ways out get the frame given back, and the block that goes somewhere gets nothing,
1134        // because a block with an edge out of it is not a block anything returns from.
1135        assert_eq!(
1136            lines,
1137            [
1138                "mfunc @f {",
1139                "block0:",
1140                "$rsp = x64.sub_ri_64 $rsp, 8",
1141                "x64.nop block1, block2",
1142                "block1:",
1143                "x64.nop",
1144                "$rsp = x64.add_ri_64 $rsp, 8",
1145                "x64.ret",
1146                "block2:",
1147                "x64.nop",
1148                "$rsp = x64.add_ri_64 $rsp, 8",
1149                "x64.ret",
1150                "}",
1151            ]
1152        );
1153    }
1154
1155    #[test]
1156    fn a_protected_function_writes_the_canary_last_and_checks_it_before_it_returns() {
1157        let (mut func, allocation, mut names) = pressure(&SYSV, 4, 2);
1158        let base = Layout::new(&SYSV, REGS);
1159        let layout = Layout { leaf: false, protect: true, ..base };
1160        let guard = SYSV.guard.as_ref().expect("this convention has somewhere to keep the word");
1161        // The two the real pipeline holds back, which are held back in the environment above too:
1162        // it hands out the first two of the convention's order and keeps everything after them.
1163        let protect = Protect { guard, branch: &BRANCH, scratch: [R10, R11] };
1164        let lines = with_protector(&mut func, &allocation, &layout, Some(protect), &mut names);
1165
1166        // The read of the word and the store into the slot come after the stack pointer has moved,
1167        // because there is no slot to store into until it has. The check is the last thing the
1168        // block that returned does and the epilogue is on the arm the canary was unchanged on, so
1169        // a function whose canary changed never gives its frame back and never returns.
1170        assert_eq!(
1171            added(&lines),
1172            [
1173                "$rsp = x64.sub_ri_64 $rsp, 24",
1174                "$r10 = x64.mov_rm_64 [fs:40]",
1175                "x64.mov_mr_64 $r10, [$rsp + 16]",
1176                "x64.mov_mr_64 $rdx, [$rsp]",
1177                "x64.mov_mr_64 $rdx, [$rsp + 8]",
1178                "$rdx = x64.mov_rm_64 [$rsp]",
1179                "$rdx = x64.mov_rm_64 [$rsp + 8]",
1180                "$r10 = x64.mov_rm_64 [$rsp + 16]",
1181                "$r11 = x64.mov_rm_64 [fs:40]",
1182                "$r11 = x64.cmp_set_ne_64 $r10, $r11",
1183                "x64.br_cond_8 $r11, block1, block2",
1184                "x64.call @__stack_chk_fail",
1185                "$rsp = x64.add_ri_64 $rsp, 24",
1186                "x64.ret",
1187            ]
1188        );
1189    }
1190
1191    #[test]
1192    fn a_frame_that_fits_in_one_page_is_taken_in_one_subtraction_even_when_pages_are_touched() {
1193        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1194        let locals = [Local { size: 4088, align: 16 }];
1195        let base = Layout::new(&SYSV, REGS);
1196        let layout = Layout { leaf: false, locals: &locals, ..base };
1197        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
1198        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
1199
1200        // A frame of one page cannot step over the page below it, because the far end of it is the
1201        // near end of that page and anything written there is written to a page that is there. So
1202        // the flag costs such a function nothing, which is most functions.
1203        assert_eq!(
1204            added(&lines),
1205            ["$rsp = x64.sub_ri_64 $rsp, 4088", "$rsp = x64.add_ri_64 $rsp, 4088", "x64.ret",]
1206        );
1207    }
1208
1209    #[test]
1210    fn a_probing_prologue_touches_every_page_of_a_frame_a_few_pages_deep() {
1211        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1212        let locals = [Local { size: 9000, align: 16 }];
1213        let base = Layout::new(&SYSV, REGS);
1214        let layout = Layout { leaf: false, locals: &locals, ..base };
1215        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
1216        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
1217
1218        // A page of the stack pointer's own, then the touch that says the page is there, and only
1219        // then the next one, which is the whole of the defence: nothing here ever moves the stack
1220        // pointer further than one page without writing where it landed. The last subtraction is
1221        // the remainder and is smaller than a page, so it needs no touch of its own, and it exists
1222        // in every frame because the count of pages is taken off one less than the size.
1223        assert_eq!(
1224            added(&lines),
1225            [
1226                "$rsp = x64.sub_ri_64 $rsp, 4096",
1227                "x64.or_mi_8 [$rsp], 0",
1228                "$rsp = x64.sub_ri_64 $rsp, 4096",
1229                "x64.or_mi_8 [$rsp], 0",
1230                "$rsp = x64.sub_ri_64 $rsp, 808",
1231                "$rsp = x64.add_ri_64 $rsp, 9000",
1232                "x64.ret",
1233            ]
1234        );
1235    }
1236
1237    #[test]
1238    fn a_probing_prologue_deeper_than_that_walks_the_pages_in_a_loop() {
1239        let (mut func, allocation, mut names) = pressure(&SYSV, 2, 4);
1240        let locals = [Local { size: 100_000, align: 16 }];
1241        let base = Layout::new(&SYSV, REGS);
1242        let layout = Layout { leaf: false, locals: &locals, ..base };
1243        let probing = Probing { probe: &PROBE, branch: &BRANCH, scratch: [R10, R11] };
1244        let lines = with_probing(&mut func, &allocation, &layout, Some(probing), &mut names);
1245
1246        // Twenty-four pages, which is more than a straight line is worth, so the prologue works out
1247        // where it is going first and then walks there. The whole listing rather than the added
1248        // lines, because what matters as much as the instructions is that the two blocks the walk
1249        // is made of come in front of the block the function began with: the body the allocator
1250        // filled is block2 here and it was block0 before this ran.
1251        assert_eq!(
1252            lines,
1253            [
1254                "mfunc @f {",
1255                "block0:",
1256                "$r10 = x64.lea_64 [$rsp - 98304], block1",
1257                "block1:",
1258                "$rsp = x64.sub_ri_64 $rsp, 4096",
1259                "x64.or_mi_8 [$rsp], 0",
1260                "$r11 = x64.cmp_set_ne_64 $rsp, $r10",
1261                "x64.br_cond_8 $r11, block1, block2",
1262                "block2:",
1263                "$rsp = x64.sub_ri_64 $rsp, 1704",
1264                "$rax = x64.nop",
1265                "$rcx = x64.nop",
1266                "x64.nop $rax",
1267                "x64.nop $rcx",
1268                "$rsp = x64.add_ri_64 $rsp, 100008",
1269                "x64.ret",
1270                "}",
1271            ]
1272        );
1273    }
1274
1275    #[test]
1276    fn a_vector_register_a_windows_call_preserves_is_stored_and_read_back() {
1277        let mut names = Interner::new();
1278        let mut func = Func::new(names.intern("f"));
1279        let opcode = Opcode::new(names.intern("x64.nop"));
1280        let block = func.create_block();
1281        // An instruction that writes one of the vector registers Windows preserves, which is what
1282        // a rule for something that has to use it produces.
1283        func.build(block, opcode).operand(Operand::write(Reg::physical(xmm(6)), XMM)).finish();
1284        let allocation = rucc_regalloc::run(&mut func, &env(&WIN64, 4), "test");
1285        let lines = written(&mut func, &allocation, &Layout::new(&WIN64, REGS), &mut names);
1286
1287        // No machine here pushes a vector register, so it is stored into the frame rather than
1288        // pushed, and the frame has to be taken before there is anywhere to put it.
1289        assert_eq!(
1290            added(&lines),
1291            [
1292                "$rsp = x64.sub_ri_64 $rsp, 24",
1293                "x64.movaps_mr $xmm6, [$rsp]",
1294                "$xmm6 = x64.movaps_rm [$rsp]",
1295                "$rsp = x64.add_ri_64 $rsp, 24",
1296                "x64.ret",
1297            ]
1298        );
1299    }
1300}