sway_ir/
instruction.rs

1//! Instructions for data manipulation, but mostly control flow.
2//!
3//! Since Sway abstracts most low level operations behind traits they are translated into function
4//! calls which contain ASM blocks.
5//!
6//! Unfortunately, using opaque ASM blocks limits the effectiveness of certain optimizations and
7//! this should be addressed in the future, perhaps by using compiler intrinsic calls instead of
8//! the ASM blocks where possible. See: https://github.com/FuelLabs/sway/issues/855,
9
10use rustc_hash::FxHashMap;
11use sway_types::Ident;
12
13use crate::{
14    asm::{AsmArg, AsmBlock},
15    block::Block,
16    context::Context,
17    function::Function,
18    irtype::Type,
19    pretty::DebugWithContext,
20    value::{Value, ValueDatum},
21    variable::LocalVar,
22    AsmInstruction, ConstantContent, GlobalVar, Module, StorageKey,
23};
24
25#[derive(Debug, Clone, DebugWithContext)]
26pub struct BranchToWithArgs {
27    pub block: Block,
28    pub args: Vec<Value>,
29}
30
31#[derive(Debug, Clone, DebugWithContext)]
32pub struct Instruction {
33    pub parent: Block,
34    pub op: InstOp,
35}
36
37impl Instruction {
38    pub fn get_type(&self, context: &Context) -> Option<Type> {
39        self.op.get_type(context)
40    }
41    /// Replace `old_val` with `new_val` if it is referenced by this instruction's arguments.
42    pub fn replace_values(&mut self, replace_map: &FxHashMap<Value, Value>) {
43        self.op.replace_values(replace_map)
44    }
45    /// Get the function containing this instruction
46    pub fn get_function(&self, context: &Context) -> Function {
47        context.blocks[self.parent.0].function
48    }
49}
50
51#[derive(Debug, Clone, DebugWithContext)]
52pub enum InstOp {
53    /// An opaque list of ASM instructions passed directly to codegen.
54    AsmBlock(AsmBlock, Vec<AsmArg>),
55    /// Unary arithmetic operations
56    UnaryOp {
57        op: UnaryOpKind,
58        arg: Value,
59    },
60    /// Binary arithmetic operations
61    BinaryOp {
62        op: BinaryOpKind,
63        arg1: Value,
64        arg2: Value,
65    },
66    /// Cast the type of a value without changing its actual content.
67    BitCast(Value, Type),
68    /// An unconditional jump.
69    Branch(BranchToWithArgs),
70    /// A function call with a list of arguments.
71    Call(Function, Vec<Value>),
72    /// Cast a value's type from one pointer to another.
73    CastPtr(Value, Type),
74    /// Comparison between two values using various comparators and returning a boolean.
75    Cmp(Predicate, Value, Value),
76    /// A conditional jump with the boolean condition value and true or false destinations.
77    ConditionalBranch {
78        cond_value: Value,
79        true_block: BranchToWithArgs,
80        false_block: BranchToWithArgs,
81    },
82    /// A contract call with a list of arguments
83    ContractCall {
84        return_type: Type,
85        name: Option<String>,
86        params: Value,
87        coins: Value,
88        asset_id: Value,
89        gas: Value,
90    },
91    /// Umbrella instruction variant for FuelVM-specific instructions
92    FuelVm(FuelVmInstruction),
93    /// Return a pointer to a local variable.
94    GetLocal(LocalVar),
95    /// Return a pointer to a global variable.
96    GetGlobal(GlobalVar),
97    /// Return a pointer to a configurable.
98    GetConfig(Module, String),
99    GetStorageKey(StorageKey),
100    /// Translate a pointer from a base to a nested element in an aggregate type.
101    GetElemPtr {
102        base: Value,
103        elem_ptr_ty: Type,
104        indices: Vec<Value>,
105    },
106    /// Re-interpret an integer value as pointer of some type
107    IntToPtr(Value, Type),
108    /// Read a value from a memory pointer.
109    Load(Value),
110    /// Copy a specified number of bytes between pointers.
111    MemCopyBytes {
112        dst_val_ptr: Value,
113        src_val_ptr: Value,
114        byte_len: u64,
115    },
116    /// Copy a value from one pointer to another.
117    MemCopyVal {
118        dst_val_ptr: Value,
119        src_val_ptr: Value,
120    },
121    /// Clear a value, fills with zero
122    MemClearVal {
123        dst_val_ptr: Value,
124    },
125    /// No-op, handy as a placeholder instruction.
126    Nop,
127    /// Cast a pointer to an integer.
128    PtrToInt(Value, Type),
129    /// Return from a function.
130    Ret(Value, Type),
131    /// Write a value to a memory pointer.
132    Store {
133        dst_val_ptr: Value,
134        stored_val: Value,
135    },
136    /// Allocate `count` objects of type `ty` on the heap.
137    Alloc {
138        ty: Type,
139        count: Value,
140    },
141}
142
143/// Metadata describing a logged event.
144///
145/// The value is packed into a `u64` by [`Self::encoded`] using the following layout:
146/// - bits 63..=56: `version`
147/// - bits 55..=48: `is_event` flag (stored as an 8-bit boolean for compatibility)
148/// - bits 47..=40: `is_indexed` flag (stored as an 8-bit boolean for compatibility)
149/// - bits 39..=32: `event_type_size` in bytes (fixed-size indexed payloads only)
150/// - bits 31..=16: `num_elements` (number of indexed fields)
151/// - bits 15..=0: reserved for future use (currently zeroed)
152#[derive(Debug, Clone, Copy, DebugWithContext)]
153pub struct LogEventData {
154    version: u8,
155    is_event: bool,
156    is_indexed: bool,
157    event_type_size: u8,
158    num_elements: u16,
159}
160
161impl Default for LogEventData {
162    fn default() -> Self {
163        Self {
164            version: Self::CURRENT_VERSION,
165            is_event: false,
166            is_indexed: false,
167            event_type_size: 0,
168            num_elements: 0,
169        }
170    }
171}
172
173impl LogEventData {
174    pub const CURRENT_VERSION: u8 = 0;
175
176    pub fn new(
177        version: u8,
178        is_event: bool,
179        is_indexed: bool,
180        event_type_size: u8,
181        num_elements: u16,
182    ) -> Self {
183        Self {
184            version,
185            is_event,
186            is_indexed,
187            event_type_size,
188            num_elements,
189        }
190    }
191
192    pub fn for_event(indexed_field_size: Option<u8>, indexed_field_count: u16) -> Self {
193        match (indexed_field_size, indexed_field_count) {
194            (Some(size), count) if count > 0 => Self {
195                version: Self::CURRENT_VERSION,
196                is_event: true,
197                is_indexed: true,
198                event_type_size: size,
199                num_elements: count,
200            },
201            _ => Self {
202                version: Self::CURRENT_VERSION,
203                is_event: true,
204                is_indexed: false,
205                event_type_size: 0,
206                num_elements: 0,
207            },
208        }
209    }
210
211    pub fn version(&self) -> u8 {
212        self.version
213    }
214
215    pub fn is_event(&self) -> bool {
216        self.is_event
217    }
218
219    pub fn is_indexed(&self) -> bool {
220        self.is_indexed
221    }
222
223    pub fn event_type_size(&self) -> u8 {
224        self.event_type_size
225    }
226
227    pub fn num_elements(&self) -> u16 {
228        self.num_elements
229    }
230
231    /// Returns the packed representation consumed by the backend.
232    ///
233    /// Boolean flags are stored as bytes—rather than single bits—to match the
234    /// FuelVM ABI expectations, leaving the lower 16 bits available for future
235    /// extensions without breaking the encoding.
236    pub fn encoded(&self) -> u64 {
237        (u64::from(self.version) << 56)
238            | (u64::from(self.is_event as u8) << 48)
239            | (u64::from(self.is_indexed as u8) << 40)
240            | (u64::from(self.event_type_size) << 32)
241            | (u64::from(self.num_elements) << 16)
242    }
243}
244
245#[derive(Debug, Clone, DebugWithContext)]
246pub enum FuelVmInstruction {
247    Gtf {
248        index: Value,
249        tx_field_id: u64,
250    },
251    /// Logs a value along with an identifier.
252    Log {
253        log_val: Value,
254        log_ty: Type,
255        log_id: Value,
256        log_data: Option<LogEventData>,
257    },
258    /// Reads a special register in the VM.
259    ReadRegister(Register),
260    /// Revert VM execution.
261    Revert(Value),
262    /// - Sends a message to an output via the `smo` FuelVM instruction.
263    /// - The first operand must be a `B256` representing the recipient.
264    /// - The second operand is the message data being sent.
265    /// - `message_size` and `coins` must be of type `U64`.
266    Smo {
267        recipient: Value,
268        message: Value,
269        message_size: Value,
270        coins: Value,
271    },
272    /// Clears `number_of_slots` storage slots (`b256` each) starting at key `key`.
273    StateClear {
274        key: Value,
275        number_of_slots: Value,
276    },
277    /// Reads `number_of_slots` slots (`b256` each) from storage starting at key `key` and stores
278    /// them in memory starting at address `load_val`.
279    StateLoadQuadWord {
280        load_val: Value,
281        key: Value,
282        number_of_slots: Value,
283    },
284    /// Reads and returns single word from a storage slot.
285    StateLoadWord(Value),
286    /// Stores `number_of_slots` slots (`b256` each) starting at address `stored_val` in memory into
287    /// storage starting at key `key`. `key` must be a `b256`.
288    StateStoreQuadWord {
289        stored_val: Value,
290        key: Value,
291        number_of_slots: Value,
292    },
293    /// Writes a single word to a storage slot. `key` must be a `b256` and the type of `stored_val`
294    /// must be a `u64`.
295    StateStoreWord {
296        stored_val: Value,
297        key: Value,
298    },
299    WideUnaryOp {
300        op: UnaryOpKind,
301        result: Value,
302        arg: Value,
303    },
304    WideBinaryOp {
305        op: BinaryOpKind,
306        result: Value,
307        arg1: Value,
308        arg2: Value,
309    },
310    WideModularOp {
311        op: BinaryOpKind,
312        result: Value,
313        arg1: Value,
314        arg2: Value,
315        arg3: Value,
316    },
317    WideCmpOp {
318        op: Predicate,
319        arg1: Value,
320        arg2: Value,
321    },
322    JmpMem,
323    Retd {
324        ptr: Value,
325        len: Value,
326    },
327}
328
329/// Comparison operations.
330#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
331pub enum Predicate {
332    Equal,
333    LessThan,
334    GreaterThan,
335}
336
337#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
338pub enum UnaryOpKind {
339    Not,
340}
341
342#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
343pub enum BinaryOpKind {
344    Add,
345    Sub,
346    Mul,
347    Div,
348    And,
349    Or,
350    Xor,
351    Mod,
352    Rsh,
353    Lsh,
354}
355
356/// Special registers in the Fuel Virtual Machine.
357#[derive(Debug, Clone, Copy, Hash)]
358pub enum Register {
359    /// Contains overflow/underflow of addition, subtraction, and multiplication.
360    Of,
361    /// The program counter. Memory address of the current instruction.
362    Pc,
363    /// Memory address of bottom of current writable stack area.
364    Ssp,
365    /// Memory address on top of current writable stack area (points to free memory).
366    Sp,
367    /// Memory address of beginning of current call frame.
368    Fp,
369    /// Memory address below the current bottom of the heap (points to free memory).
370    Hp,
371    /// Error codes for particular operations.
372    Error,
373    /// Remaining gas globally.
374    Ggas,
375    /// Remaining gas in the context.
376    Cgas,
377    /// Received balance for this context.
378    Bal,
379    /// Pointer to the start of the currently-executing code.
380    Is,
381    /// Return value or pointer.
382    Ret,
383    /// Return value length in bytes.
384    Retl,
385    /// Flags register.
386    Flag,
387}
388
389impl InstOp {
390    /// Some [`Instruction`]s can return a value, but for some a return value doesn't make sense.
391    ///
392    /// Those which perform side effects such as writing to memory and also terminators such as
393    /// `Ret` do not have a type.
394    pub fn get_type(&self, context: &Context) -> Option<Type> {
395        match self {
396            // These all return something in particular.
397            InstOp::AsmBlock(asm_block, _) => Some(asm_block.return_type),
398            InstOp::UnaryOp { arg, .. } => arg.get_type(context),
399            InstOp::BinaryOp { arg1, .. } => arg1.get_type(context),
400            InstOp::BitCast(_, ty) => Some(*ty),
401            InstOp::Call(function, _) => Some(context.functions[function.0].return_type),
402            InstOp::CastPtr(_val, ty) => Some(*ty),
403            InstOp::Cmp(..) => Some(Type::get_bool(context)),
404            InstOp::ContractCall { return_type, .. } => Some(*return_type),
405            InstOp::FuelVm(FuelVmInstruction::Gtf { .. }) => Some(Type::get_uint64(context)),
406            InstOp::FuelVm(FuelVmInstruction::Log { .. }) => Some(Type::get_unit(context)),
407            InstOp::FuelVm(FuelVmInstruction::ReadRegister(_)) => Some(Type::get_uint64(context)),
408            InstOp::FuelVm(FuelVmInstruction::Smo { .. }) => Some(Type::get_unit(context)),
409
410            // Load needs to strip the pointer from the source type.
411            InstOp::Load(ptr_val) => match &context.values[ptr_val.0].value {
412                ValueDatum::Argument(arg) => arg.ty.get_pointee_type(context),
413                ValueDatum::Constant(cons) => {
414                    cons.get_content(context).ty.get_pointee_type(context)
415                }
416                ValueDatum::Instruction(ins) => ins
417                    .get_type(context)
418                    .and_then(|ty| ty.get_pointee_type(context)),
419            },
420
421            // These return pointer types.
422            InstOp::GetElemPtr { elem_ptr_ty, .. } => Some(*elem_ptr_ty),
423            InstOp::GetLocal(local_var) => Some(local_var.get_type(context)),
424            InstOp::GetGlobal(global_var) => Some(global_var.get_type(context)),
425            InstOp::GetConfig(module, name) => Some(match module.get_config(context, name)? {
426                crate::ConfigContent::V0 { ptr_ty, .. } => *ptr_ty,
427                crate::ConfigContent::V1 { ptr_ty, .. } => *ptr_ty,
428            }),
429            InstOp::GetStorageKey(storage_key) => Some(storage_key.get_type(context)),
430            InstOp::Alloc { ty: _, count: _ } => Some(Type::get_ptr(context)),
431
432            // Use for casting between pointers and pointer-width integers.
433            InstOp::IntToPtr(_, ptr_ty) => Some(*ptr_ty),
434            InstOp::PtrToInt(_, int_ty) => Some(*int_ty),
435
436            // These are all terminators which don't return, essentially.  No type.
437            InstOp::Branch(_)
438            | InstOp::ConditionalBranch { .. }
439            | InstOp::FuelVm(
440                FuelVmInstruction::Revert(..)
441                | FuelVmInstruction::JmpMem
442                | FuelVmInstruction::Retd { .. },
443            )
444            | InstOp::Ret(..) => None,
445
446            // No-op is also no-type.
447            InstOp::Nop => None,
448
449            // State load returns a u64, other state ops return a bool.
450            InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_)) => Some(Type::get_uint64(context)),
451            InstOp::FuelVm(FuelVmInstruction::StateClear { .. })
452            | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
453            | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
454            | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. }) => {
455                Some(Type::get_bool(context))
456            }
457
458            // Memory writes return unit.
459            InstOp::MemCopyBytes { .. }
460            | InstOp::MemCopyVal { .. }
461            | InstOp::MemClearVal { .. }
462            | InstOp::Store { .. } => Some(Type::get_unit(context)),
463
464            // Wide Operations
465            InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { result, .. }) => {
466                result.get_type(context)
467            }
468            InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { result, .. }) => {
469                result.get_type(context)
470            }
471            InstOp::FuelVm(FuelVmInstruction::WideCmpOp { .. }) => Some(Type::get_bool(context)),
472            InstOp::FuelVm(FuelVmInstruction::WideModularOp { result, .. }) => {
473                result.get_type(context)
474            }
475        }
476    }
477
478    pub fn get_operands(&self) -> Vec<Value> {
479        match self {
480            InstOp::AsmBlock(_, args) => args.iter().filter_map(|aa| aa.initializer).collect(),
481            InstOp::BitCast(v, _) => vec![*v],
482            InstOp::UnaryOp { op: _, arg } => vec![*arg],
483            InstOp::BinaryOp { op: _, arg1, arg2 } => vec![*arg1, *arg2],
484            InstOp::Branch(BranchToWithArgs { args, .. }) => args.clone(),
485            InstOp::Call(_, vs) => vs.clone(),
486            InstOp::CastPtr(val, _ty) => vec![*val],
487            InstOp::Cmp(_, lhs, rhs) => vec![*lhs, *rhs],
488            InstOp::ConditionalBranch {
489                cond_value,
490                true_block,
491                false_block,
492            } => {
493                let mut v = vec![*cond_value];
494                v.extend_from_slice(&true_block.args);
495                v.extend_from_slice(&false_block.args);
496                v
497            }
498            InstOp::ContractCall {
499                return_type: _,
500                name: _,
501                params,
502                coins,
503                asset_id,
504                gas,
505            } => vec![*params, *coins, *asset_id, *gas],
506            InstOp::GetElemPtr {
507                base,
508                elem_ptr_ty: _,
509                indices,
510            } => {
511                let mut vals = indices.clone();
512                vals.push(*base);
513                vals
514            }
515            InstOp::GetLocal(_local_var) => {
516                // `GetLocal` returns an SSA `Value` but does not take any as an operand.
517                vec![]
518            }
519            InstOp::GetGlobal(_global_var) => {
520                // `GetGlobal` returns an SSA `Value` but does not take any as an operand.
521                vec![]
522            }
523            InstOp::GetConfig(_, _) => {
524                // `GetConfig` returns an SSA `Value` but does not take any as an operand.
525                vec![]
526            }
527            InstOp::GetStorageKey(_) => {
528                // `GetStorageKey` returns an SSA `Value` but does not take any as an operand.
529                vec![]
530            }
531            InstOp::Alloc { ty: _, count } => vec![*count],
532            InstOp::IntToPtr(v, _) => vec![*v],
533            InstOp::Load(v) => vec![*v],
534            InstOp::MemCopyBytes {
535                dst_val_ptr,
536                src_val_ptr,
537                byte_len: _,
538            } => {
539                vec![*dst_val_ptr, *src_val_ptr]
540            }
541            InstOp::MemCopyVal {
542                dst_val_ptr,
543                src_val_ptr,
544            } => {
545                vec![*dst_val_ptr, *src_val_ptr]
546            }
547            InstOp::MemClearVal { dst_val_ptr } => {
548                vec![*dst_val_ptr]
549            }
550            InstOp::Nop => vec![],
551            InstOp::PtrToInt(v, _) => vec![*v],
552            InstOp::Ret(v, _) => vec![*v],
553            InstOp::Store {
554                dst_val_ptr,
555                stored_val,
556            } => {
557                vec![*dst_val_ptr, *stored_val]
558            }
559
560            InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
561                FuelVmInstruction::Gtf {
562                    index,
563                    tx_field_id: _,
564                } => vec![*index],
565                FuelVmInstruction::Log {
566                    log_val, log_id, ..
567                } => vec![*log_val, *log_id],
568                FuelVmInstruction::ReadRegister(_) => vec![],
569                FuelVmInstruction::Revert(v) => vec![*v],
570                FuelVmInstruction::JmpMem => vec![],
571                FuelVmInstruction::Smo {
572                    recipient,
573                    message,
574                    message_size,
575                    coins,
576                } => vec![*recipient, *message, *message_size, *coins],
577                FuelVmInstruction::StateClear {
578                    key,
579                    number_of_slots,
580                } => vec![*key, *number_of_slots],
581                FuelVmInstruction::StateLoadQuadWord {
582                    load_val,
583                    key,
584                    number_of_slots,
585                } => vec![*load_val, *key, *number_of_slots],
586                FuelVmInstruction::StateLoadWord(key) => vec![*key],
587                FuelVmInstruction::StateStoreQuadWord {
588                    stored_val,
589                    key,
590                    number_of_slots,
591                } => {
592                    vec![*stored_val, *key, *number_of_slots]
593                }
594                FuelVmInstruction::StateStoreWord { stored_val, key } => vec![*stored_val, *key],
595                FuelVmInstruction::WideUnaryOp { arg, result, .. } => vec![*result, *arg],
596                FuelVmInstruction::WideBinaryOp {
597                    arg1, arg2, result, ..
598                } => vec![*result, *arg1, *arg2],
599                FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => vec![*arg1, *arg2],
600                FuelVmInstruction::WideModularOp {
601                    result,
602                    arg1,
603                    arg2,
604                    arg3,
605                    ..
606                } => vec![*result, *arg1, *arg2, *arg3],
607                FuelVmInstruction::Retd { ptr, len } => {
608                    vec![*ptr, *len]
609                }
610            },
611        }
612    }
613
614    /// Set the operand at the given index to the provided value.
615    /// The indices are in the same order as returned by `get_operands`.
616    pub fn set_operand(&mut self, replacement: Value, idx: usize) {
617        match self {
618            InstOp::AsmBlock(_, args) => {
619                // Because get_operand only returns operands that have an
620                // initializer, we also iterate over only those, to match indices.
621                let mut cur_idx = 0;
622                for arg in args.iter_mut() {
623                    if let Some(_asm_arg) = arg.initializer {
624                        if cur_idx == idx {
625                            arg.initializer = Some(replacement);
626                            return;
627                        }
628                        cur_idx += 1;
629                    }
630                }
631                panic!("Invalid index for AsmBlock");
632            }
633            InstOp::BitCast(v, _) | InstOp::UnaryOp { arg: v, .. } => {
634                if idx == 0 {
635                    *v = replacement;
636                } else {
637                    panic!("Invalid index for Op");
638                }
639            }
640            InstOp::BinaryOp { op: _, arg1, arg2 } => {
641                if idx == 0 {
642                    *arg1 = replacement;
643                } else if idx == 1 {
644                    *arg2 = replacement;
645                } else {
646                    panic!("Invalid index for BinaryOp");
647                }
648            }
649            InstOp::Branch(BranchToWithArgs { args, .. }) => {
650                if idx < args.len() {
651                    args[idx] = replacement;
652                } else {
653                    panic!("Invalid index for Branch");
654                }
655            }
656            InstOp::Call(_, vs) => {
657                if idx < vs.len() {
658                    vs[idx] = replacement;
659                } else {
660                    panic!("Invalid index for Call");
661                }
662            }
663            InstOp::CastPtr(val, _ty) => {
664                if idx == 0 {
665                    *val = replacement;
666                } else {
667                    panic!("Invalid index for CastPtr");
668                }
669            }
670            InstOp::Cmp(_, lhs, rhs) => {
671                if idx == 0 {
672                    *lhs = replacement;
673                } else if idx == 1 {
674                    *rhs = replacement;
675                } else {
676                    panic!("Invalid index for Cmp");
677                }
678            }
679            InstOp::ConditionalBranch {
680                cond_value,
681                true_block,
682                false_block,
683            } => {
684                if idx == 0 {
685                    *cond_value = replacement;
686                } else if idx - 1 < true_block.args.len() {
687                    true_block.args[idx - 1] = replacement;
688                } else if idx - 1 - true_block.args.len() < false_block.args.len() {
689                    false_block.args[idx - 1 - true_block.args.len()] = replacement;
690                } else {
691                    panic!("Invalid index for ConditionalBranch");
692                }
693            }
694            InstOp::ContractCall {
695                return_type: _,
696                name: _,
697                params,
698                coins,
699                asset_id,
700                gas,
701            } => {
702                if idx == 0 {
703                    *params = replacement;
704                } else if idx == 1 {
705                    *coins = replacement;
706                } else if idx == 2 {
707                    *asset_id = replacement;
708                } else if idx == 3 {
709                    *gas = replacement;
710                } else {
711                    panic!("Invalid index for ContractCall");
712                }
713            }
714            InstOp::GetElemPtr {
715                base,
716                elem_ptr_ty: _,
717                indices,
718            } => {
719                use std::cmp::Ordering;
720                match idx.cmp(&indices.len()) {
721                    Ordering::Less => {
722                        indices[idx] = replacement;
723                    }
724                    Ordering::Equal => {
725                        *base = replacement;
726                    }
727                    Ordering::Greater => {
728                        panic!("Invalid index for GetElemPtr");
729                    }
730                }
731            }
732            InstOp::GetLocal(_local_var) => {
733                // `GetLocal` returns an SSA `Value` but does not take any as an operand.
734                panic!("Invalid index for GetLocal");
735            }
736            InstOp::GetGlobal(_global_var) => {
737                // `GetGlobal` returns an SSA `Value` but does not take any as an operand.
738                panic!("Invalid index for GetGlobal");
739            }
740            InstOp::GetConfig(_, _) => {
741                // `GetConfig` returns an SSA `Value` but does not take any as an operand.
742                panic!("Invalid index for GetConfig");
743            }
744            InstOp::GetStorageKey(_) => {
745                // `GetStorageKey` returns an SSA `Value` but does not take any as an operand.
746                panic!("Invalid index for GetStorageKey");
747            }
748            InstOp::Alloc { ty: _, count } => {
749                if idx == 0 {
750                    *count = replacement;
751                } else {
752                    panic!("Invalid index for Alloc");
753                }
754            }
755            InstOp::IntToPtr(v, _) => {
756                if idx == 0 {
757                    *v = replacement;
758                } else {
759                    panic!("Invalid index for IntToPtr");
760                }
761            }
762            InstOp::Load(v) => {
763                if idx == 0 {
764                    *v = replacement;
765                } else {
766                    panic!("Invalid index for Load");
767                }
768            }
769            InstOp::MemCopyBytes {
770                dst_val_ptr,
771                src_val_ptr,
772                byte_len: _,
773            } => {
774                if idx == 0 {
775                    *dst_val_ptr = replacement;
776                } else if idx == 1 {
777                    *src_val_ptr = replacement;
778                } else {
779                    panic!("Invalid index for MemCopyBytes");
780                }
781            }
782            InstOp::MemCopyVal {
783                dst_val_ptr,
784                src_val_ptr,
785            } => {
786                if idx == 0 {
787                    *dst_val_ptr = replacement;
788                } else if idx == 1 {
789                    *src_val_ptr = replacement;
790                } else {
791                    panic!("Invalid index for MemCopyVal");
792                }
793            }
794            InstOp::MemClearVal { dst_val_ptr } => {
795                if idx == 0 {
796                    *dst_val_ptr = replacement;
797                } else {
798                    panic!("Invalid index for MemClearVal");
799                }
800            }
801            InstOp::Nop => (),
802            InstOp::PtrToInt(v, _) => {
803                if idx == 0 {
804                    *v = replacement;
805                } else {
806                    panic!("Invalid index for PtrToInt");
807                }
808            }
809            InstOp::Ret(v, _) => {
810                if idx == 0 {
811                    *v = replacement;
812                } else {
813                    panic!("Invalid index for Ret");
814                }
815            }
816            InstOp::Store {
817                dst_val_ptr,
818                stored_val,
819            } => {
820                if idx == 0 {
821                    *dst_val_ptr = replacement;
822                } else if idx == 1 {
823                    *stored_val = replacement;
824                } else {
825                    panic!("Invalid index for Store");
826                }
827            }
828
829            InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
830                FuelVmInstruction::Gtf {
831                    index,
832                    tx_field_id: _,
833                } => {
834                    if idx == 0 {
835                        *index = replacement;
836                    } else {
837                        panic!("Invalid index for Gtf");
838                    }
839                }
840                FuelVmInstruction::Log {
841                    log_val, log_id, ..
842                } => {
843                    if idx == 0 {
844                        *log_val = replacement;
845                    } else if idx == 1 {
846                        *log_id = replacement;
847                    } else {
848                        panic!("Invalid index for Log");
849                    }
850                }
851                FuelVmInstruction::ReadRegister(_) => {
852                    // `ReadRegister` returns an SSA `Value` but does not take any as an operand.
853                    panic!("Invalid index for ReadRegister");
854                }
855                FuelVmInstruction::Revert(v) => {
856                    if idx == 0 {
857                        *v = replacement;
858                    } else {
859                        panic!("Invalid index for Revert");
860                    }
861                }
862                FuelVmInstruction::JmpMem => {
863                    // `JmpMem` does not take any operand.
864                    panic!("Invalid index for JmpMem");
865                }
866                FuelVmInstruction::Smo {
867                    recipient,
868                    message,
869                    message_size,
870                    coins,
871                } => {
872                    if idx == 0 {
873                        *recipient = replacement;
874                    } else if idx == 1 {
875                        *message = replacement;
876                    } else if idx == 2 {
877                        *message_size = replacement;
878                    } else if idx == 3 {
879                        *coins = replacement;
880                    } else {
881                        panic!("Invalid index for Smo");
882                    }
883                }
884                FuelVmInstruction::StateClear {
885                    key,
886                    number_of_slots,
887                } => {
888                    if idx == 0 {
889                        *key = replacement;
890                    } else if idx == 1 {
891                        *number_of_slots = replacement;
892                    } else {
893                        panic!("Invalid index for StateClear");
894                    }
895                }
896                FuelVmInstruction::StateLoadQuadWord {
897                    load_val,
898                    key,
899                    number_of_slots,
900                } => {
901                    if idx == 0 {
902                        *load_val = replacement;
903                    } else if idx == 1 {
904                        *key = replacement;
905                    } else if idx == 2 {
906                        *number_of_slots = replacement;
907                    } else {
908                        panic!("Invalid index for StateLoadQuadWord");
909                    }
910                }
911                FuelVmInstruction::StateLoadWord(key) => {
912                    if idx == 0 {
913                        *key = replacement;
914                    } else {
915                        panic!("Invalid index for StateLoadWord");
916                    }
917                }
918                FuelVmInstruction::StateStoreQuadWord {
919                    stored_val,
920                    key,
921                    number_of_slots,
922                } => {
923                    if idx == 0 {
924                        *stored_val = replacement;
925                    } else if idx == 1 {
926                        *key = replacement;
927                    } else if idx == 2 {
928                        *number_of_slots = replacement;
929                    } else {
930                        panic!("Invalid index for StateStoreQuadWord");
931                    }
932                }
933                FuelVmInstruction::StateStoreWord { stored_val, key } => {
934                    if idx == 0 {
935                        *stored_val = replacement;
936                    } else if idx == 1 {
937                        *key = replacement;
938                    } else {
939                        panic!("Invalid index for StateStoreWord");
940                    }
941                }
942                FuelVmInstruction::WideUnaryOp { arg, result, .. } => {
943                    if idx == 0 {
944                        *result = replacement;
945                    } else if idx == 1 {
946                        *arg = replacement;
947                    } else {
948                        panic!("Invalid index for WideUnaryOp");
949                    }
950                }
951                FuelVmInstruction::WideBinaryOp {
952                    arg1, arg2, result, ..
953                } => {
954                    if idx == 0 {
955                        *result = replacement;
956                    } else if idx == 1 {
957                        *arg1 = replacement;
958                    } else if idx == 2 {
959                        *arg2 = replacement;
960                    } else {
961                        panic!("Invalid index for WideBinaryOp");
962                    }
963                }
964                FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => {
965                    if idx == 0 {
966                        *arg1 = replacement;
967                    } else if idx == 1 {
968                        *arg2 = replacement;
969                    } else {
970                        panic!("Invalid index for WideCmpOp");
971                    }
972                }
973                FuelVmInstruction::WideModularOp {
974                    result,
975                    arg1,
976                    arg2,
977                    arg3,
978                    ..
979                } => {
980                    if idx == 0 {
981                        *result = replacement;
982                    } else if idx == 1 {
983                        *arg1 = replacement;
984                    } else if idx == 2 {
985                        *arg2 = replacement;
986                    } else if idx == 3 {
987                        *arg3 = replacement;
988                    } else {
989                        panic!("Invalid index for WideModularOp");
990                    }
991                }
992                FuelVmInstruction::Retd { ptr, len } => {
993                    if idx == 0 {
994                        *ptr = replacement;
995                    } else if idx == 1 {
996                        *len = replacement;
997                    } else {
998                        panic!("Invalid index for Retd");
999                    }
1000                }
1001            },
1002        }
1003    }
1004
1005    /// Replace `old_val` with `new_val` if it is referenced by this instruction's arguments.
1006    pub fn replace_values(&mut self, replace_map: &FxHashMap<Value, Value>) {
1007        let replace = |val: &mut Value| {
1008            while let Some(new_val) = replace_map.get(val) {
1009                *val = *new_val;
1010            }
1011        };
1012        match self {
1013            InstOp::AsmBlock(_, args) => args
1014                .iter_mut()
1015                .for_each(|asm_arg| asm_arg.initializer.iter_mut().for_each(replace)),
1016            InstOp::BitCast(value, _) => replace(value),
1017            InstOp::UnaryOp { op: _, arg } => {
1018                replace(arg);
1019            }
1020            InstOp::BinaryOp { op: _, arg1, arg2 } => {
1021                replace(arg1);
1022                replace(arg2);
1023            }
1024            InstOp::Branch(block) => {
1025                block.args.iter_mut().for_each(replace);
1026            }
1027            InstOp::Call(_, args) => args.iter_mut().for_each(replace),
1028            InstOp::CastPtr(val, _ty) => replace(val),
1029            InstOp::Cmp(_, lhs_val, rhs_val) => {
1030                replace(lhs_val);
1031                replace(rhs_val);
1032            }
1033            InstOp::ConditionalBranch {
1034                cond_value,
1035                true_block,
1036                false_block,
1037            } => {
1038                replace(cond_value);
1039                true_block.args.iter_mut().for_each(replace);
1040                false_block.args.iter_mut().for_each(replace);
1041            }
1042            InstOp::ContractCall {
1043                params,
1044                coins,
1045                asset_id,
1046                gas,
1047                ..
1048            } => {
1049                replace(params);
1050                replace(coins);
1051                replace(asset_id);
1052                replace(gas);
1053            }
1054            InstOp::GetLocal(_) => (),
1055            InstOp::GetGlobal(_) => (),
1056            InstOp::GetConfig(_, _) => (),
1057            InstOp::GetStorageKey(_) => (),
1058            InstOp::GetElemPtr {
1059                base,
1060                elem_ptr_ty: _,
1061                indices,
1062            } => {
1063                replace(base);
1064                indices.iter_mut().for_each(replace);
1065            }
1066            InstOp::Alloc { ty: _, count } => replace(count),
1067            InstOp::IntToPtr(value, _) => replace(value),
1068            InstOp::Load(ptr) => replace(ptr),
1069            InstOp::MemCopyBytes {
1070                dst_val_ptr,
1071                src_val_ptr,
1072                ..
1073            } => {
1074                replace(dst_val_ptr);
1075                replace(src_val_ptr);
1076            }
1077            InstOp::MemCopyVal {
1078                dst_val_ptr,
1079                src_val_ptr,
1080            } => {
1081                replace(dst_val_ptr);
1082                replace(src_val_ptr);
1083            }
1084            InstOp::MemClearVal { dst_val_ptr } => {
1085                replace(dst_val_ptr);
1086            }
1087            InstOp::Nop => (),
1088            InstOp::PtrToInt(value, _) => replace(value),
1089            InstOp::Ret(ret_val, _) => replace(ret_val),
1090            InstOp::Store {
1091                stored_val,
1092                dst_val_ptr,
1093            } => {
1094                replace(stored_val);
1095                replace(dst_val_ptr);
1096            }
1097
1098            InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
1099                FuelVmInstruction::Gtf { index, .. } => replace(index),
1100                FuelVmInstruction::Log {
1101                    log_val, log_id, ..
1102                } => {
1103                    replace(log_val);
1104                    replace(log_id);
1105                }
1106                FuelVmInstruction::ReadRegister { .. } => (),
1107                FuelVmInstruction::Revert(revert_val) => replace(revert_val),
1108                FuelVmInstruction::JmpMem => (),
1109                FuelVmInstruction::Smo {
1110                    recipient,
1111                    message,
1112                    message_size,
1113                    coins,
1114                } => {
1115                    replace(recipient);
1116                    replace(message);
1117                    replace(message_size);
1118                    replace(coins);
1119                }
1120                FuelVmInstruction::StateClear {
1121                    key,
1122                    number_of_slots,
1123                } => {
1124                    replace(key);
1125                    replace(number_of_slots);
1126                }
1127                FuelVmInstruction::StateLoadQuadWord {
1128                    load_val,
1129                    key,
1130                    number_of_slots,
1131                } => {
1132                    replace(load_val);
1133                    replace(key);
1134                    replace(number_of_slots);
1135                }
1136                FuelVmInstruction::StateLoadWord(key) => {
1137                    replace(key);
1138                }
1139                FuelVmInstruction::StateStoreQuadWord {
1140                    stored_val,
1141                    key,
1142                    number_of_slots,
1143                } => {
1144                    replace(key);
1145                    replace(stored_val);
1146                    replace(number_of_slots);
1147                }
1148                FuelVmInstruction::StateStoreWord { stored_val, key } => {
1149                    replace(key);
1150                    replace(stored_val);
1151                }
1152                FuelVmInstruction::WideUnaryOp { arg, result, .. } => {
1153                    replace(arg);
1154                    replace(result);
1155                }
1156                FuelVmInstruction::WideBinaryOp {
1157                    arg1, arg2, result, ..
1158                } => {
1159                    replace(arg1);
1160                    replace(arg2);
1161                    replace(result);
1162                }
1163                FuelVmInstruction::WideCmpOp { arg1, arg2, .. } => {
1164                    replace(arg1);
1165                    replace(arg2);
1166                }
1167                FuelVmInstruction::WideModularOp {
1168                    result,
1169                    arg1,
1170                    arg2,
1171                    arg3,
1172                    ..
1173                } => {
1174                    replace(result);
1175                    replace(arg1);
1176                    replace(arg2);
1177                    replace(arg3);
1178                }
1179                FuelVmInstruction::Retd { ptr, len } => {
1180                    replace(ptr);
1181                    replace(len);
1182                }
1183            },
1184        }
1185    }
1186
1187    pub fn may_have_side_effect(&self) -> bool {
1188        match self {
1189            InstOp::AsmBlock(asm, _) => !asm.body.is_empty(),
1190            InstOp::Call(..)
1191            | InstOp::ContractCall { .. }
1192            | InstOp::FuelVm(FuelVmInstruction::Log { .. })
1193            | InstOp::FuelVm(FuelVmInstruction::Smo { .. })
1194            | InstOp::FuelVm(FuelVmInstruction::StateClear { .. })
1195            | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
1196            | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
1197            | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. })
1198            | InstOp::FuelVm(FuelVmInstruction::Revert(..))
1199            | InstOp::FuelVm(FuelVmInstruction::JmpMem)
1200            | InstOp::FuelVm(FuelVmInstruction::Retd { .. })
1201            | InstOp::MemCopyBytes { .. }
1202            | InstOp::MemCopyVal { .. }
1203            | InstOp::MemClearVal { .. }
1204            | InstOp::Store { .. }
1205            | InstOp::Ret(..)
1206            | InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { .. })
1207            | InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { .. })
1208            | InstOp::FuelVm(FuelVmInstruction::WideCmpOp { .. })
1209            | InstOp::FuelVm(FuelVmInstruction::WideModularOp { .. }) => true,
1210
1211            InstOp::UnaryOp { .. }
1212            | InstOp::BinaryOp { .. }
1213            | InstOp::BitCast(..)
1214            | InstOp::Branch(_)
1215            | InstOp::CastPtr { .. }
1216            | InstOp::Cmp(..)
1217            | InstOp::ConditionalBranch { .. }
1218            | InstOp::FuelVm(FuelVmInstruction::Gtf { .. })
1219            | InstOp::FuelVm(FuelVmInstruction::ReadRegister(_))
1220            | InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_))
1221            | InstOp::GetElemPtr { .. }
1222            | InstOp::GetLocal(_)
1223            | InstOp::GetGlobal(_)
1224            | InstOp::GetConfig(_, _)
1225            | InstOp::GetStorageKey(_)
1226            | InstOp::IntToPtr(..)
1227            | InstOp::Load(_)
1228            | InstOp::Nop
1229            | InstOp::PtrToInt(..) => false,
1230
1231            // Although `Alloc` is a side-effecting operation in the general sense (it changes
1232            // program state by allocating memory), it does not have observable side effects that
1233            // would affect program behavior in a way that matters for optimizations like
1234            // dead code elimination. Therefore, we treat it as not having side effects here.
1235            InstOp::Alloc { .. } => false,
1236        }
1237    }
1238
1239    pub fn is_terminator(&self) -> bool {
1240        matches!(
1241            self,
1242            InstOp::Branch(_)
1243                | InstOp::ConditionalBranch { .. }
1244                | InstOp::Ret(..)
1245                | InstOp::FuelVm(
1246                    FuelVmInstruction::Revert(..)
1247                        | FuelVmInstruction::JmpMem
1248                        | FuelVmInstruction::Retd { .. }
1249                )
1250        )
1251    }
1252}
1253
1254/// Iterate over all [`Instruction`]s in a specific [`Block`].
1255pub struct InstructionIterator {
1256    instructions: Vec<slotmap::DefaultKey>,
1257    next: usize,
1258    next_back: isize,
1259}
1260
1261impl InstructionIterator {
1262    pub fn new(context: &Context, block: &Block) -> Self {
1263        // Copy all the current instruction indices, so they may be modified in the context during
1264        // iteration.
1265        InstructionIterator {
1266            instructions: context.blocks[block.0]
1267                .instructions
1268                .iter()
1269                .map(|val| val.0)
1270                .collect(),
1271            next: 0,
1272            next_back: context.blocks[block.0].instructions.len() as isize - 1,
1273        }
1274    }
1275}
1276
1277impl Iterator for InstructionIterator {
1278    type Item = Value;
1279
1280    fn next(&mut self) -> Option<Value> {
1281        if self.next < self.instructions.len() {
1282            let idx = self.next;
1283            self.next += 1;
1284            Some(Value(self.instructions[idx]))
1285        } else {
1286            None
1287        }
1288    }
1289}
1290
1291impl DoubleEndedIterator for InstructionIterator {
1292    fn next_back(&mut self) -> Option<Value> {
1293        if self.next_back >= 0 {
1294            let idx = self.next_back;
1295            self.next_back -= 1;
1296            Some(Value(self.instructions[idx as usize]))
1297        } else {
1298            None
1299        }
1300    }
1301}
1302
1303/// Where to insert new instructions in the block.
1304pub enum InsertionPosition {
1305    // Insert at the start of the basic block.
1306    Start,
1307    // Insert at the end of the basic block (append).
1308    End,
1309    // Insert after instruction.
1310    After(Value),
1311    // Insert before instruction.
1312    Before(Value),
1313    // Insert at position / index.
1314    At(usize),
1315}
1316
1317/// Provide a context for inserting new [`Instruction`]s to a [`Block`].
1318pub struct InstructionInserter<'a, 'eng> {
1319    context: &'a mut Context<'eng>,
1320    block: Block,
1321    position: InsertionPosition,
1322}
1323
1324macro_rules! insert_instruction {
1325    ($self: ident, $ctor: expr) => {{
1326        let instruction_val = Value::new_instruction($self.context, $self.block, $ctor);
1327        let pos = $self.get_position_index();
1328        let instructions = &mut $self.context.blocks[$self.block.0].instructions;
1329        instructions.insert(pos, instruction_val);
1330        instruction_val
1331    }};
1332}
1333
1334impl<'a, 'eng> InstructionInserter<'a, 'eng> {
1335    /// Return a new [`InstructionInserter`] context for `block`.
1336    pub fn new(
1337        context: &'a mut Context<'eng>,
1338        block: Block,
1339        position: InsertionPosition,
1340    ) -> InstructionInserter<'a, 'eng> {
1341        InstructionInserter {
1342            context,
1343            block,
1344            position,
1345        }
1346    }
1347
1348    // Recomputes the index in the instruction vec. O(n) in the worst case.
1349    fn get_position_index(&self) -> usize {
1350        let instructions = &self.context.blocks[self.block.0].instructions;
1351        match self.position {
1352            InsertionPosition::Start => 0,
1353            InsertionPosition::End => instructions.len(),
1354            InsertionPosition::After(inst) => {
1355                instructions
1356                    .iter()
1357                    .position(|val| *val == inst)
1358                    .expect("Provided position for insertion does not exist")
1359                    + 1
1360            }
1361            InsertionPosition::Before(inst) => instructions
1362                .iter()
1363                .position(|val| *val == inst)
1364                .expect("Provided position for insertion does not exist"),
1365            InsertionPosition::At(pos) => pos,
1366        }
1367    }
1368
1369    // Insert a slice of instructions.
1370    pub fn insert_slice(&mut self, slice: &[Value]) {
1371        let pos = self.get_position_index();
1372        self.context.blocks[self.block.0]
1373            .instructions
1374            .splice(pos..pos, slice.iter().cloned());
1375    }
1376
1377    // Insert a single instruction.
1378    pub fn insert(&mut self, inst: Value) {
1379        let pos = self.get_position_index();
1380        self.context.blocks[self.block.0]
1381            .instructions
1382            .insert(pos, inst);
1383    }
1384
1385    //
1386    // XXX Maybe these should return result, in case they get bad args?
1387    //
1388
1389    /// Append a new [InstOp::AsmBlock] from `args` and a `body`.
1390    pub fn asm_block(
1391        self,
1392        args: Vec<AsmArg>,
1393        body: Vec<AsmInstruction>,
1394        return_type: Type,
1395        return_name: Option<Ident>,
1396    ) -> Value {
1397        let asm = AsmBlock::new(
1398            args.iter().map(|arg| arg.name.clone()).collect(),
1399            body,
1400            return_type,
1401            return_name,
1402        );
1403        self.asm_block_from_asm(asm, args)
1404    }
1405
1406    pub fn asm_block_from_asm(self, asm: AsmBlock, args: Vec<AsmArg>) -> Value {
1407        insert_instruction!(self, InstOp::AsmBlock(asm, args))
1408    }
1409
1410    pub fn bitcast(self, value: Value, ty: Type) -> Value {
1411        insert_instruction!(self, InstOp::BitCast(value, ty))
1412    }
1413
1414    pub fn unary_op(self, op: UnaryOpKind, arg: Value) -> Value {
1415        insert_instruction!(self, InstOp::UnaryOp { op, arg })
1416    }
1417
1418    pub fn wide_unary_op(self, op: UnaryOpKind, arg: Value, result: Value) -> Value {
1419        insert_instruction!(
1420            self,
1421            InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { op, arg, result })
1422        )
1423    }
1424
1425    pub fn wide_binary_op(
1426        self,
1427        op: BinaryOpKind,
1428        arg1: Value,
1429        arg2: Value,
1430        result: Value,
1431    ) -> Value {
1432        insert_instruction!(
1433            self,
1434            InstOp::FuelVm(FuelVmInstruction::WideBinaryOp {
1435                op,
1436                arg1,
1437                arg2,
1438                result
1439            })
1440        )
1441    }
1442
1443    pub fn wide_modular_op(
1444        self,
1445        op: BinaryOpKind,
1446        result: Value,
1447        arg1: Value,
1448        arg2: Value,
1449        arg3: Value,
1450    ) -> Value {
1451        insert_instruction!(
1452            self,
1453            InstOp::FuelVm(FuelVmInstruction::WideModularOp {
1454                op,
1455                result,
1456                arg1,
1457                arg2,
1458                arg3,
1459            })
1460        )
1461    }
1462
1463    pub fn wide_cmp_op(self, op: Predicate, arg1: Value, arg2: Value) -> Value {
1464        insert_instruction!(
1465            self,
1466            InstOp::FuelVm(FuelVmInstruction::WideCmpOp { op, arg1, arg2 })
1467        )
1468    }
1469
1470    pub fn binary_op(self, op: BinaryOpKind, arg1: Value, arg2: Value) -> Value {
1471        insert_instruction!(self, InstOp::BinaryOp { op, arg1, arg2 })
1472    }
1473
1474    pub fn branch(self, to_block: Block, dest_params: Vec<Value>) -> Value {
1475        let br_val = Value::new_instruction(
1476            self.context,
1477            self.block,
1478            InstOp::Branch(BranchToWithArgs {
1479                block: to_block,
1480                args: dest_params,
1481            }),
1482        );
1483        to_block.add_pred(self.context, &self.block);
1484        self.context.blocks[self.block.0].instructions.push(br_val);
1485        br_val
1486    }
1487
1488    pub fn call(self, function: Function, args: &[Value]) -> Value {
1489        insert_instruction!(self, InstOp::Call(function, args.to_vec()))
1490    }
1491
1492    pub fn cast_ptr(self, val: Value, ty: Type) -> Value {
1493        insert_instruction!(self, InstOp::CastPtr(val, ty))
1494    }
1495
1496    pub fn cmp(self, pred: Predicate, lhs_value: Value, rhs_value: Value) -> Value {
1497        insert_instruction!(self, InstOp::Cmp(pred, lhs_value, rhs_value))
1498    }
1499
1500    pub fn conditional_branch(
1501        self,
1502        cond_value: Value,
1503        true_block: Block,
1504        false_block: Block,
1505        true_dest_params: Vec<Value>,
1506        false_dest_params: Vec<Value>,
1507    ) -> Value {
1508        let cbr_val = Value::new_instruction(
1509            self.context,
1510            self.block,
1511            InstOp::ConditionalBranch {
1512                cond_value,
1513                true_block: BranchToWithArgs {
1514                    block: true_block,
1515                    args: true_dest_params,
1516                },
1517                false_block: BranchToWithArgs {
1518                    block: false_block,
1519                    args: false_dest_params,
1520                },
1521            },
1522        );
1523        true_block.add_pred(self.context, &self.block);
1524        false_block.add_pred(self.context, &self.block);
1525        self.context.blocks[self.block.0].instructions.push(cbr_val);
1526        cbr_val
1527    }
1528
1529    pub fn contract_call(
1530        self,
1531        return_type: Type,
1532        name: Option<String>,
1533        params: Value,
1534        coins: Value,    // amount of coins to forward
1535        asset_id: Value, // b256 asset ID of the coint being forwarded
1536        gas: Value,      // amount of gas to forward
1537    ) -> Value {
1538        insert_instruction!(
1539            self,
1540            InstOp::ContractCall {
1541                return_type,
1542                name,
1543                params,
1544                coins,
1545                asset_id,
1546                gas,
1547            }
1548        )
1549    }
1550
1551    pub fn gtf(self, index: Value, tx_field_id: u64) -> Value {
1552        insert_instruction!(
1553            self,
1554            InstOp::FuelVm(FuelVmInstruction::Gtf { index, tx_field_id })
1555        )
1556    }
1557
1558    // get_elem_ptr() and get_elem_ptr_*() all take the element type and will store the pointer to
1559    // that type in the instruction, which is later returned by Instruction::get_type().
1560    pub fn get_elem_ptr(self, base: Value, elem_ty: Type, indices: Vec<Value>) -> Value {
1561        let elem_ptr_ty = Type::new_typed_pointer(self.context, elem_ty);
1562        insert_instruction!(
1563            self,
1564            InstOp::GetElemPtr {
1565                base,
1566                elem_ptr_ty,
1567                indices
1568            }
1569        )
1570    }
1571
1572    pub fn get_elem_ptr_with_idx(self, base: Value, elem_ty: Type, index: u64) -> Value {
1573        let idx_val = ConstantContent::get_uint(self.context, 64, index);
1574        self.get_elem_ptr(base, elem_ty, vec![idx_val])
1575    }
1576
1577    pub fn get_elem_ptr_with_idcs(self, base: Value, elem_ty: Type, indices: &[u64]) -> Value {
1578        let idx_vals = indices
1579            .iter()
1580            .map(|idx| ConstantContent::get_uint(self.context, 64, *idx))
1581            .collect();
1582        self.get_elem_ptr(base, elem_ty, idx_vals)
1583    }
1584
1585    pub fn get_local(self, local_var: LocalVar) -> Value {
1586        insert_instruction!(self, InstOp::GetLocal(local_var))
1587    }
1588
1589    pub fn get_global(self, global_var: GlobalVar) -> Value {
1590        insert_instruction!(self, InstOp::GetGlobal(global_var))
1591    }
1592
1593    pub fn get_config(self, module: Module, name: String) -> Value {
1594        insert_instruction!(self, InstOp::GetConfig(module, name))
1595    }
1596
1597    pub fn alloc(self, ty: Type, count: Value) -> Value {
1598        insert_instruction!(self, InstOp::Alloc { ty, count })
1599    }
1600
1601    pub fn get_storage_key(self, storage_key: StorageKey) -> Value {
1602        insert_instruction!(self, InstOp::GetStorageKey(storage_key))
1603    }
1604
1605    pub fn int_to_ptr(self, value: Value, ty: Type) -> Value {
1606        insert_instruction!(self, InstOp::IntToPtr(value, ty))
1607    }
1608
1609    pub fn load(self, src_val: Value) -> Value {
1610        insert_instruction!(self, InstOp::Load(src_val))
1611    }
1612
1613    pub fn log(
1614        self,
1615        log_val: Value,
1616        log_ty: Type,
1617        log_id: Value,
1618        log_data: Option<LogEventData>,
1619    ) -> Value {
1620        insert_instruction!(
1621            self,
1622            InstOp::FuelVm(FuelVmInstruction::Log {
1623                log_val,
1624                log_ty,
1625                log_id,
1626                log_data
1627            })
1628        )
1629    }
1630
1631    pub fn mem_copy_bytes(self, dst_val_ptr: Value, src_val_ptr: Value, byte_len: u64) -> Value {
1632        insert_instruction!(
1633            self,
1634            InstOp::MemCopyBytes {
1635                dst_val_ptr,
1636                src_val_ptr,
1637                byte_len
1638            }
1639        )
1640    }
1641
1642    pub fn mem_copy_val(self, dst_val_ptr: Value, src_val_ptr: Value) -> Value {
1643        insert_instruction!(
1644            self,
1645            InstOp::MemCopyVal {
1646                dst_val_ptr,
1647                src_val_ptr,
1648            }
1649        )
1650    }
1651
1652    pub fn nop(self) -> Value {
1653        insert_instruction!(self, InstOp::Nop)
1654    }
1655
1656    pub fn ptr_to_int(self, value: Value, ty: Type) -> Value {
1657        insert_instruction!(self, InstOp::PtrToInt(value, ty))
1658    }
1659
1660    pub fn read_register(self, reg: Register) -> Value {
1661        insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::ReadRegister(reg)))
1662    }
1663
1664    pub fn ret(self, value: Value, ty: Type) -> Value {
1665        insert_instruction!(self, InstOp::Ret(value, ty))
1666    }
1667
1668    pub fn retd(self, ptr: Value, len: Value) -> Value {
1669        insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::Retd { ptr, len }))
1670    }
1671
1672    pub fn revert(self, value: Value) -> Value {
1673        let revert_val = Value::new_instruction(
1674            self.context,
1675            self.block,
1676            InstOp::FuelVm(FuelVmInstruction::Revert(value)),
1677        );
1678        self.context.blocks[self.block.0]
1679            .instructions
1680            .push(revert_val);
1681        revert_val
1682    }
1683
1684    pub fn jmp_mem(self) -> Value {
1685        let ldc_exec = Value::new_instruction(
1686            self.context,
1687            self.block,
1688            InstOp::FuelVm(FuelVmInstruction::JmpMem),
1689        );
1690        self.context.blocks[self.block.0]
1691            .instructions
1692            .push(ldc_exec);
1693        ldc_exec
1694    }
1695
1696    pub fn smo(self, recipient: Value, message: Value, message_size: Value, coins: Value) -> Value {
1697        insert_instruction!(
1698            self,
1699            InstOp::FuelVm(FuelVmInstruction::Smo {
1700                recipient,
1701                message,
1702                message_size,
1703                coins,
1704            })
1705        )
1706    }
1707
1708    pub fn state_clear(self, key: Value, number_of_slots: Value) -> Value {
1709        insert_instruction!(
1710            self,
1711            InstOp::FuelVm(FuelVmInstruction::StateClear {
1712                key,
1713                number_of_slots
1714            })
1715        )
1716    }
1717
1718    pub fn state_load_quad_word(
1719        self,
1720        load_val: Value,
1721        key: Value,
1722        number_of_slots: Value,
1723    ) -> Value {
1724        insert_instruction!(
1725            self,
1726            InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord {
1727                load_val,
1728                key,
1729                number_of_slots
1730            })
1731        )
1732    }
1733
1734    pub fn state_load_word(self, key: Value) -> Value {
1735        insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::StateLoadWord(key)))
1736    }
1737
1738    pub fn state_store_quad_word(
1739        self,
1740        stored_val: Value,
1741        key: Value,
1742        number_of_slots: Value,
1743    ) -> Value {
1744        insert_instruction!(
1745            self,
1746            InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord {
1747                stored_val,
1748                key,
1749                number_of_slots
1750            })
1751        )
1752    }
1753
1754    pub fn state_store_word(self, stored_val: Value, key: Value) -> Value {
1755        insert_instruction!(
1756            self,
1757            InstOp::FuelVm(FuelVmInstruction::StateStoreWord { stored_val, key })
1758        )
1759    }
1760
1761    pub fn store(self, dst_val_ptr: Value, stored_val: Value) -> Value {
1762        insert_instruction!(
1763            self,
1764            InstOp::Store {
1765                dst_val_ptr,
1766                stored_val,
1767            }
1768        )
1769    }
1770
1771    pub fn mem_clear_val(self, dst_val_ptr: Value) -> Value {
1772        insert_instruction!(self, InstOp::MemClearVal { dst_val_ptr })
1773    }
1774}