rucc_mir/func.rs
1//! The function: its blocks, its instructions, and the tables they live in.
2//!
3//! Design: `spec/10-backend.md` section 10.1.
4//!
5//! One [`Func`] owns everything in it, the same shape `rucc-ir` uses and for the same reasons:
6//! nothing is boxed, a reference to an instruction is a four-byte index, and the whole function
7//! is dropped in one go.
8//!
9//! The instructions in a block are a doubly linked list rather than a run, because the passes
10//! that run over MIR are the ones that insert most: the allocator writes spills and reloads
11//! between existing instructions, edge moves appear after it, and the peepholes of
12//! `spec/10-backend.md` section 10.9 delete. A run would move everything after each edit and
13//! invalidate every index a pass was holding.
14//!
15//! A block's parameters and its successors are `Vec`s rather than runs in a pool, because both
16//! grow after the block exists. Splitting a critical edge adds a block whose successor list is
17//! written after it is created, and the allocator's live-range splitting adds parameters.
18//!
19//! # What the builder keeps
20//!
21//! An instruction's operands are in one order and only one: the ones it writes, then the ones it
22//! reads, then the registers its memory operand names. The printer writes that order and the
23//! parser rebuilds it, so a function whose operands are in some other order prints as text that
24//! reads back as a different function. [`InstBuilder`] is what makes the order an invariant
25//! rather than a rule every caller has to remember, which is why it is the only way to make an
26//! instruction that is in a block.
27
28use std::ops::{Index, IndexMut};
29
30use rucc_base::{Idx, IdxRange, Symbol};
31use rucc_diag::Span;
32use rucc_target::RegClass;
33
34use crate::inst::{
35 Amode, Block, BlockCall, BlockData, Imm, ImmRef, Inst, InstData, InstLayout, Mem, MemRef,
36 Opcode, Operand, OperandList, Param, Reg,
37};
38
39/// How far a function's name reaches.
40///
41/// The three an object file can say, which is fewer than the five the IR has, and the narrowing
42/// is done where a function is lowered rather than where it is written out. What it is here for
43/// is to survive the trip: everything between the IR and the object file is handed machine
44/// functions, so a fact about the symbol that is not on one is a fact that is gone.
45#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
46pub enum Binding {
47 /// Visible to every other object, and the definition here is the definition. What a plain
48 /// definition at file scope gets, which is why it is the one a function starts as.
49 #[default]
50 Global,
51 /// Invisible outside this object, which is what `static` at file scope means. Two files may
52 /// each have one of the same name and they are two functions.
53 Local,
54 /// Visible, and allowed to lose to a definition in another object.
55 Weak,
56}
57
58/// How far outside a shared library a function's name reaches.
59///
60/// Here for the reason [`Binding`] is here and not for any other: everything between the IR and
61/// the object file is handed machine functions, so a fact about the symbol that is not on one is
62/// a fact that is gone by the time anything can write it down. Nothing in this crate reads it.
63///
64/// The three the IR has, unnarrowed, because ELF says all three and the format is what decides
65/// how many there are rather than the machine.
66#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
67pub enum Visibility {
68 /// In the dynamic symbol table and interposable, which is what a name nothing marked gets.
69 #[default]
70 Default,
71 /// Not in the dynamic symbol table, so nothing outside the library can name it.
72 Hidden,
73 /// In the dynamic symbol table, and a call from inside the library binds to the definition
74 /// inside it.
75 Protected,
76}
77
78/// One row of the table that says what the frame looks like at a given instruction.
79///
80/// Design: `spec/11-asm-objects-debug.md` section 11.6.
81///
82/// An unwinder is handed a return address and has to answer two questions about the function it
83/// landed in: where the caller's stack pointer was, and where the caller's copy of each register
84/// this function overwrote went. The first answer is a register and an offset and is called the
85/// canonical frame address. The second is one entry per register that was saved. Both change as
86/// the prologue runs, which is why this is a table over the function and not a fact about it.
87///
88/// The register numbers here are DWARF's and not the machine's, because the two disagree on
89/// x86-64 and there is no reason to write the mapping down twice: `rucc-target` holds it, the
90/// prologue asks for it once, and the number that comes out is the number the listing prints and
91/// the number the FDE encodes. That is why this enum can live in a crate that knows nothing about
92/// any particular machine.
93///
94/// Every row takes effect after the instruction it is attached to, which is the only arrangement
95/// that works: a rule describes the state a machine is in, and the machine is not in that state
96/// until the instruction that puts it there has run.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum CfiOp {
99 /// The canonical frame address is that register plus that offset from here on.
100 DefCfa {
101 /// The register it is counted from, by DWARF's number for it.
102 reg: u16,
103 /// How far above that register's value it is.
104 offset: i32,
105 },
106 /// The same register as before and a new offset, which is what a push or a subtraction from
107 /// the stack pointer produces while the stack pointer is still what the address is counted
108 /// from.
109 DefCfaOffset(i32),
110 /// The same offset as before and a new register, which is what pointing the frame pointer at
111 /// the frame produces, and is the whole reason a frame pointer is worth having to an
112 /// unwinder: after it the address stops depending on what the body does to the stack.
113 DefCfaRegister(u16),
114 /// The caller's copy of that register is in memory, that far from the canonical frame
115 /// address. The offset is almost always negative, since the frame is below the address.
116 Offset {
117 /// The register that was saved, by DWARF's number for it.
118 reg: u16,
119 /// Where it went, counted from the canonical frame address.
120 offset: i32,
121 },
122 /// That register holds what the caller left in it again, so the rule that said where the
123 /// saved copy went stops applying.
124 ///
125 /// Worth writing down rather than leaving the old rule standing, because a table that is
126 /// asked about every instruction is asked about the ones between a pop and the return, and by
127 /// then the memory the old rule points at is above the stack pointer and is where a signal
128 /// handler's own frame goes.
129 Restore(u16),
130 /// Put the whole rule set on a stack, so that an epilogue can undo its own changes without
131 /// the next one starting from what it left behind.
132 ///
133 /// A function with several returns has several epilogues, and they are laid out one after
134 /// another rather than nested, so without this the second one would begin from the rules the
135 /// first one ended with rather than from the rules the body had.
136 RememberState,
137 /// Take the rule set back off that stack.
138 RestoreState,
139}
140
141/// Where the room a patcher was promised at the top of a function is.
142///
143/// What `-fpatchable-function-entry=` asks for. The room is a run of the shortest instruction that
144/// does nothing, and the point of it is that something else is written over it once the program is
145/// running, so what has to survive to the writers is where it starts and how much of it there is.
146///
147/// Two halves, because the room can be on either side of the function's own label and the two sides
148/// reach the writers differently. What is after the label is in the instruction stream like anything
149/// else, so it is named by the first instruction of it. What is in front of the label is not in the
150/// stream at all, since the stream starts at the label, so it is a count the writer lays down
151/// itself, out of the one opcode kept here for it.
152///
153/// The address the room is recorded under is its start, which is the front of the half in front of
154/// the label in a function that has one and the front of the other half otherwise. The two halves
155/// are not always next to each other: a function that also opens with a landing pad has the pad
156/// between them, because the pad has to be the first thing after the label and the room does not.
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub struct Patch {
159 /// How many bytes go in front of the function's own label.
160 pub before: u32,
161 /// What one of those is written as, which is the instruction that does nothing.
162 ///
163 /// Kept because they are the only instructions in a finished function that are not in a block,
164 /// so there is nowhere else for a writer to read the opcode off. The ones after the label are
165 /// in the stream and this is the opcode they carry too.
166 pub pad: Opcode,
167 /// The first instruction of the room after the label, or `None` in a function whose room is all
168 /// in front of it.
169 pub after: Option<Inst>,
170}
171
172/// One function, in machine instructions.
173#[derive(Debug)]
174pub struct Func {
175 /// The name it is called by, which is the name of the IR function it was lowered from.
176 pub name: Symbol,
177 /// What its first instruction has to be aligned to, from the IR function it was lowered
178 /// from, or `None` for the alignment every function gets anyway.
179 pub align: Option<u32>,
180 /// How far the name reaches, from the linkage of the IR function it was lowered from.
181 ///
182 /// Nothing in this crate reads it. It is here because it is the last place the fact can be
183 /// kept: the assembler and the object writer are handed functions and nothing else, so a
184 /// machine function that does not carry this is one whose symbol they have no choice but to
185 /// make global, and a `static` function that every object declares global is a link that
186 /// fails the moment two files have one of the same name.
187 pub binding: Binding,
188 /// How far the name reaches outside a shared library, from the visibility of the IR function
189 /// it was lowered from. Carried for the reason the binding above is carried.
190 pub visibility: Visibility,
191 /// What the frame looks like as the function runs, as rows attached to the instructions they
192 /// take effect after. See [`CfiOp`].
193 ///
194 /// Written by whatever builds the prologue and the epilogues, because that is the only thing
195 /// that knows what they did, and read by the listing and by the object writer. Empty until
196 /// then, and empty for a machine nothing here writes a table for.
197 pub cfi: Vec<(Inst, CfiOp)>,
198 /// Where the room a patcher was promised is, or `None` in a function that was promised none,
199 /// which is every function on a command line that did not ask. See [`Patch`].
200 ///
201 /// Written by whatever builds the prologue, for the reason [`Func::cfi`] is: a run of bytes
202 /// that do nothing is indistinguishable from any other run of them once it is in the stream,
203 /// so which one was reserved has to be said rather than looked for.
204 pub patch: Option<Patch>,
205
206 insts: Vec<InstData>,
207 inst_layout: Vec<InstLayout>,
208 inst_spans: Vec<Span>,
209 blocks: Vec<BlockData>,
210
211 operands: Vec<Operand>,
212 imms: Vec<Imm>,
213 amodes: Vec<Amode>,
214 /// The class of each virtual register, which is what says how many there are and what the
215 /// allocator may put each of them in.
216 vregs: Vec<RegClass>,
217
218 first_block: Option<Block>,
219 last_block: Option<Block>,
220}
221
222impl Func {
223 /// A function of that name with nothing in it.
224 #[must_use]
225 pub fn new(name: Symbol) -> Self {
226 Self {
227 name,
228 align: None,
229 binding: Binding::Global,
230 visibility: Visibility::Default,
231 cfi: Vec::new(),
232 patch: None,
233 insts: Vec::new(),
234 inst_layout: Vec::new(),
235 inst_spans: Vec::new(),
236 blocks: Vec::new(),
237 operands: Vec::new(),
238 imms: Vec::new(),
239 amodes: Vec::new(),
240 vregs: Vec::new(),
241 first_block: None,
242 last_block: None,
243 }
244 }
245
246 // Registers.
247
248 /// A virtual register of that class, which nothing has defined yet.
249 ///
250 /// # Panics
251 ///
252 /// Panics if the function already has two billion of them, which no function does.
253 pub fn new_vreg(&mut self, class: RegClass) -> Reg {
254 let number = u32::try_from(self.vregs.len()).expect("too many virtual registers");
255 self.vregs.push(class);
256 Reg::virtual_reg(number)
257 }
258
259 /// How many virtual registers the function has, which is what the allocator sizes itself
260 /// against.
261 #[must_use]
262 pub fn vregs(&self) -> usize {
263 self.vregs.len()
264 }
265
266 /// The class of a virtual register, or `None` for a physical one or a number this function
267 /// never handed out.
268 #[must_use]
269 pub fn class_of(&self, reg: Reg) -> Option<RegClass> {
270 self.vregs.get(usize::try_from(reg.number()?).ok()?).copied()
271 }
272
273 // Blocks.
274
275 /// Creates a block with no parameters and nothing in it, at the end of the layout.
276 pub fn create_block(&mut self) -> Block {
277 let block = Idx::from_usize(self.blocks.len());
278 self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
279 match self.last_block {
280 Some(last) => self.blocks[last.index()].next = Some(block),
281 None => self.first_block = Some(block),
282 }
283 self.last_block = Some(block);
284 block
285 }
286
287 /// The entry block, which is the first in layout order, or `None` before there is one.
288 #[must_use]
289 pub fn entry(&self) -> Option<Block> {
290 self.first_block
291 }
292
293 /// How many blocks the function has ever had, which is what a table indexed by block is
294 /// sized against. A block taken out of the layout still counts, because it keeps its index.
295 #[must_use]
296 pub fn block_count(&self) -> usize {
297 self.blocks.len()
298 }
299
300 /// How many instructions the function has ever had, which is what a table indexed by
301 /// instruction is sized against. One taken out of a block still counts, because it keeps its
302 /// index.
303 #[must_use]
304 pub fn inst_count(&self) -> usize {
305 self.insts.len()
306 }
307
308 /// Its blocks, in layout order, which is the order they are printed and emitted in.
309 pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
310 std::iter::successors(self.first_block, |&block| self[block].next)
311 }
312
313 /// Puts the blocks in that order, which is the order they are printed and emitted in.
314 ///
315 /// A block keeps its index, so nothing holding one is invalidated and nothing else in the
316 /// function has to be touched: the order is a linked list and this relinks it. That is the
317 /// whole reason the list is a list rather than the order the blocks were created in.
318 ///
319 /// The first block in the order becomes the entry, which is a real decision rather than a
320 /// consequence: on this machine a function is entered at its first byte, so the block that
321 /// runs first has to be laid out first.
322 ///
323 /// # Panics
324 ///
325 /// Panics unless `order` is every block of the function exactly once. A block left out would
326 /// be unreachable in a way nothing later could notice, and one named twice would make the
327 /// list a loop, so both are worth finding here rather than in the encoder.
328 pub fn set_block_order(&mut self, order: &[Block]) {
329 assert_eq!(order.len(), self.blocks.len(), "the order is not every block of the function");
330 let mut seen = vec![false; self.blocks.len()];
331 for &block in order {
332 assert!(!seen[block.index()], "the order names a block twice");
333 seen[block.index()] = true;
334 }
335 for (at, &block) in order.iter().enumerate() {
336 let data = &mut self.blocks[block.index()];
337 data.prev = at.checked_sub(1).map(|before| order[before]);
338 data.next = order.get(at + 1).copied();
339 }
340 self.first_block = order.first().copied();
341 self.last_block = order.last().copied();
342 }
343
344 /// Adds a parameter of that class to a block, and gives back the virtual register it
345 /// arrives as.
346 ///
347 /// Every predecessor's arm has to grow an argument to match, which is what
348 /// [`Func::succs_mut`] is for.
349 pub fn append_param(&mut self, block: Block, class: RegClass) -> Reg {
350 let reg = self.new_vreg(class);
351 self.blocks[block.index()].params.push(Param { reg, class });
352 reg
353 }
354
355 /// Adds a parameter that is already a particular register, which is what allocation leaves
356 /// behind.
357 pub fn append_given_param(&mut self, block: Block, param: Param) {
358 self.blocks[block.index()].params.push(param);
359 }
360
361 /// What arrives in a block, to be read or replaced.
362 ///
363 /// Allocation is what replaces it: once every parameter is a place and every argument is a
364 /// place, an edge is a set of moves and the parameters are what those moves write, so the
365 /// block stops asking for anything and the machine IR stops being in SSA form.
366 pub fn params_mut(&mut self, block: Block) -> &mut Vec<Param> {
367 &mut self.blocks[block.index()].params
368 }
369
370 /// Where a block goes, to be read or replaced.
371 ///
372 /// The arms are in the order the terminator's own arms run, so the first is the arm a
373 /// conditional branch takes when its condition holds.
374 pub fn succs_mut(&mut self, block: Block) -> &mut Vec<BlockCall> {
375 &mut self.blocks[block.index()].succs
376 }
377
378 // Instructions.
379
380 /// The instructions of a block, in order.
381 pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
382 std::iter::successors(self[block].first_inst, |&inst| self.inst_layout[inst.index()].next)
383 }
384
385 /// The last instruction of a block, which is its terminator once it has one.
386 #[must_use]
387 pub fn terminator(&self, block: Block) -> Option<Inst> {
388 self[block].last_inst
389 }
390
391 /// Which block an instruction is in, or `None` for one that has been taken out of its
392 /// block.
393 #[must_use]
394 pub fn block_of(&self, inst: Inst) -> Option<Block> {
395 self.inst_layout[inst.index()].block
396 }
397
398 /// Where an instruction came from.
399 #[must_use]
400 pub fn span(&self, inst: Inst) -> Span {
401 self.inst_spans[inst.index()]
402 }
403
404 /// Starts an instruction at the end of that block.
405 ///
406 /// Nothing is added to the function until [`InstBuilder::finish`], so a builder that is
407 /// dropped leaves no trace.
408 pub fn build(&mut self, block: Block, opcode: Opcode) -> InstBuilder<'_> {
409 InstBuilder {
410 func: self,
411 block: Some(block),
412 opcode,
413 operands: Vec::new(),
414 imm: None,
415 mem: None,
416 symbol: None,
417 span: Span::DUMMY,
418 }
419 }
420
421 /// Puts an instruction that is in no block at the end of one.
422 ///
423 /// # Panics
424 ///
425 /// Panics if the instruction is already in a block, because an instruction in two blocks is
426 /// the kind of thing that is found much later and somewhere else.
427 pub fn append_inst(&mut self, block: Block, inst: Inst) {
428 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
429 let last = self.blocks[block.index()].last_inst;
430 self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
431 match last {
432 Some(last) => self.inst_layout[last.index()].next = Some(inst),
433 None => self.blocks[block.index()].first_inst = Some(inst),
434 }
435 self.blocks[block.index()].last_inst = Some(inst);
436 }
437
438 /// Puts an instruction that is in no block immediately after another one.
439 ///
440 /// # Panics
441 ///
442 /// Panics if the instruction is already in a block, or if the one it is to follow is in
443 /// none.
444 pub fn insert_after(&mut self, after: Inst, inst: Inst) {
445 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
446 let layout = self.inst_layout[after.index()];
447 let block = layout.block.expect("the instruction to insert after is in no block");
448 self.inst_layout[inst.index()] =
449 InstLayout { block: Some(block), prev: Some(after), next: layout.next };
450 self.inst_layout[after.index()].next = Some(inst);
451 match layout.next {
452 Some(next) => self.inst_layout[next.index()].prev = Some(inst),
453 None => self.blocks[block.index()].last_inst = Some(inst),
454 }
455 }
456
457 /// Starts an instruction that will be in no block until something puts it in one.
458 ///
459 /// This is what a pass that inserts rather than appends builds with, and it hands the
460 /// instruction to [`Func::prepend_inst`], [`Func::insert_before`] or [`Func::insert_after`].
461 /// Everything else about it is the same, which is the point: the operand order is the
462 /// builder's invariant wherever the instruction ends up.
463 pub fn build_loose(&mut self, opcode: Opcode) -> InstBuilder<'_> {
464 InstBuilder {
465 func: self,
466 block: None,
467 opcode,
468 operands: Vec::new(),
469 imm: None,
470 mem: None,
471 symbol: None,
472 span: Span::DUMMY,
473 }
474 }
475
476 /// Puts an instruction that is in no block at the start of one, in front of everything in it.
477 ///
478 /// # Panics
479 ///
480 /// Panics if the instruction is already in a block.
481 pub fn prepend_inst(&mut self, block: Block, inst: Inst) {
482 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
483 let first = self.blocks[block.index()].first_inst;
484 self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: None, next: first };
485 match first {
486 Some(first) => self.inst_layout[first.index()].prev = Some(inst),
487 None => self.blocks[block.index()].last_inst = Some(inst),
488 }
489 self.blocks[block.index()].first_inst = Some(inst);
490 }
491
492 /// Puts an instruction that is in no block immediately before another one.
493 ///
494 /// This is what a reload is: the instruction that wants the value has to see it already
495 /// read in, so the load goes in front of it rather than behind whatever came before, which
496 /// is the same place only when something came before.
497 ///
498 /// # Panics
499 ///
500 /// Panics if the instruction is already in a block, or if the one it is to precede is in
501 /// none.
502 pub fn insert_before(&mut self, before: Inst, inst: Inst) {
503 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
504 let layout = self.inst_layout[before.index()];
505 let block = layout.block.expect("the instruction to insert before is in no block");
506 self.inst_layout[inst.index()] =
507 InstLayout { block: Some(block), prev: layout.prev, next: Some(before) };
508 self.inst_layout[before.index()].prev = Some(inst);
509 match layout.prev {
510 Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
511 None => self.blocks[block.index()].first_inst = Some(inst),
512 }
513 }
514
515 /// Takes an instruction out of its block, leaving it in the function's tables.
516 ///
517 /// It keeps its index, the way a removed block keeps its number, because renumbering would
518 /// invalidate every index anything else was holding.
519 pub fn remove_inst(&mut self, inst: Inst) {
520 let layout = self.inst_layout[inst.index()];
521 let Some(block) = layout.block else { return };
522 match layout.prev {
523 Some(prev) => self.inst_layout[prev.index()].next = layout.next,
524 None => self.blocks[block.index()].first_inst = layout.next,
525 }
526 match layout.next {
527 Some(next) => self.inst_layout[next.index()].prev = layout.prev,
528 None => self.blocks[block.index()].last_inst = layout.prev,
529 }
530 self.inst_layout[inst.index()] = InstLayout::default();
531 }
532
533 /// The frame rules that take effect after that instruction, in the order they were written.
534 ///
535 /// A scan rather than an index, because a prologue is a handful of rows and a function is
536 /// walked once by each of the two things that read them. An index would cost more to build
537 /// than the scans it saves.
538 pub fn cfi_after(&self, inst: Inst) -> impl Iterator<Item = CfiOp> + '_ {
539 self.cfi.iter().filter(move |&&(at, _)| at == inst).map(|&(_, op)| op)
540 }
541
542 /// The instruction the record stops after, which is the last one in the layout.
543 ///
544 /// A record covers the function and not the byte after it, so a row attached to this
545 /// instruction describes an address nothing can return to and is dropped. What is otherwise
546 /// found there is the epilogue of the last block putting back a state no unwinder will read.
547 ///
548 /// Asked for here rather than worked out by each of the two things that write a record out,
549 /// because the two of them writing different tables for one function is exactly what
550 /// `spec/11-asm-objects-debug.md` section 11.1 says must not be possible.
551 pub fn cfi_end(&self) -> Option<Inst> {
552 self.blocks().last().and_then(|block| self.insts(block).last())
553 }
554
555 // The tables.
556
557 /// Puts a run of operands in the operand table and gives back the run.
558 pub fn push_operands(&mut self, operands: &[Operand]) -> OperandList {
559 let start = Idx::from_usize(self.operands.len());
560 self.operands.extend_from_slice(operands);
561 IdxRange::new(start, Idx::from_usize(self.operands.len()))
562 }
563
564 /// Puts an immediate in the immediate table.
565 pub fn add_imm(&mut self, value: i64) -> ImmRef {
566 self.imms.push(Imm(value));
567 Idx::from_usize(self.imms.len() - 1)
568 }
569
570 /// Puts an addressing mode in the table of them.
571 pub fn add_amode(&mut self, amode: Amode) -> MemRef {
572 self.amodes.push(amode);
573 Idx::from_usize(self.amodes.len() - 1)
574 }
575
576 /// Creates an instruction that is in no block yet.
577 pub fn create_inst(&mut self, data: InstData, span: Span) -> Inst {
578 self.insts.push(data);
579 self.inst_layout.push(InstLayout::default());
580 self.inst_spans.push(span);
581 Idx::from_usize(self.insts.len() - 1)
582 }
583}
584
585impl Index<Inst> for Func {
586 type Output = InstData;
587
588 fn index(&self, inst: Inst) -> &InstData {
589 &self.insts[inst.index()]
590 }
591}
592
593impl IndexMut<Inst> for Func {
594 fn index_mut(&mut self, inst: Inst) -> &mut InstData {
595 &mut self.insts[inst.index()]
596 }
597}
598
599impl Index<Block> for Func {
600 type Output = BlockData;
601
602 fn index(&self, block: Block) -> &BlockData {
603 &self.blocks[block.index()]
604 }
605}
606
607impl Index<OperandList> for Func {
608 type Output = [Operand];
609
610 fn index(&self, list: OperandList) -> &[Operand] {
611 &self.operands[list.as_usize_range()]
612 }
613}
614
615impl IndexMut<OperandList> for Func {
616 fn index_mut(&mut self, list: OperandList) -> &mut [Operand] {
617 &mut self.operands[list.as_usize_range()]
618 }
619}
620
621impl Index<ImmRef> for Func {
622 type Output = Imm;
623
624 fn index(&self, at: ImmRef) -> &Imm {
625 &self.imms[at.index()]
626 }
627}
628
629impl Index<MemRef> for Func {
630 type Output = Amode;
631
632 fn index(&self, at: MemRef) -> &Amode {
633 &self.amodes[at.index()]
634 }
635}
636
637impl IndexMut<MemRef> for Func {
638 fn index_mut(&mut self, at: MemRef) -> &mut Amode {
639 &mut self.amodes[at.index()]
640 }
641}
642
643/// One instruction being built.
644///
645/// The order the operands are given in is the order they are stored in, and the builder is what
646/// insists that order is the one the printer and the parser agree on.
647#[derive(Debug)]
648pub struct InstBuilder<'a> {
649 func: &'a mut Func,
650 block: Option<Block>,
651 opcode: Opcode,
652 operands: Vec<Operand>,
653 imm: Option<i64>,
654 mem: Option<Amode>,
655 symbol: Option<Symbol>,
656 span: Span,
657}
658
659impl InstBuilder<'_> {
660 /// Adds an operand.
661 ///
662 /// # Panics
663 ///
664 /// Panics if an operand the instruction writes is given after one it reads, or if either is
665 /// given after the memory operand, because both make the instruction print as text that
666 /// reads back as a different one.
667 #[must_use]
668 pub fn operand(mut self, operand: Operand) -> Self {
669 assert!(self.mem.is_none(), "the memory operand's registers come last");
670 if operand.role.is_def() {
671 let reads = self.operands.iter().any(|earlier| !earlier.role.is_def());
672 assert!(!reads, "the operands an instruction writes come first");
673 }
674 self.operands.push(operand);
675 self
676 }
677
678 /// Adds the operand the instruction writes, in the common case where nothing constrains it.
679 #[must_use]
680 pub fn def(self, reg: Reg, class: RegClass) -> Self {
681 self.operand(Operand::write(reg, class))
682 }
683
684 /// Adds an operand the instruction reads, in the common case where nothing constrains it.
685 #[must_use]
686 pub fn uses(self, reg: Reg, class: RegClass) -> Self {
687 self.operand(Operand::read(reg, class))
688 }
689
690 /// Gives the instruction a memory operand, whose registers become its last operands.
691 ///
692 /// # Panics
693 ///
694 /// Panics if it already has one.
695 #[must_use]
696 pub fn mem(mut self, mem: Mem) -> Self {
697 assert!(self.mem.is_none(), "the instruction already has a memory operand");
698 let mut amode = Amode {
699 base: None,
700 index: None,
701 scale: mem.scale.max(1),
702 disp: mem.disp,
703 symbol: mem.symbol,
704 got: mem.got,
705 segment: mem.segment,
706 };
707 if let Some(base) = mem.base {
708 amode.base = Some(self.next_operand());
709 self.operands.push(base);
710 }
711 if let Some(index) = mem.index {
712 amode.index = Some(self.next_operand());
713 self.operands.push(index);
714 }
715 self.mem = Some(amode);
716 self
717 }
718
719 /// Gives the instruction an immediate.
720 #[must_use]
721 pub fn imm(mut self, value: i64) -> Self {
722 self.imm = Some(value);
723 self
724 }
725
726 /// Gives the instruction the symbol it names.
727 #[must_use]
728 pub fn symbol(mut self, symbol: Symbol) -> Self {
729 self.symbol = Some(symbol);
730 self
731 }
732
733 /// Says where in the source the instruction came from.
734 #[must_use]
735 pub fn at(mut self, span: Span) -> Self {
736 self.span = span;
737 self
738 }
739
740 /// Puts the instruction at the end of the block it was started in, or in no block at all if
741 /// it was started loose.
742 pub fn finish(self) -> Inst {
743 let InstBuilder { func, block, opcode, operands, imm, mem, symbol, span } = self;
744 let data = InstData {
745 opcode,
746 operands: func.push_operands(&operands),
747 imm: imm.map(|value| func.add_imm(value)),
748 mem: mem.map(|amode| func.add_amode(amode)),
749 symbol,
750 };
751 let inst = func.create_inst(data, span);
752 if let Some(block) = block {
753 func.append_inst(block, inst);
754 }
755 inst
756 }
757
758 /// The index the next operand will have, for an addressing mode to point at.
759 ///
760 /// # Panics
761 ///
762 /// Panics past 255 operands, which is far more than any instruction of any target we have
763 /// and which the index in an addressing mode could not name anyway.
764 fn next_operand(&self) -> u8 {
765 u8::try_from(self.operands.len()).expect("too many operands on one instruction")
766 }
767}
768
769/// Whether an operand is one the instruction writes, for a printer or a pass that splits the
770/// operand vector at the point the writes stop.
771#[must_use]
772pub fn defs(operands: &[Operand]) -> usize {
773 operands.iter().position(|operand| !operand.role.is_def()).unwrap_or(operands.len())
774}
775
776#[cfg(test)]
777mod tests {
778 use rucc_base::Interner;
779
780 use super::*;
781
782 fn class() -> RegClass {
783 RegClass::new(0)
784 }
785
786 #[test]
787 fn the_blocks_can_be_put_in_another_order_and_keep_everything_in_them() {
788 let mut names = Interner::new();
789 let mut func = Func::new(names.intern("f"));
790 let opcode = Opcode::new(names.intern("x64.nop"));
791 let blocks: Vec<Block> = (0..4).map(|_| func.create_block()).collect();
792 let marker = func.build(blocks[3], opcode).finish();
793
794 func.set_block_order(&[blocks[0], blocks[3], blocks[1], blocks[2]]);
795 assert_eq!(
796 func.blocks().collect::<Vec<_>>(),
797 vec![blocks[0], blocks[3], blocks[1], blocks[2]]
798 );
799 assert_eq!(func.entry(), Some(blocks[0]));
800 // A block keeps its index, so what was in it is still in it and an instruction still
801 // knows which block it is in. That is the whole point of relinking rather than moving.
802 assert_eq!(func.block_of(marker), Some(blocks[3]));
803 assert_eq!(func.insts(blocks[3]).collect::<Vec<_>>(), vec![marker]);
804
805 // And backwards, since the list is doubly linked and a pass may walk it either way.
806 func.set_block_order(&[blocks[2], blocks[1], blocks[0], blocks[3]]);
807 assert_eq!(func.entry(), Some(blocks[2]));
808 let mut back = Vec::new();
809 let mut at = Some(blocks[3]);
810 while let Some(block) = at {
811 back.push(block);
812 at = func[block].prev;
813 }
814 assert_eq!(back, vec![blocks[3], blocks[0], blocks[1], blocks[2]]);
815 }
816
817 #[test]
818 #[should_panic(expected = "the order names a block twice")]
819 fn an_order_that_names_a_block_twice_is_refused_rather_than_made_into_a_loop() {
820 let mut names = Interner::new();
821 let mut func = Func::new(names.intern("f"));
822 let first = func.create_block();
823 let _second = func.create_block();
824 func.set_block_order(&[first, first]);
825 }
826
827 #[test]
828 #[should_panic(expected = "the order is not every block of the function")]
829 fn an_order_that_leaves_a_block_out_is_refused() {
830 let mut names = Interner::new();
831 let mut func = Func::new(names.intern("f"));
832 let first = func.create_block();
833 let _second = func.create_block();
834 func.set_block_order(&[first]);
835 }
836
837 #[test]
838 fn instructions_come_back_in_the_order_they_were_built() {
839 let mut names = Interner::new();
840 let mut func = Func::new(names.intern("f"));
841 let block = func.create_block();
842 let opcode = Opcode::new(names.intern("x64.nop"));
843 let first = func.build(block, opcode).finish();
844 let second = func.build(block, opcode).finish();
845 assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, second]);
846 assert_eq!(func.terminator(block), Some(second));
847 assert_eq!(func.block_of(first), Some(block));
848 }
849
850 #[test]
851 fn a_removed_instruction_is_in_no_block_and_the_rest_still_link_up() {
852 let mut names = Interner::new();
853 let mut func = Func::new(names.intern("f"));
854 let block = func.create_block();
855 let opcode = Opcode::new(names.intern("x64.nop"));
856 let first = func.build(block, opcode).finish();
857 let second = func.build(block, opcode).finish();
858 let third = func.build(block, opcode).finish();
859 func.remove_inst(second);
860 assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, third]);
861 assert_eq!(func.block_of(second), None);
862 }
863
864 #[test]
865 fn an_instruction_can_be_put_back_between_two_others() {
866 let mut names = Interner::new();
867 let mut func = Func::new(names.intern("f"));
868 let block = func.create_block();
869 let opcode = Opcode::new(names.intern("x64.nop"));
870 let first = func.build(block, opcode).finish();
871 let last = func.build(block, opcode).finish();
872 let spill = func.create_inst(InstData::new(opcode), Span::DUMMY);
873 func.insert_after(first, spill);
874 assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, spill, last]);
875 assert_eq!(func.terminator(block), Some(last));
876 }
877
878 #[test]
879 fn an_instruction_can_be_put_in_front_of_the_first_one_in_a_block() {
880 let mut names = Interner::new();
881 let mut func = Func::new(names.intern("f"));
882 let block = func.create_block();
883 let opcode = Opcode::new(names.intern("x64.nop"));
884 let first = func.build(block, opcode).finish();
885 let last = func.build(block, opcode).finish();
886 let reload = func.create_inst(InstData::new(opcode), Span::DUMMY);
887 let prologue = func.create_inst(InstData::new(opcode), Span::DUMMY);
888 func.insert_before(last, reload);
889 func.prepend_inst(block, prologue);
890 assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![prologue, first, reload, last]);
891 assert_eq!(func.terminator(block), Some(last));
892 assert_eq!(func.block_of(prologue), Some(block));
893 }
894
895 #[test]
896 fn the_first_instruction_in_an_empty_block_is_also_its_last() {
897 let mut names = Interner::new();
898 let mut func = Func::new(names.intern("f"));
899 let block = func.create_block();
900 let opcode = Opcode::new(names.intern("x64.ret"));
901 let only = func.create_inst(InstData::new(opcode), Span::DUMMY);
902 func.prepend_inst(block, only);
903 assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![only]);
904 assert_eq!(func.terminator(block), Some(only));
905 }
906
907 #[test]
908 fn a_memory_operand_names_the_operands_holding_its_registers() {
909 let mut names = Interner::new();
910 let mut func = Func::new(names.intern("f"));
911 let block = func.create_block();
912 let base = func.new_vreg(class());
913 let index = func.new_vreg(class());
914 let dest = func.new_vreg(class());
915 let inst = func
916 .build(block, Opcode::new(names.intern("x64.lea")))
917 .def(dest, class())
918 .mem(
919 Mem::at(Operand::read(base, class()))
920 .indexed(Operand::read(index, class()), 4)
921 .plus(16),
922 )
923 .finish();
924 let data = func[inst];
925 let amode = func[data.mem.expect("it was given a memory operand")];
926 assert_eq!(amode.base, Some(1));
927 assert_eq!(amode.index, Some(2));
928 assert_eq!(amode.scale, 4);
929 assert_eq!(amode.disp, 16);
930 assert_eq!(func[data.operands][1].reg, base);
931 assert_eq!(defs(&func[data.operands]), 1);
932 }
933
934 #[test]
935 #[should_panic(expected = "the operands an instruction writes come first")]
936 fn a_def_after_a_use_is_refused() {
937 let mut names = Interner::new();
938 let mut func = Func::new(names.intern("f"));
939 let block = func.create_block();
940 let reg = func.new_vreg(class());
941 let _ = func
942 .build(block, Opcode::new(names.intern("x64.add")))
943 .uses(reg, class())
944 .def(reg, class());
945 }
946
947 #[test]
948 fn a_block_parameter_is_a_virtual_register_of_its_class() {
949 let mut names = Interner::new();
950 let mut func = Func::new(names.intern("f"));
951 let block = func.create_block();
952 let param = func.append_param(block, class());
953 assert_eq!(func[block].params, vec![Param { reg: param, class: class() }]);
954 assert_eq!(func.class_of(param), Some(class()));
955 assert_eq!(func.vregs(), 1);
956 assert_eq!(func.entry(), Some(block));
957 }
958}