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/// One function, in machine instructions.
40#[derive(Debug)]
41pub struct Func {
42    /// The name it is called by, which is the name of the IR function it was lowered from.
43    pub name: Symbol,
44
45    insts: Vec<InstData>,
46    inst_layout: Vec<InstLayout>,
47    inst_spans: Vec<Span>,
48    blocks: Vec<BlockData>,
49
50    operands: Vec<Operand>,
51    imms: Vec<Imm>,
52    amodes: Vec<Amode>,
53    /// The class of each virtual register, which is what says how many there are and what the
54    /// allocator may put each of them in.
55    vregs: Vec<RegClass>,
56
57    first_block: Option<Block>,
58    last_block: Option<Block>,
59}
60
61impl Func {
62    /// A function of that name with nothing in it.
63    #[must_use]
64    pub fn new(name: Symbol) -> Self {
65        Self {
66            name,
67            insts: Vec::new(),
68            inst_layout: Vec::new(),
69            inst_spans: Vec::new(),
70            blocks: Vec::new(),
71            operands: Vec::new(),
72            imms: Vec::new(),
73            amodes: Vec::new(),
74            vregs: Vec::new(),
75            first_block: None,
76            last_block: None,
77        }
78    }
79
80    // Registers.
81
82    /// A virtual register of that class, which nothing has defined yet.
83    ///
84    /// # Panics
85    ///
86    /// Panics if the function already has two billion of them, which no function does.
87    pub fn new_vreg(&mut self, class: RegClass) -> Reg {
88        let number = u32::try_from(self.vregs.len()).expect("too many virtual registers");
89        self.vregs.push(class);
90        Reg::virtual_reg(number)
91    }
92
93    /// How many virtual registers the function has, which is what the allocator sizes itself
94    /// against.
95    #[must_use]
96    pub fn vregs(&self) -> usize {
97        self.vregs.len()
98    }
99
100    /// The class of a virtual register, or `None` for a physical one or a number this function
101    /// never handed out.
102    #[must_use]
103    pub fn class_of(&self, reg: Reg) -> Option<RegClass> {
104        self.vregs.get(usize::try_from(reg.number()?).ok()?).copied()
105    }
106
107    // Blocks.
108
109    /// Creates a block with no parameters and nothing in it, at the end of the layout.
110    pub fn create_block(&mut self) -> Block {
111        let block = Idx::from_usize(self.blocks.len());
112        self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
113        match self.last_block {
114            Some(last) => self.blocks[last.index()].next = Some(block),
115            None => self.first_block = Some(block),
116        }
117        self.last_block = Some(block);
118        block
119    }
120
121    /// The entry block, which is the first in layout order, or `None` before there is one.
122    #[must_use]
123    pub fn entry(&self) -> Option<Block> {
124        self.first_block
125    }
126
127    /// How many blocks the function has ever had, which is what a table indexed by block is
128    /// sized against. A block taken out of the layout still counts, because it keeps its index.
129    #[must_use]
130    pub fn block_count(&self) -> usize {
131        self.blocks.len()
132    }
133
134    /// Its blocks, in layout order, which is the order they are printed and emitted in.
135    pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
136        std::iter::successors(self.first_block, |&block| self[block].next)
137    }
138
139    /// Adds a parameter of that class to a block, and gives back the virtual register it
140    /// arrives as.
141    ///
142    /// Every predecessor's arm has to grow an argument to match, which is what
143    /// [`Func::succs_mut`] is for.
144    pub fn append_param(&mut self, block: Block, class: RegClass) -> Reg {
145        let reg = self.new_vreg(class);
146        self.blocks[block.index()].params.push(Param { reg, class });
147        reg
148    }
149
150    /// Adds a parameter that is already a particular register, which is what allocation leaves
151    /// behind.
152    pub fn append_given_param(&mut self, block: Block, param: Param) {
153        self.blocks[block.index()].params.push(param);
154    }
155
156    /// Where a block goes, to be read or replaced.
157    ///
158    /// The arms are in the order the terminator's own arms run, so the first is the arm a
159    /// conditional branch takes when its condition holds.
160    pub fn succs_mut(&mut self, block: Block) -> &mut Vec<BlockCall> {
161        &mut self.blocks[block.index()].succs
162    }
163
164    // Instructions.
165
166    /// The instructions of a block, in order.
167    pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
168        std::iter::successors(self[block].first_inst, |&inst| self.inst_layout[inst.index()].next)
169    }
170
171    /// The last instruction of a block, which is its terminator once it has one.
172    #[must_use]
173    pub fn terminator(&self, block: Block) -> Option<Inst> {
174        self[block].last_inst
175    }
176
177    /// Which block an instruction is in, or `None` for one that has been taken out of its
178    /// block.
179    #[must_use]
180    pub fn block_of(&self, inst: Inst) -> Option<Block> {
181        self.inst_layout[inst.index()].block
182    }
183
184    /// Where an instruction came from.
185    #[must_use]
186    pub fn span(&self, inst: Inst) -> Span {
187        self.inst_spans[inst.index()]
188    }
189
190    /// Starts an instruction at the end of that block.
191    ///
192    /// Nothing is added to the function until [`InstBuilder::finish`], so a builder that is
193    /// dropped leaves no trace.
194    pub fn build(&mut self, block: Block, opcode: Opcode) -> InstBuilder<'_> {
195        InstBuilder {
196            func: self,
197            block,
198            opcode,
199            operands: Vec::new(),
200            imm: None,
201            mem: None,
202            symbol: None,
203            span: Span::DUMMY,
204        }
205    }
206
207    /// Puts an instruction that is in no block at the end of one.
208    ///
209    /// # Panics
210    ///
211    /// Panics if the instruction is already in a block, because an instruction in two blocks is
212    /// the kind of thing that is found much later and somewhere else.
213    pub fn append_inst(&mut self, block: Block, inst: Inst) {
214        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
215        let last = self.blocks[block.index()].last_inst;
216        self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
217        match last {
218            Some(last) => self.inst_layout[last.index()].next = Some(inst),
219            None => self.blocks[block.index()].first_inst = Some(inst),
220        }
221        self.blocks[block.index()].last_inst = Some(inst);
222    }
223
224    /// Puts an instruction that is in no block immediately after another one.
225    ///
226    /// # Panics
227    ///
228    /// Panics if the instruction is already in a block, or if the one it is to follow is in
229    /// none.
230    pub fn insert_after(&mut self, after: Inst, inst: Inst) {
231        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
232        let layout = self.inst_layout[after.index()];
233        let block = layout.block.expect("the instruction to insert after is in no block");
234        self.inst_layout[inst.index()] =
235            InstLayout { block: Some(block), prev: Some(after), next: layout.next };
236        self.inst_layout[after.index()].next = Some(inst);
237        match layout.next {
238            Some(next) => self.inst_layout[next.index()].prev = Some(inst),
239            None => self.blocks[block.index()].last_inst = Some(inst),
240        }
241    }
242
243    /// Takes an instruction out of its block, leaving it in the function's tables.
244    ///
245    /// It keeps its index, the way a removed block keeps its number, because renumbering would
246    /// invalidate every index anything else was holding.
247    pub fn remove_inst(&mut self, inst: Inst) {
248        let layout = self.inst_layout[inst.index()];
249        let Some(block) = layout.block else { return };
250        match layout.prev {
251            Some(prev) => self.inst_layout[prev.index()].next = layout.next,
252            None => self.blocks[block.index()].first_inst = layout.next,
253        }
254        match layout.next {
255            Some(next) => self.inst_layout[next.index()].prev = layout.prev,
256            None => self.blocks[block.index()].last_inst = layout.prev,
257        }
258        self.inst_layout[inst.index()] = InstLayout::default();
259    }
260
261    // The tables.
262
263    /// Puts a run of operands in the operand table and gives back the run.
264    pub fn push_operands(&mut self, operands: &[Operand]) -> OperandList {
265        let start = Idx::from_usize(self.operands.len());
266        self.operands.extend_from_slice(operands);
267        IdxRange::new(start, Idx::from_usize(self.operands.len()))
268    }
269
270    /// Puts an immediate in the immediate table.
271    pub fn add_imm(&mut self, value: i64) -> ImmRef {
272        self.imms.push(Imm(value));
273        Idx::from_usize(self.imms.len() - 1)
274    }
275
276    /// Puts an addressing mode in the table of them.
277    pub fn add_amode(&mut self, amode: Amode) -> MemRef {
278        self.amodes.push(amode);
279        Idx::from_usize(self.amodes.len() - 1)
280    }
281
282    /// Creates an instruction that is in no block yet.
283    pub fn create_inst(&mut self, data: InstData, span: Span) -> Inst {
284        self.insts.push(data);
285        self.inst_layout.push(InstLayout::default());
286        self.inst_spans.push(span);
287        Idx::from_usize(self.insts.len() - 1)
288    }
289}
290
291impl Index<Inst> for Func {
292    type Output = InstData;
293
294    fn index(&self, inst: Inst) -> &InstData {
295        &self.insts[inst.index()]
296    }
297}
298
299impl IndexMut<Inst> for Func {
300    fn index_mut(&mut self, inst: Inst) -> &mut InstData {
301        &mut self.insts[inst.index()]
302    }
303}
304
305impl Index<Block> for Func {
306    type Output = BlockData;
307
308    fn index(&self, block: Block) -> &BlockData {
309        &self.blocks[block.index()]
310    }
311}
312
313impl Index<OperandList> for Func {
314    type Output = [Operand];
315
316    fn index(&self, list: OperandList) -> &[Operand] {
317        &self.operands[list.as_usize_range()]
318    }
319}
320
321impl IndexMut<OperandList> for Func {
322    fn index_mut(&mut self, list: OperandList) -> &mut [Operand] {
323        &mut self.operands[list.as_usize_range()]
324    }
325}
326
327impl Index<ImmRef> for Func {
328    type Output = Imm;
329
330    fn index(&self, at: ImmRef) -> &Imm {
331        &self.imms[at.index()]
332    }
333}
334
335impl Index<MemRef> for Func {
336    type Output = Amode;
337
338    fn index(&self, at: MemRef) -> &Amode {
339        &self.amodes[at.index()]
340    }
341}
342
343/// One instruction being built.
344///
345/// The order the operands are given in is the order they are stored in, and the builder is what
346/// insists that order is the one the printer and the parser agree on.
347#[derive(Debug)]
348pub struct InstBuilder<'a> {
349    func: &'a mut Func,
350    block: Block,
351    opcode: Opcode,
352    operands: Vec<Operand>,
353    imm: Option<i64>,
354    mem: Option<Amode>,
355    symbol: Option<Symbol>,
356    span: Span,
357}
358
359impl InstBuilder<'_> {
360    /// Adds an operand.
361    ///
362    /// # Panics
363    ///
364    /// Panics if an operand the instruction writes is given after one it reads, or if either is
365    /// given after the memory operand, because both make the instruction print as text that
366    /// reads back as a different one.
367    #[must_use]
368    pub fn operand(mut self, operand: Operand) -> Self {
369        assert!(self.mem.is_none(), "the memory operand's registers come last");
370        if operand.role.is_def() {
371            let reads = self.operands.iter().any(|earlier| !earlier.role.is_def());
372            assert!(!reads, "the operands an instruction writes come first");
373        }
374        self.operands.push(operand);
375        self
376    }
377
378    /// Adds the operand the instruction writes, in the common case where nothing constrains it.
379    #[must_use]
380    pub fn def(self, reg: Reg, class: RegClass) -> Self {
381        self.operand(Operand::write(reg, class))
382    }
383
384    /// Adds an operand the instruction reads, in the common case where nothing constrains it.
385    #[must_use]
386    pub fn uses(self, reg: Reg, class: RegClass) -> Self {
387        self.operand(Operand::read(reg, class))
388    }
389
390    /// Gives the instruction a memory operand, whose registers become its last operands.
391    ///
392    /// # Panics
393    ///
394    /// Panics if it already has one.
395    #[must_use]
396    pub fn mem(mut self, mem: Mem) -> Self {
397        assert!(self.mem.is_none(), "the instruction already has a memory operand");
398        let mut amode = Amode {
399            base: None,
400            index: None,
401            scale: mem.scale.max(1),
402            disp: mem.disp,
403            symbol: mem.symbol,
404        };
405        if let Some(base) = mem.base {
406            amode.base = Some(self.next_operand());
407            self.operands.push(base);
408        }
409        if let Some(index) = mem.index {
410            amode.index = Some(self.next_operand());
411            self.operands.push(index);
412        }
413        self.mem = Some(amode);
414        self
415    }
416
417    /// Gives the instruction an immediate.
418    #[must_use]
419    pub fn imm(mut self, value: i64) -> Self {
420        self.imm = Some(value);
421        self
422    }
423
424    /// Gives the instruction the symbol it names.
425    #[must_use]
426    pub fn symbol(mut self, symbol: Symbol) -> Self {
427        self.symbol = Some(symbol);
428        self
429    }
430
431    /// Says where in the source the instruction came from.
432    #[must_use]
433    pub fn at(mut self, span: Span) -> Self {
434        self.span = span;
435        self
436    }
437
438    /// Puts the instruction at the end of the block it was started in.
439    pub fn finish(self) -> Inst {
440        let InstBuilder { func, block, opcode, operands, imm, mem, symbol, span } = self;
441        let data = InstData {
442            opcode,
443            operands: func.push_operands(&operands),
444            imm: imm.map(|value| func.add_imm(value)),
445            mem: mem.map(|amode| func.add_amode(amode)),
446            symbol,
447        };
448        let inst = func.create_inst(data, span);
449        func.append_inst(block, inst);
450        inst
451    }
452
453    /// The index the next operand will have, for an addressing mode to point at.
454    ///
455    /// # Panics
456    ///
457    /// Panics past 255 operands, which is far more than any instruction of any target we have
458    /// and which the index in an addressing mode could not name anyway.
459    fn next_operand(&self) -> u8 {
460        u8::try_from(self.operands.len()).expect("too many operands on one instruction")
461    }
462}
463
464/// Whether an operand is one the instruction writes, for a printer or a pass that splits the
465/// operand vector at the point the writes stop.
466#[must_use]
467pub fn defs(operands: &[Operand]) -> usize {
468    operands.iter().position(|operand| !operand.role.is_def()).unwrap_or(operands.len())
469}
470
471#[cfg(test)]
472mod tests {
473    use rucc_base::Interner;
474
475    use super::*;
476
477    fn class() -> RegClass {
478        RegClass::new(0)
479    }
480
481    #[test]
482    fn instructions_come_back_in_the_order_they_were_built() {
483        let mut names = Interner::new();
484        let mut func = Func::new(names.intern("f"));
485        let block = func.create_block();
486        let opcode = Opcode::new(names.intern("x64.nop"));
487        let first = func.build(block, opcode).finish();
488        let second = func.build(block, opcode).finish();
489        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, second]);
490        assert_eq!(func.terminator(block), Some(second));
491        assert_eq!(func.block_of(first), Some(block));
492    }
493
494    #[test]
495    fn a_removed_instruction_is_in_no_block_and_the_rest_still_link_up() {
496        let mut names = Interner::new();
497        let mut func = Func::new(names.intern("f"));
498        let block = func.create_block();
499        let opcode = Opcode::new(names.intern("x64.nop"));
500        let first = func.build(block, opcode).finish();
501        let second = func.build(block, opcode).finish();
502        let third = func.build(block, opcode).finish();
503        func.remove_inst(second);
504        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, third]);
505        assert_eq!(func.block_of(second), None);
506    }
507
508    #[test]
509    fn an_instruction_can_be_put_back_between_two_others() {
510        let mut names = Interner::new();
511        let mut func = Func::new(names.intern("f"));
512        let block = func.create_block();
513        let opcode = Opcode::new(names.intern("x64.nop"));
514        let first = func.build(block, opcode).finish();
515        let last = func.build(block, opcode).finish();
516        let spill = func.create_inst(InstData::new(opcode), Span::DUMMY);
517        func.insert_after(first, spill);
518        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, spill, last]);
519        assert_eq!(func.terminator(block), Some(last));
520    }
521
522    #[test]
523    fn a_memory_operand_names_the_operands_holding_its_registers() {
524        let mut names = Interner::new();
525        let mut func = Func::new(names.intern("f"));
526        let block = func.create_block();
527        let base = func.new_vreg(class());
528        let index = func.new_vreg(class());
529        let dest = func.new_vreg(class());
530        let inst = func
531            .build(block, Opcode::new(names.intern("x64.lea")))
532            .def(dest, class())
533            .mem(
534                Mem::at(Operand::read(base, class()))
535                    .indexed(Operand::read(index, class()), 4)
536                    .plus(16),
537            )
538            .finish();
539        let data = func[inst];
540        let amode = func[data.mem.expect("it was given a memory operand")];
541        assert_eq!(amode.base, Some(1));
542        assert_eq!(amode.index, Some(2));
543        assert_eq!(amode.scale, 4);
544        assert_eq!(amode.disp, 16);
545        assert_eq!(func[data.operands][1].reg, base);
546        assert_eq!(defs(&func[data.operands]), 1);
547    }
548
549    #[test]
550    #[should_panic(expected = "the operands an instruction writes come first")]
551    fn a_def_after_a_use_is_refused() {
552        let mut names = Interner::new();
553        let mut func = Func::new(names.intern("f"));
554        let block = func.create_block();
555        let reg = func.new_vreg(class());
556        let _ = func
557            .build(block, Opcode::new(names.intern("x64.add")))
558            .uses(reg, class())
559            .def(reg, class());
560    }
561
562    #[test]
563    fn a_block_parameter_is_a_virtual_register_of_its_class() {
564        let mut names = Interner::new();
565        let mut func = Func::new(names.intern("f"));
566        let block = func.create_block();
567        let param = func.append_param(block, class());
568        assert_eq!(func[block].params, vec![Param { reg: param, class: class() }]);
569        assert_eq!(func.class_of(param), Some(class()));
570        assert_eq!(func.vregs(), 1);
571        assert_eq!(func.entry(), Some(block));
572    }
573}