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    /// How many instructions the function has ever had, which is what a table indexed by
135    /// instruction is sized against. One taken out of a block still counts, because it keeps its
136    /// index.
137    #[must_use]
138    pub fn inst_count(&self) -> usize {
139        self.insts.len()
140    }
141
142    /// Its blocks, in layout order, which is the order they are printed and emitted in.
143    pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
144        std::iter::successors(self.first_block, |&block| self[block].next)
145    }
146
147    /// Puts the blocks in that order, which is the order they are printed and emitted in.
148    ///
149    /// A block keeps its index, so nothing holding one is invalidated and nothing else in the
150    /// function has to be touched: the order is a linked list and this relinks it. That is the
151    /// whole reason the list is a list rather than the order the blocks were created in.
152    ///
153    /// The first block in the order becomes the entry, which is a real decision rather than a
154    /// consequence: on this machine a function is entered at its first byte, so the block that
155    /// runs first has to be laid out first.
156    ///
157    /// # Panics
158    ///
159    /// Panics unless `order` is every block of the function exactly once. A block left out would
160    /// be unreachable in a way nothing later could notice, and one named twice would make the
161    /// list a loop, so both are worth finding here rather than in the encoder.
162    pub fn set_block_order(&mut self, order: &[Block]) {
163        assert_eq!(order.len(), self.blocks.len(), "the order is not every block of the function");
164        let mut seen = vec![false; self.blocks.len()];
165        for &block in order {
166            assert!(!seen[block.index()], "the order names a block twice");
167            seen[block.index()] = true;
168        }
169        for (at, &block) in order.iter().enumerate() {
170            let data = &mut self.blocks[block.index()];
171            data.prev = at.checked_sub(1).map(|before| order[before]);
172            data.next = order.get(at + 1).copied();
173        }
174        self.first_block = order.first().copied();
175        self.last_block = order.last().copied();
176    }
177
178    /// Adds a parameter of that class to a block, and gives back the virtual register it
179    /// arrives as.
180    ///
181    /// Every predecessor's arm has to grow an argument to match, which is what
182    /// [`Func::succs_mut`] is for.
183    pub fn append_param(&mut self, block: Block, class: RegClass) -> Reg {
184        let reg = self.new_vreg(class);
185        self.blocks[block.index()].params.push(Param { reg, class });
186        reg
187    }
188
189    /// Adds a parameter that is already a particular register, which is what allocation leaves
190    /// behind.
191    pub fn append_given_param(&mut self, block: Block, param: Param) {
192        self.blocks[block.index()].params.push(param);
193    }
194
195    /// What arrives in a block, to be read or replaced.
196    ///
197    /// Allocation is what replaces it: once every parameter is a place and every argument is a
198    /// place, an edge is a set of moves and the parameters are what those moves write, so the
199    /// block stops asking for anything and the machine IR stops being in SSA form.
200    pub fn params_mut(&mut self, block: Block) -> &mut Vec<Param> {
201        &mut self.blocks[block.index()].params
202    }
203
204    /// Where a block goes, to be read or replaced.
205    ///
206    /// The arms are in the order the terminator's own arms run, so the first is the arm a
207    /// conditional branch takes when its condition holds.
208    pub fn succs_mut(&mut self, block: Block) -> &mut Vec<BlockCall> {
209        &mut self.blocks[block.index()].succs
210    }
211
212    // Instructions.
213
214    /// The instructions of a block, in order.
215    pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
216        std::iter::successors(self[block].first_inst, |&inst| self.inst_layout[inst.index()].next)
217    }
218
219    /// The last instruction of a block, which is its terminator once it has one.
220    #[must_use]
221    pub fn terminator(&self, block: Block) -> Option<Inst> {
222        self[block].last_inst
223    }
224
225    /// Which block an instruction is in, or `None` for one that has been taken out of its
226    /// block.
227    #[must_use]
228    pub fn block_of(&self, inst: Inst) -> Option<Block> {
229        self.inst_layout[inst.index()].block
230    }
231
232    /// Where an instruction came from.
233    #[must_use]
234    pub fn span(&self, inst: Inst) -> Span {
235        self.inst_spans[inst.index()]
236    }
237
238    /// Starts an instruction at the end of that block.
239    ///
240    /// Nothing is added to the function until [`InstBuilder::finish`], so a builder that is
241    /// dropped leaves no trace.
242    pub fn build(&mut self, block: Block, opcode: Opcode) -> InstBuilder<'_> {
243        InstBuilder {
244            func: self,
245            block: Some(block),
246            opcode,
247            operands: Vec::new(),
248            imm: None,
249            mem: None,
250            symbol: None,
251            span: Span::DUMMY,
252        }
253    }
254
255    /// Puts an instruction that is in no block at the end of one.
256    ///
257    /// # Panics
258    ///
259    /// Panics if the instruction is already in a block, because an instruction in two blocks is
260    /// the kind of thing that is found much later and somewhere else.
261    pub fn append_inst(&mut self, block: Block, inst: Inst) {
262        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
263        let last = self.blocks[block.index()].last_inst;
264        self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
265        match last {
266            Some(last) => self.inst_layout[last.index()].next = Some(inst),
267            None => self.blocks[block.index()].first_inst = Some(inst),
268        }
269        self.blocks[block.index()].last_inst = Some(inst);
270    }
271
272    /// Puts an instruction that is in no block immediately after another one.
273    ///
274    /// # Panics
275    ///
276    /// Panics if the instruction is already in a block, or if the one it is to follow is in
277    /// none.
278    pub fn insert_after(&mut self, after: Inst, inst: Inst) {
279        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
280        let layout = self.inst_layout[after.index()];
281        let block = layout.block.expect("the instruction to insert after is in no block");
282        self.inst_layout[inst.index()] =
283            InstLayout { block: Some(block), prev: Some(after), next: layout.next };
284        self.inst_layout[after.index()].next = Some(inst);
285        match layout.next {
286            Some(next) => self.inst_layout[next.index()].prev = Some(inst),
287            None => self.blocks[block.index()].last_inst = Some(inst),
288        }
289    }
290
291    /// Starts an instruction that will be in no block until something puts it in one.
292    ///
293    /// This is what a pass that inserts rather than appends builds with, and it hands the
294    /// instruction to [`Func::prepend_inst`], [`Func::insert_before`] or [`Func::insert_after`].
295    /// Everything else about it is the same, which is the point: the operand order is the
296    /// builder's invariant wherever the instruction ends up.
297    pub fn build_loose(&mut self, opcode: Opcode) -> InstBuilder<'_> {
298        InstBuilder {
299            func: self,
300            block: None,
301            opcode,
302            operands: Vec::new(),
303            imm: None,
304            mem: None,
305            symbol: None,
306            span: Span::DUMMY,
307        }
308    }
309
310    /// Puts an instruction that is in no block at the start of one, in front of everything in it.
311    ///
312    /// # Panics
313    ///
314    /// Panics if the instruction is already in a block.
315    pub fn prepend_inst(&mut self, block: Block, inst: Inst) {
316        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
317        let first = self.blocks[block.index()].first_inst;
318        self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: None, next: first };
319        match first {
320            Some(first) => self.inst_layout[first.index()].prev = Some(inst),
321            None => self.blocks[block.index()].last_inst = Some(inst),
322        }
323        self.blocks[block.index()].first_inst = Some(inst);
324    }
325
326    /// Puts an instruction that is in no block immediately before another one.
327    ///
328    /// This is what a reload is: the instruction that wants the value has to see it already
329    /// read in, so the load goes in front of it rather than behind whatever came before, which
330    /// is the same place only when something came before.
331    ///
332    /// # Panics
333    ///
334    /// Panics if the instruction is already in a block, or if the one it is to precede is in
335    /// none.
336    pub fn insert_before(&mut self, before: Inst, inst: Inst) {
337        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
338        let layout = self.inst_layout[before.index()];
339        let block = layout.block.expect("the instruction to insert before is in no block");
340        self.inst_layout[inst.index()] =
341            InstLayout { block: Some(block), prev: layout.prev, next: Some(before) };
342        self.inst_layout[before.index()].prev = Some(inst);
343        match layout.prev {
344            Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
345            None => self.blocks[block.index()].first_inst = Some(inst),
346        }
347    }
348
349    /// Takes an instruction out of its block, leaving it in the function's tables.
350    ///
351    /// It keeps its index, the way a removed block keeps its number, because renumbering would
352    /// invalidate every index anything else was holding.
353    pub fn remove_inst(&mut self, inst: Inst) {
354        let layout = self.inst_layout[inst.index()];
355        let Some(block) = layout.block else { return };
356        match layout.prev {
357            Some(prev) => self.inst_layout[prev.index()].next = layout.next,
358            None => self.blocks[block.index()].first_inst = layout.next,
359        }
360        match layout.next {
361            Some(next) => self.inst_layout[next.index()].prev = layout.prev,
362            None => self.blocks[block.index()].last_inst = layout.prev,
363        }
364        self.inst_layout[inst.index()] = InstLayout::default();
365    }
366
367    // The tables.
368
369    /// Puts a run of operands in the operand table and gives back the run.
370    pub fn push_operands(&mut self, operands: &[Operand]) -> OperandList {
371        let start = Idx::from_usize(self.operands.len());
372        self.operands.extend_from_slice(operands);
373        IdxRange::new(start, Idx::from_usize(self.operands.len()))
374    }
375
376    /// Puts an immediate in the immediate table.
377    pub fn add_imm(&mut self, value: i64) -> ImmRef {
378        self.imms.push(Imm(value));
379        Idx::from_usize(self.imms.len() - 1)
380    }
381
382    /// Puts an addressing mode in the table of them.
383    pub fn add_amode(&mut self, amode: Amode) -> MemRef {
384        self.amodes.push(amode);
385        Idx::from_usize(self.amodes.len() - 1)
386    }
387
388    /// Creates an instruction that is in no block yet.
389    pub fn create_inst(&mut self, data: InstData, span: Span) -> Inst {
390        self.insts.push(data);
391        self.inst_layout.push(InstLayout::default());
392        self.inst_spans.push(span);
393        Idx::from_usize(self.insts.len() - 1)
394    }
395}
396
397impl Index<Inst> for Func {
398    type Output = InstData;
399
400    fn index(&self, inst: Inst) -> &InstData {
401        &self.insts[inst.index()]
402    }
403}
404
405impl IndexMut<Inst> for Func {
406    fn index_mut(&mut self, inst: Inst) -> &mut InstData {
407        &mut self.insts[inst.index()]
408    }
409}
410
411impl Index<Block> for Func {
412    type Output = BlockData;
413
414    fn index(&self, block: Block) -> &BlockData {
415        &self.blocks[block.index()]
416    }
417}
418
419impl Index<OperandList> for Func {
420    type Output = [Operand];
421
422    fn index(&self, list: OperandList) -> &[Operand] {
423        &self.operands[list.as_usize_range()]
424    }
425}
426
427impl IndexMut<OperandList> for Func {
428    fn index_mut(&mut self, list: OperandList) -> &mut [Operand] {
429        &mut self.operands[list.as_usize_range()]
430    }
431}
432
433impl Index<ImmRef> for Func {
434    type Output = Imm;
435
436    fn index(&self, at: ImmRef) -> &Imm {
437        &self.imms[at.index()]
438    }
439}
440
441impl Index<MemRef> for Func {
442    type Output = Amode;
443
444    fn index(&self, at: MemRef) -> &Amode {
445        &self.amodes[at.index()]
446    }
447}
448
449impl IndexMut<MemRef> for Func {
450    fn index_mut(&mut self, at: MemRef) -> &mut Amode {
451        &mut self.amodes[at.index()]
452    }
453}
454
455/// One instruction being built.
456///
457/// The order the operands are given in is the order they are stored in, and the builder is what
458/// insists that order is the one the printer and the parser agree on.
459#[derive(Debug)]
460pub struct InstBuilder<'a> {
461    func: &'a mut Func,
462    block: Option<Block>,
463    opcode: Opcode,
464    operands: Vec<Operand>,
465    imm: Option<i64>,
466    mem: Option<Amode>,
467    symbol: Option<Symbol>,
468    span: Span,
469}
470
471impl InstBuilder<'_> {
472    /// Adds an operand.
473    ///
474    /// # Panics
475    ///
476    /// Panics if an operand the instruction writes is given after one it reads, or if either is
477    /// given after the memory operand, because both make the instruction print as text that
478    /// reads back as a different one.
479    #[must_use]
480    pub fn operand(mut self, operand: Operand) -> Self {
481        assert!(self.mem.is_none(), "the memory operand's registers come last");
482        if operand.role.is_def() {
483            let reads = self.operands.iter().any(|earlier| !earlier.role.is_def());
484            assert!(!reads, "the operands an instruction writes come first");
485        }
486        self.operands.push(operand);
487        self
488    }
489
490    /// Adds the operand the instruction writes, in the common case where nothing constrains it.
491    #[must_use]
492    pub fn def(self, reg: Reg, class: RegClass) -> Self {
493        self.operand(Operand::write(reg, class))
494    }
495
496    /// Adds an operand the instruction reads, in the common case where nothing constrains it.
497    #[must_use]
498    pub fn uses(self, reg: Reg, class: RegClass) -> Self {
499        self.operand(Operand::read(reg, class))
500    }
501
502    /// Gives the instruction a memory operand, whose registers become its last operands.
503    ///
504    /// # Panics
505    ///
506    /// Panics if it already has one.
507    #[must_use]
508    pub fn mem(mut self, mem: Mem) -> Self {
509        assert!(self.mem.is_none(), "the instruction already has a memory operand");
510        let mut amode = Amode {
511            base: None,
512            index: None,
513            scale: mem.scale.max(1),
514            disp: mem.disp,
515            symbol: mem.symbol,
516        };
517        if let Some(base) = mem.base {
518            amode.base = Some(self.next_operand());
519            self.operands.push(base);
520        }
521        if let Some(index) = mem.index {
522            amode.index = Some(self.next_operand());
523            self.operands.push(index);
524        }
525        self.mem = Some(amode);
526        self
527    }
528
529    /// Gives the instruction an immediate.
530    #[must_use]
531    pub fn imm(mut self, value: i64) -> Self {
532        self.imm = Some(value);
533        self
534    }
535
536    /// Gives the instruction the symbol it names.
537    #[must_use]
538    pub fn symbol(mut self, symbol: Symbol) -> Self {
539        self.symbol = Some(symbol);
540        self
541    }
542
543    /// Says where in the source the instruction came from.
544    #[must_use]
545    pub fn at(mut self, span: Span) -> Self {
546        self.span = span;
547        self
548    }
549
550    /// Puts the instruction at the end of the block it was started in, or in no block at all if
551    /// it was started loose.
552    pub fn finish(self) -> Inst {
553        let InstBuilder { func, block, opcode, operands, imm, mem, symbol, span } = self;
554        let data = InstData {
555            opcode,
556            operands: func.push_operands(&operands),
557            imm: imm.map(|value| func.add_imm(value)),
558            mem: mem.map(|amode| func.add_amode(amode)),
559            symbol,
560        };
561        let inst = func.create_inst(data, span);
562        if let Some(block) = block {
563            func.append_inst(block, inst);
564        }
565        inst
566    }
567
568    /// The index the next operand will have, for an addressing mode to point at.
569    ///
570    /// # Panics
571    ///
572    /// Panics past 255 operands, which is far more than any instruction of any target we have
573    /// and which the index in an addressing mode could not name anyway.
574    fn next_operand(&self) -> u8 {
575        u8::try_from(self.operands.len()).expect("too many operands on one instruction")
576    }
577}
578
579/// Whether an operand is one the instruction writes, for a printer or a pass that splits the
580/// operand vector at the point the writes stop.
581#[must_use]
582pub fn defs(operands: &[Operand]) -> usize {
583    operands.iter().position(|operand| !operand.role.is_def()).unwrap_or(operands.len())
584}
585
586#[cfg(test)]
587mod tests {
588    use rucc_base::Interner;
589
590    use super::*;
591
592    fn class() -> RegClass {
593        RegClass::new(0)
594    }
595
596    #[test]
597    fn the_blocks_can_be_put_in_another_order_and_keep_everything_in_them() {
598        let mut names = Interner::new();
599        let mut func = Func::new(names.intern("f"));
600        let opcode = Opcode::new(names.intern("x64.nop"));
601        let blocks: Vec<Block> = (0..4).map(|_| func.create_block()).collect();
602        let marker = func.build(blocks[3], opcode).finish();
603
604        func.set_block_order(&[blocks[0], blocks[3], blocks[1], blocks[2]]);
605        assert_eq!(
606            func.blocks().collect::<Vec<_>>(),
607            vec![blocks[0], blocks[3], blocks[1], blocks[2]]
608        );
609        assert_eq!(func.entry(), Some(blocks[0]));
610        // A block keeps its index, so what was in it is still in it and an instruction still
611        // knows which block it is in. That is the whole point of relinking rather than moving.
612        assert_eq!(func.block_of(marker), Some(blocks[3]));
613        assert_eq!(func.insts(blocks[3]).collect::<Vec<_>>(), vec![marker]);
614
615        // And backwards, since the list is doubly linked and a pass may walk it either way.
616        func.set_block_order(&[blocks[2], blocks[1], blocks[0], blocks[3]]);
617        assert_eq!(func.entry(), Some(blocks[2]));
618        let mut back = Vec::new();
619        let mut at = Some(blocks[3]);
620        while let Some(block) = at {
621            back.push(block);
622            at = func[block].prev;
623        }
624        assert_eq!(back, vec![blocks[3], blocks[0], blocks[1], blocks[2]]);
625    }
626
627    #[test]
628    #[should_panic(expected = "the order names a block twice")]
629    fn an_order_that_names_a_block_twice_is_refused_rather_than_made_into_a_loop() {
630        let mut names = Interner::new();
631        let mut func = Func::new(names.intern("f"));
632        let first = func.create_block();
633        let _second = func.create_block();
634        func.set_block_order(&[first, first]);
635    }
636
637    #[test]
638    #[should_panic(expected = "the order is not every block of the function")]
639    fn an_order_that_leaves_a_block_out_is_refused() {
640        let mut names = Interner::new();
641        let mut func = Func::new(names.intern("f"));
642        let first = func.create_block();
643        let _second = func.create_block();
644        func.set_block_order(&[first]);
645    }
646
647    #[test]
648    fn instructions_come_back_in_the_order_they_were_built() {
649        let mut names = Interner::new();
650        let mut func = Func::new(names.intern("f"));
651        let block = func.create_block();
652        let opcode = Opcode::new(names.intern("x64.nop"));
653        let first = func.build(block, opcode).finish();
654        let second = func.build(block, opcode).finish();
655        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, second]);
656        assert_eq!(func.terminator(block), Some(second));
657        assert_eq!(func.block_of(first), Some(block));
658    }
659
660    #[test]
661    fn a_removed_instruction_is_in_no_block_and_the_rest_still_link_up() {
662        let mut names = Interner::new();
663        let mut func = Func::new(names.intern("f"));
664        let block = func.create_block();
665        let opcode = Opcode::new(names.intern("x64.nop"));
666        let first = func.build(block, opcode).finish();
667        let second = func.build(block, opcode).finish();
668        let third = func.build(block, opcode).finish();
669        func.remove_inst(second);
670        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, third]);
671        assert_eq!(func.block_of(second), None);
672    }
673
674    #[test]
675    fn an_instruction_can_be_put_back_between_two_others() {
676        let mut names = Interner::new();
677        let mut func = Func::new(names.intern("f"));
678        let block = func.create_block();
679        let opcode = Opcode::new(names.intern("x64.nop"));
680        let first = func.build(block, opcode).finish();
681        let last = func.build(block, opcode).finish();
682        let spill = func.create_inst(InstData::new(opcode), Span::DUMMY);
683        func.insert_after(first, spill);
684        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, spill, last]);
685        assert_eq!(func.terminator(block), Some(last));
686    }
687
688    #[test]
689    fn an_instruction_can_be_put_in_front_of_the_first_one_in_a_block() {
690        let mut names = Interner::new();
691        let mut func = Func::new(names.intern("f"));
692        let block = func.create_block();
693        let opcode = Opcode::new(names.intern("x64.nop"));
694        let first = func.build(block, opcode).finish();
695        let last = func.build(block, opcode).finish();
696        let reload = func.create_inst(InstData::new(opcode), Span::DUMMY);
697        let prologue = func.create_inst(InstData::new(opcode), Span::DUMMY);
698        func.insert_before(last, reload);
699        func.prepend_inst(block, prologue);
700        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![prologue, first, reload, last]);
701        assert_eq!(func.terminator(block), Some(last));
702        assert_eq!(func.block_of(prologue), Some(block));
703    }
704
705    #[test]
706    fn the_first_instruction_in_an_empty_block_is_also_its_last() {
707        let mut names = Interner::new();
708        let mut func = Func::new(names.intern("f"));
709        let block = func.create_block();
710        let opcode = Opcode::new(names.intern("x64.ret"));
711        let only = func.create_inst(InstData::new(opcode), Span::DUMMY);
712        func.prepend_inst(block, only);
713        assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![only]);
714        assert_eq!(func.terminator(block), Some(only));
715    }
716
717    #[test]
718    fn a_memory_operand_names_the_operands_holding_its_registers() {
719        let mut names = Interner::new();
720        let mut func = Func::new(names.intern("f"));
721        let block = func.create_block();
722        let base = func.new_vreg(class());
723        let index = func.new_vreg(class());
724        let dest = func.new_vreg(class());
725        let inst = func
726            .build(block, Opcode::new(names.intern("x64.lea")))
727            .def(dest, class())
728            .mem(
729                Mem::at(Operand::read(base, class()))
730                    .indexed(Operand::read(index, class()), 4)
731                    .plus(16),
732            )
733            .finish();
734        let data = func[inst];
735        let amode = func[data.mem.expect("it was given a memory operand")];
736        assert_eq!(amode.base, Some(1));
737        assert_eq!(amode.index, Some(2));
738        assert_eq!(amode.scale, 4);
739        assert_eq!(amode.disp, 16);
740        assert_eq!(func[data.operands][1].reg, base);
741        assert_eq!(defs(&func[data.operands]), 1);
742    }
743
744    #[test]
745    #[should_panic(expected = "the operands an instruction writes come first")]
746    fn a_def_after_a_use_is_refused() {
747        let mut names = Interner::new();
748        let mut func = Func::new(names.intern("f"));
749        let block = func.create_block();
750        let reg = func.new_vreg(class());
751        let _ = func
752            .build(block, Opcode::new(names.intern("x64.add")))
753            .uses(reg, class())
754            .def(reg, class());
755    }
756
757    #[test]
758    fn a_block_parameter_is_a_virtual_register_of_its_class() {
759        let mut names = Interner::new();
760        let mut func = Func::new(names.intern("f"));
761        let block = func.create_block();
762        let param = func.append_param(block, class());
763        assert_eq!(func[block].params, vec![Param { reg: param, class: class() }]);
764        assert_eq!(func.class_of(param), Some(class()));
765        assert_eq!(func.vregs(), 1);
766        assert_eq!(func.entry(), Some(block));
767    }
768}