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