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