Skip to main content

rucc_ir/
func.rs

1//! The function: its blocks, its instructions, its values, and the tables they live in.
2//!
3//! Design: `spec/08-ir.md` sections 8.1 and 8.6.
4//!
5//! One [`Func`] owns everything in it. Nothing is boxed and nothing is individually freed: the
6//! instructions are a flat vector, a reference to one is a four-byte index, and the whole
7//! function is dropped in one go. The same shape as the AST, for the same reasons.
8//!
9//! Two things are not flat, and both for the same reason, which is that SSA construction
10//! finishes a loop header long after it has built the blocks inside the loop.
11//!
12//! The instructions in a block are a doubly linked list rather than a run, because the
13//! optimizer inserts and removes instructions constantly and a run would move every
14//! instruction after the edit, invalidating every [`Inst`] anybody was holding.
15//!
16//! A block's parameters are a `Vec` rather than a run in a pool, because a run in a pool
17//! cannot grow once something else has been put after it, and adding a parameter to a loop
18//! header is exactly the operation that has to grow one.
19//!
20//! # CFG invariants
21//!
22//! The entry block has no predecessors and its parameters are the function's arguments in
23//! their C-level form, before the ABI has been applied. Every other block ends in exactly one
24//! terminator and contains no terminator anywhere else. These are checked by the verifier
25//! rather than by the builder, because a function under construction breaks all of them and
26//! the useful question is whether it still does when the pass that was building it says it has
27//! finished.
28
29use std::ops::{Index, IndexMut};
30
31use rucc_base::{Idx, Symbol};
32use rucc_diag::Span;
33use rucc_target::Slot;
34
35use crate::inst::{
36    Abi, AbiList, AsmInfo, Block, BlockCall, BlockCallList, BlockData, CallInfo, Def, Extra, Imm,
37    ImmList, Inst, InstData, InstLayout, MemInfo, Sig, Signature, SlotList, SwitchInfo, VaInfo,
38    Value, ValueData, ValueList,
39};
40use crate::module::{Linkage, Visibility};
41use crate::{Attrs, Flags, FloatPred, IntPred, Opcode, Type};
42
43/// One function.
44#[derive(Debug)]
45pub struct Func {
46    /// The name it is called by, which is what a direct call to it names.
47    pub name: Symbol,
48    /// How the linker sees it. `Internal` for a `static` function.
49    pub linkage: Linkage,
50    /// How the dynamic linker sees it.
51    pub visibility: Visibility,
52    /// The section to put it in, from `__attribute__((section(...)))`, or `None` to let the
53    /// object writer choose.
54    pub section: Option<Symbol>,
55    /// What its first instruction has to be aligned to, from `__attribute__((aligned(...)))`, or
56    /// `None` for the alignment the target gives every function anyway.
57    ///
58    /// A raise and never a lower, the way the attribute is everywhere: a function asked to be at
59    /// a multiple of two hundred and fifty six is at one, and one asked for less than the target's
60    /// own alignment keeps the target's.
61    pub align: Option<u32>,
62    /// What is true of the whole function, which is what a caller reads when it wants to know
63    /// what a call to it does without looking inside.
64    pub attrs: Attrs,
65
66    values: Vec<ValueData>,
67    insts: Vec<InstData>,
68    inst_layout: Vec<InstLayout>,
69    inst_spans: Vec<Span>,
70    blocks: Vec<BlockData>,
71
72    value_pool: Vec<Value>,
73    block_calls: Vec<BlockCall>,
74    imms: Vec<Imm>,
75    mem: Vec<MemInfo>,
76    calls: Vec<CallInfo>,
77    abis: Vec<Abi>,
78    switches: Vec<SwitchInfo>,
79    asms: Vec<AsmInfo>,
80    slots: Vec<Slot>,
81    va_objects: Vec<VaInfo>,
82    signatures: Vec<Signature>,
83
84    first_block: Option<Block>,
85    last_block: Option<Block>,
86}
87
88impl Func {
89    /// A function with that name and that signature, and nothing in it.
90    ///
91    /// The signature becomes signature zero, which is what [`Func::signature`] gives back. The
92    /// entry block is not created here, because the caller is about to create it and give it
93    /// the parameters, and a half-built entry block is worse than no entry block. So a
94    /// function fresh from here is a declaration, and stops being one when it gets a block.
95    #[must_use]
96    pub fn new(name: Symbol, signature: Signature) -> Self {
97        Self {
98            name,
99            linkage: Linkage::External,
100            visibility: Visibility::Default,
101            section: None,
102            align: None,
103            attrs: Attrs::NONE,
104            values: Vec::new(),
105            insts: Vec::new(),
106            inst_layout: Vec::new(),
107            inst_spans: Vec::new(),
108            blocks: Vec::new(),
109            value_pool: Vec::new(),
110            block_calls: Vec::new(),
111            imms: Vec::new(),
112            mem: Vec::new(),
113            calls: Vec::new(),
114            abis: Vec::new(),
115            switches: Vec::new(),
116            asms: Vec::new(),
117            slots: Vec::new(),
118            va_objects: Vec::new(),
119            signatures: vec![signature],
120            first_block: None,
121            last_block: None,
122        }
123    }
124
125    /// Its own signature.
126    #[must_use]
127    pub fn signature(&self) -> &Signature {
128        &self.signatures[0]
129    }
130
131    /// Every signature the function holds, its own first and then the ones its calls name.
132    pub fn signatures(&self) -> impl Iterator<Item = &Signature> {
133        self.signatures.iter()
134    }
135
136    /// Records a signature a `call_indirect` is made with, and gives back its index.
137    pub fn add_signature(&mut self, signature: Signature) -> Sig {
138        self.signatures.push(signature);
139        Idx::from_usize(self.signatures.len() - 1)
140    }
141
142    /// The entry block, which is the first one in layout order.
143    ///
144    /// `None` only before one has been created. The verifier is what insists a finished
145    /// function has one.
146    #[must_use]
147    pub fn entry(&self) -> Option<Block> {
148        self.first_block
149    }
150
151    /// Whether this only says the function exists somewhere, which is a function with no
152    /// blocks in it.
153    ///
154    /// `extern int puts(const char *);` and every other declaration of something defined in
155    /// another object is one of these, and it is here rather than left out of the module
156    /// because a call needs its signature and its linkage.
157    #[must_use]
158    pub fn is_declaration(&self) -> bool {
159        self.first_block.is_none()
160    }
161
162    // Blocks.
163
164    /// Creates a block with no parameters and no instructions, at the end of the layout.
165    pub fn create_block(&mut self) -> Block {
166        let block = Idx::from_usize(self.blocks.len());
167        self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
168        match self.last_block {
169            Some(last) => self.blocks[last.index()].next = Some(block),
170            None => self.first_block = Some(block),
171        }
172        self.last_block = Some(block);
173        block
174    }
175
176    /// Takes a block out of the layout, along with everything in it.
177    ///
178    /// The block keeps its number, the way a removed instruction keeps its own, because
179    /// renumbering would move every block after it and invalidate every index anybody was
180    /// holding. What it stops being is a block of this function: nothing walks it, nothing
181    /// prints it, and the values defined in it are as gone as the instructions that defined
182    /// them. Deleting one whose branches something still reaches is how a function ends up
183    /// branching to nowhere, so the caller is the one that has to know nothing reaches it.
184    ///
185    /// # Panics
186    ///
187    /// Panics if the block is the entry block, which is the one block a function has to have.
188    pub fn remove_block(&mut self, block: Block) {
189        assert!(self.first_block != Some(block), "the entry block is not removable");
190        let (prev, next) = (self.blocks[block.index()].prev, self.blocks[block.index()].next);
191        match prev {
192            Some(prev) => self.blocks[prev.index()].next = next,
193            None => self.first_block = next,
194        }
195        match next {
196            Some(next) => self.blocks[next.index()].prev = prev,
197            None => self.last_block = prev,
198        }
199        // The instructions say they are in no block now, which is what a removed instruction
200        // says, so that asking one where it is gives an answer rather than a block nothing
201        // walks.
202        let insts: Vec<Inst> = self.insts(block).collect();
203        for inst in insts {
204            self.inst_layout[inst.index()] = InstLayout::default();
205        }
206        self.blocks[block.index()] = BlockData::default();
207    }
208
209    /// Adds a parameter of that type to a block, and gives back the value it arrives as.
210    ///
211    /// Every predecessor's branch has to grow an argument to match, which is
212    /// [`Func::append_arg`], and the verifier is what notices if one of them did not.
213    ///
214    /// # Panics
215    ///
216    /// Panics if the block already has four billion parameters, which no block does.
217    pub fn append_param(&mut self, block: Block, ty: Type) -> Value {
218        let index = u32::try_from(self.blocks[block.index()].params.len())
219            .expect("a block with four billion parameters");
220        let value = self.add_value(ValueData { ty, def: Def::Param { block, index } });
221        self.blocks[block.index()].params.push(value);
222        value
223    }
224
225    /// Drops the parameters of a block that a predicate turns down, and renumbers the rest.
226    ///
227    /// The predicate is asked about each parameter in the order the block takes them. A
228    /// parameter that goes has to take the argument in the same position out of every branch
229    /// to the block, which is the caller's work rather than this method's, because only the
230    /// caller knows which branches there are. This is what removing a redundant block
231    /// parameter is, and SSA construction is the thing that makes them.
232    ///
233    /// # Panics
234    ///
235    /// Panics if the block has four billion parameters, which no block does.
236    pub fn retain_params(&mut self, block: Block, mut keep: impl FnMut(Value) -> bool) {
237        let mut params = std::mem::take(&mut self.blocks[block.index()].params);
238        params.retain(|&value| keep(value));
239        for (index, &value) in params.iter().enumerate() {
240            let index = u32::try_from(index).expect("a block with four billion parameters");
241            self.values[value.index()].def = Def::Param { block, index };
242        }
243        self.blocks[block.index()].params = params;
244    }
245
246    /// Every block, in layout order.
247    pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
248        std::iter::successors(self.first_block, move |&block| self.blocks[block.index()].next)
249    }
250
251    /// Every instruction in a block, in order.
252    pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
253        std::iter::successors(self.blocks[block.index()].first, move |&inst| {
254            self.inst_layout[inst.index()].next
255        })
256    }
257
258    /// The last instruction of a block, which is its terminator once it is finished.
259    #[must_use]
260    pub fn terminator(&self, block: Block) -> Option<Inst> {
261        self.blocks[block.index()].last.filter(|&inst| self.is_terminator(inst))
262    }
263
264    /// Whether control leaves the block at this instruction.
265    ///
266    /// A question for the function rather than for the instruction, because inline assembly is
267    /// the one case where the opcode is not enough: `asm goto` has labels and everything else
268    /// does not, and the labels are in the function's table rather than on the instruction.
269    #[must_use]
270    pub fn is_terminator(&self, inst: Inst) -> bool {
271        let data = &self[inst];
272        match data.extra {
273            Extra::Asm(info) => {
274                data.opcode.is_terminator() || !self.asms[info.index()].targets.is_empty()
275            }
276            _ => data.opcode.is_terminator(),
277        }
278    }
279
280    // Instructions.
281
282    /// Creates an instruction and its result values, without putting it in a block.
283    ///
284    /// The results are allocated here and are contiguous, which is what lets an instruction
285    /// hold the first of them and a count rather than a list.
286    ///
287    /// # Panics
288    ///
289    /// Panics if `results` has more than 255 types, which no instruction in the set does.
290    pub fn create_inst(&mut self, mut data: InstData, results: &[Type], span: Span) -> Inst {
291        let inst = Idx::from_usize(self.insts.len());
292        data.results = u8::try_from(results.len()).expect("an instruction with too many results");
293        data.first_result = results.first().map(|_| Idx::from_usize(self.values.len()));
294        for (index, &ty) in results.iter().enumerate() {
295            let index = u8::try_from(index).expect("checked just above");
296            self.add_value(ValueData { ty, def: Def::Result { inst, index } });
297        }
298        self.insts.push(data);
299        self.inst_layout.push(InstLayout::default());
300        self.inst_spans.push(span);
301        inst
302    }
303
304    /// Puts an instruction at the end of a block.
305    ///
306    /// # Panics
307    ///
308    /// Panics if the instruction is already in a block. Moving one is removing it and
309    /// appending it, and doing it by accident is how a linked list ends up in two pieces.
310    pub fn append_inst(&mut self, block: Block, inst: Inst) {
311        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
312        let last = self.blocks[block.index()].last;
313        self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
314        match last {
315            Some(last) => self.inst_layout[last.index()].next = Some(inst),
316            None => self.blocks[block.index()].first = Some(inst),
317        }
318        self.blocks[block.index()].last = Some(inst);
319    }
320
321    /// Puts an instruction immediately before another one, in the block that one is in.
322    ///
323    /// # Panics
324    ///
325    /// Panics if `inst` is already in a block, or if `before` is not in one.
326    pub fn insert_before(&mut self, inst: Inst, before: Inst) {
327        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
328        let at = self.inst_layout[before.index()];
329        let block = at.block.expect("the instruction to insert before is not in a block");
330        self.inst_layout[inst.index()] =
331            InstLayout { block: Some(block), prev: at.prev, next: Some(before) };
332        self.inst_layout[before.index()].prev = Some(inst);
333        match at.prev {
334            Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
335            None => self.blocks[block.index()].first = Some(inst),
336        }
337    }
338
339    /// Takes an instruction out of its block, leaving it and its results in the tables.
340    ///
341    /// The instruction is not deleted, because deleting it would move every instruction after
342    /// it. A removed instruction is unreachable from any block and is dropped when the whole
343    /// function is.
344    ///
345    /// # Panics
346    ///
347    /// Panics if the instruction is not in a block.
348    pub fn remove_inst(&mut self, inst: Inst) {
349        let at = self.inst_layout[inst.index()];
350        let block = at.block.expect("the instruction is not in a block");
351        match at.prev {
352            Some(prev) => self.inst_layout[prev.index()].next = at.next,
353            None => self.blocks[block.index()].first = at.next,
354        }
355        match at.next {
356            Some(next) => self.inst_layout[next.index()].prev = at.prev,
357            None => self.blocks[block.index()].last = at.prev,
358        }
359        self.inst_layout[inst.index()] = InstLayout::default();
360    }
361
362    /// The block an instruction is in, or `None` if it has been removed from one.
363    #[must_use]
364    pub fn block_of(&self, inst: Inst) -> Option<Block> {
365        self.inst_layout[inst.index()].block
366    }
367
368    /// Where an instruction came from in the source.
369    #[must_use]
370    pub fn span(&self, inst: Inst) -> Span {
371        self.inst_spans[inst.index()]
372    }
373
374    /// Where an instruction branches to, which is empty when it does not branch.
375    ///
376    /// This is the one place that knows a `switch` keeps its targets in a side table and
377    /// `asm goto` in another one, so nothing walking the CFG has to.
378    pub fn successors(&self, inst: Inst) -> impl Iterator<Item = BlockCall> + use<'_> {
379        self.block_calls[self.target_list(inst).as_usize_range()].iter().copied()
380    }
381
382    /// Where a terminator keeps its targets, for something that edits them rather than reads
383    /// them.
384    ///
385    /// [`Func::successors`] is what walking the CFG wants. This is what recording an edge
386    /// wants, because an edge that will grow an argument later has to be named by its place in
387    /// the table rather than by the block it went to.
388    #[must_use]
389    pub fn target_list(&self, inst: Inst) -> BlockCallList {
390        match self[inst].extra {
391            Extra::Targets(targets) => targets,
392            Extra::Switch(info) => self.switches[info.index()].targets,
393            Extra::Asm(info) => self.asms[info.index()].targets,
394            _ => BlockCallList::EMPTY,
395        }
396    }
397
398    // The pools.
399
400    /// Records a run of value operands.
401    pub fn push_values(&mut self, values: &[Value]) -> ValueList {
402        let start = Idx::from_usize(self.value_pool.len());
403        self.value_pool.extend_from_slice(values);
404        ValueList::new(start, Idx::from_usize(self.value_pool.len()))
405    }
406
407    /// Adds one value to the end of a run, giving back the run it became.
408    ///
409    /// The run grows in place when nothing has been put after it, which is the case while a
410    /// list is being built. Otherwise it is copied to the end and the old space is left
411    /// behind, which is what makes adding a parameter to a loop header possible at all. That
412    /// happens once per value carried around a loop, so the copying is not what costs.
413    pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList {
414        let range = list.as_usize_range();
415        if range.end == self.value_pool.len() {
416            self.value_pool.push(value);
417            return ValueList::new(Idx::from_usize(range.start), Idx::from_usize(range.end + 1));
418        }
419        let start = self.value_pool.len();
420        self.value_pool.extend_from_within(range);
421        self.value_pool.push(value);
422        ValueList::new(Idx::from_usize(start), Idx::from_usize(self.value_pool.len()))
423    }
424
425    /// Replaces the values in a run, which is what substituting one definition for another is.
426    ///
427    /// A run is a run whether it is an instruction's operands or a branch's arguments, so this
428    /// is the whole of the rewriting a substitution has to do.
429    pub fn rewrite(&mut self, list: ValueList, mut with: impl FnMut(Value) -> Value) {
430        for value in &mut self.value_pool[list.as_usize_range()] {
431            *value = with(*value);
432        }
433    }
434
435    /// Records a run of branch targets.
436    pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList {
437        let start = Idx::from_usize(self.block_calls.len());
438        self.block_calls.extend_from_slice(calls);
439        BlockCallList::new(start, Idx::from_usize(self.block_calls.len()))
440    }
441
442    /// Replaces one branch target, which is what redirecting an edge is.
443    pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall) {
444        self.block_calls[at.index()] = call;
445    }
446
447    /// Records a run of case values.
448    pub fn push_imms(&mut self, imms: &[Imm]) -> ImmList {
449        let start = Idx::from_usize(self.imms.len());
450        self.imms.extend_from_slice(imms);
451        ImmList::new(start, Idx::from_usize(self.imms.len()))
452    }
453
454    /// Records a constant.
455    pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
456        self.imms.push(imm);
457        Idx::from_usize(self.imms.len() - 1)
458    }
459
460    /// Records where each eightbyte of an object travelled.
461    pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList {
462        let start = Idx::from_usize(self.slots.len());
463        self.slots.extend_from_slice(slots);
464        SlotList::new(start, Idx::from_usize(self.slots.len()))
465    }
466
467    /// Records an object read off a variable argument list.
468    pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo> {
469        self.va_objects.push(info);
470        Idx::from_usize(self.va_objects.len() - 1)
471    }
472
473    /// Records what an access does.
474    pub fn add_mem(&mut self, info: MemInfo) -> Idx<MemInfo> {
475        self.mem.push(info);
476        Idx::from_usize(self.mem.len() - 1)
477    }
478
479    /// Records what the ABI asks of the arguments a call's signature does not name.
480    pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList {
481        let start = Idx::from_usize(self.abis.len());
482        self.abis.extend_from_slice(abis);
483        AbiList::new(start, Idx::from_usize(self.abis.len()))
484    }
485
486    /// Records a call's callee and signature.
487    pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo> {
488        self.calls.push(info);
489        Idx::from_usize(self.calls.len() - 1)
490    }
491
492    /// Records a `switch`'s targets and case values.
493    pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo> {
494        self.switches.push(info);
495        Idx::from_usize(self.switches.len() - 1)
496    }
497
498    /// Records an inline assembly instruction's template and constraints.
499    pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo> {
500        self.asms.push(info);
501        Idx::from_usize(self.asms.len() - 1)
502    }
503
504    /// How many values, instructions and blocks there are, for a reader that wants to size
505    /// something by them.
506    #[must_use]
507    pub fn counts(&self) -> Counts {
508        Counts { values: self.values.len(), insts: self.insts.len(), blocks: self.blocks.len() }
509    }
510
511    fn add_value(&mut self, data: ValueData) -> Value {
512        self.values.push(data);
513        Idx::from_usize(self.values.len() - 1)
514    }
515}
516
517/// How many of each thing a function holds.
518#[derive(Clone, Copy, Debug, PartialEq, Eq)]
519pub struct Counts {
520    /// Values, including the ones whose defining instruction has been removed.
521    pub values: usize,
522    /// Instructions, including the ones that have been removed from their block.
523    pub insts: usize,
524    /// Blocks.
525    pub blocks: usize,
526}
527
528// Reading is indexing. There is one of these for each handle, so `func[inst]` and `func[value]`
529// and `&func[args]` all work and none of them needs a method whose name says which table.
530impl Index<Value> for Func {
531    type Output = ValueData;
532
533    fn index(&self, value: Value) -> &ValueData {
534        &self.values[value.index()]
535    }
536}
537
538impl Index<Inst> for Func {
539    type Output = InstData;
540
541    fn index(&self, inst: Inst) -> &InstData {
542        &self.insts[inst.index()]
543    }
544}
545
546impl IndexMut<Inst> for Func {
547    fn index_mut(&mut self, inst: Inst) -> &mut InstData {
548        &mut self.insts[inst.index()]
549    }
550}
551
552impl Index<Block> for Func {
553    type Output = BlockData;
554
555    fn index(&self, block: Block) -> &BlockData {
556        &self.blocks[block.index()]
557    }
558}
559
560impl Index<Sig> for Func {
561    type Output = Signature;
562
563    fn index(&self, sig: Sig) -> &Signature {
564        &self.signatures[sig.index()]
565    }
566}
567
568impl Index<ValueList> for Func {
569    type Output = [Value];
570
571    fn index(&self, list: ValueList) -> &[Value] {
572        &self.value_pool[list.as_usize_range()]
573    }
574}
575
576impl Index<BlockCallList> for Func {
577    type Output = [BlockCall];
578
579    fn index(&self, list: BlockCallList) -> &[BlockCall] {
580        &self.block_calls[list.as_usize_range()]
581    }
582}
583
584impl Index<Idx<BlockCall>> for Func {
585    type Output = BlockCall;
586
587    fn index(&self, at: Idx<BlockCall>) -> &BlockCall {
588        &self.block_calls[at.index()]
589    }
590}
591
592impl Index<ImmList> for Func {
593    type Output = [Imm];
594
595    fn index(&self, list: ImmList) -> &[Imm] {
596        &self.imms[list.as_usize_range()]
597    }
598}
599
600impl Index<Idx<Imm>> for Func {
601    type Output = Imm;
602
603    fn index(&self, at: Idx<Imm>) -> &Imm {
604        &self.imms[at.index()]
605    }
606}
607
608impl Index<Idx<MemInfo>> for Func {
609    type Output = MemInfo;
610
611    fn index(&self, at: Idx<MemInfo>) -> &MemInfo {
612        &self.mem[at.index()]
613    }
614}
615
616impl Index<AbiList> for Func {
617    type Output = [Abi];
618
619    fn index(&self, list: AbiList) -> &[Abi] {
620        &self.abis[list.as_usize_range()]
621    }
622}
623
624impl Index<SlotList> for Func {
625    type Output = [Slot];
626
627    fn index(&self, list: SlotList) -> &[Slot] {
628        &self.slots[list.as_usize_range()]
629    }
630}
631
632impl Index<Idx<VaInfo>> for Func {
633    type Output = VaInfo;
634
635    fn index(&self, at: Idx<VaInfo>) -> &VaInfo {
636        &self.va_objects[at.index()]
637    }
638}
639
640impl Index<Idx<CallInfo>> for Func {
641    type Output = CallInfo;
642
643    fn index(&self, at: Idx<CallInfo>) -> &CallInfo {
644        &self.calls[at.index()]
645    }
646}
647
648impl Index<Idx<SwitchInfo>> for Func {
649    type Output = SwitchInfo;
650
651    fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo {
652        &self.switches[at.index()]
653    }
654}
655
656impl Index<Idx<AsmInfo>> for Func {
657    type Output = AsmInfo;
658
659    fn index(&self, at: Idx<AsmInfo>) -> &AsmInfo {
660        &self.asms[at.index()]
661    }
662}
663
664/// A cursor that appends to the end of one block.
665///
666/// This is the shape lowering wants: it works on one block at a time, it appends, and it wants
667/// the value back so it can use it in the next instruction. Everything here is a thin wrapper
668/// over [`Func::create_inst`] and [`Func::append_inst`], and anything the wrappers do not
669/// cover is done with those two directly.
670#[derive(Debug)]
671pub struct Builder<'a> {
672    func: &'a mut Func,
673    block: Block,
674    span: Span,
675}
676
677impl<'a> Builder<'a> {
678    /// A cursor appending to that block, with every instruction taking that source location.
679    pub fn new(func: &'a mut Func, block: Block) -> Self {
680        Self { func, block, span: Span::DUMMY }
681    }
682
683    /// The same cursor, with a source location for the instructions after this.
684    #[must_use]
685    pub fn at(mut self, span: Span) -> Self {
686        self.span = span;
687        self
688    }
689
690    /// Sets the source location for the instructions after this.
691    pub fn set_span(&mut self, span: Span) {
692        self.span = span;
693    }
694
695    /// The function being built.
696    pub fn func(&mut self) -> &mut Func {
697        self.func
698    }
699
700    /// The block being appended to.
701    #[must_use]
702    pub fn block(&self) -> Block {
703        self.block
704    }
705
706    /// Appends an instruction as it is, and gives back its results.
707    pub fn inst(&mut self, data: InstData, results: &[Type]) -> Inst {
708        let inst = self.func.create_inst(data, results, self.span);
709        self.func.append_inst(self.block, inst);
710        inst
711    }
712
713    /// The one value an instruction produces.
714    ///
715    /// # Panics
716    ///
717    /// Panics if it did not produce exactly one.
718    pub fn value(&mut self, data: InstData, ty: Type) -> Value {
719        let inst = self.inst(data, &[ty]);
720        self.func[inst].first_result.expect("one result was asked for")
721    }
722
723    /// An integer constant.
724    ///
725    /// # Panics
726    ///
727    /// Panics if `ty` is not an integer type.
728    pub fn iconst(&mut self, ty: Type, value: i128) -> Value {
729        let imm = self.func.add_imm(Imm::int(value, ty.lane()));
730        self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) }, ty)
731    }
732
733    /// A floating point constant, given as the bits of its format.
734    pub fn fconst(&mut self, ty: Type, bits: u128) -> Value {
735        let imm = self.func.add_imm(Imm::from_bits(bits));
736        self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::FConst) }, ty)
737    }
738
739    /// A two-operand instruction whose result has the type of its operands.
740    pub fn binary(&mut self, opcode: Opcode, lhs: Value, rhs: Value, flags: Flags) -> Value {
741        let ty = self.func[lhs].ty;
742        let args = self.func.push_values(&[lhs, rhs]);
743        self.value(InstData { args, flags, ..InstData::new(opcode) }, ty)
744    }
745
746    /// A one-operand instruction whose result has the type given.
747    pub fn unary(&mut self, opcode: Opcode, arg: Value, ty: Type) -> Value {
748        let args = self.func.push_values(&[arg]);
749        self.value(InstData { args, ..InstData::new(opcode) }, ty)
750    }
751
752    /// An integer comparison, which produces one `i1` per lane.
753    pub fn icmp(&mut self, pred: IntPred, lhs: Value, rhs: Value) -> Value {
754        let ty = self.func[lhs].ty.with_lane(Type::I1);
755        let args = self.func.push_values(&[lhs, rhs]);
756        self.value(
757            InstData { args, extra: Extra::IntPred(pred), ..InstData::new(Opcode::ICmp) },
758            ty,
759        )
760    }
761
762    /// A floating point comparison, which produces one `i1` per lane.
763    pub fn fcmp(&mut self, pred: FloatPred, lhs: Value, rhs: Value, flags: Flags) -> Value {
764        let ty = self.func[lhs].ty.with_lane(Type::I1);
765        let args = self.func.push_values(&[lhs, rhs]);
766        self.value(
767            InstData { args, flags, extra: Extra::FloatPred(pred), ..InstData::new(Opcode::FCmp) },
768            ty,
769        )
770    }
771
772    /// A read of that type from that address.
773    pub fn load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
774        let mem = self.func.add_mem(info);
775        let args = self.func.push_values(&[addr]);
776        self.value(
777            InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) },
778            ty,
779        )
780    }
781
782    /// A write of a value to an address.
783    pub fn store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
784        let mem = self.func.add_mem(info);
785        let args = self.func.push_values(&[value, addr]);
786        self.inst(
787            InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Store) },
788            &[],
789        )
790    }
791
792    /// An unconditional branch.
793    pub fn jump(&mut self, target: Block, args: &[Value]) -> Inst {
794        let call = self.block_call(target, args);
795        let targets = self.func.push_block_calls(&[call]);
796        self.inst(InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::Jump) }, &[])
797    }
798
799    /// The address of a block, which is a value a later `indirect_br` can branch to.
800    ///
801    /// The block is a target here in the same sense a branch's is, so everything that asks an
802    /// instruction which blocks it names finds this one, and a block whose address is taken is
803    /// not mistaken for a block nothing mentions.
804    pub fn block_addr(&mut self, target: Block) -> Value {
805        let call = self.block_call(target, &[]);
806        let targets = self.func.push_block_calls(&[call]);
807        self.value(
808            InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::BlockAddr) },
809            Type::PTR,
810        )
811    }
812
813    /// A branch to an address, which arrives at one of the blocks listed.
814    ///
815    /// Every block the address can hold has to be there. The list is what the rest of the
816    /// compiler reads, so a block left out of it is a block the branch is saying it never
817    /// reaches, and none of it is checked against the addresses anybody took.
818    pub fn indirect_br(&mut self, addr: Value, targets: &[Block]) -> Inst {
819        let calls: Vec<BlockCall> =
820            targets.iter().map(|&target| self.block_call(target, &[])).collect();
821        let targets = self.func.push_block_calls(&calls);
822        let args = self.func.push_values(&[addr]);
823        self.inst(
824            InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::IndirectBr) },
825            &[],
826        )
827    }
828
829    /// A two-way branch, taking the first target when the condition is one.
830    pub fn br_if(
831        &mut self,
832        cond: Value,
833        then_block: Block,
834        then_args: &[Value],
835        else_block: Block,
836        else_args: &[Value],
837    ) -> Inst {
838        let then_call = self.block_call(then_block, then_args);
839        let else_call = self.block_call(else_block, else_args);
840        let targets = self.func.push_block_calls(&[then_call, else_call]);
841        let args = self.func.push_values(&[cond]);
842        self.inst(
843            InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::BrIf) },
844            &[],
845        )
846    }
847
848    /// A branch on an integer, taking the target its value selects and the default when it
849    /// selects none.
850    ///
851    /// The cases are values and blocks rather than a table with the default in it, because the
852    /// order the side table wants, which is the default first, is not an order anybody building
853    /// a `switch` has their cases in.
854    pub fn switch(&mut self, value: Value, default: Block, cases: &[(i128, Block)]) -> Inst {
855        let ty = self.func[value].ty.lane();
856        let mut calls = vec![self.block_call(default, &[])];
857        let mut values = Vec::with_capacity(cases.len());
858        for &(value, block) in cases {
859            calls.push(self.block_call(block, &[]));
860            values.push(Imm::int(value, ty));
861        }
862        let targets = self.func.push_block_calls(&calls);
863        let cases = self.func.push_imms(&values);
864        let info = self.func.add_switch(SwitchInfo { targets, cases });
865        let args = self.func.push_values(&[value]);
866        self.inst(
867            InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) },
868            &[],
869        )
870    }
871
872    /// A return of the values the signature says.
873    pub fn ret(&mut self, values: &[Value]) -> Inst {
874        let args = self.func.push_values(values);
875        self.inst(InstData { args, ..InstData::new(Opcode::Return) }, &[])
876    }
877
878    /// A place control does not reach.
879    pub fn unreachable(&mut self) -> Inst {
880        self.inst(InstData::new(Opcode::Unreachable), &[])
881    }
882
883    /// A direct call, with the results its signature says it produces.
884    pub fn call(&mut self, callee: Symbol, signature: Sig, args: &[Value]) -> Inst {
885        self.call_varargs(callee, signature, args, &[])
886    }
887
888    /// The same, saying how the arguments the signature does not name travel.
889    ///
890    /// Empty says they all travel as the values in hand, which is what [`Builder::call`] passes
891    /// and is the usual case. Anything else has one entry for each argument past the ones the
892    /// signature names.
893    pub fn call_varargs(
894        &mut self,
895        callee: Symbol,
896        signature: Sig,
897        args: &[Value],
898        varargs: &[Abi],
899    ) -> Inst {
900        let varargs = self.func.push_abis(varargs);
901        let info = self.func.add_call(CallInfo { callee: Some(callee), signature, varargs });
902        let returns: Vec<Type> = self.func[signature].return_types().collect();
903        let args = self.func.push_values(args);
904        self.inst(
905            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) },
906            &returns,
907        )
908    }
909
910    /// Inline assembly, which is a terminator when the info carries targets.
911    ///
912    /// The targets are built by the caller, because the frontend is the only thing that knows
913    /// which block is the one control reaches when the assembly does not jump, and that block
914    /// has to come first.
915    pub fn inline_asm(
916        &mut self,
917        info: AsmInfo,
918        args: &[Value],
919        results: &[Type],
920        flags: Flags,
921    ) -> Inst {
922        let info = self.func.add_asm(info);
923        let args = self.func.push_values(args);
924        self.inst(
925            InstData { args, flags, extra: Extra::Asm(info), ..InstData::new(Opcode::InlineAsm) },
926            results,
927        )
928    }
929
930    fn block_call(&mut self, block: Block, args: &[Value]) -> BlockCall {
931        BlockCall { block, args: self.func.push_values(args) }
932    }
933}
934
935#[cfg(test)]
936mod tests {
937    use rucc_base::Interner;
938
939    use super::*;
940    use crate::MemOrder;
941    use crate::inst::BlockCallList;
942
943    /// The example from the spec, near enough: a loop that sums one to n and stores it.
944    fn sum() -> (Func, Block, Block, Block) {
945        let mut names = Interner::new();
946        let i32_ = Type::int(32);
947        let mut func = Func::new(
948            names.intern("sum"),
949            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
950        );
951
952        let entry = func.create_block();
953        let n = func.append_param(entry, i32_);
954        let header = func.create_block();
955        let acc = func.append_param(header, i32_);
956        let i = func.append_param(header, i32_);
957        let exit = func.create_block();
958        let result = func.append_param(exit, i32_);
959
960        let mut b = Builder::new(&mut func, entry);
961        let zero = b.iconst(i32_, 0);
962        let cmp = b.icmp(IntPred::Sle, n, zero);
963        b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
964
965        let mut b = Builder::new(&mut func, header);
966        let one = b.iconst(i32_, 1);
967        let next = b.binary(Opcode::Add, i, one, Flags::NSW);
968        let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
969        let done = b.icmp(IntPred::Sge, next, n);
970        b.br_if(done, exit, &[total], header, &[total, next]);
971
972        let mut b = Builder::new(&mut func, exit);
973        b.ret(&[result]);
974
975        (func, entry, header, exit)
976    }
977
978    #[test]
979    fn the_blocks_come_back_in_the_order_they_were_made() {
980        let (func, entry, header, exit) = sum();
981        assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, header, exit]);
982        assert_eq!(func.entry(), Some(entry));
983    }
984
985    #[test]
986    fn a_removed_block_is_gone_from_the_layout_and_so_is_what_was_in_it() {
987        let (mut func, entry, header, exit) = sum();
988        let inside: Vec<Inst> = func.insts(header).collect();
989        func.remove_block(header);
990        assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, exit]);
991        assert_eq!(func.entry(), Some(entry));
992        assert_eq!(func[entry].next, Some(exit));
993        assert_eq!(func[exit].prev, Some(entry));
994        // The instructions say they are in no block, the way a removed one does.
995        assert!(inside.iter().all(|&inst| func.block_of(inst).is_none()));
996        assert!(func.insts(header).next().is_none());
997    }
998
999    #[test]
1000    fn each_block_holds_what_was_appended_to_it() {
1001        let (func, entry, header, exit) = sum();
1002        let opcodes =
1003            |block| func.insts(block).map(|inst| func[inst].opcode.name()).collect::<Vec<_>>();
1004        assert_eq!(opcodes(entry), ["iconst", "icmp", "br_if"]);
1005        assert_eq!(opcodes(header), ["iconst", "add", "add", "icmp", "br_if"]);
1006        assert_eq!(opcodes(exit), ["return"]);
1007    }
1008
1009    #[test]
1010    fn asm_ends_a_block_when_it_has_labels_and_not_otherwise() {
1011        // The labels are in the function's table, so the instruction on its own cannot answer
1012        // and anything asking it rather than the function would walk off the end of the block.
1013        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1014        let block = func.create_block();
1015        let plain = func.add_asm(AsmInfo {
1016            template: Symbol::from_raw(0),
1017            constraints: Symbol::from_raw(0),
1018            clobbers: Symbol::from_raw(0),
1019            targets: BlockCallList::EMPTY,
1020        });
1021        let call = BlockCall { block, args: ValueList::EMPTY };
1022        let targets = func.push_block_calls(&[call]);
1023        let labelled = func.add_asm(AsmInfo {
1024            template: Symbol::from_raw(0),
1025            constraints: Symbol::from_raw(0),
1026            clobbers: Symbol::from_raw(0),
1027            targets,
1028        });
1029
1030        let mut make = |extra| {
1031            let data = InstData { extra, ..InstData::new(Opcode::InlineAsm) };
1032            func.create_inst(data, &[], Span::DUMMY)
1033        };
1034        let plain = make(Extra::Asm(plain));
1035        let labelled = make(Extra::Asm(labelled));
1036        assert!(!func.is_terminator(plain));
1037        assert!(func.is_terminator(labelled));
1038    }
1039
1040    #[test]
1041    fn every_block_ends_in_its_terminator() {
1042        let (func, entry, header, exit) = sum();
1043        for block in [entry, header, exit] {
1044            let last = func.terminator(block).expect("a terminator");
1045            assert_eq!(Some(last), func.insts(block).last());
1046        }
1047    }
1048
1049    #[test]
1050    fn a_branch_carries_the_arguments_the_block_takes() {
1051        let (func, entry, header, _) = sum();
1052        let br = func.terminator(entry).expect("a terminator");
1053        let calls: Vec<BlockCall> = func.successors(br).collect();
1054        assert_eq!(calls.len(), 2);
1055        // The loop header takes two parameters, so the branch to it passes two.
1056        assert_eq!(calls[1].block, header);
1057        assert_eq!(func[calls[1].args].len(), 2);
1058        assert_eq!(func[header].params.len(), 2);
1059        assert_eq!(func[calls[0].args].len(), 1);
1060    }
1061
1062    #[test]
1063    fn a_value_knows_what_defined_it() {
1064        let (func, entry, _, _) = sum();
1065        let first = func.insts(entry).next().expect("an instruction");
1066        let value = func[first].first_result.expect("a result");
1067        assert_eq!(func[value].def, Def::Result { inst: first, index: 0 });
1068        assert_eq!(func[value].ty, Type::int(32));
1069
1070        let param = func[entry].params[0];
1071        assert_eq!(func[param].def, Def::Param { block: entry, index: 0 });
1072    }
1073
1074    #[test]
1075    fn a_comparison_produces_one_bit() {
1076        let (func, entry, _, _) = sum();
1077        let cmp = func.insts(entry).nth(1).expect("the comparison");
1078        let value = func[cmp].first_result.expect("a result");
1079        assert_eq!(func[value].ty, Type::I1);
1080        assert_eq!(func[cmp].extra, Extra::IntPred(IntPred::Sle));
1081    }
1082
1083    #[test]
1084    fn flags_ride_along_on_the_instruction_that_was_given_them() {
1085        let (func, _, header, _) = sum();
1086        let add = func.insts(header).nth(1).expect("the addition");
1087        assert_eq!(func[add].flags, Flags::NSW);
1088        let cmp = func.insts(header).nth(3).expect("the comparison");
1089        assert_eq!(func[cmp].flags, Flags::NONE);
1090    }
1091
1092    #[test]
1093    fn removing_an_instruction_takes_it_out_of_the_middle() {
1094        let (mut func, _, header, _) = sum();
1095        let add = func.insts(header).nth(1).expect("the addition");
1096        func.remove_inst(add);
1097        let opcodes: Vec<&str> = func.insts(header).map(|inst| func[inst].opcode.name()).collect();
1098        assert_eq!(opcodes, ["iconst", "add", "icmp", "br_if"]);
1099        assert_eq!(func.block_of(add), None);
1100    }
1101
1102    #[test]
1103    fn removing_the_first_and_the_last_keeps_the_ends_right() {
1104        let (mut func, entry, _, _) = sum();
1105        let first = func.insts(entry).next().expect("an instruction");
1106        let last = func.terminator(entry).expect("a terminator");
1107        func.remove_inst(first);
1108        func.remove_inst(last);
1109        let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1110        assert_eq!(opcodes, ["icmp"]);
1111        assert_eq!(func[entry].first, func[entry].last);
1112    }
1113
1114    #[test]
1115    fn removing_the_only_instruction_empties_the_block() {
1116        let (mut func, _, _, exit) = sum();
1117        let only = func.insts(exit).next().expect("an instruction");
1118        func.remove_inst(only);
1119        assert_eq!(func.insts(exit).count(), 0);
1120        assert_eq!(func[exit].first, None);
1121        assert_eq!(func[exit].last, None);
1122    }
1123
1124    #[test]
1125    fn inserting_before_puts_it_in_the_right_place() {
1126        let (mut func, entry, _, _) = sum();
1127        let cmp = func.insts(entry).nth(1).expect("the comparison");
1128        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1129        func.insert_before(made, cmp);
1130        let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1131        assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1132    }
1133
1134    #[test]
1135    fn inserting_before_the_first_makes_it_the_first() {
1136        let (mut func, entry, _, _) = sum();
1137        let first = func.insts(entry).next().expect("an instruction");
1138        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1139        func.insert_before(made, first);
1140        assert_eq!(func.insts(entry).next(), Some(made));
1141        assert_eq!(func[entry].first, Some(made));
1142    }
1143
1144    #[test]
1145    fn a_list_grows_in_place_while_it_is_the_last_thing_in_the_pool() {
1146        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1147        let block = func.create_block();
1148        let a = func.append_param(block, Type::int(32));
1149        let b = func.append_param(block, Type::int(32));
1150        let list = func.push_values(&[a]);
1151        let grown = func.append_arg(list, b);
1152        assert_eq!(func[grown], [a, b]);
1153        assert_eq!(grown.as_usize_range().start, list.as_usize_range().start);
1154    }
1155
1156    #[test]
1157    fn a_list_is_copied_when_something_is_behind_it() {
1158        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1159        let block = func.create_block();
1160        let a = func.append_param(block, Type::int(32));
1161        let b = func.append_param(block, Type::int(32));
1162        let list = func.push_values(&[a, a]);
1163        let behind = func.push_values(&[b]);
1164        let grown = func.append_arg(list, b);
1165        assert_eq!(func[grown], [a, a, b]);
1166        assert_eq!(func[list], [a, a], "the old run is still readable");
1167        assert_eq!(func[behind], [b], "and so is what was behind it");
1168        assert_ne!(grown.as_usize_range().start, list.as_usize_range().start);
1169    }
1170
1171    #[test]
1172    fn a_parameter_added_late_is_the_next_one_along() {
1173        // This is the shape SSA construction leaves: the loop header gains a parameter after
1174        // the blocks that branch to it already exist, and each of their branches grows an
1175        // argument to match.
1176        let (mut func, entry, header, _) = sum();
1177        let extra = func.append_param(header, Type::int(32));
1178        assert_eq!(func[header].params.len(), 3);
1179        assert_eq!(func[extra].def, Def::Param { block: header, index: 2 });
1180
1181        let br = func.terminator(entry).expect("a terminator");
1182        let call = func.successors(br).nth(1).expect("the branch to the header");
1183        let grown = func.append_arg(call.args, extra);
1184        assert_eq!(func[grown].len(), 3);
1185    }
1186
1187    #[test]
1188    fn a_span_rides_along_with_the_instruction() {
1189        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1190        let block = func.create_block();
1191        let span = Span::new(10, 20);
1192        let mut b = Builder::new(&mut func, block).at(span);
1193        let value = b.iconst(Type::int(32), 7);
1194        let inst = match func[value].def {
1195            Def::Result { inst, .. } => inst,
1196            Def::Param { .. } => unreachable!("a constant is not a parameter"),
1197        };
1198        assert_eq!(func.span(inst), span);
1199    }
1200
1201    #[test]
1202    fn a_store_produces_nothing_and_a_load_produces_one_value() {
1203        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1204        let block = func.create_block();
1205        let addr = func.append_param(block, Type::PTR);
1206        let info = MemInfo { size: 4, align: 4, order: MemOrder::NotAtomic, tbaa: None };
1207        let mut b = Builder::new(&mut func, block);
1208        let value = b.load(Type::int(32), addr, info, Flags::NONE);
1209        let store = b.store(value, addr, info, Flags::VOLATILE);
1210        assert_eq!(func[store].results, 0);
1211        assert_eq!(func[store].flags, Flags::VOLATILE);
1212        assert_eq!(func[value].ty, Type::int(32));
1213    }
1214
1215    #[test]
1216    fn a_call_produces_what_its_signature_returns() {
1217        let mut names = Interner::new();
1218        let mut func = Func::new(names.intern("caller"), Signature::new());
1219        let sig = func.add_signature(
1220            Signature::new().with_params(&[Type::int(32)]).with_returns(&[Type::int(64)]),
1221        );
1222        let block = func.create_block();
1223        let arg = func.append_param(block, Type::int(32));
1224        let callee = names.intern("callee");
1225        let mut b = Builder::new(&mut func, block);
1226        let call = b.call(callee, sig, &[arg]);
1227        assert_eq!(func[call].results, 1);
1228        let value = func[call].first_result.expect("a result");
1229        assert_eq!(func[value].ty, Type::int(64));
1230        assert_eq!(func[call].extra, Extra::Call(Idx::new(0)));
1231    }
1232
1233    #[test]
1234    fn the_counts_are_what_was_made() {
1235        let (func, _, _, _) = sum();
1236        let counts = func.counts();
1237        assert_eq!(counts.blocks, 3);
1238        assert_eq!(counts.insts, 9);
1239        // Four block parameters and five instruction results, which is the two constants, the
1240        // two additions and the two comparisons less the branches, which produce nothing.
1241        assert_eq!(counts.values, 4 + 6);
1242    }
1243
1244    #[test]
1245    #[should_panic(expected = "the instruction is in a block")]
1246    fn appending_an_instruction_twice_is_refused() {
1247        let (mut func, entry, _, _) = sum();
1248        let first = func.insts(entry).next().expect("an instruction");
1249        func.append_inst(entry, first);
1250    }
1251
1252    #[test]
1253    #[should_panic(expected = "the instruction is not in a block")]
1254    fn removing_an_instruction_twice_is_refused() {
1255        let (mut func, entry, _, _) = sum();
1256        let first = func.insts(entry).next().expect("an instruction");
1257        func.remove_inst(first);
1258        func.remove_inst(first);
1259    }
1260}