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