sway_ir/
block.rs

1//! Represents a 'basic block' of [`Instruction`]s in a control flow graph.
2//!
3//! [`Block`]s contain zero or more _non-terminating_ instructions and at most one _terminating_
4//! instruction or _terminator_.  Terminators are either branches or a return instruction and are
5//! the last instruction in the block.
6//!
7//! Blocks also contain a single 'phi' instruction at its start.  In
8//! [SSA](https://en.wikipedia.org/wiki/Static_single_assignment_form) form 'phi' instructions are
9//! used to merge values from preceding blocks.
10//!
11//! Every [`Function`] has at least one block, the first of which is usually labeled `entry`.
12
13use rustc_hash::{FxHashMap, FxHashSet};
14
15use crate::{
16    context::Context,
17    error::IrError,
18    function::Function,
19    instruction::{FuelVmInstruction, InstOp},
20    value::{Value, ValueDatum},
21    BranchToWithArgs, DebugWithContext, Instruction, InstructionInserter, InstructionIterator,
22    Module, Type,
23};
24
25/// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the
26/// [`Context`].
27#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)]
28pub struct Block(pub slotmap::DefaultKey);
29
30impl Block {
31    pub fn get_module<'a>(&self, context: &'a Context) -> &'a Module {
32        let f = context.blocks[self.0].function;
33        &context.functions[f.0].module
34    }
35}
36
37#[doc(hidden)]
38pub struct BlockContent {
39    /// Block label, useful for printing.
40    pub label: Label,
41    /// The function containing this block.
42    pub function: Function,
43    /// List of instructions in the block.
44    pub(crate) instructions: Vec<Value>,
45    /// Block arguments: Another form of SSA PHIs.
46    pub args: Vec<Value>,
47    /// CFG predecessors
48    pub preds: FxHashSet<Block>,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DebugWithContext)]
52pub struct BlockArgument {
53    /// The block of which this is an argument.
54    pub block: Block,
55    /// idx'th argument of the block.
56    pub idx: usize,
57    pub ty: Type,
58}
59
60impl BlockArgument {
61    /// Get the actual parameter passed to this block argument from `from_block`.
62    pub fn get_val_coming_from(&self, context: &Context, from_block: &Block) -> Option<Value> {
63        for BranchToWithArgs {
64            block: succ_block,
65            args,
66        } in from_block.successors(context)
67        {
68            if succ_block == self.block {
69                return Some(args[self.idx]);
70            }
71        }
72        None
73    }
74}
75
76/// Each block may be explicitly named.  A [`Label`] is a simple `String` synonym.
77pub type Label = String;
78
79impl Block {
80    /// Return a new block handle.
81    ///
82    /// Creates a new Block belonging to `function` in the context and returns its handle.  `label`
83    /// is optional and is used only when printing the IR.
84    pub fn new(context: &mut Context, function: Function, label: Option<String>) -> Block {
85        let label = function.get_unique_label(context, label);
86        let content = BlockContent {
87            label,
88            function,
89            instructions: vec![],
90            args: vec![],
91            preds: FxHashSet::default(),
92        };
93        Block(context.blocks.insert(content))
94    }
95
96    /// Get the parent function for this block.
97    pub fn get_function(&self, context: &Context) -> Function {
98        context.blocks[self.0].function
99    }
100
101    /// Create a new [`InstructionInserter`] to more easily append instructions to this block.
102    pub fn append<'a, 'eng>(
103        &self,
104        context: &'a mut Context<'eng>,
105    ) -> InstructionInserter<'a, 'eng> {
106        InstructionInserter::new(context, *self, crate::InsertionPosition::End)
107    }
108
109    /// Get the label of this block.  If it wasn't given one upon creation it will be a generated
110    /// label.
111    pub fn get_label(&self, context: &Context) -> String {
112        context.blocks[self.0].label.clone()
113    }
114
115    /// Set the label of this block.  If the label isn't unique it will be made so.
116    pub fn set_label(&self, context: &mut Context, new_label: Option<Label>) {
117        let unique_label = self
118            .get_function(context)
119            .get_unique_label(context, new_label);
120        context.blocks[self.0].label = unique_label;
121    }
122
123    /// Get the number of instructions in this block.
124    pub fn num_instructions(&self, context: &Context) -> usize {
125        context.blocks[self.0].instructions.len()
126    }
127
128    /// Get the i'th block arg.
129    pub fn get_arg(&self, context: &Context, index: usize) -> Option<Value> {
130        context.blocks[self.0].args.get(index).cloned()
131    }
132
133    /// Get the number of predecessor blocks, i.e., blocks which branch to this one.
134    pub fn num_predecessors(&self, context: &Context) -> usize {
135        context.blocks[self.0].preds.len()
136    }
137
138    /// Add a new block argument of type `ty`. Returns its index.
139    pub fn new_arg(&self, context: &mut Context, ty: Type) -> usize {
140        let idx = context.blocks[self.0].args.len();
141        let arg_val = Value::new_argument(
142            context,
143            BlockArgument {
144                block: *self,
145                idx,
146                ty,
147            },
148        );
149        context.blocks[self.0].args.push(arg_val);
150        idx
151    }
152
153    pub fn set_arg(&self, context: &mut Context, arg: Value) {
154        match context.values[arg.0].value {
155            ValueDatum::Argument(BlockArgument { block, idx, ty: _ })
156                if block == *self && idx < context.blocks[self.0].args.len() =>
157            {
158                context.blocks[self.0].args[idx] = arg;
159            }
160            _ => panic!("Inconsistent block argument being set"),
161        }
162    }
163
164    /// Add a block argument, asserts that `arg` is suitable here.
165    pub fn add_arg(&self, context: &mut Context, arg: Value) {
166        match context.values[arg.0].value {
167            ValueDatum::Argument(BlockArgument { block, idx, ty: _ })
168                if block == *self && idx == context.blocks[self.0].args.len() =>
169            {
170                context.blocks[self.0].args.push(arg);
171            }
172            _ => panic!("Inconsistent block argument being added"),
173        }
174    }
175
176    /// Get an iterator over this block's args.
177    pub fn arg_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Value> {
178        context.blocks[self.0].args.iter()
179    }
180
181    /// How many args does this block have?
182    pub fn num_args(&self, context: &Context) -> usize {
183        context.blocks[self.0].args.len()
184    }
185
186    /// Get an iterator over this block's predecessor blocks.
187    pub fn pred_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Block> {
188        context.blocks[self.0].preds.iter()
189    }
190
191    /// Add `from_block` to the set of predecessors of this block.
192    pub fn add_pred(&self, context: &mut Context, from_block: &Block) {
193        context.blocks[self.0].preds.insert(*from_block);
194    }
195
196    /// Remove `from_block` from the set of predecessors of this block.
197    pub fn remove_pred(&self, context: &mut Context, from_block: &Block) {
198        context.blocks[self.0].preds.remove(from_block);
199    }
200
201    /// Replace a `old_source` with `new_source` as a predecessor.
202    pub fn replace_pred(&self, context: &mut Context, old_source: &Block, new_source: &Block) {
203        self.remove_pred(context, old_source);
204        self.add_pred(context, new_source);
205    }
206
207    /// Get instruction at position `pos`.
208    ///
209    /// Returns `None` if block is empty or if `pos` is out of bounds.
210    pub fn get_instruction_at(&self, context: &Context, pos: usize) -> Option<Value> {
211        context.blocks[self.0].instructions.get(pos).cloned()
212    }
213
214    /// Get a reference to the final instruction in the block, provided it is a terminator.
215    ///
216    /// Returns `None` if the final instruction is not a terminator. This can only happen during IR
217    /// generation when the block is still being populated.
218    pub fn get_terminator<'a>(&self, context: &'a Context) -> Option<&'a Instruction> {
219        context.blocks[self.0].instructions.last().and_then(|val| {
220            // It's guaranteed to be an instruction value.
221            if let ValueDatum::Instruction(term_inst) = &context.values[val.0].value {
222                if term_inst.op.is_terminator() {
223                    Some(term_inst)
224                } else {
225                    None
226                }
227            } else {
228                None
229            }
230        })
231    }
232
233    /// Get a mutable reference to the final instruction in the block, provided it is a terminator.
234    ///
235    /// Returns `None` if the final instruction is not a terminator. This can only happen during IR
236    /// generation when the block is still being populated.
237    pub fn get_terminator_mut<'a>(&self, context: &'a mut Context) -> Option<&'a mut Instruction> {
238        context.blocks[self.0].instructions.last().and_then(|val| {
239            // It's guaranteed to be an instruction value.
240            if let ValueDatum::Instruction(term_inst) = &mut context.values[val.0].value {
241                if term_inst.op.is_terminator() {
242                    Some(term_inst)
243                } else {
244                    None
245                }
246            } else {
247                None
248            }
249        })
250    }
251
252    /// Get the CFG successors (and the parameters passed to them) of this block.
253    pub(super) fn successors<'a>(&'a self, context: &'a Context) -> Vec<BranchToWithArgs> {
254        match self.get_terminator(context) {
255            Some(Instruction {
256                op:
257                    InstOp::ConditionalBranch {
258                        true_block,
259                        false_block,
260                        ..
261                    },
262                ..
263            }) => vec![true_block.clone(), false_block.clone()],
264
265            Some(Instruction {
266                op: InstOp::Branch(block),
267                ..
268            }) => vec![block.clone()],
269
270            _otherwise => Vec::new(),
271        }
272    }
273
274    /// For a particular successor (if it indeed is one), get the arguments passed.
275    pub fn get_succ_params(&self, context: &Context, succ: &Block) -> Vec<Value> {
276        self.successors(context)
277            .iter()
278            .find(|branch| &branch.block == succ)
279            .map_or(vec![], |branch| branch.args.clone())
280    }
281
282    /// For a particular successor (if it indeed is one), get a mut ref to parameters passed.
283    pub fn get_succ_params_mut<'a>(
284        &'a self,
285        context: &'a mut Context,
286        succ: &Block,
287    ) -> Option<&'a mut Vec<Value>> {
288        match self.get_terminator_mut(context) {
289            Some(Instruction {
290                op:
291                    InstOp::ConditionalBranch {
292                        true_block,
293                        false_block,
294                        ..
295                    },
296                ..
297            }) => {
298                if true_block.block == *succ {
299                    Some(&mut true_block.args)
300                } else if false_block.block == *succ {
301                    Some(&mut false_block.args)
302                } else {
303                    None
304                }
305            }
306            Some(Instruction {
307                op: InstOp::Branch(block),
308                ..
309            }) if block.block == *succ => Some(&mut block.args),
310            _ => None,
311        }
312    }
313
314    /// Replace successor `old_succ` with `new_succ`.
315    /// Updates `preds` of both `old_succ` and `new_succ`.
316    pub(super) fn replace_successor(
317        &self,
318        context: &mut Context,
319        old_succ: Block,
320        new_succ: Block,
321        new_params: Vec<Value>,
322    ) {
323        let mut modified = false;
324        if let Some(term) = self.get_terminator_mut(context) {
325            match term {
326                Instruction {
327                    op:
328                        InstOp::ConditionalBranch {
329                            true_block:
330                                BranchToWithArgs {
331                                    block: true_block,
332                                    args: true_opds,
333                                },
334                            false_block:
335                                BranchToWithArgs {
336                                    block: false_block,
337                                    args: false_opds,
338                                },
339                            cond_value: _,
340                        },
341                    ..
342                } => {
343                    if old_succ == *true_block {
344                        modified = true;
345                        *true_block = new_succ;
346                        true_opds.clone_from(&new_params);
347                    }
348                    if old_succ == *false_block {
349                        modified = true;
350                        *false_block = new_succ;
351                        *false_opds = new_params
352                    }
353                }
354
355                Instruction {
356                    op: InstOp::Branch(BranchToWithArgs { block, args }),
357                    ..
358                } if *block == old_succ => {
359                    *block = new_succ;
360                    *args = new_params;
361                    modified = true;
362                }
363                _ => (),
364            }
365        }
366        if modified {
367            old_succ.remove_pred(context, self);
368            new_succ.add_pred(context, self);
369        }
370    }
371
372    /// Return whether this block is already terminated by non-branching instructions,
373    /// means with instructions that cause either revert, or local or context returns.
374    /// Those instructions are: [InstOp::Ret], [FuelVmInstruction::Retd],
375    /// [FuelVmInstruction::JmpMem], and [FuelVmInstruction::Revert]).
376    pub fn is_terminated_by_return_or_revert(&self, context: &Context) -> bool {
377        self.get_terminator(context).is_some_and(|i| {
378            matches!(
379                i,
380                Instruction {
381                    op: InstOp::Ret(..)
382                        | InstOp::FuelVm(
383                            FuelVmInstruction::Revert(..)
384                                | FuelVmInstruction::JmpMem
385                                | FuelVmInstruction::Retd { .. }
386                        ),
387                    ..
388                }
389            )
390        })
391    }
392
393    /// Replace a value within this block.
394    ///
395    /// For every instruction within the block, any reference to `old_val` is replaced with
396    /// `new_val`.
397    pub fn replace_values(&self, context: &mut Context, replace_map: &FxHashMap<Value, Value>) {
398        for ins_idx in 0..context.blocks[self.0].instructions.len() {
399            let ins = context.blocks[self.0].instructions[ins_idx];
400            ins.replace_instruction_values(context, replace_map);
401        }
402    }
403
404    /// Remove an instruction from this block.
405    ///
406    /// **NOTE:** We must be very careful!  We mustn't remove the phi or the terminator.  Some
407    /// extra checks should probably be performed here to avoid corruption! Ideally we use get a
408    /// user/uses system implemented.  Using `Vec::remove()` is also O(n) which we may want to
409    /// avoid someday.
410    pub fn remove_instruction(&self, context: &mut Context, instr_val: Value) {
411        let ins = &mut context.blocks[self.0].instructions;
412        if let Some(pos) = ins.iter().position(|iv| *iv == instr_val) {
413            ins.remove(pos);
414        }
415    }
416
417    /// Remove an instruction at position `pos` from this block.
418    ///
419    /// **NOTE:** We must be very careful!  We mustn't remove the phi or the terminator.  Some
420    /// extra checks should probably be performed here to avoid corruption! Ideally we use get a
421    /// user/uses system implemented.  Using `Vec::remove()` is also O(n) which we may want to
422    /// avoid someday.
423    pub fn remove_instruction_at(&self, context: &mut Context, pos: usize) {
424        context.blocks[self.0].instructions.remove(pos);
425    }
426
427    /// Remove the last instruction in the block.
428    ///
429    /// **NOTE:** The caller must be very careful if removing the terminator.
430    pub fn remove_last_instruction(&self, context: &mut Context) {
431        context.blocks[self.0].instructions.pop();
432    }
433
434    /// Remove instructions from block that satisfy a given predicate.
435    pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
436        let ins = &mut context.blocks[self.0].instructions;
437        ins.retain(|value| !pred(*value));
438    }
439
440    /// Clear the current instruction list and take the one provided.
441    pub fn take_body(&self, context: &mut Context, new_insts: Vec<Value>) {
442        let _ = std::mem::replace(&mut (context.blocks[self.0].instructions), new_insts);
443        for inst in &context.blocks[self.0].instructions {
444            let ValueDatum::Instruction(inst) = &mut context.values[inst.0].value else {
445                continue;
446            };
447            inst.parent = *self;
448        }
449    }
450
451    /// Insert instruction(s) at the beginning of the block.
452    pub fn prepend_instructions(&self, context: &mut Context, mut insts: Vec<Value>) {
453        let block_ins = &mut context.blocks[self.0].instructions;
454        insts.append(block_ins);
455        context.blocks[self.0].instructions = insts;
456    }
457
458    /// Replace an instruction in this block with another.  Will return a ValueNotFound on error.
459    /// Any use of the old instruction value will also be replaced by the new value throughout the
460    /// owning function if `replace_uses` is set.
461    pub fn replace_instruction(
462        &self,
463        context: &mut Context,
464        old_instr_val: Value,
465        new_instr_val: Value,
466        replace_uses: bool,
467    ) -> Result<(), IrError> {
468        match context.blocks[self.0]
469            .instructions
470            .iter_mut()
471            .find(|instr_val| *instr_val == &old_instr_val)
472        {
473            None => Err(IrError::ValueNotFound(
474                "Attempting to replace instruction.".to_owned(),
475            )),
476            Some(instr_val) => {
477                *instr_val = new_instr_val;
478                if replace_uses {
479                    self.get_function(context).replace_value(
480                        context,
481                        old_instr_val,
482                        new_instr_val,
483                        Some(*self),
484                    );
485                }
486                Ok(())
487            }
488        }
489    }
490
491    /// Split the block into two.
492    ///
493    /// This will create a new block and move the instructions at and following `split_idx` to it.
494    /// Returns both blocks.
495    pub fn split_at(&self, context: &mut Context, split_idx: usize) -> (Block, Block) {
496        let function = context.blocks[self.0].function;
497        if split_idx == 0 {
498            // We can just create a new empty block and put it before this one.  We know that it
499            // will succeed because self is definitely in the function, so we can unwrap().
500            //
501            // If self is the entry block then for now we need to rename it from 'entry' and call
502            // our new block 'entry'.
503            let new_block_name = (*self == self.get_function(context).get_entry_block(context))
504                .then(|| {
505                    self.set_label(context, None);
506                    "entry".to_owned()
507                });
508            let new_block = function
509                .create_block_before(context, self, new_block_name)
510                .unwrap();
511
512            // Move the block arguments to the new block. We collect because we want to mutate next.
513            #[allow(clippy::needless_collect)]
514            let args: Vec<_> = self.arg_iter(context).copied().collect();
515            for arg in args.into_iter() {
516                match &mut context.values[arg.0].value {
517                    ValueDatum::Argument(BlockArgument {
518                        block,
519                        idx: _,
520                        ty: _,
521                    }) => {
522                        // We modify the Value in place to be a BlockArgument for the new block.
523                        *block = new_block;
524                    }
525                    _ => unreachable!("Block arg value inconsistent"),
526                }
527                new_block.add_arg(context, arg);
528            }
529            context.blocks[self.0].args.clear();
530
531            (new_block, *self)
532        } else {
533            // Again, we know that it will succeed because self is definitely in the function, and
534            // so we can unwrap().
535            let new_block = function.create_block_after(context, self, None).unwrap();
536
537            // Split the instructions at the index and append them to the new block.
538            let mut tail_instructions = context.blocks[self.0].instructions.split_off(split_idx);
539            // Update the parent of tail_instructions.
540            for instr in &tail_instructions {
541                instr.get_instruction_mut(context).unwrap().parent = new_block;
542            }
543            context.blocks[new_block.0]
544                .instructions
545                .append(&mut tail_instructions);
546
547            // If the terminator of the old block (now the new block) was a branch then we need to
548            // update the destination block's preds.
549            //
550            // Copying the candidate blocks and putting them in a vector to avoid borrowing context
551            // as immutable and then mutable in the loop body.
552            for to_block in match new_block.get_terminator(context) {
553                Some(Instruction {
554                    op: InstOp::Branch(to_block),
555                    ..
556                }) => {
557                    vec![to_block.block]
558                }
559                Some(Instruction {
560                    op:
561                        InstOp::ConditionalBranch {
562                            true_block,
563                            false_block,
564                            ..
565                        },
566                    ..
567                }) => {
568                    vec![true_block.block, false_block.block]
569                }
570
571                _ => Vec::new(),
572            } {
573                to_block.replace_pred(context, self, &new_block);
574            }
575
576            (*self, new_block)
577        }
578    }
579
580    /// Return an instruction iterator for each instruction in this block.
581    pub fn instruction_iter(&self, context: &Context) -> InstructionIterator {
582        InstructionIterator::new(context, self)
583    }
584}
585
586/// An iterator over each block in a [`Function`].
587pub struct BlockIterator {
588    blocks: Vec<slotmap::DefaultKey>,
589    next: usize,
590}
591
592impl BlockIterator {
593    /// Return a new iterator for each block in `function`.
594    pub fn new(context: &Context, function: &Function) -> Self {
595        // Copy all the current block indices, so they may be modified in the context during
596        // iteration.
597        BlockIterator {
598            blocks: context.functions[function.0]
599                .blocks
600                .iter()
601                .map(|block| block.0)
602                .collect(),
603            next: 0,
604        }
605    }
606}
607
608impl Iterator for BlockIterator {
609    type Item = Block;
610
611    fn next(&mut self) -> Option<Block> {
612        if self.next < self.blocks.len() {
613            let idx = self.next;
614            self.next += 1;
615            Some(Block(self.blocks[idx]))
616        } else {
617            None
618        }
619    }
620}