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