rucc_target/frame.rs
1//! The instructions a frame is made of.
2//!
3//! Design: `spec/10-backend.md` sections 10.7 and 10.8.
4//!
5//! A prologue pushes registers and moves the stack pointer, an epilogue puts them back, and a
6//! spill is a store and a reload is a load. None of that is chosen by a lowering rule, because
7//! none of it comes from anything the program wrote: it comes from how many registers the
8//! allocator ran out of and which of them the convention says a call leaves alone. So the
9//! opcodes are named here, which is the list of what a frame may produce, rather than only in
10//! [`crate::x86_64::INSTS`], which is the list of what the selector may produce and what the
11//! allocator therefore has to understand. The encoder reads both.
12//!
13//! Some names are in both lists, which is not a duplication of anything. A load is a load
14//! whether a rule selected it or a reload wrote it, and the instruction description in `INSTS`
15//! is what the allocator reads about the one the selector produced. What the two lists are is
16//! two answers to two questions, and an instruction being an answer to both is ordinary. What
17//! would be a mistake is a frame opcode nobody has described anywhere, which is why an entry
18//! here that is not in `INSTS` is still an entry the encoder has to know.
19//!
20//! Everything named here is a name rather than a variant, for the same reason
21//! `rucc_mir::Opcode` is: the crate that writes the prologue is a pipeline crate and
22//! `spec/10-backend.md` section 10.8 says a pipeline crate holds no target-specific code. It
23//! reads the names out of the target it was handed and writes them into the machine IR, and what
24//! any of them means is the encoder's answer against this same description.
25//!
26//! # What each one has to be
27//!
28//! The shapes are fixed, because the code that writes them writes one shape each. A push reads
29//! one register and a pop writes one. A move writes a register and reads another of the same
30//! class. A load writes a register and reads memory, a store reads a register and writes memory,
31//! and both reach the frame through the stack pointer with a constant added. The arithmetic on
32//! the stack pointer is two-address, so it writes the stack pointer and reads it back, whether
33//! the amount is a constant or a register. A target whose instructions do not fit those shapes
34//! needs more than a table, and it will say so by not being able to fill this in.
35
36use crate::regs::RegClass;
37
38/// How a register of one class is moved between two registers and between a register and the
39/// frame.
40///
41/// Three names rather than one, because a machine that moves a general purpose register with
42/// `mov` moves a vector register with something else, and because a load and a store are
43/// different instructions on every machine here even when a dump writes them with the same
44/// mnemonic.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct ClassMoves {
47 /// Writes the first register with what is in the second.
48 pub mov: &'static str,
49 /// Writes the register with what is in the frame.
50 pub load: &'static str,
51 /// Writes the frame with what is in the register.
52 pub store: &'static str,
53}
54
55/// How a prologue touches the stack as it takes a frame, on a target that can.
56///
57/// What `-fstack-clash-protection` asks for. An operating system leaves one page below every
58/// stack unmapped, so that a stack which grows into it faults rather than running into whatever
59/// is under it, and a frame larger than that page can move the stack pointer clean over it
60/// without ever writing to it. A prologue that takes the frame a page at a time and writes
61/// something to each page as it arrives cannot: the first page it reaches that is not mapped is
62/// the one that faults.
63///
64/// Two facts rather than one because neither implies the other. What touches a page is an
65/// instruction of the machine, and how far apart the pages are is what the kernel that runs the
66/// program left, and they are together here because a prologue that has one and not the other
67/// cannot write anything.
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct Probe {
70 /// Writes an address without changing what is there, which is what makes it safe to do to a
71 /// page a local has not been put in yet.
72 pub inst: &'static str,
73 /// How far apart the touches are, which is the page the operating system leaves below the
74 /// stack. A prologue never moves the stack pointer further than this without touching where
75 /// it landed.
76 pub interval: u32,
77}
78
79/// The two instructions that put two registers on the stack in one go and take them back.
80///
81/// AArch64 has these as `stp` and `ldp` with a writeback, and it is how the frame pointer and the
82/// link register go on the stack together as the frame record. The first register named is the one
83/// that ends up at the lower address.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct Pair {
86 /// Stores two registers below the stack pointer and moves it down past both.
87 pub push: &'static str,
88 /// Loads two registers from the stack pointer and moves it back up past both.
89 pub pop: &'static str,
90}
91
92/// Every instruction a prologue, an epilogue, a spill or a reload is made of.
93#[derive(Debug, Clone, Copy)]
94pub struct FrameInsts {
95 /// What a rule file and the machine IR put in front of this target's opcodes, such as
96 /// `x64.`, which says which target a term belongs to and is not part of the opcode.
97 pub prefix: &'static str,
98 /// How a register of each class is moved, one for each class of the register file in the
99 /// order the file numbers them.
100 ///
101 /// Shorter than the file when the classes at the end are ones nothing spills. An x87 stack
102 /// register is one of those: the allocator is never given one to hand out, so nothing ever
103 /// asks how to move it, and a target that answered anyway would be writing down a guess.
104 pub classes: &'static [ClassMoves],
105 /// Puts a register on the stack and moves the stack pointer down by one word.
106 pub push: &'static str,
107 /// Takes a word off the stack into a register and moves the stack pointer back up.
108 pub pop: &'static str,
109 /// Pushes two registers at once, or `None` on a machine that pushes one at a time. See
110 /// [`Pair`].
111 pub pair: Option<Pair>,
112 /// Adds a constant to the stack pointer, which is how an epilogue gives the frame back.
113 pub add: &'static str,
114 /// Takes a constant off the stack pointer, which is how a prologue takes the frame.
115 pub sub: &'static str,
116 /// Takes whatever is in a register off the stack pointer, which is how a function makes room
117 /// for an array whose size it does not know until it runs.
118 ///
119 /// The same shape as [`Self::sub`] and different in where the amount comes from, which is the
120 /// whole of the difference between the bytes a prologue takes and the bytes a variable length
121 /// array takes. A prologue knows its number when it is written and a declaration in the body
122 /// does not know it until the expression in the brackets has been worked out.
123 pub grow: &'static str,
124 /// Clears the low bits of the stack pointer, which is how a prologue forces an alignment
125 /// nothing else can give it.
126 pub align: &'static str,
127 /// Writes a constant into a general purpose register.
128 ///
129 /// The one thing a prologue has to do that is not about the stack pointer, and it is here for
130 /// a platform that hands the size of the frame to a routine rather than reaching the pages
131 /// itself. See [`crate::Chkstk`]. A rule file selects this same opcode for a constant the
132 /// program wrote, for the reason the header of a target's table gives: a prologue writing a
133 /// number into a register is the same instruction as an assignment, and the encoder should
134 /// not have two answers for it.
135 pub imm: &'static str,
136 /// Writes a register with an address rather than with what is at it, which is how an
137 /// epilogue puts the stack pointer back when the frame pointer is the only record of where
138 /// it was.
139 pub lea: &'static str,
140 /// Adds two general purpose registers at the width of an address.
141 ///
142 /// Not something a prologue writes. It is here because the sum of two registers is the one
143 /// address the selector leaves as arithmetic rather than as a `lea`, and the pass that folds
144 /// addresses into their readers has to know which instruction that is to read it as a base and
145 /// an index at a scale of one.
146 pub sum: &'static str,
147 /// Returns to the caller.
148 pub ret: &'static str,
149 /// Compares two general purpose registers and writes whether they differ into a third.
150 ///
151 /// The stack protector's check is the only thing that asks for this, and it is here rather
152 /// than left to a lowering rule because no rule ever sees the comparison: the two words being
153 /// compared are the canary the prologue wrote and the one the runtime still holds, and neither
154 /// of them is a value the program named.
155 pub differ: &'static str,
156 /// Compares two general purpose registers as unsigned numbers and writes whether the first is
157 /// above the second into a third.
158 ///
159 /// Here for the same reason [`Self::differ`] is, and asked for by the one loop that walks a
160 /// distance nothing knew when it was written, which is the pages a variable length array takes.
161 /// A prologue knows how many pages its own frame is and can stop when the stack pointer reaches
162 /// an address worked out in advance, so equality is enough for it. A declaration in the body
163 /// does not: the bytes arrive in a register, the last step down is a whole page whatever is
164 /// left, and the stack pointer lands at or past where it was going rather than on it.
165 ///
166 /// Unsigned because both registers hold addresses. A stack that has grown past the middle of
167 /// the address space is one where a signed comparison of two stack pointers says the wrong
168 /// thing, and nothing about a guard page cares which half of the space it is in.
169 pub above: &'static str,
170 /// Calls the name it is given and reads no register.
171 ///
172 /// Here for the same reason, and used for the one call an epilogue can make, which is the one
173 /// a changed canary makes.
174 pub call: &'static str,
175 /// How a prologue touches a page of the stack, or `None` on a target where nothing can.
176 ///
177 /// See [`Probe`]. It is an option rather than a name because a target that has no such
178 /// instruction is a target where `-fstack-clash-protection` has to do nothing, and a name
179 /// standing for nothing is worse than an absence a caller has to look at.
180 pub probe: Option<Probe>,
181 /// What says an indirect branch may arrive at an address, or `None` on a target where nothing
182 /// does.
183 ///
184 /// What `-fcf-protection=branch` asks for, and an option for the same reason [`Self::probe`]
185 /// is: a target with no such instruction is one the flag cannot be honoured on, and the answer
186 /// there is to say so rather than to write a name that stands for nothing. A prologue puts one
187 /// at the top of every function, because a function's own address is the one address of it a
188 /// pointer can hold, and one goes at the top of every label a program took the address of,
189 /// because a computed `goto` is an indirect branch and those are the addresses it arrives at.
190 pub landing: Option<&'static str>,
191 /// A byte that does nothing, or `None` on a target where nothing is written for the purpose.
192 ///
193 /// What `-fpatchable-function-entry=` reserves room with, and an option for the same reason
194 /// [`Self::landing`] is. The room is counted in bytes, so what is wanted is the shortest
195 /// instruction the machine has that does nothing rather than the shortest sequence that adds
196 /// up to the length: a patcher writes over the room from its start and wants a whole number of
197 /// places it could have started at.
198 pub pad: Option<&'static str>,
199 /// How many bits of constant [`Self::add`] and [`Self::sub`] carry, when a frame can need
200 /// more, or `None` when they carry any size a frame can be.
201 ///
202 /// AArch64's carry twelve bits, or twelve bits shifted up by twelve, which is the machine's
203 /// whole answer and not a form the encoder has yet to learn. A frame of 4608 bytes is taken as
204 /// 4096 and then 512, which is what gcc writes, and [`Self::steps`] is the rule for that.
205 pub step_bits: Option<u32>,
206 /// Whether the instruction of that name can carry that displacement from the stack pointer or
207 /// the frame pointer, or `None` on a target where every offset a frame has fits.
208 ///
209 /// An AArch64 load reaches 4095 bytes, or that many of its own size, and `add` carries twelve
210 /// bits, so a local more than a few kilobytes into a large frame is out of reach of the one
211 /// instruction the lowering wrote for it. What reaches is the encoder's to say, since it is
212 /// the one that refuses, and the finish pass asks it and writes the address into a scratch
213 /// register first when the answer is no.
214 pub reaches: Option<fn(&str, i32) -> bool>,
215}
216
217impl FrameInsts {
218 /// The amounts one [`Self::add`] or [`Self::sub`] each moves the stack pointer by, which
219 /// together move it by `bytes`.
220 ///
221 /// One step on a machine whose instruction carries the whole of it. Otherwise the part above
222 /// the low bits goes first, in steps as large as the shifted form holds, and the low bits
223 /// last, so a frame under sixteen megabytes on AArch64 is at most two instructions.
224 #[must_use]
225 pub fn steps(&self, bytes: u32) -> Vec<u32> {
226 let Some(bits) = self.step_bits else { return vec![bytes] };
227 let low = bytes & ((1 << bits) - 1);
228 let most = ((1 << bits) - 1) << bits;
229 let mut high = bytes - low;
230 let mut steps = Vec::new();
231 while high > 0 {
232 let step = high.min(most);
233 steps.push(step);
234 high -= step;
235 }
236 if low > 0 || steps.is_empty() {
237 steps.push(low);
238 }
239 steps
240 }
241
242 /// How a register of that class is moved, or `None` for a class nothing spills.
243 #[must_use]
244 pub fn moves(&self, class: RegClass) -> Option<ClassMoves> {
245 self.classes.get(usize::from(class.number())).copied()
246 }
247}