fuel_asm/
lib.rs

1//! FuelVM instruction and opcodes representation.
2
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4#![cfg_attr(not(feature = "std"), no_std)]
5#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
6#![deny(
7    clippy::arithmetic_side_effects,
8    clippy::cast_sign_loss,
9    clippy::cast_possible_truncation,
10    clippy::cast_possible_wrap,
11    clippy::string_slice
12)]
13#![deny(missing_docs)]
14#![deny(unsafe_code)]
15#![deny(unused_crate_dependencies)]
16
17#[cfg(feature = "alloc")]
18extern crate alloc;
19
20mod args;
21mod panic_instruction;
22// This is `pub` to make documentation for the private `impl_instructions!` macro more
23// accessible.
24#[macro_use]
25pub mod macros;
26pub mod op;
27mod pack;
28mod panic_reason;
29mod unpack;
30
31#[cfg(test)]
32mod encoding_tests;
33
34#[doc(no_inline)]
35pub use args::{
36    wideint,
37    GMArgs,
38    GTFArgs,
39};
40
41/// Register ID type
42pub type RegisterId = usize;
43
44/// Register value type
45pub type Word = u64;
46
47pub use panic_instruction::PanicInstruction;
48pub use panic_reason::PanicReason;
49
50/// Represents a 6-bit register ID, guaranteed to be masked by construction.
51#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
53pub struct RegId(u8);
54
55/// Represents a 6-bit immediate value, guaranteed to be masked by construction.
56#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
57#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
58pub struct Imm06(u8);
59
60/// Represents a 12-bit immediate value, guaranteed to be masked by construction.
61#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
63pub struct Imm12(u16);
64
65/// Represents a 18-bit immediate value, guaranteed to be masked by construction.
66#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
67#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
68pub struct Imm18(u32);
69
70/// Represents a 24-bit immediate value, guaranteed to be masked by construction.
71#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
72#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
73pub struct Imm24(u32);
74
75/// An instruction in its raw, packed, unparsed representation.
76pub type RawInstruction = u32;
77
78/// Given opcode doesn't exist, or is the reserved part of
79/// the instruction (i.e. space outside arguments) is non-zero.
80#[derive(Debug, Eq, PartialEq)]
81pub struct InvalidOpcode;
82
83bitflags::bitflags! {
84    /// Possible values for the FLAG instruction.
85    /// See https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/index.md#flags
86    pub struct Flags: Word {
87        /// If set, arithmetic errors result in setting $err instead of panicking.
88        /// This includes cases where result of a computation is undefined, like
89        /// division by zero. Arithmetic overflows still cause a panic, but that be
90        /// controlled with [`Flags::WRAPPING`].
91        const UNSAFEMATH = 0x01;
92        /// If set, arithmetic overflows result in setting $of instead of panicking.
93        const WRAPPING = 0x02;
94    }
95}
96/// Type is convertible to a [`RegId`]
97pub trait CheckRegId {
98    /// Convert to a [`RegId`], or panic
99    fn check(self) -> RegId;
100}
101
102impl CheckRegId for RegId {
103    fn check(self) -> RegId {
104        self
105    }
106}
107
108impl CheckRegId for u8 {
109    fn check(self) -> RegId {
110        RegId::new_checked(self).expect("CheckRegId was given invalid RegId")
111    }
112}
113
114// Defines the `Instruction` and `Opcode` types, along with an `op` module declaring a
115// unique type for each opcode's instruction variant. For a detailed explanation of how
116// this works, see the `fuel_asm::macros` module level documentation.
117impl_instructions! {
118    "Adds two registers."
119    0x10 ADD add [dst: RegId lhs: RegId rhs: RegId]
120    "Bitwise ANDs two registers."
121    0x11 AND and [dst: RegId lhs: RegId rhs: RegId]
122    "Divides two registers."
123    0x12 DIV div [dst: RegId lhs: RegId rhs: RegId]
124    "Compares two registers for equality."
125    0x13 EQ eq [dst: RegId lhs: RegId rhs: RegId]
126    "Raises one register to the power of another."
127    0x14 EXP exp [dst: RegId lhs: RegId rhs: RegId]
128    "Compares two registers for greater-than."
129    0x15 GT gt [dst: RegId lhs: RegId rhs: RegId]
130    "Compares two registers for less-than."
131    0x16 LT lt [dst: RegId lhs: RegId rhs: RegId]
132    "The integer logarithm of a register."
133    0x17 MLOG mlog [dst: RegId lhs: RegId rhs: RegId]
134    "The integer root of a register."
135    0x18 MROO mroo [dst: RegId lhs: RegId rhs: RegId]
136    "Modulo remainder of two registers."
137    0x19 MOD mod_ [dst: RegId lhs: RegId rhs: RegId]
138    "Copy from one register to another."
139    0x1A MOVE move_ [dst: RegId src: RegId]
140    "Multiplies two registers."
141    0x1B MUL mul [dst: RegId lhs: RegId rhs: RegId]
142    "Bitwise NOT a register."
143    0x1C NOT not [dst: RegId arg: RegId]
144    "Bitwise ORs two registers."
145    0x1D OR or [dst: RegId lhs: RegId rhs: RegId]
146    "Left shifts a register by a register."
147    0x1E SLL sll [dst: RegId lhs: RegId rhs: RegId]
148    "Right shifts a register by a register."
149    0x1F SRL srl [dst: RegId lhs: RegId rhs: RegId]
150    "Subtracts two registers."
151    0x20 SUB sub [dst: RegId lhs: RegId rhs: RegId]
152    "Bitwise XORs two registers."
153    0x21 XOR xor [dst: RegId lhs: RegId rhs: RegId]
154    "Fused multiply-divide with arbitrary precision intermediate step."
155    0x22 MLDV mldv [dst: RegId mul_lhs: RegId mul_rhs: RegId divisor: RegId]
156
157    "Return from context."
158    0x24 RET ret [value: RegId]
159    "Return from context with data."
160    0x25 RETD retd [addr: RegId len: RegId]
161    "Allocate a number of bytes from the heap."
162    0x26 ALOC aloc [bytes: RegId]
163    "Clear a variable number of bytes in memory."
164    0x27 MCL mcl [dst_addr: RegId len: RegId]
165    "Copy a variable number of bytes in memory."
166    0x28 MCP mcp [dst_addr: RegId src_addr: RegId len: RegId]
167    "Compare bytes in memory."
168    0x29 MEQ meq [result: RegId lhs_addr: RegId rhs_addr: RegId len: RegId]
169    "Get block header hash for height."
170    0x2A BHSH bhsh [dst: RegId heigth: RegId]
171    "Get current block height."
172    0x2B BHEI bhei [dst: RegId]
173    "Burns `amount` coins of the asset ID created from `sub_id` for the current contract."
174    0x2C BURN burn [amount: RegId sub_id_addr: RegId]
175    "Call a contract."
176    0x2D CALL call [target_struct: RegId fwd_coins: RegId asset_id_addr: RegId fwd_gas: RegId]
177    "Copy contract code for a contract."
178    0x2E CCP ccp [dst_addr: RegId contract_id_addr: RegId offset: RegId len: RegId]
179    "Get code root of a contract."
180    0x2F CROO croo [dst_addr: RegId contract_id_addr: RegId]
181    "Get code size of a contract."
182    0x30 CSIZ csiz [dst: RegId contract_id_addr: RegId]
183    "Get current block proposer's address."
184    0x31 CB cb [dst: RegId]
185    "Load code as executable either from contract, blob, or memory."
186    0x32 LDC ldc [src_addr: RegId offset: RegId len: RegId mode: Imm06]
187    "Log an event."
188    0x33 LOG log [a: RegId b: RegId c: RegId d: RegId]
189    "Log data."
190    0x34 LOGD logd [a: RegId b: RegId addr: RegId len: RegId]
191    "Mints `amount` coins of the asset ID created from `sub_id` for the current contract."
192    0x35 MINT mint [amount: RegId sub_id_addr: RegId]
193    "Halt execution, reverting state changes and returning a value."
194    0x36 RVRT rvrt [value: RegId]
195    "Clear a series of slots from contract storage."
196    0x37 SCWQ scwq [key_addr: RegId status: RegId lenq: RegId]
197    "Load a word from contract storage."
198    0x38 SRW srw [dst: RegId status: RegId key_addr: RegId]
199    "Load a series of 32 byte slots from contract storage."
200    0x39 SRWQ srwq [dst_addr: RegId status: RegId key_addr:RegId lenq: RegId]
201    "Store a word in contract storage."
202    0x3A SWW sww [key_addr: RegId status: RegId value: RegId]
203    "Store a series of 32 byte slots in contract storage."
204    0x3B SWWQ swwq [key_addr: RegId status: RegId src_addr: RegId lenq: RegId]
205    "Transfer coins to a contract unconditionally."
206    0x3C TR tr [contract_id_addr: RegId amount: RegId asset_id_addr: RegId]
207    "Transfer coins to a variable output."
208    0x3D TRO tro [contract_id_addr: RegId output_index: RegId amount: RegId asset_id_addr: RegId]
209    "The 64-byte public key (x, y) recovered from 64-byte signature on 32-byte message hash."
210    0x3E ECK1 eck1 [dst_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
211    "The 64-byte Secp256r1 public key (x, y) recovered from 64-byte signature on 32-byte message hash."
212    0x3F ECR1 ecr1 [dst_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
213    "Verify ED25519 public key and signature match a message."
214    0x40 ED19 ed19 [pub_key_addr: RegId sig_addr: RegId msg_addr: RegId msg_len: RegId]
215    "The keccak-256 hash of a slice."
216    0x41 K256 k256 [dst_addr: RegId src_addr: RegId len: RegId]
217    "The SHA-2-256 hash of a slice."
218    0x42 S256 s256 [dst_addr: RegId src_addr: RegId len: RegId]
219    "Get timestamp of block at given height."
220    0x43 TIME time [dst: RegId heigth: RegId]
221
222    "Performs no operation."
223    0x47 NOOP noop []
224    "Set flag register to a register."
225    0x48 FLAG flag [value: RegId]
226    "Get the balance of contract of an asset ID."
227    0x49 BAL bal [dst: RegId asset_id_addr: RegId contract_id_addr: RegId]
228    "Dynamic jump."
229    0x4A JMP jmp [abs_target: RegId]
230    "Conditional dynamic jump."
231    0x4B JNE jne [abs_target: RegId lhs: RegId rhs: RegId]
232    "Send a message to recipient address with call abi, coins, and output."
233    0x4C SMO smo [recipient_addr: RegId data_addr: RegId data_len: RegId coins: RegId]
234
235    "Adds a register and an immediate value."
236    0x50 ADDI addi [dst: RegId lhs: RegId rhs: Imm12]
237    "Bitwise ANDs a register and an immediate value."
238    0x51 ANDI andi [dst: RegId lhs: RegId rhs: Imm12]
239    "Divides a register and an immediate value."
240    0x52 DIVI divi [dst: RegId lhs: RegId rhs: Imm12]
241    "Raises one register to the power of an immediate value."
242    0x53 EXPI expi [dst: RegId lhs: RegId rhs: Imm12]
243    "Modulo remainder of a register and an immediate value."
244    0x54 MODI modi [dst: RegId lhs: RegId rhs: Imm12]
245    "Multiplies a register and an immediate value."
246    0x55 MULI muli [dst: RegId lhs: RegId rhs: Imm12]
247    "Bitwise ORs a register and an immediate value."
248    0x56 ORI ori [dst: RegId lhs: RegId rhs: Imm12]
249    "Left shifts a register by an immediate value."
250    0x57 SLLI slli [dst: RegId lhs: RegId rhs: Imm12]
251    "Right shifts a register by an immediate value."
252    0x58 SRLI srli [dst: RegId lhs: RegId rhs: Imm12]
253    "Subtracts a register and an immediate value."
254    0x59 SUBI subi [dst: RegId lhs: RegId rhs: Imm12]
255    "Bitwise XORs a register and an immediate value."
256    0x5A XORI xori [dst: RegId lhs: RegId rhs: Imm12]
257    "Conditional jump."
258    0x5B JNEI jnei [cond_lhs: RegId cond_rhs: RegId abs_target: Imm12]
259    "A byte is loaded from the specified address offset by an immediate value."
260    0x5C LB lb [dst: RegId addr: RegId offset: Imm12]
261    "A word is loaded from the specified address offset by an immediate value."
262    0x5D LW lw [dst: RegId addr: RegId offset: Imm12]
263    "Write the least significant byte of a register to memory."
264    0x5E SB sb [addr: RegId value: RegId offset: Imm12]
265    "Write a register to memory."
266    0x5F SW sw [addr: RegId value: RegId offset: Imm12]
267    "Copy an immediate number of bytes in memory."
268    0x60 MCPI mcpi [dst_addr: RegId src_addr: RegId len: Imm12]
269    "Get transaction fields."
270    0x61 GTF gtf [dst: RegId arg: RegId selector: Imm12]
271
272    "Clear an immediate number of bytes in memory."
273    0x70 MCLI mcli [addr: RegId count: Imm18]
274    "Get metadata from memory."
275    0x71 GM gm [dst: RegId selector: Imm18]
276    "Copy immediate value into a register"
277    0x72 MOVI movi [dst: RegId val: Imm18]
278    "Conditional jump against zero."
279    0x73 JNZI jnzi [cond_nz: RegId abs_target: Imm18]
280    "Unconditional dynamic relative jump forwards, with a constant offset."
281    0x74 JMPF jmpf [dynamic: RegId fixed: Imm18]
282    "Unconditional dynamic relative jump backwards, with a constant offset."
283    0x75 JMPB jmpb [dynamic: RegId fixed: Imm18]
284    "Dynamic relative jump forwards, conditional against zero, with a constant offset."
285    0x76 JNZF jnzf [cond_nz: RegId dynamic: RegId fixed: Imm12]
286    "Dynamic relative jump backwards, conditional against zero, with a constant offset."
287    0x77 JNZB jnzb [cond_nz: RegId dynamic: RegId fixed: Imm12]
288    "Dynamic relative jump forwards, conditional on comparsion, with a constant offset."
289    0x78 JNEF jnef [cond_lhs: RegId cond_rhs: RegId dynamic: RegId fixed: Imm06]
290    "Dynamic relative jump backwards, conditional on comparsion, with a constant offset."
291    0x79 JNEB jneb [cond_lhs: RegId cond_rhs: RegId dynamic: RegId fixed: Imm06]
292
293    "Jump."
294    0x90 JI ji [abs_target: Imm24]
295    "Extend the current call frame's stack by an immediate value."
296    0x91 CFEI cfei [amount: Imm24]
297    "Shrink the current call frame's stack by an immediate value."
298    0x92 CFSI cfsi [amount: Imm24]
299    "Extend the current call frame's stack"
300    0x93 CFE cfe [amount: RegId]
301    "Shrink the current call frame's stack"
302    0x94 CFS cfs [amount: RegId]
303    "Push a bitmask-selected set of registers in range 16..40 to the stack."
304    0x95 PSHL pshl [bitmask: Imm24]
305    "Push a bitmask-selected set of registers in range 40..64 to the stack."
306    0x96 PSHH pshh [bitmask: Imm24]
307    "Pop a bitmask-selected set of registers in range 16..40 to the stack."
308    0x97 POPL popl [bitmask: Imm24]
309    "Pop a bitmask-selected set of registers in range 40..64 to the stack."
310    0x98 POPH poph [bitmask: Imm24]
311
312    "Compare 128bit integers"
313    0xa0 WDCM wdcm [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
314    "Compare 256bit integers"
315    0xa1 WQCM wqcm [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
316    "Simple 128bit operations"
317    0xa2 WDOP wdop [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
318    "Simple 256bit operations"
319    0xa3 WQOP wqop [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
320    "Multiply 128bit"
321    0xa4 WDML wdml [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
322    "Multiply 256bit"
323    0xa5 WQML wqml [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
324    "Divide 128bit"
325    0xa6 WDDV wddv [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
326    "Divide 256bit"
327    0xa7 WQDV wqdv [dst: RegId lhs: RegId rhs: RegId flags: Imm06]
328    "Fused multiply-divide 128bit"
329    0xa8 WDMD wdmd [dst: RegId mul_lhs: RegId mul_rhs: RegId divisor: RegId]
330    "Fused multiply-divide 256bit"
331    0xa9 WQMD wqmd [dst: RegId mul_lhs: RegId mul_rhs: RegId divisor: RegId]
332    "AddMod 128bit"
333    0xaa WDAM wdam [dst: RegId add_lhs: RegId add_rhs: RegId modulo: RegId]
334    "AddMod 256bit"
335    0xab WQAM wqam [dst: RegId add_lhs: RegId add_rhs: RegId modulo: RegId]
336    "MulMod 128bit"
337    0xac WDMM wdmm [dst: RegId mul_lhs: RegId mul_rhs: RegId modulo: RegId]
338    "MulMod 256bit"
339    0xad WQMM wqmm [dst: RegId mul_lhs: RegId mul_rhs: RegId modulo: RegId]
340
341    "Call external function"
342    0xb0 ECAL ecal [a: RegId b: RegId c: RegId d: RegId]
343
344    "Get blob size"
345    0xba BSIZ bsiz [dst: RegId blob_id_ptr: RegId]
346    "Load blob as data"
347    0xbb BLDD bldd [dst_ptr: RegId blob_id_ptr: RegId offset: RegId len: RegId]
348    "Given some curve, performs an operation on points"
349    0xbc ECOP ecop [dst: RegId curve_id: RegId operation_type: RegId points_ptr: RegId]
350    "Given some curve, performs a pairing on groups of points"
351    0xbe EPAR epar [success: RegId curve_id: RegId number_elements: RegId points_ptr: RegId]
352}
353
354impl Instruction {
355    /// Size of an instruction in bytes
356    pub const SIZE: usize = core::mem::size_of::<Instruction>();
357
358    /// Convenience method for converting to bytes
359    pub fn to_bytes(self) -> [u8; 4] {
360        self.into()
361    }
362}
363
364#[cfg(feature = "typescript")]
365mod typescript {
366    /// Representation of a single instruction for the interpreter.
367    ///
368    /// The opcode is represented in the tag (variant), or may be retrieved in the
369    /// form of an `Opcode` byte using the `opcode` method.
370    ///
371    /// The register and immediate data associated with the instruction is represented
372    /// within an inner unit type wrapper around the 3 remaining bytes.
373    #[derive(Clone, Eq, Hash, PartialEq)]
374    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
375    #[wasm_bindgen::prelude::wasm_bindgen]
376    pub struct Instruction(Box<crate::Instruction>);
377
378    impl Instruction {
379        pub fn new(instruction: crate::Instruction) -> Self {
380            Self(Box::new(instruction))
381        }
382    }
383
384    #[wasm_bindgen::prelude::wasm_bindgen]
385    impl Instruction {
386        /// Convenience method for converting to bytes
387        pub fn to_bytes(&self) -> Vec<u8> {
388            use core::ops::Deref;
389            self.deref().to_bytes().to_vec()
390        }
391
392        /// Size of an instruction in bytes
393        pub fn size() -> usize {
394            crate::Instruction::SIZE
395        }
396    }
397
398    impl core::ops::Deref for Instruction {
399        type Target = crate::Instruction;
400
401        fn deref(&self) -> &crate::Instruction {
402            self.0.as_ref()
403        }
404    }
405
406    impl core::ops::DerefMut for Instruction {
407        fn deref_mut(&mut self) -> &mut crate::Instruction {
408            self.0.as_mut()
409        }
410    }
411
412    impl core::borrow::Borrow<crate::Instruction> for Instruction {
413        fn borrow(&self) -> &crate::Instruction {
414            self.0.as_ref()
415        }
416    }
417
418    impl core::borrow::BorrowMut<crate::Instruction> for Instruction {
419        fn borrow_mut(&mut self) -> &mut crate::Instruction {
420            self.0.as_mut()
421        }
422    }
423}
424
425impl RegId {
426    /// Received balance for this context.
427    pub const BAL: Self = Self(0x0B);
428    /// Remaining gas in the context.
429    pub const CGAS: Self = Self(0x0A);
430    /// Error codes for particular operations.
431    pub const ERR: Self = Self(0x08);
432    /// Flags register.
433    pub const FLAG: Self = Self(0x0F);
434    /// Frame pointer. Memory address of beginning of current call frame.
435    pub const FP: Self = Self(0x06);
436    /// Remaining gas globally.
437    pub const GGAS: Self = Self(0x09);
438    /// Heap pointer. Memory address below the current bottom of the heap (points to free
439    /// memory).
440    pub const HP: Self = Self(0x07);
441    /// Instructions start. Pointer to the start of the currently-executing code.
442    pub const IS: Self = Self(0x0C);
443    /// Contains overflow/underflow of addition, subtraction, and multiplication.
444    pub const OF: Self = Self(0x02);
445    /// Contains one (1), for convenience.
446    pub const ONE: Self = Self(0x01);
447    /// The program counter. Memory address of the current instruction.
448    pub const PC: Self = Self(0x03);
449    /// Return value or pointer.
450    pub const RET: Self = Self(0x0D);
451    /// Return value length in bytes.
452    pub const RETL: Self = Self(0x0E);
453    /// Stack pointer. Memory address on top of current writable stack area (points to
454    /// free memory).
455    pub const SP: Self = Self(0x05);
456    /// Stack start pointer. Memory address of bottom of current writable stack area.
457    pub const SSP: Self = Self(0x04);
458    /// Smallest writable register.
459    pub const WRITABLE: Self = Self(0x10);
460    /// Contains zero (0), for convenience.
461    pub const ZERO: Self = Self(0x00);
462
463    /// Construct a register ID from the given value.
464    ///
465    /// The given value will be masked to 6 bits.
466    pub const fn new(u: u8) -> Self {
467        Self(u & 0b_0011_1111)
468    }
469
470    /// A const alternative to the `Into<u8>` implementation.
471    pub const fn to_u8(self) -> u8 {
472        self.0
473    }
474}
475
476#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
477impl RegId {
478    /// Construct a register ID from the given value.
479    ///
480    /// Returns `None` if the value is outside the 6-bit value range.
481    pub fn new_checked(u: u8) -> Option<RegId> {
482        let r = Self::new(u);
483        (r.0 == u).then_some(r)
484    }
485}
486
487#[cfg(feature = "typescript")]
488#[wasm_bindgen::prelude::wasm_bindgen]
489impl RegId {
490    /// Received balance for this context.
491    pub fn bal() -> Self {
492        Self::BAL
493    }
494
495    /// Remaining gas in the context.
496    pub fn cgas() -> Self {
497        Self::CGAS
498    }
499
500    /// Error codes for particular operations.
501    pub fn err() -> Self {
502        Self::ERR
503    }
504
505    /// Flags register.
506    pub fn flag() -> Self {
507        Self::FLAG
508    }
509
510    /// Frame pointer. Memory address of beginning of current call frame.
511    pub fn fp() -> Self {
512        Self::FP
513    }
514
515    /// Remaining gas globally.
516    pub fn ggas() -> Self {
517        Self::GGAS
518    }
519
520    /// Heap pointer. Memory address below the current bottom of the heap (points to free
521    /// memory).
522    pub fn hp() -> Self {
523        Self::HP
524    }
525
526    /// Instructions start. Pointer to the start of the currently-executing code.
527    pub fn is() -> Self {
528        Self::IS
529    }
530
531    /// Contains overflow/underflow of addition, subtraction, and multiplication.
532    pub fn of() -> Self {
533        Self::OF
534    }
535
536    /// Contains one (1), for convenience.
537    pub fn one() -> Self {
538        Self::ONE
539    }
540
541    /// The program counter. Memory address of the current instruction.
542    pub fn pc() -> Self {
543        Self::PC
544    }
545
546    /// Return value or pointer.
547    pub fn ret() -> Self {
548        Self::RET
549    }
550
551    /// Return value length in bytes.
552    pub fn retl() -> Self {
553        Self::RETL
554    }
555
556    /// Stack pointer. Memory address on top of current writable stack area (points to
557    /// free memory).
558    pub fn sp() -> Self {
559        Self::SP
560    }
561
562    /// Stack start pointer. Memory address of bottom of current writable stack area.
563    pub fn spp() -> Self {
564        Self::SSP
565    }
566
567    /// Smallest writable register.
568    pub fn writable() -> Self {
569        Self::WRITABLE
570    }
571
572    /// Contains zero (0), for convenience.
573    pub fn zero() -> Self {
574        Self::ZERO
575    }
576
577    /// Construct a register ID from the given value.
578    ///
579    /// The given value will be masked to 6 bits.
580    #[wasm_bindgen(constructor)]
581    pub fn new_typescript(u: u8) -> Self {
582        Self::new(u)
583    }
584
585    /// A const alternative to the `Into<u8>` implementation.
586    #[wasm_bindgen(js_name = to_u8)]
587    pub fn to_u8_typescript(self) -> u8 {
588        self.to_u8()
589    }
590}
591
592impl Imm06 {
593    /// Max value for the type
594    pub const MAX: Self = Self(0b_0011_1111);
595
596    /// Construct an immediate value.
597    ///
598    /// The given value will be masked to 6 bits.
599    pub const fn new(u: u8) -> Self {
600        Self(u & Self::MAX.0)
601    }
602
603    /// Construct an immediate value.
604    ///
605    /// Returns `None` if the value is outside the 6-bit value range.
606    pub fn new_checked(u: u8) -> Option<Self> {
607        let imm = Self::new(u);
608        (imm.0 == u).then_some(imm)
609    }
610
611    /// A const alternative to the `Into<u8>` implementation.
612    pub const fn to_u8(self) -> u8 {
613        self.0
614    }
615}
616
617impl Imm12 {
618    /// Max value for the type
619    pub const MAX: Self = Self(0b_0000_1111_1111_1111);
620
621    /// Construct an immediate value.
622    ///
623    /// The given value will be masked to 12 bits.
624    pub const fn new(u: u16) -> Self {
625        Self(u & Self::MAX.0)
626    }
627
628    /// Construct an immediate value.
629    ///
630    /// Returns `None` if the value is outside the 12-bit value range.
631    pub fn new_checked(u: u16) -> Option<Self> {
632        let imm = Self::new(u);
633        (imm.0 == u).then_some(imm)
634    }
635
636    /// A const alternative to the `Into<u16>` implementation.
637    pub const fn to_u16(self) -> u16 {
638        self.0
639    }
640}
641
642impl Imm18 {
643    /// Max value for the type
644    pub const MAX: Self = Self(0b_0000_0000_0000_0011_1111_1111_1111_1111);
645
646    /// Construct an immediate value.
647    ///
648    /// The given value will be masked to 18 bits.
649    pub const fn new(u: u32) -> Self {
650        Self(u & Self::MAX.0)
651    }
652
653    /// Construct an immediate value.
654    ///
655    /// Returns `None` if the value is outside the 18-bit value range.
656    pub fn new_checked(u: u32) -> Option<Self> {
657        let imm = Self::new(u);
658        (imm.0 == u).then_some(imm)
659    }
660
661    /// A const alternative to the `Into<u32>` implementation.
662    pub const fn to_u32(self) -> u32 {
663        self.0
664    }
665}
666
667impl Imm24 {
668    /// Max value for the type
669    pub const MAX: Self = Self(0b_0000_0000_1111_1111_1111_1111_1111_1111);
670
671    /// Construct an immediate value.
672    ///
673    /// The given value will be masked to 24 bits.
674    pub const fn new(u: u32) -> Self {
675        Self(u & Self::MAX.0)
676    }
677
678    /// Construct an immediate value.
679    ///
680    /// Returns `None` if the value is outside the 24-bit value range.
681    pub fn new_checked(u: u32) -> Option<Self> {
682        let imm = Self::new(u);
683        (imm.0 == u).then_some(imm)
684    }
685
686    /// A const alternative to the `Into<u32>` implementation.
687    pub const fn to_u32(self) -> u32 {
688        self.0
689    }
690}
691
692impl Opcode {
693    /// Check if the opcode is allowed for predicates.
694    ///
695    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/index.md#predicate-verification>
696    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/instruction-set.md#contract-instructions>
697    #[allow(clippy::match_like_matches_macro)]
698    pub fn is_predicate_allowed(&self) -> bool {
699        use Opcode::*;
700        match self {
701            ADD | AND | DIV | EQ | EXP | GT | LT | MLOG | MROO | MOD | MOVE | MUL
702            | NOT | OR | SLL | SRL | SUB | XOR | WDCM | WQCM | WDOP | WQOP | WDML
703            | WQML | WDDV | WQDV | WDMD | WQMD | WDAM | WQAM | WDMM | WQMM | PSHH
704            | PSHL | POPH | POPL | RET | ALOC | MCL | MCP | MEQ | ECK1 | ECR1 | ED19
705            | K256 | S256 | NOOP | FLAG | ADDI | ANDI | DIVI | EXPI | MODI | MULI
706            | MLDV | ORI | SLLI | SRLI | SUBI | XORI | JNEI | LB | LW | SB | SW
707            | MCPI | MCLI | GM | MOVI | JNZI | JI | JMP | JNE | JMPF | JMPB | JNZF
708            | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF | LDC | BSIZ | BLDD
709            | ECOP | EPAR => true,
710            _ => false,
711        }
712    }
713}
714
715// Direct conversions
716
717impl From<u8> for RegId {
718    fn from(u: u8) -> Self {
719        RegId::new(u)
720    }
721}
722
723impl From<u8> for Imm06 {
724    fn from(u: u8) -> Self {
725        Imm06::new(u)
726    }
727}
728
729impl From<u16> for Imm12 {
730    fn from(u: u16) -> Self {
731        Imm12::new(u)
732    }
733}
734
735impl From<u32> for Imm18 {
736    fn from(u: u32) -> Self {
737        Imm18::new(u)
738    }
739}
740
741impl From<u32> for Imm24 {
742    fn from(u: u32) -> Self {
743        Imm24::new(u)
744    }
745}
746
747impl From<RegId> for u8 {
748    fn from(RegId(u): RegId) -> Self {
749        u
750    }
751}
752
753impl From<Imm06> for u8 {
754    fn from(Imm06(u): Imm06) -> Self {
755        u
756    }
757}
758
759impl From<Imm12> for u16 {
760    fn from(Imm12(u): Imm12) -> Self {
761        u
762    }
763}
764
765impl From<Imm18> for u32 {
766    fn from(Imm18(u): Imm18) -> Self {
767        u
768    }
769}
770
771impl From<Imm24> for u32 {
772    fn from(Imm24(u): Imm24) -> Self {
773        u
774    }
775}
776
777// Lossless, convenience conversions
778
779impl From<RegId> for usize {
780    fn from(r: RegId) -> usize {
781        u8::from(r).into()
782    }
783}
784
785impl From<Imm06> for u16 {
786    fn from(imm: Imm06) -> Self {
787        u8::from(imm).into()
788    }
789}
790
791impl From<Imm06> for u32 {
792    fn from(imm: Imm06) -> Self {
793        u8::from(imm).into()
794    }
795}
796
797impl From<Imm06> for u64 {
798    fn from(imm: Imm06) -> Self {
799        u8::from(imm).into()
800    }
801}
802
803impl From<Imm06> for u128 {
804    fn from(imm: Imm06) -> Self {
805        u8::from(imm).into()
806    }
807}
808
809impl From<Imm12> for u32 {
810    fn from(imm: Imm12) -> Self {
811        u16::from(imm).into()
812    }
813}
814
815impl From<Imm12> for u64 {
816    fn from(imm: Imm12) -> Self {
817        u16::from(imm).into()
818    }
819}
820
821impl From<Imm12> for u128 {
822    fn from(imm: Imm12) -> Self {
823        u16::from(imm).into()
824    }
825}
826
827impl From<Imm18> for u64 {
828    fn from(imm: Imm18) -> Self {
829        u32::from(imm).into()
830    }
831}
832
833impl From<Imm18> for u128 {
834    fn from(imm: Imm18) -> Self {
835        u32::from(imm).into()
836    }
837}
838
839impl From<Imm24> for u64 {
840    fn from(imm: Imm24) -> Self {
841        u32::from(imm).into()
842    }
843}
844
845impl From<Imm24> for u128 {
846    fn from(imm: Imm24) -> Self {
847        u32::from(imm).into()
848    }
849}
850
851impl From<Opcode> for u8 {
852    fn from(op: Opcode) -> Self {
853        op as u8
854    }
855}
856
857impl From<Instruction> for RawInstruction {
858    fn from(inst: Instruction) -> Self {
859        RawInstruction::from_be_bytes(inst.into())
860    }
861}
862
863impl core::convert::TryFrom<RawInstruction> for Instruction {
864    type Error = InvalidOpcode;
865
866    fn try_from(u: RawInstruction) -> Result<Self, Self::Error> {
867        Self::try_from(u.to_be_bytes())
868    }
869}
870
871// Index slices with `RegId`
872
873impl<T> core::ops::Index<RegId> for [T]
874where
875    [T]: core::ops::Index<usize, Output = T>,
876{
877    type Output = T;
878
879    fn index(&self, ix: RegId) -> &Self::Output {
880        &self[usize::from(ix)]
881    }
882}
883
884impl<T> core::ops::IndexMut<RegId> for [T]
885where
886    [T]: core::ops::IndexMut<usize, Output = T>,
887{
888    fn index_mut(&mut self, ix: RegId) -> &mut Self::Output {
889        &mut self[usize::from(ix)]
890    }
891}
892
893// Collect instructions into bytes or halfwords
894
895#[cfg(feature = "alloc")]
896impl core::iter::FromIterator<Instruction> for alloc::vec::Vec<u8> {
897    fn from_iter<I: IntoIterator<Item = Instruction>>(iter: I) -> Self {
898        iter.into_iter().flat_map(Instruction::to_bytes).collect()
899    }
900}
901
902#[cfg(feature = "alloc")]
903impl core::iter::FromIterator<Instruction> for alloc::vec::Vec<u32> {
904    fn from_iter<I: IntoIterator<Item = Instruction>>(iter: I) -> Self {
905        iter.into_iter().map(u32::from).collect()
906    }
907}
908
909/// Given an iterator yielding bytes, produces an iterator yielding `Instruction`s.
910///
911/// This function assumes each consecutive 4 bytes aligns with an instruction.
912///
913/// The produced iterator yields an `Err` in the case that an instruction fails to parse
914/// from 4 consecutive bytes.
915pub fn from_bytes<I>(bs: I) -> impl Iterator<Item = Result<Instruction, InvalidOpcode>>
916where
917    I: IntoIterator<Item = u8>,
918{
919    let mut iter = bs.into_iter();
920    core::iter::from_fn(move || {
921        let a = iter.next()?;
922        let b = iter.next()?;
923        let c = iter.next()?;
924        let d = iter.next()?;
925        Some(Instruction::try_from([a, b, c, d]))
926    })
927}
928
929/// Given an iterator yielding u32s (i.e. "half words" or "raw instructions"), produces an
930/// iterator yielding `Instruction`s.
931///
932/// This function assumes each consecutive 4 bytes aligns with an instruction.
933///
934/// The produced iterator yields an `Err` in the case that an instruction fails to parse.
935pub fn from_u32s<I>(us: I) -> impl Iterator<Item = Result<Instruction, InvalidOpcode>>
936where
937    I: IntoIterator<Item = u32>,
938{
939    us.into_iter().map(Instruction::try_from)
940}
941
942// Short-hand, `panic!`ing constructors for the short-hand instruction construtors (e.g
943// op::add).
944
945fn check_imm06(u: u8) -> Imm06 {
946    Imm06::new_checked(u)
947        .unwrap_or_else(|| panic!("Value `{u}` out of range for 6-bit immediate"))
948}
949
950fn check_imm12(u: u16) -> Imm12 {
951    Imm12::new_checked(u)
952        .unwrap_or_else(|| panic!("Value `{u}` out of range for 12-bit immediate"))
953}
954
955fn check_imm18(u: u32) -> Imm18 {
956    Imm18::new_checked(u)
957        .unwrap_or_else(|| panic!("Value `{u}` out of range for 18-bit immediate"))
958}
959
960fn check_imm24(u: u32) -> Imm24 {
961    Imm24::new_checked(u)
962        .unwrap_or_else(|| panic!("Value `{u}` out of range for 24-bit immediate"))
963}
964
965// --------------------------------------------------------
966
967// The size of the instruction isn't larger than necessary.
968// 1 byte for the opcode, 3 bytes for registers and immediates.
969#[test]
970fn test_instruction_size() {
971    // NOTE: Throughout `fuel-vm`, we use the `Instruction::SIZE` associated
972    // const to refer to offsets within raw instruction data. As a result, it's
973    // *essential* that this equivalence remains the same. If you've added
974    // a new field or changed the size of `Instruction` somehow and have
975    // arrived at this assertion, ensure that you also revisit all sites where
976    // `Instruction::SIZE` is used and make sure we're using the right value
977    // (in most cases, the right value is `core::mem::size_of::<RawInstruction>()`).
978    assert_eq!(
979        core::mem::size_of::<Instruction>(),
980        core::mem::size_of::<RawInstruction>()
981    );
982
983    assert_eq!(core::mem::size_of::<Instruction>(), Instruction::SIZE);
984}
985
986// The size of the opcode is exactly one byte.
987#[test]
988fn test_opcode_size() {
989    assert_eq!(core::mem::size_of::<Opcode>(), 1);
990}
991
992#[test]
993#[allow(clippy::match_like_matches_macro)]
994fn check_predicate_allowed() {
995    use Opcode::*;
996    for byte in 0..u8::MAX {
997        if let Ok(repr) = Opcode::try_from(byte) {
998            let should_allow = match repr {
999                BAL | BHEI | BHSH | BURN | CALL | CB | CCP | CROO | CSIZ | LOG | LOGD
1000                | MINT | RETD | RVRT | SMO | SCWQ | SRW | SRWQ | SWW | SWWQ | TIME
1001                | TR | TRO | ECAL => false,
1002                _ => true,
1003            };
1004            assert_eq!(should_allow, repr.is_predicate_allowed());
1005        }
1006    }
1007}
1008
1009// Test roundtrip conversion for all valid opcodes.
1010#[test]
1011fn test_opcode_u8_conv() {
1012    for u in 0..=u8::MAX {
1013        if let Ok(op) = Opcode::try_from(u) {
1014            assert_eq!(op as u8, u);
1015        }
1016    }
1017}