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