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, Facts, 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    facts: Vec<(Value, Facts)>,
84
85    first_block: Option<Block>,
86    last_block: Option<Block>,
87}
88
89impl Func {
90    /// A function with that name and that signature, and nothing in it.
91    ///
92    /// The signature becomes signature zero, which is what [`Func::signature`] gives back. The
93    /// entry block is not created here, because the caller is about to create it and give it
94    /// the parameters, and a half-built entry block is worse than no entry block. So a
95    /// function fresh from here is a declaration, and stops being one when it gets a block.
96    #[must_use]
97    pub fn new(name: Symbol, signature: Signature) -> Self {
98        Self {
99            name,
100            linkage: Linkage::External,
101            visibility: Visibility::Default,
102            section: None,
103            align: None,
104            attrs: Attrs::NONE,
105            values: Vec::new(),
106            insts: Vec::new(),
107            inst_layout: Vec::new(),
108            inst_spans: Vec::new(),
109            blocks: Vec::new(),
110            value_pool: Vec::new(),
111            block_calls: Vec::new(),
112            imms: Vec::new(),
113            mem: Vec::new(),
114            calls: Vec::new(),
115            abis: Vec::new(),
116            switches: Vec::new(),
117            asms: Vec::new(),
118            slots: Vec::new(),
119            va_objects: Vec::new(),
120            signatures: vec![signature],
121            facts: Vec::new(),
122            first_block: None,
123            last_block: None,
124        }
125    }
126
127    /// Its own signature.
128    #[must_use]
129    pub fn signature(&self) -> &Signature {
130        &self.signatures[0]
131    }
132
133    /// Every signature the function holds, its own first and then the ones its calls name.
134    pub fn signatures(&self) -> impl Iterator<Item = &Signature> {
135        self.signatures.iter()
136    }
137
138    /// Records a signature a `call_indirect` is made with, and gives back its index.
139    pub fn add_signature(&mut self, signature: Signature) -> Sig {
140        self.signatures.push(signature);
141        Idx::from_usize(self.signatures.len() - 1)
142    }
143
144    /// The entry block, which is the first one in layout order.
145    ///
146    /// `None` only before one has been created. The verifier is what insists a finished
147    /// function has one.
148    #[must_use]
149    pub fn entry(&self) -> Option<Block> {
150        self.first_block
151    }
152
153    /// Whether this only says the function exists somewhere, which is a function with no
154    /// blocks in it.
155    ///
156    /// `extern int puts(const char *);` and every other declaration of something defined in
157    /// another object is one of these, and it is here rather than left out of the module
158    /// because a call needs its signature and its linkage.
159    #[must_use]
160    pub fn is_declaration(&self) -> bool {
161        self.first_block.is_none()
162    }
163
164    // Blocks.
165
166    /// Creates a block with no parameters and no instructions, at the end of the layout.
167    pub fn create_block(&mut self) -> Block {
168        let block = Idx::from_usize(self.blocks.len());
169        self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
170        match self.last_block {
171            Some(last) => self.blocks[last.index()].next = Some(block),
172            None => self.first_block = Some(block),
173        }
174        self.last_block = Some(block);
175        block
176    }
177
178    /// Takes a block out of the layout, along with everything in it.
179    ///
180    /// The block keeps its number, the way a removed instruction keeps its own, because
181    /// renumbering would move every block after it and invalidate every index anybody was
182    /// holding. What it stops being is a block of this function: nothing walks it, nothing
183    /// prints it, and the values defined in it are as gone as the instructions that defined
184    /// them. Deleting one whose branches something still reaches is how a function ends up
185    /// branching to nowhere, so the caller is the one that has to know nothing reaches it.
186    ///
187    /// # Panics
188    ///
189    /// Panics if the block is the entry block, which is the one block a function has to have.
190    pub fn remove_block(&mut self, block: Block) {
191        assert!(self.first_block != Some(block), "the entry block is not removable");
192        let (prev, next) = (self.blocks[block.index()].prev, self.blocks[block.index()].next);
193        match prev {
194            Some(prev) => self.blocks[prev.index()].next = next,
195            None => self.first_block = next,
196        }
197        match next {
198            Some(next) => self.blocks[next.index()].prev = prev,
199            None => self.last_block = prev,
200        }
201        // The instructions say they are in no block now, which is what a removed instruction
202        // says, so that asking one where it is gives an answer rather than a block nothing
203        // walks.
204        let insts: Vec<Inst> = self.insts(block).collect();
205        for inst in insts {
206            self.inst_layout[inst.index()] = InstLayout::default();
207        }
208        self.blocks[block.index()] = BlockData::default();
209    }
210
211    /// Adds a parameter of that type to a block, and gives back the value it arrives as.
212    ///
213    /// Every predecessor's branch has to grow an argument to match, which is
214    /// [`Func::append_arg`], and the verifier is what notices if one of them did not.
215    ///
216    /// # Panics
217    ///
218    /// Panics if the block already has four billion parameters, which no block does.
219    pub fn append_param(&mut self, block: Block, ty: Type) -> Value {
220        let index = u32::try_from(self.blocks[block.index()].params.len())
221            .expect("a block with four billion parameters");
222        let value = self.add_value(ValueData { ty, def: Def::Param { block, index } });
223        self.blocks[block.index()].params.push(value);
224        value
225    }
226
227    /// Drops the parameters of a block that a predicate turns down, and renumbers the rest.
228    ///
229    /// The predicate is asked about each parameter in the order the block takes them. A
230    /// parameter that goes has to take the argument in the same position out of every branch
231    /// to the block, which is the caller's work rather than this method's, because only the
232    /// caller knows which branches there are. This is what removing a redundant block
233    /// parameter is, and SSA construction is the thing that makes them.
234    ///
235    /// # Panics
236    ///
237    /// Panics if the block has four billion parameters, which no block does.
238    pub fn retain_params(&mut self, block: Block, mut keep: impl FnMut(Value) -> bool) {
239        let mut params = std::mem::take(&mut self.blocks[block.index()].params);
240        params.retain(|&value| keep(value));
241        for (index, &value) in params.iter().enumerate() {
242            let index = u32::try_from(index).expect("a block with four billion parameters");
243            self.values[value.index()].def = Def::Param { block, index };
244        }
245        self.blocks[block.index()].params = params;
246    }
247
248    /// Gives a value a different type, leaving where it comes from alone.
249    ///
250    /// There is one caller and it is the back end pass that puts an integer of a width the
251    /// machine has no register for into the width it does have one for. Nothing in the middle
252    /// end changes a value's type, because a value's type is what the instruction that made it
253    /// produces and changing one without changing the other is how an IR stops meaning
254    /// anything. That pass changes both, which is why this is a method and not a field.
255    ///
256    /// # Panics
257    ///
258    /// Panics if the value is not one of this function's.
259    pub fn retype(&mut self, value: Value, ty: Type) {
260        self.values[value.index()].ty = ty;
261    }
262
263    /// Every value the function has, including ones whose defining instruction has gone.
264    ///
265    /// In the order they were created, which is the order a pass that walks all of them wants:
266    /// a value is defined before it is used, so a walk in this order sees a definition first.
267    pub fn values(&self) -> impl Iterator<Item = Value> + use<'_> {
268        (0..self.values.len()).map(Idx::from_usize)
269    }
270
271    /// Every block, in layout order.
272    pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
273        std::iter::successors(self.first_block, move |&block| self.blocks[block.index()].next)
274    }
275
276    /// Every instruction in a block, in order.
277    pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
278        std::iter::successors(self.blocks[block.index()].first, move |&inst| {
279            self.inst_layout[inst.index()].next
280        })
281    }
282
283    /// Every instruction in a block, last first.
284    ///
285    /// Which is the order a liveness walk needs, and it is here rather than at the caller because
286    /// the layout links are private and collecting the block into a vector to reverse it is an
287    /// allocation per block per round of a fixpoint.
288    pub fn insts_backwards(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
289        std::iter::successors(self.blocks[block.index()].last, move |&inst| {
290            self.inst_layout[inst.index()].prev
291        })
292    }
293
294    /// The last instruction of a block, which is its terminator once it is finished.
295    #[must_use]
296    pub fn terminator(&self, block: Block) -> Option<Inst> {
297        self.blocks[block.index()].last.filter(|&inst| self.is_terminator(inst))
298    }
299
300    /// Whether control leaves the block at this instruction.
301    ///
302    /// A question for the function rather than for the instruction, because inline assembly is
303    /// the one case where the opcode is not enough: `asm goto` has labels and everything else
304    /// does not, and the labels are in the function's table rather than on the instruction.
305    #[must_use]
306    pub fn is_terminator(&self, inst: Inst) -> bool {
307        let data = &self[inst];
308        match data.extra {
309            Extra::Asm(info) => {
310                data.opcode.is_terminator() || !self.asms[info.index()].targets.is_empty()
311            }
312            _ => data.opcode.is_terminator(),
313        }
314    }
315
316    // Instructions.
317
318    /// Creates an instruction and its result values, without putting it in a block.
319    ///
320    /// The results are allocated here and are contiguous, which is what lets an instruction
321    /// hold the first of them and a count rather than a list.
322    ///
323    /// # Panics
324    ///
325    /// Panics if `results` has more than 255 types, which no instruction in the set does.
326    pub fn create_inst(&mut self, mut data: InstData, results: &[Type], span: Span) -> Inst {
327        let inst = Idx::from_usize(self.insts.len());
328        data.results = u8::try_from(results.len()).expect("an instruction with too many results");
329        data.first_result = results.first().map(|_| Idx::from_usize(self.values.len()));
330        for (index, &ty) in results.iter().enumerate() {
331            let index = u8::try_from(index).expect("checked just above");
332            self.add_value(ValueData { ty, def: Def::Result { inst, index } });
333        }
334        self.insts.push(data);
335        self.inst_layout.push(InstLayout::default());
336        self.inst_spans.push(span);
337        inst
338    }
339
340    /// Puts an instruction at the end of a block.
341    ///
342    /// # Panics
343    ///
344    /// Panics if the instruction is already in a block. Moving one is removing it and
345    /// appending it, and doing it by accident is how a linked list ends up in two pieces.
346    pub fn append_inst(&mut self, block: Block, inst: Inst) {
347        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
348        let last = self.blocks[block.index()].last;
349        self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
350        match last {
351            Some(last) => self.inst_layout[last.index()].next = Some(inst),
352            None => self.blocks[block.index()].first = Some(inst),
353        }
354        self.blocks[block.index()].last = Some(inst);
355    }
356
357    /// Puts an instruction immediately before another one, in the block that one is in.
358    ///
359    /// # Panics
360    ///
361    /// Panics if `inst` is already in a block, or if `before` is not in one.
362    pub fn insert_before(&mut self, inst: Inst, before: Inst) {
363        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
364        let at = self.inst_layout[before.index()];
365        let block = at.block.expect("the instruction to insert before is not in a block");
366        self.inst_layout[inst.index()] =
367            InstLayout { block: Some(block), prev: at.prev, next: Some(before) };
368        self.inst_layout[before.index()].prev = Some(inst);
369        match at.prev {
370            Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
371            None => self.blocks[block.index()].first = Some(inst),
372        }
373    }
374
375    /// Puts an instruction immediately after another one, in the block that one is in.
376    ///
377    /// The mirror of [`Func::insert_before`], and it exists because a pass that has to talk about
378    /// a value an instruction produced has nowhere else to put what it is adding. Check insertion
379    /// is the caller: `check_deriv` is handed the pointer the derivation produced, so it goes
380    /// after the derivation and no amount of rearranging moves it earlier.
381    ///
382    /// # Panics
383    ///
384    /// Panics if `inst` is already in a block, if `after` is not in one, or if `after` is the
385    /// block's terminator, since nothing may come between a terminator and the branch it is.
386    pub fn insert_after(&mut self, inst: Inst, after: Inst) {
387        assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
388        let at = self.inst_layout[after.index()];
389        let block = at.block.expect("the instruction to insert after is not in a block");
390        assert!(at.next.is_some(), "nothing goes after a terminator");
391        self.inst_layout[inst.index()] =
392            InstLayout { block: Some(block), prev: Some(after), next: at.next };
393        self.inst_layout[after.index()].next = Some(inst);
394        if let Some(next) = at.next {
395            self.inst_layout[next.index()].prev = Some(inst);
396        }
397    }
398
399    /// Takes an instruction out of its block, leaving it and its results in the tables.
400    ///
401    /// The instruction is not deleted, because deleting it would move every instruction after
402    /// it. A removed instruction is unreachable from any block and is dropped when the whole
403    /// function is.
404    ///
405    /// # Panics
406    ///
407    /// Panics if the instruction is not in a block.
408    pub fn remove_inst(&mut self, inst: Inst) {
409        let at = self.inst_layout[inst.index()];
410        let block = at.block.expect("the instruction is not in a block");
411        match at.prev {
412            Some(prev) => self.inst_layout[prev.index()].next = at.next,
413            None => self.blocks[block.index()].first = at.next,
414        }
415        match at.next {
416            Some(next) => self.inst_layout[next.index()].prev = at.prev,
417            None => self.blocks[block.index()].last = at.prev,
418        }
419        self.inst_layout[inst.index()] = InstLayout::default();
420    }
421
422    /// The block an instruction is in, or `None` if it has been removed from one.
423    #[must_use]
424    pub fn block_of(&self, inst: Inst) -> Option<Block> {
425        self.inst_layout[inst.index()].block
426    }
427
428    /// The version of memory an instruction reads, when the function carries memory SSA.
429    ///
430    /// Document 09 of `spec/optimizer`. Memory is a value of type `mem`, it is the last operand
431    /// of every instruction that touches memory, and it is absent in a function that does not
432    /// carry it, which is what `-O0` and `-O1` produce. Absent means unordered with respect to
433    /// everything, so a reader that gets `None` asks the alias analysis directly.
434    ///
435    /// The operand is last rather than first on purpose. Every other operand keeps the position
436    /// it had, so a pass that reads the address of a load as `args[0]` goes on working whether
437    /// or not memory has been threaded, and the only code that has to know about the extra
438    /// operand is this accessor and the verifier.
439    #[must_use]
440    pub fn mem_in(&self, inst: Inst) -> Option<Value> {
441        let args = &self[self[inst].args];
442        args.last().copied().filter(|&arg| self[arg].ty.is_mem())
443    }
444
445    /// The version of memory an instruction produces, when it writes memory and the function
446    /// carries memory SSA.
447    ///
448    /// Last among the results, for the reason [`Func::mem_in`] is last among the operands. A
449    /// `load` never has one, because it reads memory without changing it.
450    ///
451    /// Nothing reads the last version in a function, and that means nothing. A store whose
452    /// memory result has no reader is not dead, and what decides whether it is dead is dead
453    /// store elimination, which is document 17's.
454    #[must_use]
455    pub fn mem_out(&self, inst: Inst) -> Option<Value> {
456        self[inst].results().last().filter(|&result| self[result].ty.is_mem())
457    }
458
459    /// Whether an instruction has been threaded onto the memory chain.
460    #[must_use]
461    pub fn carries_mem(&self, inst: Inst) -> bool {
462        self.mem_in(inst).is_some() || self.mem_out(inst).is_some()
463    }
464
465    /// The same instruction with a version of memory threaded through it.
466    ///
467    /// A result cannot be added to an instruction that already exists, because the results of one
468    /// are values next to each other and there is no room after them. So threading memory makes a
469    /// new instruction and the caller puts it where the old one was, forwards the old results to
470    /// the new ones, which are at the same positions, and deletes the old one. That is what memory
471    /// SSA construction does in one pass over the function.
472    ///
473    /// The new instruction is not in any block. Its results are what the old one produced, in the
474    /// same order, and then the new version of memory where the opcode writes memory.
475    ///
476    /// # Panics
477    ///
478    /// Panics if `incoming` is not memory, if the instruction does not touch memory, or if it is
479    /// already on the chain. All three are a construction bug rather than bad input.
480    pub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst {
481        assert!(self[incoming].ty.is_mem(), "the incoming version of memory is not memory");
482        assert!(self[inst].opcode.touches_memory(), "this does not touch memory");
483        assert!(self.mem_in(inst).is_none(), "this is already on the memory chain");
484        let data = self[inst];
485        let mut args = self[data.args].to_vec();
486        args.push(incoming);
487        let mut results: Vec<Type> = data.results().map(|result| self[result].ty).collect();
488        if data.opcode.writes_memory() {
489            results.push(Type::MEM);
490        }
491        let span = self.span(inst);
492        let args = self.push_values(&args);
493        self.create_inst(InstData { args, ..data }, &results, span)
494    }
495
496    /// Where an instruction came from in the source.
497    #[must_use]
498    pub fn span(&self, inst: Inst) -> Span {
499        self.inst_spans[inst.index()]
500    }
501
502    /// Where an instruction branches to, which is empty when it does not branch.
503    ///
504    /// This is the one place that knows a `switch` keeps its targets in a side table and
505    /// `asm goto` in another one, so nothing walking the CFG has to.
506    pub fn successors(&self, inst: Inst) -> impl Iterator<Item = BlockCall> + use<'_> {
507        self.block_calls[self.target_list(inst).as_usize_range()].iter().copied()
508    }
509
510    /// Where a terminator keeps its targets, for something that edits them rather than reads
511    /// them.
512    ///
513    /// [`Func::successors`] is what walking the CFG wants. This is what recording an edge
514    /// wants, because an edge that will grow an argument later has to be named by its place in
515    /// the table rather than by the block it went to.
516    #[must_use]
517    pub fn target_list(&self, inst: Inst) -> BlockCallList {
518        match self[inst].extra {
519            Extra::Targets(targets) => targets,
520            Extra::Switch(info) => self.switches[info.index()].targets,
521            Extra::Asm(info) => self.asms[info.index()].targets,
522            _ => BlockCallList::EMPTY,
523        }
524    }
525
526    // The pools.
527
528    /// Records a run of value operands.
529    pub fn push_values(&mut self, values: &[Value]) -> ValueList {
530        let start = Idx::from_usize(self.value_pool.len());
531        self.value_pool.extend_from_slice(values);
532        ValueList::new(start, Idx::from_usize(self.value_pool.len()))
533    }
534
535    /// Adds one value to the end of a run, giving back the run it became.
536    ///
537    /// The run grows in place when nothing has been put after it, which is the case while a
538    /// list is being built. Otherwise it is copied to the end and the old space is left
539    /// behind, which is what makes adding a parameter to a loop header possible at all. That
540    /// happens once per value carried around a loop, so the copying is not what costs.
541    pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList {
542        let range = list.as_usize_range();
543        if range.end == self.value_pool.len() {
544            self.value_pool.push(value);
545            return ValueList::new(Idx::from_usize(range.start), Idx::from_usize(range.end + 1));
546        }
547        let start = self.value_pool.len();
548        self.value_pool.extend_from_within(range);
549        self.value_pool.push(value);
550        ValueList::new(Idx::from_usize(start), Idx::from_usize(self.value_pool.len()))
551    }
552
553    /// Replaces the values in a run, which is what substituting one definition for another is.
554    ///
555    /// A run is a run whether it is an instruction's operands or a branch's arguments, so this
556    /// is the whole of the rewriting a substitution has to do.
557    pub fn rewrite(&mut self, list: ValueList, mut with: impl FnMut(Value) -> Value) {
558        for value in &mut self.value_pool[list.as_usize_range()] {
559            *value = with(*value);
560        }
561    }
562
563    /// Records a run of branch targets.
564    pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList {
565        let start = Idx::from_usize(self.block_calls.len());
566        self.block_calls.extend_from_slice(calls);
567        BlockCallList::new(start, Idx::from_usize(self.block_calls.len()))
568    }
569
570    /// Replaces one branch target, which is what redirecting an edge is.
571    pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall) {
572        self.block_calls[at.index()] = call;
573    }
574
575    /// Records a run of case values.
576    pub fn push_imms(&mut self, imms: &[Imm]) -> ImmList {
577        let start = Idx::from_usize(self.imms.len());
578        self.imms.extend_from_slice(imms);
579        ImmList::new(start, Idx::from_usize(self.imms.len()))
580    }
581
582    /// Records a constant.
583    pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
584        self.imms.push(imm);
585        Idx::from_usize(self.imms.len() - 1)
586    }
587
588    /// Records where each eightbyte of an object travelled.
589    pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList {
590        let start = Idx::from_usize(self.slots.len());
591        self.slots.extend_from_slice(slots);
592        SlotList::new(start, Idx::from_usize(self.slots.len()))
593    }
594
595    /// Records an object read off a variable argument list.
596    pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo> {
597        self.va_objects.push(info);
598        Idx::from_usize(self.va_objects.len() - 1)
599    }
600
601    /// Records what an access does.
602    pub fn add_mem(&mut self, info: MemInfo) -> Idx<MemInfo> {
603        self.mem.push(info);
604        Idx::from_usize(self.mem.len() - 1)
605    }
606
607    /// Records what the ABI asks of the arguments a call's signature does not name.
608    pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList {
609        let start = Idx::from_usize(self.abis.len());
610        self.abis.extend_from_slice(abis);
611        AbiList::new(start, Idx::from_usize(self.abis.len()))
612    }
613
614    /// Records a call's callee and signature.
615    pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo> {
616        self.calls.push(info);
617        Idx::from_usize(self.calls.len() - 1)
618    }
619
620    /// Records a `switch`'s targets and case values.
621    pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo> {
622        self.switches.push(info);
623        Idx::from_usize(self.switches.len() - 1)
624    }
625
626    /// Records an inline assembly instruction's template and constraints.
627    pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo> {
628        self.asms.push(info);
629        Idx::from_usize(self.asms.len() - 1)
630    }
631
632    /// How many values, instructions and blocks there are, for a reader that wants to size
633    /// something by them.
634    #[must_use]
635    pub fn counts(&self) -> Counts {
636        Counts { values: self.values.len(), insts: self.insts.len(), blocks: self.blocks.len() }
637    }
638
639    /// What is known about a value, which is nothing at all unless somebody said otherwise.
640    ///
641    /// Section 6.2.3 of `spec/safe-memory/06-instrumentation.md`. Facts are in a side table and
642    /// not in the value, so a function nobody has said anything about carries no facts and is
643    /// the same size it was before facts existed.
644    #[must_use]
645    pub fn facts(&self, value: Value) -> Facts {
646        match self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw()) {
647            Ok(at) => self.facts[at].1,
648            Err(_) => Facts::NONE,
649        }
650    }
651
652    /// Says what is known about a value, replacing whatever was known before.
653    ///
654    /// Setting [`Facts::NONE`] takes the value back out of the table, which is what keeps the
655    /// table empty in a function that has had facts put on and then taken off again.
656    pub fn set_facts(&mut self, value: Value, facts: Facts) {
657        let found = self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw());
658        match (found, facts.is_empty()) {
659            (Ok(at), true) => drop(self.facts.remove(at)),
660            (Ok(at), false) => self.facts[at].1 = facts,
661            (Err(_), true) => {}
662            (Err(at), false) => self.facts.insert(at, (value, facts)),
663        }
664    }
665
666    /// Every value something is known about, in value order.
667    pub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_ {
668        self.facts.iter().copied()
669    }
670
671    fn add_value(&mut self, data: ValueData) -> Value {
672        self.values.push(data);
673        Idx::from_usize(self.values.len() - 1)
674    }
675}
676
677/// How many of each thing a function holds.
678#[derive(Clone, Copy, Debug, PartialEq, Eq)]
679pub struct Counts {
680    /// Values, including the ones whose defining instruction has been removed.
681    pub values: usize,
682    /// Instructions, including the ones that have been removed from their block.
683    pub insts: usize,
684    /// Blocks.
685    pub blocks: usize,
686}
687
688// Reading is indexing. There is one of these for each handle, so `func[inst]` and `func[value]`
689// and `&func[args]` all work and none of them needs a method whose name says which table.
690impl Index<Value> for Func {
691    type Output = ValueData;
692
693    fn index(&self, value: Value) -> &ValueData {
694        &self.values[value.index()]
695    }
696}
697
698impl Index<Inst> for Func {
699    type Output = InstData;
700
701    fn index(&self, inst: Inst) -> &InstData {
702        &self.insts[inst.index()]
703    }
704}
705
706impl IndexMut<Inst> for Func {
707    fn index_mut(&mut self, inst: Inst) -> &mut InstData {
708        &mut self.insts[inst.index()]
709    }
710}
711
712impl Index<Block> for Func {
713    type Output = BlockData;
714
715    fn index(&self, block: Block) -> &BlockData {
716        &self.blocks[block.index()]
717    }
718}
719
720impl Index<Sig> for Func {
721    type Output = Signature;
722
723    fn index(&self, sig: Sig) -> &Signature {
724        &self.signatures[sig.index()]
725    }
726}
727
728impl Index<ValueList> for Func {
729    type Output = [Value];
730
731    fn index(&self, list: ValueList) -> &[Value] {
732        &self.value_pool[list.as_usize_range()]
733    }
734}
735
736impl Index<BlockCallList> for Func {
737    type Output = [BlockCall];
738
739    fn index(&self, list: BlockCallList) -> &[BlockCall] {
740        &self.block_calls[list.as_usize_range()]
741    }
742}
743
744impl Index<Idx<BlockCall>> for Func {
745    type Output = BlockCall;
746
747    fn index(&self, at: Idx<BlockCall>) -> &BlockCall {
748        &self.block_calls[at.index()]
749    }
750}
751
752impl Index<ImmList> for Func {
753    type Output = [Imm];
754
755    fn index(&self, list: ImmList) -> &[Imm] {
756        &self.imms[list.as_usize_range()]
757    }
758}
759
760impl Index<Idx<Imm>> for Func {
761    type Output = Imm;
762
763    fn index(&self, at: Idx<Imm>) -> &Imm {
764        &self.imms[at.index()]
765    }
766}
767
768impl Index<Idx<MemInfo>> for Func {
769    type Output = MemInfo;
770
771    fn index(&self, at: Idx<MemInfo>) -> &MemInfo {
772        &self.mem[at.index()]
773    }
774}
775
776impl Index<AbiList> for Func {
777    type Output = [Abi];
778
779    fn index(&self, list: AbiList) -> &[Abi] {
780        &self.abis[list.as_usize_range()]
781    }
782}
783
784impl Index<SlotList> for Func {
785    type Output = [Slot];
786
787    fn index(&self, list: SlotList) -> &[Slot] {
788        &self.slots[list.as_usize_range()]
789    }
790}
791
792impl Index<Idx<VaInfo>> for Func {
793    type Output = VaInfo;
794
795    fn index(&self, at: Idx<VaInfo>) -> &VaInfo {
796        &self.va_objects[at.index()]
797    }
798}
799
800impl Index<Idx<CallInfo>> for Func {
801    type Output = CallInfo;
802
803    fn index(&self, at: Idx<CallInfo>) -> &CallInfo {
804        &self.calls[at.index()]
805    }
806}
807
808impl Index<Idx<SwitchInfo>> for Func {
809    type Output = SwitchInfo;
810
811    fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo {
812        &self.switches[at.index()]
813    }
814}
815
816impl Index<Idx<AsmInfo>> for Func {
817    type Output = AsmInfo;
818
819    fn index(&self, at: Idx<AsmInfo>) -> &AsmInfo {
820        &self.asms[at.index()]
821    }
822}
823
824/// A cursor that appends to the end of one block.
825///
826/// This is the shape lowering wants: it works on one block at a time, it appends, and it wants
827/// the value back so it can use it in the next instruction. Everything here is a thin wrapper
828/// over [`Func::create_inst`] and [`Func::append_inst`], and anything the wrappers do not
829/// cover is done with those two directly.
830#[derive(Debug)]
831pub struct Builder<'a> {
832    func: &'a mut Func,
833    block: Block,
834    span: Span,
835}
836
837impl<'a> Builder<'a> {
838    /// A cursor appending to that block, with every instruction taking that source location.
839    pub fn new(func: &'a mut Func, block: Block) -> Self {
840        Self { func, block, span: Span::DUMMY }
841    }
842
843    /// The same cursor, with a source location for the instructions after this.
844    #[must_use]
845    pub fn at(mut self, span: Span) -> Self {
846        self.span = span;
847        self
848    }
849
850    /// Sets the source location for the instructions after this.
851    pub fn set_span(&mut self, span: Span) {
852        self.span = span;
853    }
854
855    /// The function being built.
856    pub fn func(&mut self) -> &mut Func {
857        self.func
858    }
859
860    /// The block being appended to.
861    #[must_use]
862    pub fn block(&self) -> Block {
863        self.block
864    }
865
866    /// Appends an instruction as it is, and gives back its results.
867    pub fn inst(&mut self, data: InstData, results: &[Type]) -> Inst {
868        let inst = self.func.create_inst(data, results, self.span);
869        self.func.append_inst(self.block, inst);
870        inst
871    }
872
873    /// The one value an instruction produces.
874    ///
875    /// # Panics
876    ///
877    /// Panics if it did not produce exactly one.
878    pub fn value(&mut self, data: InstData, ty: Type) -> Value {
879        let inst = self.inst(data, &[ty]);
880        self.func[inst].first_result.expect("one result was asked for")
881    }
882
883    /// An integer constant.
884    ///
885    /// # Panics
886    ///
887    /// Panics if `ty` is not an integer type.
888    pub fn iconst(&mut self, ty: Type, value: i128) -> Value {
889        let imm = self.func.add_imm(Imm::int(value, ty.lane()));
890        self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) }, ty)
891    }
892
893    /// A floating point constant, given as the bits of its format.
894    pub fn fconst(&mut self, ty: Type, bits: u128) -> Value {
895        let imm = self.func.add_imm(Imm::from_bits(bits));
896        self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::FConst) }, ty)
897    }
898
899    /// A two-operand instruction whose result has the type of its operands.
900    pub fn binary(&mut self, opcode: Opcode, lhs: Value, rhs: Value, flags: Flags) -> Value {
901        let ty = self.func[lhs].ty;
902        let args = self.func.push_values(&[lhs, rhs]);
903        self.value(InstData { args, flags, ..InstData::new(opcode) }, ty)
904    }
905
906    /// A one-operand instruction whose result has the type given.
907    pub fn unary(&mut self, opcode: Opcode, arg: Value, ty: Type) -> Value {
908        let args = self.func.push_values(&[arg]);
909        self.value(InstData { args, ..InstData::new(opcode) }, ty)
910    }
911
912    /// An integer comparison, which produces one `i1` per lane.
913    pub fn icmp(&mut self, pred: IntPred, lhs: Value, rhs: Value) -> Value {
914        let ty = self.func[lhs].ty.with_lane(Type::I1);
915        let args = self.func.push_values(&[lhs, rhs]);
916        self.value(
917            InstData { args, extra: Extra::IntPred(pred), ..InstData::new(Opcode::ICmp) },
918            ty,
919        )
920    }
921
922    /// A floating point comparison, which produces one `i1` per lane.
923    pub fn fcmp(&mut self, pred: FloatPred, lhs: Value, rhs: Value, flags: Flags) -> Value {
924        let ty = self.func[lhs].ty.with_lane(Type::I1);
925        let args = self.func.push_values(&[lhs, rhs]);
926        self.value(
927            InstData { args, flags, extra: Extra::FloatPred(pred), ..InstData::new(Opcode::FCmp) },
928            ty,
929        )
930    }
931
932    /// Memory as the function found it, which is where a memory SSA chain starts.
933    ///
934    /// It belongs at the top of the entry block and there is one of them in a function.
935    pub fn mem_entry(&mut self) -> Value {
936        self.value(InstData::new(Opcode::MemEntry), Type::MEM)
937    }
938
939    /// A read of that type from that address.
940    pub fn load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
941        let mem = self.func.add_mem(info);
942        let args = self.func.push_values(&[addr]);
943        self.value(
944            InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) },
945            ty,
946        )
947    }
948
949    /// A write of a value to an address.
950    pub fn store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
951        let mem = self.func.add_mem(info);
952        let args = self.func.push_values(&[value, addr]);
953        self.inst(
954            InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Store) },
955            &[],
956        )
957    }
958
959    /// An unconditional branch.
960    pub fn jump(&mut self, target: Block, args: &[Value]) -> Inst {
961        let call = self.block_call(target, args);
962        let targets = self.func.push_block_calls(&[call]);
963        self.inst(InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::Jump) }, &[])
964    }
965
966    /// The address of a block, which is a value a later `indirect_br` can branch to.
967    ///
968    /// The block is a target here in the same sense a branch's is, so everything that asks an
969    /// instruction which blocks it names finds this one, and a block whose address is taken is
970    /// not mistaken for a block nothing mentions.
971    pub fn block_addr(&mut self, target: Block) -> Value {
972        let call = self.block_call(target, &[]);
973        let targets = self.func.push_block_calls(&[call]);
974        self.value(
975            InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::BlockAddr) },
976            Type::PTR,
977        )
978    }
979
980    /// A branch to an address, which arrives at one of the blocks listed.
981    ///
982    /// Every block the address can hold has to be there. The list is what the rest of the
983    /// compiler reads, so a block left out of it is a block the branch is saying it never
984    /// reaches, and none of it is checked against the addresses anybody took.
985    pub fn indirect_br(&mut self, addr: Value, targets: &[Block]) -> Inst {
986        let calls: Vec<BlockCall> =
987            targets.iter().map(|&target| self.block_call(target, &[])).collect();
988        let targets = self.func.push_block_calls(&calls);
989        let args = self.func.push_values(&[addr]);
990        self.inst(
991            InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::IndirectBr) },
992            &[],
993        )
994    }
995
996    /// A two-way branch, taking the first target when the condition is one.
997    pub fn br_if(
998        &mut self,
999        cond: Value,
1000        then_block: Block,
1001        then_args: &[Value],
1002        else_block: Block,
1003        else_args: &[Value],
1004    ) -> Inst {
1005        let then_call = self.block_call(then_block, then_args);
1006        let else_call = self.block_call(else_block, else_args);
1007        let targets = self.func.push_block_calls(&[then_call, else_call]);
1008        let args = self.func.push_values(&[cond]);
1009        self.inst(
1010            InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::BrIf) },
1011            &[],
1012        )
1013    }
1014
1015    /// A branch on an integer, taking the target its value selects and the default when it
1016    /// selects none.
1017    ///
1018    /// The cases are values and blocks rather than a table with the default in it, because the
1019    /// order the side table wants, which is the default first, is not an order anybody building
1020    /// a `switch` has their cases in.
1021    pub fn switch(&mut self, value: Value, default: Block, cases: &[(i128, Block)]) -> Inst {
1022        let ty = self.func[value].ty.lane();
1023        let mut calls = vec![self.block_call(default, &[])];
1024        let mut values = Vec::with_capacity(cases.len());
1025        for &(value, block) in cases {
1026            calls.push(self.block_call(block, &[]));
1027            values.push(Imm::int(value, ty));
1028        }
1029        let targets = self.func.push_block_calls(&calls);
1030        let cases = self.func.push_imms(&values);
1031        let info = self.func.add_switch(SwitchInfo { targets, cases });
1032        let args = self.func.push_values(&[value]);
1033        self.inst(
1034            InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) },
1035            &[],
1036        )
1037    }
1038
1039    /// A return of the values the signature says.
1040    pub fn ret(&mut self, values: &[Value]) -> Inst {
1041        let args = self.func.push_values(values);
1042        self.inst(InstData { args, ..InstData::new(Opcode::Return) }, &[])
1043    }
1044
1045    /// A place control does not reach.
1046    pub fn unreachable(&mut self) -> Inst {
1047        self.inst(InstData::new(Opcode::Unreachable), &[])
1048    }
1049
1050    /// A direct call, with the results its signature says it produces.
1051    pub fn call(&mut self, callee: Symbol, signature: Sig, args: &[Value]) -> Inst {
1052        self.call_varargs(callee, signature, args, &[])
1053    }
1054
1055    /// The same, saying how the arguments the signature does not name travel.
1056    ///
1057    /// Empty says they all travel as the values in hand, which is what [`Builder::call`] passes
1058    /// and is the usual case. Anything else has one entry for each argument past the ones the
1059    /// signature names.
1060    pub fn call_varargs(
1061        &mut self,
1062        callee: Symbol,
1063        signature: Sig,
1064        args: &[Value],
1065        varargs: &[Abi],
1066    ) -> Inst {
1067        let varargs = self.func.push_abis(varargs);
1068        let info = self.func.add_call(CallInfo { callee: Some(callee), signature, varargs });
1069        let returns: Vec<Type> = self.func[signature].return_types().collect();
1070        let args = self.func.push_values(args);
1071        self.inst(
1072            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) },
1073            &returns,
1074        )
1075    }
1076
1077    /// Inline assembly, which is a terminator when the info carries targets.
1078    ///
1079    /// The targets are built by the caller, because the frontend is the only thing that knows
1080    /// which block is the one control reaches when the assembly does not jump, and that block
1081    /// has to come first.
1082    pub fn inline_asm(
1083        &mut self,
1084        info: AsmInfo,
1085        args: &[Value],
1086        results: &[Type],
1087        flags: Flags,
1088    ) -> Inst {
1089        let info = self.func.add_asm(info);
1090        let args = self.func.push_values(args);
1091        self.inst(
1092            InstData { args, flags, extra: Extra::Asm(info), ..InstData::new(Opcode::InlineAsm) },
1093            results,
1094        )
1095    }
1096
1097    fn block_call(&mut self, block: Block, args: &[Value]) -> BlockCall {
1098        BlockCall { block, args: self.func.push_values(args) }
1099    }
1100}
1101
1102#[cfg(test)]
1103mod tests {
1104    use rucc_base::Interner;
1105
1106    use super::*;
1107    use crate::inst::BlockCallList;
1108    use crate::{MemOrder, Restrict};
1109
1110    /// The example from the spec, near enough: a loop that sums one to n and stores it.
1111    fn sum() -> (Func, Block, Block, Block) {
1112        let mut names = Interner::new();
1113        let i32_ = Type::int(32);
1114        let mut func = Func::new(
1115            names.intern("sum"),
1116            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1117        );
1118
1119        let entry = func.create_block();
1120        let n = func.append_param(entry, i32_);
1121        let header = func.create_block();
1122        let acc = func.append_param(header, i32_);
1123        let i = func.append_param(header, i32_);
1124        let exit = func.create_block();
1125        let result = func.append_param(exit, i32_);
1126
1127        let mut b = Builder::new(&mut func, entry);
1128        let zero = b.iconst(i32_, 0);
1129        let cmp = b.icmp(IntPred::Sle, n, zero);
1130        b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
1131
1132        let mut b = Builder::new(&mut func, header);
1133        let one = b.iconst(i32_, 1);
1134        let next = b.binary(Opcode::Add, i, one, Flags::NSW);
1135        let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
1136        let done = b.icmp(IntPred::Sge, next, n);
1137        b.br_if(done, exit, &[total], header, &[total, next]);
1138
1139        let mut b = Builder::new(&mut func, exit);
1140        b.ret(&[result]);
1141
1142        (func, entry, header, exit)
1143    }
1144
1145    #[test]
1146    fn the_blocks_come_back_in_the_order_they_were_made() {
1147        let (func, entry, header, exit) = sum();
1148        assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, header, exit]);
1149        assert_eq!(func.entry(), Some(entry));
1150    }
1151
1152    #[test]
1153    fn a_removed_block_is_gone_from_the_layout_and_so_is_what_was_in_it() {
1154        let (mut func, entry, header, exit) = sum();
1155        let inside: Vec<Inst> = func.insts(header).collect();
1156        func.remove_block(header);
1157        assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, exit]);
1158        assert_eq!(func.entry(), Some(entry));
1159        assert_eq!(func[entry].next, Some(exit));
1160        assert_eq!(func[exit].prev, Some(entry));
1161        // The instructions say they are in no block, the way a removed one does.
1162        assert!(inside.iter().all(|&inst| func.block_of(inst).is_none()));
1163        assert!(func.insts(header).next().is_none());
1164    }
1165
1166    #[test]
1167    fn each_block_holds_what_was_appended_to_it() {
1168        let (func, entry, header, exit) = sum();
1169        let opcodes =
1170            |block| func.insts(block).map(|inst| func[inst].opcode.name()).collect::<Vec<_>>();
1171        assert_eq!(opcodes(entry), ["iconst", "icmp", "br_if"]);
1172        assert_eq!(opcodes(header), ["iconst", "add", "add", "icmp", "br_if"]);
1173        assert_eq!(opcodes(exit), ["return"]);
1174    }
1175
1176    #[test]
1177    fn asm_ends_a_block_when_it_has_labels_and_not_otherwise() {
1178        // The labels are in the function's table, so the instruction on its own cannot answer
1179        // and anything asking it rather than the function would walk off the end of the block.
1180        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1181        let block = func.create_block();
1182        let plain = func.add_asm(AsmInfo {
1183            template: Symbol::from_raw(0),
1184            constraints: Symbol::from_raw(0),
1185            clobbers: Symbol::from_raw(0),
1186            targets: BlockCallList::EMPTY,
1187        });
1188        let call = BlockCall { block, args: ValueList::EMPTY };
1189        let targets = func.push_block_calls(&[call]);
1190        let labelled = func.add_asm(AsmInfo {
1191            template: Symbol::from_raw(0),
1192            constraints: Symbol::from_raw(0),
1193            clobbers: Symbol::from_raw(0),
1194            targets,
1195        });
1196
1197        let mut make = |extra| {
1198            let data = InstData { extra, ..InstData::new(Opcode::InlineAsm) };
1199            func.create_inst(data, &[], Span::DUMMY)
1200        };
1201        let plain = make(Extra::Asm(plain));
1202        let labelled = make(Extra::Asm(labelled));
1203        assert!(!func.is_terminator(plain));
1204        assert!(func.is_terminator(labelled));
1205    }
1206
1207    #[test]
1208    fn every_block_ends_in_its_terminator() {
1209        let (func, entry, header, exit) = sum();
1210        for block in [entry, header, exit] {
1211            let last = func.terminator(block).expect("a terminator");
1212            assert_eq!(Some(last), func.insts(block).last());
1213        }
1214    }
1215
1216    #[test]
1217    fn a_branch_carries_the_arguments_the_block_takes() {
1218        let (func, entry, header, _) = sum();
1219        let br = func.terminator(entry).expect("a terminator");
1220        let calls: Vec<BlockCall> = func.successors(br).collect();
1221        assert_eq!(calls.len(), 2);
1222        // The loop header takes two parameters, so the branch to it passes two.
1223        assert_eq!(calls[1].block, header);
1224        assert_eq!(func[calls[1].args].len(), 2);
1225        assert_eq!(func[header].params.len(), 2);
1226        assert_eq!(func[calls[0].args].len(), 1);
1227    }
1228
1229    #[test]
1230    fn a_value_knows_what_defined_it() {
1231        let (func, entry, _, _) = sum();
1232        let first = func.insts(entry).next().expect("an instruction");
1233        let value = func[first].first_result.expect("a result");
1234        assert_eq!(func[value].def, Def::Result { inst: first, index: 0 });
1235        assert_eq!(func[value].ty, Type::int(32));
1236
1237        let param = func[entry].params[0];
1238        assert_eq!(func[param].def, Def::Param { block: entry, index: 0 });
1239    }
1240
1241    #[test]
1242    fn a_comparison_produces_one_bit() {
1243        let (func, entry, _, _) = sum();
1244        let cmp = func.insts(entry).nth(1).expect("the comparison");
1245        let value = func[cmp].first_result.expect("a result");
1246        assert_eq!(func[value].ty, Type::I1);
1247        assert_eq!(func[cmp].extra, Extra::IntPred(IntPred::Sle));
1248    }
1249
1250    #[test]
1251    fn flags_ride_along_on_the_instruction_that_was_given_them() {
1252        let (func, _, header, _) = sum();
1253        let add = func.insts(header).nth(1).expect("the addition");
1254        assert_eq!(func[add].flags, Flags::NSW);
1255        let cmp = func.insts(header).nth(3).expect("the comparison");
1256        assert_eq!(func[cmp].flags, Flags::NONE);
1257    }
1258
1259    #[test]
1260    fn removing_an_instruction_takes_it_out_of_the_middle() {
1261        let (mut func, _, header, _) = sum();
1262        let add = func.insts(header).nth(1).expect("the addition");
1263        func.remove_inst(add);
1264        let opcodes: Vec<&str> = func.insts(header).map(|inst| func[inst].opcode.name()).collect();
1265        assert_eq!(opcodes, ["iconst", "add", "icmp", "br_if"]);
1266        assert_eq!(func.block_of(add), None);
1267    }
1268
1269    #[test]
1270    fn removing_the_first_and_the_last_keeps_the_ends_right() {
1271        let (mut func, entry, _, _) = sum();
1272        let first = func.insts(entry).next().expect("an instruction");
1273        let last = func.terminator(entry).expect("a terminator");
1274        func.remove_inst(first);
1275        func.remove_inst(last);
1276        let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1277        assert_eq!(opcodes, ["icmp"]);
1278        assert_eq!(func[entry].first, func[entry].last);
1279    }
1280
1281    #[test]
1282    fn removing_the_only_instruction_empties_the_block() {
1283        let (mut func, _, _, exit) = sum();
1284        let only = func.insts(exit).next().expect("an instruction");
1285        func.remove_inst(only);
1286        assert_eq!(func.insts(exit).count(), 0);
1287        assert_eq!(func[exit].first, None);
1288        assert_eq!(func[exit].last, None);
1289    }
1290
1291    #[test]
1292    fn inserting_before_puts_it_in_the_right_place() {
1293        let (mut func, entry, _, _) = sum();
1294        let cmp = func.insts(entry).nth(1).expect("the comparison");
1295        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1296        func.insert_before(made, cmp);
1297        let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1298        assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1299    }
1300
1301    #[test]
1302    fn inserting_before_the_first_makes_it_the_first() {
1303        let (mut func, entry, _, _) = sum();
1304        let first = func.insts(entry).next().expect("an instruction");
1305        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1306        func.insert_before(made, first);
1307        assert_eq!(func.insts(entry).next(), Some(made));
1308        assert_eq!(func[entry].first, Some(made));
1309    }
1310
1311    #[test]
1312    fn inserting_after_puts_it_in_the_right_place() {
1313        let (mut func, entry, _, _) = sum();
1314        let first = func.insts(entry).next().expect("an instruction");
1315        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1316        func.insert_after(made, first);
1317        let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1318        assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1319        assert_eq!(func[entry].first, Some(first));
1320    }
1321
1322    #[test]
1323    #[should_panic(expected = "nothing goes after a terminator")]
1324    fn inserting_after_the_terminator_is_refused() {
1325        // A block ends where its branch is, so an instruction after one would be in no block that
1326        // control ever reaches, and the layout would be claiming otherwise.
1327        let (mut func, entry, _, _) = sum();
1328        let last = func.insts(entry).last().expect("a terminator");
1329        let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1330        func.insert_after(made, last);
1331    }
1332
1333    #[test]
1334    fn a_list_grows_in_place_while_it_is_the_last_thing_in_the_pool() {
1335        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1336        let block = func.create_block();
1337        let a = func.append_param(block, Type::int(32));
1338        let b = func.append_param(block, Type::int(32));
1339        let list = func.push_values(&[a]);
1340        let grown = func.append_arg(list, b);
1341        assert_eq!(func[grown], [a, b]);
1342        assert_eq!(grown.as_usize_range().start, list.as_usize_range().start);
1343    }
1344
1345    #[test]
1346    fn a_list_is_copied_when_something_is_behind_it() {
1347        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1348        let block = func.create_block();
1349        let a = func.append_param(block, Type::int(32));
1350        let b = func.append_param(block, Type::int(32));
1351        let list = func.push_values(&[a, a]);
1352        let behind = func.push_values(&[b]);
1353        let grown = func.append_arg(list, b);
1354        assert_eq!(func[grown], [a, a, b]);
1355        assert_eq!(func[list], [a, a], "the old run is still readable");
1356        assert_eq!(func[behind], [b], "and so is what was behind it");
1357        assert_ne!(grown.as_usize_range().start, list.as_usize_range().start);
1358    }
1359
1360    #[test]
1361    fn a_parameter_added_late_is_the_next_one_along() {
1362        // This is the shape SSA construction leaves: the loop header gains a parameter after
1363        // the blocks that branch to it already exist, and each of their branches grows an
1364        // argument to match.
1365        let (mut func, entry, header, _) = sum();
1366        let extra = func.append_param(header, Type::int(32));
1367        assert_eq!(func[header].params.len(), 3);
1368        assert_eq!(func[extra].def, Def::Param { block: header, index: 2 });
1369
1370        let br = func.terminator(entry).expect("a terminator");
1371        let call = func.successors(br).nth(1).expect("the branch to the header");
1372        let grown = func.append_arg(call.args, extra);
1373        assert_eq!(func[grown].len(), 3);
1374    }
1375
1376    #[test]
1377    fn a_span_rides_along_with_the_instruction() {
1378        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1379        let block = func.create_block();
1380        let span = Span::new(10, 20);
1381        let mut b = Builder::new(&mut func, block).at(span);
1382        let value = b.iconst(Type::int(32), 7);
1383        let inst = match func[value].def {
1384            Def::Result { inst, .. } => inst,
1385            Def::Param { .. } => unreachable!("a constant is not a parameter"),
1386        };
1387        assert_eq!(func.span(inst), span);
1388    }
1389
1390    #[test]
1391    fn a_store_produces_nothing_and_a_load_produces_one_value() {
1392        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1393        let block = func.create_block();
1394        let addr = func.append_param(block, Type::PTR);
1395        let info = MemInfo {
1396            size: 4,
1397            align: 4,
1398            order: MemOrder::NotAtomic,
1399            tbaa: None,
1400            restrict: Restrict::NONE,
1401        };
1402        let mut b = Builder::new(&mut func, block);
1403        let value = b.load(Type::int(32), addr, info, Flags::NONE);
1404        let store = b.store(value, addr, info, Flags::VOLATILE);
1405        assert_eq!(func[store].results, 0);
1406        assert_eq!(func[store].flags, Flags::VOLATILE);
1407        assert_eq!(func[value].ty, Type::int(32));
1408    }
1409
1410    #[test]
1411    fn a_call_produces_what_its_signature_returns() {
1412        let mut names = Interner::new();
1413        let mut func = Func::new(names.intern("caller"), Signature::new());
1414        let sig = func.add_signature(
1415            Signature::new().with_params(&[Type::int(32)]).with_returns(&[Type::int(64)]),
1416        );
1417        let block = func.create_block();
1418        let arg = func.append_param(block, Type::int(32));
1419        let callee = names.intern("callee");
1420        let mut b = Builder::new(&mut func, block);
1421        let call = b.call(callee, sig, &[arg]);
1422        assert_eq!(func[call].results, 1);
1423        let value = func[call].first_result.expect("a result");
1424        assert_eq!(func[value].ty, Type::int(64));
1425        assert_eq!(func[call].extra, Extra::Call(Idx::new(0)));
1426    }
1427
1428    #[test]
1429    fn the_counts_are_what_was_made() {
1430        let (func, _, _, _) = sum();
1431        let counts = func.counts();
1432        assert_eq!(counts.blocks, 3);
1433        assert_eq!(counts.insts, 9);
1434        // Four block parameters and five instruction results, which is the two constants, the
1435        // two additions and the two comparisons less the branches, which produce nothing.
1436        assert_eq!(counts.values, 4 + 6);
1437    }
1438
1439    #[test]
1440    #[should_panic(expected = "the instruction is in a block")]
1441    fn appending_an_instruction_twice_is_refused() {
1442        let (mut func, entry, _, _) = sum();
1443        let first = func.insts(entry).next().expect("an instruction");
1444        func.append_inst(entry, first);
1445    }
1446
1447    #[test]
1448    #[should_panic(expected = "the instruction is not in a block")]
1449    fn removing_an_instruction_twice_is_refused() {
1450        let (mut func, entry, _, _) = sum();
1451        let first = func.insts(entry).next().expect("an instruction");
1452        func.remove_inst(first);
1453        func.remove_inst(first);
1454    }
1455
1456    /// A store and a load with memory threaded through them, as memory SSA construction does it.
1457    fn threaded() -> (Func, Inst, Inst) {
1458        let mut names = Interner::new();
1459        let i32_ = Type::int(32);
1460        let mut func = Func::new(
1461            names.intern("thread"),
1462            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1463        );
1464        let entry = func.create_block();
1465        let addr = func.append_param(entry, Type::PTR);
1466        let info = MemInfo {
1467            size: 4,
1468            align: 4,
1469            order: MemOrder::NotAtomic,
1470            tbaa: None,
1471            restrict: Restrict::NONE,
1472        };
1473
1474        let mut b = Builder::new(&mut func, entry);
1475        let start = b.mem_entry();
1476        let seven = b.iconst(i32_, 7);
1477        let store = b.store(seven, addr, info, Flags::NONE);
1478        let value = b.load(i32_, addr, info, Flags::NONE);
1479        let Def::Result { inst: load, .. } = func[value].def else {
1480            panic!("the load produced it");
1481        };
1482
1483        let store = func.with_mem(store, start);
1484        let after = func.mem_out(store).expect("a store makes a new version");
1485        let load = func.with_mem(load, after);
1486        (func, store, load)
1487    }
1488
1489    #[test]
1490    fn threading_memory_puts_it_last_and_leaves_everything_else_where_it_was() {
1491        let (func, store, load) = threaded();
1492        assert_eq!(func.mem_in(store), func.mem_out(store).map(|_| func[func[store].args][2]));
1493        assert_eq!(func[func[store].args].len(), 3);
1494        assert!(func.carries_mem(store));
1495        assert!(func.carries_mem(load));
1496
1497        // The address of the load is still its first operand, which is the point of putting
1498        // memory last: nothing that read the operands before has to learn about it.
1499        assert_eq!(func[func[load].args][0], func[func.entry().expect("an entry")].params[0]);
1500        assert_eq!(func.mem_in(load), func.mem_out(store));
1501        assert_eq!(func.mem_out(load), None);
1502    }
1503
1504    #[test]
1505    #[should_panic(expected = "this is already on the memory chain")]
1506    fn threading_memory_through_the_same_instruction_twice_is_refused() {
1507        let (mut func, store, _) = threaded();
1508        let start = func.mem_in(store).expect("it was threaded");
1509        func.with_mem(store, start);
1510    }
1511}