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