Skip to main content

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}