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. A target
33//! whose instructions do not fit those shapes needs more than a table, and it will say so by not
34//! 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/// Every instruction a prologue, an epilogue, a spill or a reload is made of.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct FrameInsts {
58 /// What a rule file and the machine IR put in front of this target's opcodes, such as
59 /// `x64.`, which says which target a term belongs to and is not part of the opcode.
60 pub prefix: &'static str,
61 /// How a register of each class is moved, one for each class of the register file in the
62 /// order the file numbers them.
63 ///
64 /// Shorter than the file when the classes at the end are ones nothing spills. An x87 stack
65 /// register is one of those: the allocator is never given one to hand out, so nothing ever
66 /// asks how to move it, and a target that answered anyway would be writing down a guess.
67 pub classes: &'static [ClassMoves],
68 /// Puts a register on the stack and moves the stack pointer down by one word.
69 pub push: &'static str,
70 /// Takes a word off the stack into a register and moves the stack pointer back up.
71 pub pop: &'static str,
72 /// Adds a constant to the stack pointer, which is how an epilogue gives the frame back.
73 pub add: &'static str,
74 /// Takes a constant off the stack pointer, which is how a prologue takes the frame.
75 pub sub: &'static str,
76 /// Clears the low bits of the stack pointer, which is how a prologue forces an alignment
77 /// nothing else can give it.
78 pub align: &'static str,
79 /// Writes a register with an address rather than with what is at it, which is how an
80 /// epilogue puts the stack pointer back when the frame pointer is the only record of where
81 /// it was.
82 pub lea: &'static str,
83 /// Returns to the caller.
84 pub ret: &'static str,
85}
86
87impl FrameInsts {
88 /// How a register of that class is moved, or `None` for a class nothing spills.
89 #[must_use]
90 pub fn moves(&self, class: RegClass) -> Option<ClassMoves> {
91 self.classes.get(usize::from(class.number())).copied()
92 }
93}