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