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