Coverage Report

Created: 2024-10-28 11:34

/home/runner/actions-runner/_work/fuel-vm/fuel-vm/fuel-asm/src/lib.rs
Line
Count
Source (jump to first uncovered line)
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")]
18
extern crate alloc;
19
20
mod args;
21
mod panic_instruction;
22
// This is `pub` to make documentation for the private `impl_instructions!` macro more
23
// accessible.
24
#[macro_use]
25
pub mod macros;
26
pub mod op;
27
mod pack;
28
mod panic_reason;
29
mod unpack;
30
31
#[cfg(test)]
32
mod encoding_tests;
33
34
#[doc(no_inline)]
35
pub use args::{
36
    wideint,
37
    GMArgs,
38
    GTFArgs,
39
};
40
41
/// Register ID type
42
pub type RegisterId = usize;
43
44
/// Register value type
45
pub type Word = u64;
46
47
pub use panic_instruction::PanicInstruction;
48
pub 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)]
53
pub 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)]
58
pub 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)]
63
pub 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)]
68
pub 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)]
73
pub struct Imm24(u32);
74
75
/// An instruction in its raw, packed, unparsed representation.
76
pub 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)]
81
pub struct InvalidOpcode;
82
83
bitflags::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`]
97
pub trait CheckRegId {
98
    /// Convert to a [`RegId`], or panic
99
    fn check(self) -> RegId;
100
}
101
102
impl CheckRegId for RegId {
103
33.1k
    fn check(self) -> RegId {
104
33.1k
        self
105
33.1k
    }
106
}
107
108
impl CheckRegId for u8 {
109
112k
    fn check(self) -> RegId {
110
112k
        RegId::new_checked(self).expect("CheckRegId was given invalid RegId")
111
112k
    }
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.
117
impl_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
}
349
350
impl Instruction {
351
    /// Size of an instruction in bytes
352
    pub const SIZE: usize = core::mem::size_of::<Instruction>();
353
354
    /// Convenience method for converting to bytes
355
270k
    pub fn to_bytes(self) -> [u8; 4] {
356
270k
        self.into()
357
270k
    }
358
}
359
360
#[cfg(feature = "typescript")]
361
mod typescript {
362
    /// Representation of a single instruction for the interpreter.
363
    ///
364
    /// The opcode is represented in the tag (variant), or may be retrieved in the
365
    /// form of an `Opcode` byte using the `opcode` method.
366
    ///
367
    /// The register and immediate data associated with the instruction is represented
368
    /// within an inner unit type wrapper around the 3 remaining bytes.
369
    #[derive(Clone, Eq, Hash, PartialEq)]
370
0
    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371
    #[wasm_bindgen::prelude::wasm_bindgen]
372
    pub struct Instruction(Box<crate::Instruction>);
373
374
    impl Instruction {
375
0
        pub fn new(instruction: crate::Instruction) -> Self {
376
0
            Self(Box::new(instruction))
377
0
        }
378
    }
379
380
0
    #[wasm_bindgen::prelude::wasm_bindgen]
381
    impl Instruction {
382
        /// Convenience method for converting to bytes
383
0
        pub fn to_bytes(&self) -> Vec<u8> {
384
0
            use core::ops::Deref;
385
0
            self.deref().to_bytes().to_vec()
386
0
        }
387
388
        /// Size of an instruction in bytes
389
0
        pub fn size() -> usize {
390
0
            crate::Instruction::SIZE
391
0
        }
392
    }
393
394
    impl core::ops::Deref for Instruction {
395
        type Target = crate::Instruction;
396
397
0
        fn deref(&self) -> &crate::Instruction {
398
0
            self.0.as_ref()
399
0
        }
400
    }
401
402
    impl core::ops::DerefMut for Instruction {
403
0
        fn deref_mut(&mut self) -> &mut crate::Instruction {
404
0
            self.0.as_mut()
405
0
        }
406
    }
407
408
    impl core::borrow::Borrow<crate::Instruction> for Instruction {
409
0
        fn borrow(&self) -> &crate::Instruction {
410
0
            self.0.as_ref()
411
0
        }
412
    }
413
414
    impl core::borrow::BorrowMut<crate::Instruction> for Instruction {
415
0
        fn borrow_mut(&mut self) -> &mut crate::Instruction {
416
0
            self.0.as_mut()
417
0
        }
418
    }
419
}
420
421
impl RegId {
422
    /// Received balance for this context.
423
    pub const BAL: Self = Self(0x0B);
424
    /// Remaining gas in the context.
425
    pub const CGAS: Self = Self(0x0A);
426
    /// Error codes for particular operations.
427
    pub const ERR: Self = Self(0x08);
428
    /// Flags register.
429
    pub const FLAG: Self = Self(0x0F);
430
    /// Frame pointer. Memory address of beginning of current call frame.
431
    pub const FP: Self = Self(0x06);
432
    /// Remaining gas globally.
433
    pub const GGAS: Self = Self(0x09);
434
    /// Heap pointer. Memory address below the current bottom of the heap (points to free
435
    /// memory).
436
    pub const HP: Self = Self(0x07);
437
    /// Instructions start. Pointer to the start of the currently-executing code.
438
    pub const IS: Self = Self(0x0C);
439
    /// Contains overflow/underflow of addition, subtraction, and multiplication.
440
    pub const OF: Self = Self(0x02);
441
    /// Contains one (1), for convenience.
442
    pub const ONE: Self = Self(0x01);
443
    /// The program counter. Memory address of the current instruction.
444
    pub const PC: Self = Self(0x03);
445
    /// Return value or pointer.
446
    pub const RET: Self = Self(0x0D);
447
    /// Return value length in bytes.
448
    pub const RETL: Self = Self(0x0E);
449
    /// Stack pointer. Memory address on top of current writable stack area (points to
450
    /// free memory).
451
    pub const SP: Self = Self(0x05);
452
    /// Stack start pointer. Memory address of bottom of current writable stack area.
453
    pub const SSP: Self = Self(0x04);
454
    /// Smallest writable register.
455
    pub const WRITABLE: Self = Self(0x10);
456
    /// Contains zero (0), for convenience.
457
    pub const ZERO: Self = Self(0x00);
458
459
    /// Construct a register ID from the given value.
460
    ///
461
    /// The given value will be masked to 6 bits.
462
2.12M
    pub const fn new(u: u8) -> Self {
463
2.12M
        Self(u & 0b_0011_1111)
464
2.12M
    }
465
466
    /// A const alternative to the `Into<u8>` implementation.
467
0
    pub const fn to_u8(self) -> u8 {
468
0
        self.0
469
0
    }
470
}
471
472
0
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
473
impl RegId {
474
    /// Construct a register ID from the given value.
475
    ///
476
    /// Returns `None` if the value is outside the 6-bit value range.
477
112k
    pub fn new_checked(u: u8) -> Option<RegId> {
478
112k
        let r = Self::new(u);
479
112k
        (r.0 == u).then_some(r)
480
112k
    }
481
}
482
483
#[cfg(feature = "typescript")]
484
0
#[wasm_bindgen::prelude::wasm_bindgen]
485
impl RegId {
486
    /// Received balance for this context.
487
0
    pub fn bal() -> Self {
488
0
        Self::BAL
489
0
    }
490
491
    /// Remaining gas in the context.
492
0
    pub fn cgas() -> Self {
493
0
        Self::CGAS
494
0
    }
495
496
    /// Error codes for particular operations.
497
0
    pub fn err() -> Self {
498
0
        Self::ERR
499
0
    }
500
501
    /// Flags register.
502
0
    pub fn flag() -> Self {
503
0
        Self::FLAG
504
0
    }
505
506
    /// Frame pointer. Memory address of beginning of current call frame.
507
0
    pub fn fp() -> Self {
508
0
        Self::FP
509
0
    }
510
511
    /// Remaining gas globally.
512
0
    pub fn ggas() -> Self {
513
0
        Self::GGAS
514
0
    }
515
516
    /// Heap pointer. Memory address below the current bottom of the heap (points to free
517
    /// memory).
518
0
    pub fn hp() -> Self {
519
0
        Self::HP
520
0
    }
521
522
    /// Instructions start. Pointer to the start of the currently-executing code.
523
0
    pub fn is() -> Self {
524
0
        Self::IS
525
0
    }
526
527
    /// Contains overflow/underflow of addition, subtraction, and multiplication.
528
0
    pub fn of() -> Self {
529
0
        Self::OF
530
0
    }
531
532
    /// Contains one (1), for convenience.
533
0
    pub fn one() -> Self {
534
0
        Self::ONE
535
0
    }
536
537
    /// The program counter. Memory address of the current instruction.
538
0
    pub fn pc() -> Self {
539
0
        Self::PC
540
0
    }
541
542
    /// Return value or pointer.
543
0
    pub fn ret() -> Self {
544
0
        Self::RET
545
0
    }
546
547
    /// Return value length in bytes.
548
0
    pub fn retl() -> Self {
549
0
        Self::RETL
550
0
    }
551
552
    /// Stack pointer. Memory address on top of current writable stack area (points to
553
    /// free memory).
554
0
    pub fn sp() -> Self {
555
0
        Self::SP
556
0
    }
557
558
    /// Stack start pointer. Memory address of bottom of current writable stack area.
559
0
    pub fn spp() -> Self {
560
0
        Self::SSP
561
0
    }
562
563
    /// Smallest writable register.
564
0
    pub fn writable() -> Self {
565
0
        Self::WRITABLE
566
0
    }
567
568
    /// Contains zero (0), for convenience.
569
0
    pub fn zero() -> Self {
570
0
        Self::ZERO
571
0
    }
572
573
    /// Construct a register ID from the given value.
574
    ///
575
    /// The given value will be masked to 6 bits.
576
    #[wasm_bindgen(constructor)]
577
0
    pub fn new_typescript(u: u8) -> Self {
578
0
        Self::new(u)
579
0
    }
580
581
    /// A const alternative to the `Into<u8>` implementation.
582
    #[wasm_bindgen(js_name = to_u8)]
583
0
    pub fn to_u8_typescript(self) -> u8 {
584
0
        self.to_u8()
585
0
    }
586
}
587
588
impl Imm06 {
589
    /// Max value for the type
590
    pub const MAX: Self = Self(0b_0011_1111);
591
592
    /// Construct an immediate value.
593
    ///
594
    /// The given value will be masked to 6 bits.
595
9.55k
    pub const fn new(u: u8) -> Self {
596
9.55k
        Self(u & Self::MAX.0)
597
9.55k
    }
598
599
    /// Construct an immediate value.
600
    ///
601
    /// Returns `None` if the value is outside the 6-bit value range.
602
103
    pub fn new_checked(u: u8) -> Option<Self> {
603
103
        let imm = Self::new(u);
604
103
        (imm.0 == u).then_some(imm)
605
103
    }
606
607
    /// A const alternative to the `Into<u8>` implementation.
608
111
    pub const fn to_u8(self) -> u8 {
609
111
        self.0
610
111
    }
611
}
612
613
impl Imm12 {
614
    /// Max value for the type
615
    pub const MAX: Self = Self(0b_0000_1111_1111_1111);
616
617
    /// Construct an immediate value.
618
    ///
619
    /// The given value will be masked to 12 bits.
620
354k
    pub const fn new(u: u16) -> Self {
621
354k
        Self(u & Self::MAX.0)
622
354k
    }
623
624
    /// Construct an immediate value.
625
    ///
626
    /// Returns `None` if the value is outside the 12-bit value range.
627
44.4k
    pub fn new_checked(u: u16) -> Option<Self> {
628
44.4k
        let imm = Self::new(u);
629
44.4k
        (imm.0 == u).then_some(imm)
630
44.4k
    }
631
632
    /// A const alternative to the `Into<u16>` implementation.
633
4
    pub const fn to_u16(self) -> u16 {
634
4
        self.0
635
4
    }
636
}
637
638
impl Imm18 {
639
    /// Max value for the type
640
    pub const MAX: Self = Self(0b_0000_0000_0000_0011_1111_1111_1111_1111);
641
642
    /// Construct an immediate value.
643
    ///
644
    /// The given value will be masked to 18 bits.
645
510k
    pub const fn new(u: u32) -> Self {
646
510k
        Self(u & Self::MAX.0)
647
510k
    }
648
649
    /// Construct an immediate value.
650
    ///
651
    /// Returns `None` if the value is outside the 18-bit value range.
652
14.2k
    pub fn new_checked(u: u32) -> Option<Self> {
653
14.2k
        let imm = Self::new(u);
654
14.2k
        (imm.0 == u).then_some(imm)
655
14.2k
    }
656
657
    /// A const alternative to the `Into<u32>` implementation.
658
0
    pub const fn to_u32(self) -> u32 {
659
0
        self.0
660
0
    }
661
}
662
663
impl Imm24 {
664
    /// Max value for the type
665
    pub const MAX: Self = Self(0b_0000_0000_1111_1111_1111_1111_1111_1111);
666
667
    /// Construct an immediate value.
668
    ///
669
    /// The given value will be masked to 24 bits.
670
204k
    pub const fn new(u: u32) -> Self {
671
204k
        Self(u & Self::MAX.0)
672
204k
    }
673
674
    /// Construct an immediate value.
675
    ///
676
    /// Returns `None` if the value is outside the 24-bit value range.
677
2.14k
    pub fn new_checked(u: u32) -> Option<Self> {
678
2.14k
        let imm = Self::new(u);
679
2.14k
        (imm.0 == u).then_some(imm)
680
2.14k
    }
681
682
    /// A const alternative to the `Into<u32>` implementation.
683
2.03k
    pub const fn to_u32(self) -> u32 {
684
2.03k
        self.0
685
2.03k
    }
686
}
687
688
impl Opcode {
689
    /// Check if the opcode is allowed for predicates.
690
    ///
691
    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/index.md#predicate-verification>
692
    /// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/instruction-set.md#contract-instructions>
693
    #[allow(clippy::match_like_matches_macro)]
694
1.02M
    pub fn is_predicate_allowed(&self) -> bool {
695
1.02M
        use Opcode::*;
696
1.02M
        match self {
697
            ADD | AND | DIV | EQ | EXP | GT | LT | MLOG | MROO | MOD | MOVE | MUL
698
            | NOT | OR | SLL | SRL | SUB | XOR | WDCM | WQCM | WDOP | WQOP | WDML
699
            | WQML | WDDV | WQDV | WDMD | WQMD | WDAM | WQAM | WDMM | WQMM | PSHH
700
            | PSHL | POPH | POPL | RET | ALOC | MCL | MCP | MEQ | ECK1 | ECR1 | ED19
701
            | K256 | S256 | NOOP | FLAG | ADDI | ANDI | DIVI | EXPI | MODI | MULI
702
            | MLDV | ORI | SLLI | SRLI | SUBI | XORI | JNEI | LB | LW | SB | SW
703
            | MCPI | MCLI | GM | MOVI | JNZI | JI | JMP | JNE | JMPF | JMPB | JNZF
704
            | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF | LDC | BSIZ | BLDD => {
705
1.02M
                true
706
            }
707
30
            _ => false,
708
        }
709
1.02M
    }
710
}
711
712
// Direct conversions
713
714
impl From<u8> for RegId {
715
22
    fn from(u: u8) -> Self {
716
22
        RegId::new(u)
717
22
    }
718
}
719
720
impl From<u8> for Imm06 {
721
255
    fn from(u: u8) -> Self {
722
255
        Imm06::new(u)
723
255
    }
724
}
725
726
impl From<u16> for Imm12 {
727
4
    fn from(u: u16) -> Self {
728
4
        Imm12::new(u)
729
4
    }
730
}
731
732
impl From<u32> for Imm18 {
733
0
    fn from(u: u32) -> Self {
734
0
        Imm18::new(u)
735
0
    }
736
}
737
738
impl From<u32> for Imm24 {
739
0
    fn from(u: u32) -> Self {
740
0
        Imm24::new(u)
741
0
    }
742
}
743
744
impl From<RegId> for u8 {
745
7.03M
    fn from(RegId(u): RegId) -> Self {
746
7.03M
        u
747
7.03M
    }
748
}
749
750
impl From<Imm06> for u8 {
751
8
    fn from(Imm06(u): Imm06) -> Self {
752
8
        u
753
8
    }
754
}
755
756
impl From<Imm12> for u16 {
757
306k
    fn from(Imm12(u): Imm12) -> Self {
758
306k
        u
759
306k
    }
760
}
761
762
impl From<Imm18> for u32 {
763
489k
    fn from(Imm18(u): Imm18) -> Self {
764
489k
        u
765
489k
    }
766
}
767
768
impl From<Imm24> for u32 {
769
200k
    fn from(Imm24(u): Imm24) -> Self {
770
200k
        u
771
200k
    }
772
}
773
774
// Lossless, convenience conversions
775
776
impl From<RegId> for usize {
777
7.03M
    fn from(r: RegId) -> usize {
778
7.03M
        u8::from(r).into()
779
7.03M
    }
780
}
781
782
impl From<Imm06> for u16 {
783
0
    fn from(imm: Imm06) -> Self {
784
0
        u8::from(imm).into()
785
0
    }
786
}
787
788
impl From<Imm06> for u32 {
789
0
    fn from(imm: Imm06) -> Self {
790
0
        u8::from(imm).into()
791
0
    }
792
}
793
794
impl From<Imm06> for u64 {
795
8
    fn from(imm: Imm06) -> Self {
796
8
        u8::from(imm).into()
797
8
    }
798
}
799
800
impl From<Imm06> for u128 {
801
0
    fn from(imm: Imm06) -> Self {
802
0
        u8::from(imm).into()
803
0
    }
804
}
805
806
impl From<Imm12> for u32 {
807
11.6k
    fn from(imm: Imm12) -> Self {
808
11.6k
        u16::from(imm).into()
809
11.6k
    }
810
}
811
812
impl From<Imm12> for u64 {
813
161k
    fn from(imm: Imm12) -> Self {
814
161k
        u16::from(imm).into()
815
161k
    }
816
}
817
818
impl From<Imm12> for u128 {
819
133k
    fn from(imm: Imm12) -> Self {
820
133k
        u16::from(imm).into()
821
133k
    }
822
}
823
824
impl From<Imm18> for u64 {
825
489k
    fn from(imm: Imm18) -> Self {
826
489k
        u32::from(imm).into()
827
489k
    }
828
}
829
830
impl From<Imm18> for u128 {
831
0
    fn from(imm: Imm18) -> Self {
832
0
        u32::from(imm).into()
833
0
    }
834
}
835
836
impl From<Imm24> for u64 {
837
200k
    fn from(imm: Imm24) -> Self {
838
200k
        u32::from(imm).into()
839
200k
    }
840
}
841
842
impl From<Imm24> for u128 {
843
0
    fn from(imm: Imm24) -> Self {
844
0
        u32::from(imm).into()
845
0
    }
846
}
847
848
impl From<Opcode> for u8 {
849
0
    fn from(op: Opcode) -> Self {
850
0
        op as u8
851
0
    }
852
}
853
854
impl From<Instruction> for RawInstruction {
855
2.19k
    fn from(inst: Instruction) -> Self {
856
2.19k
        RawInstruction::from_be_bytes(inst.into())
857
2.19k
    }
858
}
859
860
impl core::convert::TryFrom<RawInstruction> for Instruction {
861
    type Error = InvalidOpcode;
862
863
1.63M
    fn try_from(u: RawInstruction) -> Result<Self, Self::Error> {
864
1.63M
        Self::try_from(u.to_be_bytes())
865
1.63M
    }
866
}
867
868
// Index slices with `RegId`
869
870
impl<T> core::ops::Index<RegId> for [T]
871
where
872
    [T]: core::ops::Index<usize, Output = T>,
873
{
874
    type Output = T;
875
876
6.78M
    fn index(&self, ix: RegId) -> &Self::Output {
877
6.78M
        &self[usize::from(ix)]
878
6.78M
    }
879
}
880
881
impl<T> core::ops::IndexMut<RegId> for [T]
882
where
883
    [T]: core::ops::IndexMut<usize, Output = T>,
884
{
885
71.6k
    fn index_mut(&mut self, ix: RegId) -> &mut Self::Output {
886
71.6k
        &mut self[usize::from(ix)]
887
71.6k
    }
888
}
889
890
// Collect instructions into bytes or halfwords
891
892
#[cfg(feature = "alloc")]
893
impl core::iter::FromIterator<Instruction> for alloc::vec::Vec<u8> {
894
5.66k
    fn from_iter<I: IntoIterator<Item = Instruction>>(iter: I) -> Self {
895
5.66k
        iter.into_iter().flat_map(Instruction::to_bytes).collect()
896
5.66k
    }
897
}
898
899
#[cfg(feature = "alloc")]
900
impl core::iter::FromIterator<Instruction> for alloc::vec::Vec<u32> {
901
0
    fn from_iter<I: IntoIterator<Item = Instruction>>(iter: I) -> Self {
902
0
        iter.into_iter().map(u32::from).collect()
903
0
    }
904
}
905
906
/// Given an iterator yielding bytes, produces an iterator yielding `Instruction`s.
907
///
908
/// This function assumes each consecutive 4 bytes aligns with an instruction.
909
///
910
/// The produced iterator yields an `Err` in the case that an instruction fails to parse
911
/// from 4 consecutive bytes.
912
1
pub fn from_bytes<I>(bs: I) -> impl Iterator<Item = Result<Instruction, InvalidOpcode>>
913
1
where
914
1
    I: IntoIterator<Item = u8>,
915
1
{
916
1
    let mut iter = bs.into_iter();
917
213
    core::iter::from_fn(move || {
918
213
        let 
a212
= iter.next()
?1
;
919
212
        let b = iter.next()
?0
;
920
212
        let c = iter.next()
?0
;
921
212
        let d = iter.next()
?0
;
922
212
        Some(Instruction::try_from([a, b, c, d]))
923
213
    })
924
1
}
925
926
/// Given an iterator yielding u32s (i.e. "half words" or "raw instructions"), produces an
927
/// iterator yielding `Instruction`s.
928
///
929
/// This function assumes each consecutive 4 bytes aligns with an instruction.
930
///
931
/// The produced iterator yields an `Err` in the case that an instruction fails to parse.
932
0
pub fn from_u32s<I>(us: I) -> impl Iterator<Item = Result<Instruction, InvalidOpcode>>
933
0
where
934
0
    I: IntoIterator<Item = u32>,
935
0
{
936
0
    us.into_iter().map(Instruction::try_from)
937
0
}
938
939
// Short-hand, `panic!`ing constructors for the short-hand instruction construtors (e.g
940
// op::add).
941
942
103
fn check_imm06(u: u8) -> Imm06 {
943
103
    Imm06::new_checked(u)
944
103
        .unwrap_or_else(|| 
panic!("Value `{u}` out of range for 6-bit immediate"0
))
945
103
}
946
947
44.4k
fn check_imm12(u: u16) -> Imm12 {
948
44.4k
    Imm12::new_checked(u)
949
44.4k
        .unwrap_or_else(|| 
panic!("Value `{u}` out of range for 12-bit immediate"0
))
950
44.4k
}
951
952
14.2k
fn check_imm18(u: u32) -> Imm18 {
953
14.2k
    Imm18::new_checked(u)
954
14.2k
        .unwrap_or_else(|| 
panic!("Value `{u}` out of range for 18-bit immediate"0
))
955
14.2k
}
956
957
2.12k
fn check_imm24(u: u32) -> Imm24 {
958
2.12k
    Imm24::new_checked(u)
959
2.12k
        .unwrap_or_else(|| 
panic!("Value `{u}` out of range for 24-bit immediate"0
))
960
2.12k
}
961
962
// --------------------------------------------------------
963
964
// The size of the instruction isn't larger than necessary.
965
// 1 byte for the opcode, 3 bytes for registers and immediates.
966
#[test]
967
1
fn test_instruction_size() {
968
1
    // NOTE: Throughout `fuel-vm`, we use the `Instruction::SIZE` associated
969
1
    // const to refer to offsets within raw instruction data. As a result, it's
970
1
    // *essential* that this equivalence remains the same. If you've added
971
1
    // a new field or changed the size of `Instruction` somehow and have
972
1
    // arrived at this assertion, ensure that you also revisit all sites where
973
1
    // `Instruction::SIZE` is used and make sure we're using the right value
974
1
    // (in most cases, the right value is `core::mem::size_of::<RawInstruction>()`).
975
1
    assert_eq!(
976
1
        core::mem::size_of::<Instruction>(),
977
1
        core::mem::size_of::<RawInstruction>()
978
1
    );
979
980
1
    assert_eq!(core::mem::size_of::<Instruction>(), Instruction::SIZE);
981
1
}
982
983
// The size of the opcode is exactly one byte.
984
#[test]
985
1
fn test_opcode_size() {
986
1
    assert_eq!(core::mem::size_of::<Opcode>(), 1);
987
1
}
988
989
#[test]
990
#[allow(clippy::match_like_matches_macro)]
991
1
fn check_predicate_allowed() {
992
    use Opcode::*;
993
256
    for 
byte255
in 0..u8::MAX {
994
255
        if let Ok(
repr111
) = Opcode::try_from(byte) {
  Branch (994:16): [True: 111, False: 144]
995
111
            let should_allow = match repr {
996
                BAL | BHEI | BHSH | BURN | CALL | CB | CCP | CROO | CSIZ | LOG | LOGD
997
                | MINT | RETD | RVRT | SMO | SCWQ | SRW | SRWQ | SWW | SWWQ | TIME
998
24
                | TR | TRO | ECAL => false,
999
87
                _ => true,
1000
            };
1001
111
            assert_eq!(should_allow, repr.is_predicate_allowed());
1002
144
        }
1003
    }
1004
1
}
1005
1006
// Test roundtrip conversion for all valid opcodes.
1007
#[test]
1008
1
fn test_opcode_u8_conv() {
1009
257
    for 
u256
in 0..=u8::MAX {
1010
256
        if let Ok(
op111
) = Opcode::try_from(u) {
  Branch (1010:16): [True: 111, False: 145]
1011
111
            assert_eq!(op as u8, u);
1012
145
        }
1013
    }
1014
1
}