Coverage Report

Created: 2026-07-28 15:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/src/execution/instructions/mod.rs
Line
Count
Source
1
//! This module contains the actual interpretation loop
2
3
use alloc::vec::Vec;
4
use core::{array, num::NonZeroU64, ops::ControlFlow};
5
6
use crate::{
7
    core::{
8
        decoding::decoder::WasmDecoder,
9
        sidetable::Sidetable,
10
        structure::{
11
            modules::indices::{DataIdx, ElemIdx, MemIdx, TableIdx},
12
            types::MemArg,
13
        },
14
        utils::ToUsizeExt,
15
    },
16
    execution::{
17
        assert_validated::UnwrapValidatedExt,
18
        config::Config,
19
        instructions::dispatch_tables::{
20
            HasBaseDispatchTable, HasFcDispatchTable, HasFdDispatchTable,
21
        },
22
        numerics::representations::LittleEndianBytes,
23
        runtime_structure::{
24
            data_instances::DataInst,
25
            element_instances::ElemInst,
26
            function_instances::FuncInst,
27
            memory_instances::MemInst,
28
            module_instances::ModuleInst,
29
            store::{Hostcode, StoreInner},
30
            table_instances::TableInst,
31
            value_stack::Stack,
32
        },
33
    },
34
    trace, AddrVec, DataAddr, ElemAddr, FuncAddr, MemAddr, ModuleAddr, RuntimeError, Store,
35
    TableAddr, TrapError, Value, WasmResumable,
36
};
37
38
mod control;
39
mod memory;
40
mod numeric;
41
mod parametric;
42
mod reference;
43
mod table;
44
mod variable;
45
mod vector;
46
47
pub mod const_interpreter_loop;
48
mod dispatch_tables;
49
50
/// A non-error outcome of execution of the interpreter loop
51
pub enum InterpreterLoopOutcome {
52
    /// Execution has returned normally, i.e. the end of the bottom-most
53
    /// function on the stack was reached.
54
    ExecutionReturned,
55
    /// Execution was preempted because there was not enough fuel in the
56
    /// [`WasmResumable`] object.
57
    ///
58
    OutOfFuel {
59
        /// The amount of fuel required to continue execution at least the next
60
        /// instruction.
61
        required_fuel: NonZeroU64,
62
    },
63
    HostCalled {
64
        func_addr: FuncAddr,
65
        // TODO this allocation might be preventable. mutably borrow the stack
66
        // instead
67
        params: Vec<Value>,
68
        hostcode: Hostcode,
69
    },
70
}
71
72
type InstructionHandlerFn =
73
    for<'wasm, 'modules> unsafe fn(
74
        wasm: &mut WasmDecoder<'wasm>,
75
        resumable: &mut WasmResumable,
76
        current_sidetable: &mut &'modules Sidetable,
77
        store_inner: &mut StoreInner,
78
        modules: &'modules AddrVec<ModuleAddr, ModuleInst<'wasm>>,
79
        current_module: &mut ModuleAddr,
80
        current_function_end_marker: &mut usize,
81
    )
82
        -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError>;
83
84
// A placeholder instruction for unassigned instruction bytes. This function is by definition dead
85
// code!
86
define_instruction!(super::unset, unset_mod, fuel_check = omit);
87
/// # Safety
88
///
89
/// This function may not be called.
90
#[inline(always)]
91
0
pub unsafe fn unset(_: State) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
92
0
    unreachable!("an invalid instruction byte was called. This should never happen, and could become UB in the future.")
93
}
94
95
/// Interprets wasm native functions. Wasm parameters and Wasm return values are passed on the stack.
96
/// Returns `Ok(ControlFlow::Continue(()))` in case execution successfully terminates, `Ok(Some(required_fuel))` if execution
97
/// terminates due to insufficient fuel, indicating how much fuel is required to resume with `required_fuel`,
98
/// and `[Error::RuntimeError]` otherwise.
99
///
100
/// # Safety
101
///
102
/// The given resumable must be valid in the given [`Store`].
103
#[inline(never)]
104
54.7k
pub(super) unsafe fn run<T: Config>(
105
54.7k
    resumable: &mut WasmResumable,
106
54.7k
    store: &mut Store<T>,
107
54.7k
) -> Result<InterpreterLoopOutcome, RuntimeError> {
108
54.7k
    let current_func_addr = resumable.current_func_addr;
109
54.7k
    let pc = resumable.pc;
110
    // SAFETY: The caller ensures that the resumable and thus also its function
111
    // address is valid in the current store.
112
54.7k
    let func_inst = unsafe { store.inner.functions.get(current_func_addr) };
113
54.7k
    let FuncInst::WasmFunc(wasm_func_inst) = &func_inst else {
114
0
        unreachable!(
115
            "the interpreter loop shall only be executed with native wasm functions as root call"
116
        );
117
    };
118
54.7k
    let mut current_module = wasm_func_inst.module_addr;
119
120
    // Start reading the function's instructions
121
    // SAFETY: This module address was just read from the current store. Every
122
    // store guarantees all addresses contained in it to be valid within itself.
123
54.7k
    let module = unsafe { store.modules.get(current_module) };
124
54.7k
    let wasm_bytecode = module.wasm_bytecode;
125
54.7k
    let wasm = &mut WasmDecoder::new(wasm_bytecode);
126
127
54.7k
    let mut current_sidetable: &Sidetable = &module.sidetable;
128
129
54.7k
    let mut current_function_end_marker =
130
54.7k
        wasm_func_inst.code_expr.from() + wasm_func_inst.code_expr.len();
131
132
54.7k
    let store_inner = &mut store.inner;
133
134
54.7k
    wasm.pc = pc;
135
136
    loop {
137
        // call the instruction hook
138
16.4M
        store.user_data.instruction_hook(wasm_bytecode, wasm.pc);
139
140
16.4M
        let prev_pc = wasm.pc;
141
142
16.4M
        let first_instr_byte = wasm.decode_u8().unwrap_validated();
143
144
        trace!(
145
            "Executing instruction {} at pc={}",
146
            crate::core::structure::instructions::instruction_byte_to_str(first_instr_byte),
147
            wasm.pc
148
        );
149
150
16.4M
        let instruction_fn = T::DISPATCH_TABLE
151
16.4M
            .get(usize::from(first_instr_byte))
152
16.4M
            .expect("the instruction to be valid because the code is validated");
153
154
        // SAFETY: All possible instruction handler functions use the same safety requirements, as
155
        // they are defined through the same macro: The caller ensures that the resumable is valid
156
        // in the current store. Also all other address types passed via the `State` must come from
157
        // the current store itself. Therefore, they are automatically valid in this store.
158
16.4M
        let instruction_result = unsafe {
159
16.4M
            instruction_fn(
160
16.4M
                wasm,
161
16.4M
                resumable,
162
16.4M
                &mut current_sidetable,
163
16.4M
                store_inner,
164
16.4M
                &store.modules,
165
16.4M
                &mut current_module,
166
16.4M
                &mut current_function_end_marker,
167
16.4M
            )
168
        };
169
170
16.4M
        if let ControlFlow::Break(
interpreter_loop_outcome51.6k
) = instruction_result
?3.09k
{
171
51.6k
            if let InterpreterLoopOutcome::OutOfFuel { .. } = interpreter_loop_outcome {
172
54
                wasm.pc = prev_pc;
173
51.5k
            }
174
175
51.6k
            resumable.pc = wasm.pc;
176
51.6k
            return Ok(interpreter_loop_outcome);
177
16.4M
        }
178
    }
179
54.7k
}
180
181
//helper function for avoiding code duplication at intraprocedural jumps
182
1.56M
fn do_sidetable_control_transfer(
183
1.56M
    wasm: &mut WasmDecoder,
184
1.56M
    stack: &mut Stack,
185
1.56M
    current_stp: &mut usize,
186
1.56M
    current_sidetable: &Sidetable,
187
1.56M
) -> Result<(), RuntimeError> {
188
1.56M
    let sidetable_entry = &current_sidetable[*current_stp];
189
190
1.56M
    stack.remove_in_between(sidetable_entry.popcnt, sidetable_entry.valcnt);
191
192
1.56M
    *current_stp = sidetable_entry.stp;
193
1.56M
    wasm.pc = sidetable_entry.pc;
194
195
1.56M
    Ok(())
196
1.56M
}
197
198
#[inline(always)]
199
931k
fn calculate_mem_address(memarg: &MemArg, relative_address: u32) -> Result<usize, RuntimeError> {
200
    // The spec states that this should be a 33 bit integer, e.g. it is not legal to wrap if the
201
    // sum of offset and relative_address exceeds u32::MAX. To emulate this behavior, we use a
202
    // checked addition.
203
    // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions
204
931k
    let 
effective_address931k
= memarg
205
931k
        .offset
206
931k
        .checked_add(relative_address)
207
931k
        .ok_or(TrapError::MemoryOrDataAccessOutOfBounds)
?43
;
208
209
931k
    Ok(effective_address.into_usize())
210
931k
}
211
212
//helpers for avoiding code duplication during module instantiation
213
/// # Safety
214
///
215
/// 1. The module address `current_module` must be valid in `store_modules` for a module instance `module_inst`.
216
/// 2. The table index `table_idx` must be valid in `module_inst` for a table address `table_addr`.
217
/// 3. `table_addr` must be valid in `store_tables`.
218
/// 4. The element index `elem_idx` must be valid in `module_inst` for an element address `elem_addr`.
219
/// 5. `elem_addr` must be valid in `store_elements`.
220
// TODO instead of passing all module instances and the current module addr
221
// separately, directly pass a `&ModuleInst`.
222
#[inline(always)]
223
#[allow(clippy::too_many_arguments)]
224
473
pub(super) unsafe fn table_init(
225
473
    store_modules: &AddrVec<ModuleAddr, ModuleInst>,
226
473
    store_tables: &mut AddrVec<TableAddr, TableInst>,
227
473
    store_elements: &AddrVec<ElemAddr, ElemInst>,
228
473
    current_module: ModuleAddr,
229
473
    elem_idx: ElemIdx,
230
473
    table_idx: TableIdx,
231
473
    n: u32,
232
473
    s: i32,
233
473
    d: i32,
234
473
) -> Result<(), RuntimeError> {
235
473
    let n = n.into_usize();
236
473
    let s = s.cast_unsigned().into_usize();
237
473
    let d = d.cast_unsigned().into_usize();
238
239
    // SAFETY: The caller ensures that this module address is valid in this
240
    // address vector (1).
241
473
    let module_inst = unsafe { store_modules.get(current_module) };
242
    // SAFETY: The caller ensures that `table_idx` is valid for this specific
243
    // `IdxVec` (2).
244
473
    let table_addr = *unsafe { module_inst.table_addrs.get(table_idx) };
245
    // SAFETY: The caller ensures that `elem_idx` is valid for this specific
246
    // `IdxVec` (4).
247
473
    let elem_addr = *unsafe { module_inst.elem_addrs.get(elem_idx) };
248
    // SAFETY: The caller ensures that this table address is valid in this
249
    // address vector (3).
250
473
    let tab = unsafe { store_tables.get_mut(table_addr) };
251
    // SAFETY: The caller ensures that this element address is valid in this
252
    // address vector (5).
253
473
    let elem = unsafe { store_elements.get(elem_addr) };
254
255
    trace!(
256
        "Instruction: table.init '{}' '{}' [{} {} {}] -> []",
257
        elem_idx,
258
        table_idx,
259
        d,
260
        s,
261
        n
262
    );
263
264
473
    let 
final_src_offset444
= s
265
473
        .checked_add(n)
266
473
        .filter(|&res| res <= elem.len())
267
473
        .ok_or(TrapError::TableOrElementAccessOutOfBounds)
?29
;
268
269
444
    if d.checked_add(n).filter(|&res| res <= tab.len()).is_none() {
270
29
        return Err(TrapError::TableOrElementAccessOutOfBounds.into());
271
415
    }
272
273
415
    let dest = &mut tab.elem[d..];
274
415
    let src = &elem.references[s..final_src_offset];
275
415
    dest[..src.len()].copy_from_slice(src);
276
415
    Ok(())
277
473
}
278
279
/// # Safety
280
///
281
/// 1. The module address `current_module` must be valid in `store_modules` for some module instance `module_inst`.
282
/// 2. The element index `elem_idx` must be valid in `module_inst` for some element address `elem_addr`.
283
/// 3. `elem_addr` must be valid in `store_elements`.
284
#[inline(always)]
285
409
pub(super) unsafe fn elem_drop(
286
409
    store_modules: &AddrVec<ModuleAddr, ModuleInst>,
287
409
    store_elements: &mut AddrVec<ElemAddr, ElemInst>,
288
409
    current_module: ModuleAddr,
289
409
    elem_idx: ElemIdx,
290
409
) {
291
    // WARN: i'm not sure if this is okay or not
292
293
    // SAFETY: The caller ensures that this module address is valid in this
294
    // address vector (1).
295
409
    let module_inst = unsafe { store_modules.get(current_module) };
296
    // SAFETY: The caller ensures that `elem_idx` is valid for this specific
297
    // `IdxVec` (2).
298
409
    let elem_addr = *unsafe { module_inst.elem_addrs.get(elem_idx) };
299
300
    // SAFETY: The caller ensures that this element address is valid in this
301
    // address vector (3).
302
409
    let elem = unsafe { store_elements.get_mut(elem_addr) };
303
304
409
    elem.references.clear();
305
409
}
306
307
/// # Safety
308
///
309
/// 1. The module address `current_module` must be valid in `store_modules` for some module instance `module_inst`.
310
/// 2. The memory index `mem_idx` must be valid in `module_inst` for some memory address `mem_addr`.
311
/// 3. `mem_addr` must be valid in `store_memories` for some memory instance `mem`.
312
/// 4. The data index `data_idx` must be valid in `module_inst` for some data address `data_addr`.
313
/// 5. `data_addr` must be valid in `store_data`.
314
#[inline(always)]
315
#[allow(clippy::too_many_arguments)]
316
262
pub(super) unsafe fn memory_init(
317
262
    store_modules: &AddrVec<ModuleAddr, ModuleInst>,
318
262
    store_memories: &mut AddrVec<MemAddr, MemInst>,
319
262
    store_data: &AddrVec<DataAddr, DataInst>,
320
262
    current_module: ModuleAddr,
321
262
    data_idx: DataIdx,
322
262
    mem_idx: MemIdx,
323
262
    n: u32,
324
262
    s: u32,
325
262
    d: u32,
326
262
) -> Result<(), RuntimeError> {
327
262
    let n = n.into_usize();
328
262
    let s = s.into_usize();
329
262
    let d = d.into_usize();
330
331
    // SAFETY: The caller ensures that this is module address is valid in this
332
    // address vector (1).
333
262
    let module_inst = unsafe { store_modules.get(current_module) };
334
    // SAFETY: The caller ensures that `mem_idx` is valid for this specific
335
    // `IdxVec` (2).
336
262
    let mem_addr = *unsafe { module_inst.mem_addrs.get(mem_idx) };
337
    // SAFETY: The caller ensures that this memory address is valid in this
338
    // address vector (3).
339
262
    let mem = unsafe { store_memories.get_mut(mem_addr) };
340
    // SAFETY: The caller ensures that `data_idx` is valid for this specific
341
    // `IdxVec` (4).
342
262
    let data_addr = *unsafe { module_inst.data_addrs.get(data_idx) };
343
    // SAFETY: The caller ensures that this data address is valid in this
344
    // address vector (5).
345
262
    let data = unsafe { store_data.get(data_addr) };
346
347
262
    mem.mem.init(d, &data.data, s, n)
?39
;
348
349
    trace!("Instruction: memory.init");
350
223
    Ok(())
351
262
}
352
353
/// # Safety
354
///
355
/// 1. The module address `current_module` must be valid in `store_modules` for some module instance `module_inst`.
356
/// 2. The data index `data_idx` must be valid in `module_inst` for some data address `data_addr`.
357
/// 3. `data_addr` must be valid in `store_data`.
358
#[inline(always)]
359
212
pub(super) unsafe fn data_drop(
360
212
    store_modules: &AddrVec<ModuleAddr, ModuleInst>,
361
212
    store_data: &mut AddrVec<DataAddr, DataInst>,
362
212
    current_module: ModuleAddr,
363
212
    data_idx: DataIdx,
364
212
) {
365
    // Here is debatable
366
    // If we were to be on par with the spec we'd have to use a DataInst struct
367
    // But since memory.init is specifically made for Passive data segments
368
    // I thought that using DataMode would be better because we can see if the
369
    // data segment is passive or active
370
371
    // Also, we should set data to null here (empty), which we do by clearing it
372
    // SAFETY: The caller guarantees this module to be valid in this address
373
    // vector (1).
374
212
    let module_inst = unsafe { store_modules.get(current_module) };
375
    // SAFETY: The caller ensures that `data_idx` is valid for this specific
376
    // `IdxVec` (2).
377
212
    let data_addr = *unsafe { module_inst.data_addrs.get(data_idx) };
378
    // SAFETY: The caller ensures that this data address is valid in this
379
    // address vector (3).
380
212
    let data = unsafe { store_data.get_mut(data_addr) };
381
382
212
    data.data.clear();
383
212
}
384
385
#[inline(always)]
386
45.4k
pub(crate) fn to_lanes<const M: usize, const N: usize, T: LittleEndianBytes<M>>(
387
45.4k
    data: [u8; 16],
388
45.4k
) -> [T; N] {
389
45.4k
    assert_eq!(M * N, 16);
390
391
45.4k
    let mut lanes = data
392
45.4k
        .chunks(M)
393
185k
        .
map45.4k
(|chunk| T::from_le_bytes(chunk.try_into().unwrap()));
394
185k
    
array::from_fn45.4k
(|_| lanes.next().unwrap())
395
45.4k
}
396
397
#[inline(always)]
398
23.7k
pub(crate) fn from_lanes<const M: usize, const N: usize, T: LittleEndianBytes<M>>(
399
23.7k
    lanes: [T; N],
400
23.7k
) -> [u8; 16] {
401
23.7k
    assert_eq!(M * N, 16);
402
403
23.7k
    let mut bytes = lanes.into_iter().flat_map(T::to_le_bytes);
404
380k
    
array::from_fn23.7k
(|_| bytes.next().unwrap())
405
23.7k
}
406
407
/// The main execution state interacted with by all instructions.
408
///
409
/// # Safety
410
///
411
/// This struct is passed across many different functions. For soundness, all address types
412
/// contained in it must be valid in the [`StoreInner`]. Also see [`StoreInner`]s safety
413
/// documentation.
414
pub(crate) struct State<'a, 'sidetable, 'wasm> {
415
    wasm: &'a mut WasmDecoder<'wasm>,
416
    resumable: &'a mut WasmResumable,
417
    current_sidetable: &'a mut &'sidetable Sidetable,
418
    store_inner: &'a mut StoreInner,
419
    modules: &'sidetable AddrVec<ModuleAddr, ModuleInst<'wasm>>,
420
    current_module: &'a mut ModuleAddr,
421
    current_function_end_marker: &'a mut usize,
422
}
423
424
macro_rules! define_instruction {
425
    ($name:expr, $name_mod:ident, fuel_check = omit) => {
426
        pub(crate) mod $name_mod {
427
            use ::core::ops::ControlFlow;
428
429
            use $crate::{
430
                core::{decoding::decoder::WasmDecoder, sidetable::Sidetable},
431
                execution::{
432
                    instructions::{InterpreterLoopOutcome, State},
433
                    resumable::WasmResumable,
434
                    runtime_structure::{
435
                        addresses::{AddrVec, ModuleAddr},
436
                        module_instances::ModuleInst,
437
                        store::StoreInner,
438
                    },
439
                },
440
                Config, RuntimeError,
441
            };
442
443
            /// # Safety
444
            ///
445
            /// The given [`WasmResumable`] and all address types contained in the [`State`] must be
446
            /// valid in the [`StoreInner`] that is also contained in the [`State`].
447
            #[allow(
448
                clippy::extra_unused_type_parameters,
449
                reason = "T is only used by some instructions"
450
            )]
451
27.5k
            pub(crate) unsafe fn wrapper<'wasm, 'modules, T: Config>(
452
27.5k
                wasm: &mut WasmDecoder<'wasm>,
453
27.5k
                resumable: &mut WasmResumable,
454
27.5k
                current_sidetable: &mut &'modules Sidetable,
455
27.5k
                store_inner: &mut StoreInner,
456
27.5k
                modules: &'modules AddrVec<ModuleAddr, ModuleInst<'wasm>>,
457
27.5k
                current_module: &mut ModuleAddr,
458
27.5k
                current_function_end_marker: &mut usize,
459
27.5k
            ) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
460
27.5k
                let state = State {
461
27.5k
                    store_inner,
462
27.5k
                    modules,
463
27.5k
                    wasm,
464
27.5k
                    current_module,
465
27.5k
                    current_function_end_marker,
466
27.5k
                    current_sidetable,
467
27.5k
                    resumable,
468
27.5k
                };
469
470
                // SAFETY: The instruction implementation requires that the `State` is correct
471
                // according to its safety documentation. The caller of the current function
472
                // guarantees the same for all fields.
473
27.5k
                unsafe { $name(state) }
474
27.5k
            }
475
        }
476
    };
477
478
    ($name:expr, $name_mod:ident, fuel_check = flat($instruction:ident)) => {
479
        pub(crate) mod $name_mod {
480
            use ::core::ops::ControlFlow;
481
482
            use $crate::{
483
                core::{
484
                    decoding::decoder::WasmDecoder, sidetable::Sidetable, structure::instructions,
485
                },
486
                execution::{
487
                    instructions::{decrement_fuel, InterpreterLoopOutcome, State},
488
                    resumable::WasmResumable,
489
                    runtime_structure::{
490
                        addresses::{AddrVec, ModuleAddr},
491
                        module_instances::ModuleInst,
492
                        store::StoreInner,
493
                    },
494
                },
495
                Config, RuntimeError,
496
            };
497
498
            /// # Safety
499
            ///
500
            /// The given [`WasmResumable`] and all address types contained in the [`State`] must be
501
            /// valid in the [`StoreInner`] that is also contained in the [`State`].
502
16.4M
            pub(crate) unsafe fn wrapper<'wasm, 'modules, T: Config>(
503
16.4M
                wasm: &mut WasmDecoder<'wasm>,
504
16.4M
                resumable: &mut WasmResumable,
505
16.4M
                current_sidetable: &mut &'modules Sidetable,
506
16.4M
                store_inner: &mut StoreInner,
507
16.4M
                modules: &'modules AddrVec<ModuleAddr, ModuleInst<'wasm>>,
508
16.4M
                current_module: &mut ModuleAddr,
509
16.4M
                current_function_end_marker: &mut usize,
510
16.4M
            ) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
511
16.4M
                if let core::ops::ControlFlow::Break(
outcome53
) = decrement_fuel(
512
16.4M
                    T::get_flat_cost(instructions::$instruction),
513
16.4M
                    &mut resumable.maybe_fuel,
514
16.4M
                ) {
515
53
                    return Ok(core::ops::ControlFlow::Break(outcome));
516
16.4M
                }
517
518
16.4M
                let state = State {
519
16.4M
                    store_inner,
520
16.4M
                    modules,
521
16.4M
                    wasm,
522
16.4M
                    current_module,
523
16.4M
                    current_function_end_marker,
524
16.4M
                    current_sidetable,
525
16.4M
                    resumable,
526
16.4M
                };
527
528
                // SAFETY: The instruction implementation requires that the `State` is correct
529
                // according to its safety documentation. The caller of the current function
530
                // guarantees the same for all fields.
531
16.4M
                unsafe { $name(state) }
532
16.4M
            }
533
        }
534
    };
535
536
    ($name:expr, $name_mod:ident, fuel_check = flat_fc($instruction:ident)) => {
537
        pub(crate) mod $name_mod {
538
            use ::core::ops::ControlFlow;
539
540
            use $crate::{
541
                core::{
542
                    decoding::decoder::WasmDecoder, sidetable::Sidetable,
543
                    structure::instructions::fc_extensions,
544
                },
545
                execution::{
546
                    instructions::{decrement_fuel, InterpreterLoopOutcome, State},
547
                    resumable::WasmResumable,
548
                    runtime_structure::{
549
                        addresses::{AddrVec, ModuleAddr},
550
                        module_instances::ModuleInst,
551
                        store::StoreInner,
552
                    },
553
                },
554
                Config, RuntimeError,
555
            };
556
557
            /// # Safety
558
            ///
559
            /// The given [`WasmResumable`] and all address types contained in the [`State`] must be
560
            /// valid in the [`StoreInner`].
561
425
            pub(crate) unsafe fn wrapper<'wasm, 'modules, T: Config>(
562
425
                wasm: &mut WasmDecoder<'wasm>,
563
425
                resumable: &mut WasmResumable,
564
425
                current_sidetable: &mut &'modules Sidetable,
565
425
                store_inner: &mut StoreInner,
566
425
                modules: &'modules AddrVec<ModuleAddr, ModuleInst<'wasm>>,
567
425
                current_module: &mut ModuleAddr,
568
425
                current_function_end_marker: &mut usize,
569
425
            ) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
570
425
                if let core::ops::ControlFlow::Break(
outcome0
) = decrement_fuel(
571
425
                    T::get_fc_extension_flat_cost(fc_extensions::$instruction),
572
425
                    &mut resumable.maybe_fuel,
573
425
                ) {
574
0
                    return Ok(core::ops::ControlFlow::Break(outcome));
575
425
                }
576
577
425
                let state = State {
578
425
                    store_inner,
579
425
                    modules,
580
425
                    wasm,
581
425
                    current_module,
582
425
                    current_function_end_marker,
583
425
                    current_sidetable,
584
425
                    resumable,
585
425
                };
586
587
                // SAFETY: The instruction implementation requires that the `State` is correct
588
                // according to its safety documentation. The caller of the current function
589
                // guarantees the same for all fields.
590
425
                unsafe { $name(state) }
591
425
            }
592
        }
593
    };
594
595
    ($name:expr, $name_mod:ident, fuel_check = flat_fd($instruction:ident)) => {
596
        pub(crate) mod $name_mod {
597
            use ::core::ops::ControlFlow;
598
599
            use $crate::{
600
                core::{
601
                    decoding::decoder::WasmDecoder, sidetable::Sidetable,
602
                    structure::instructions::fd_extensions,
603
                },
604
                execution::{
605
                    instructions::{decrement_fuel, InterpreterLoopOutcome, State},
606
                    resumable::WasmResumable,
607
                    runtime_structure::{
608
                        addresses::{AddrVec, ModuleAddr},
609
                        module_instances::ModuleInst,
610
                        store::StoreInner,
611
                    },
612
                },
613
                Config, RuntimeError,
614
            };
615
616
            /// # Safety
617
            ///
618
            /// The given [`WasmResumable`] and all address types contained in the [`State`] must be
619
            /// valid in the [`StoreInner`] that is also contained in the [`State`].
620
25.8k
            pub(crate) unsafe fn wrapper<'wasm, 'modules, T: Config>(
621
25.8k
                wasm: &mut WasmDecoder<'wasm>,
622
25.8k
                resumable: &mut WasmResumable,
623
25.8k
                current_sidetable: &mut &'modules Sidetable,
624
25.8k
                store_inner: &mut StoreInner,
625
25.8k
                modules: &'modules AddrVec<ModuleAddr, ModuleInst<'wasm>>,
626
25.8k
                current_module: &mut ModuleAddr,
627
25.8k
                current_function_end_marker: &mut usize,
628
25.8k
            ) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
629
25.8k
                if let ControlFlow::Break(
outcome0
) = decrement_fuel(
630
25.8k
                    T::get_fd_extension_flat_cost(fd_extensions::$instruction),
631
25.8k
                    &mut resumable.maybe_fuel,
632
25.8k
                ) {
633
0
                    return Ok(core::ops::ControlFlow::Break(outcome));
634
25.8k
                }
635
636
25.8k
                let state = State {
637
25.8k
                    store_inner,
638
25.8k
                    modules,
639
25.8k
                    wasm,
640
25.8k
                    current_module,
641
25.8k
                    current_function_end_marker,
642
25.8k
                    current_sidetable,
643
25.8k
                    resumable,
644
25.8k
                };
645
646
                // SAFETY: The instruction implementation requires that the `State` is correct
647
                // according to its safety documentation. The caller of the current function
648
                // guarantees the same for all fields.
649
25.8k
                unsafe { $name(state) }
650
25.8k
            }
651
        }
652
    };
653
}
654
655
pub(crate) use define_instruction;
656
657
#[inline(always)]
658
16.4M
fn decrement_fuel(cost: u64, maybe_fuel: &mut Option<u64>) -> ControlFlow<InterpreterLoopOutcome> {
659
16.4M
    if let Some(
fuel275
) = maybe_fuel {
660
275
        if *fuel >= cost {
661
222
            *fuel -= cost;
662
222
        } else {
663
53
            return ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
664
53
                required_fuel: NonZeroU64::new(cost - *fuel)
665
53
                    .expect("the last check guarantees that the current fuel is smaller than cost"),
666
53
            });
667
        }
668
16.4M
    }
669
670
16.4M
    ControlFlow::Continue(())
671
16.4M
}
672
673
define_instruction!(
674
    super::fc_extensions_dispatch::<T>,
675
    fc_extensions_dispatch_mod,
676
    fuel_check = omit
677
);
678
/// # Safety
679
///
680
/// The given [`WasmResumable`] and all address types contained in the [`State`] must be
681
/// valid in the [`StoreInner`] that is also contained in the [`State`].
682
#[inline(always)]
683
1.00k
pub unsafe fn fc_extensions_dispatch<T: Config>(
684
1.00k
    state: State,
685
1.00k
) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
686
    // should we call instruction hook here as well? multibyte instruction
687
1.00k
    let second_instr = state.wasm.decode_var_u32().unwrap_validated();
688
689
    trace!(
690
        "Executing FC instruction {} at pc={}",
691
        crate::core::structure::instructions::fc_extension_instruction_to_str(second_instr),
692
        state.wasm.pc
693
    );
694
695
1.00k
    let instruction_fn = T::FC_DISPATCH_TABLE
696
1.00k
        .get(second_instr.into_usize())
697
1.00k
        .expect("the instruction to be valid because the code is validated");
698
699
    // SAFETY: All possible instruction handler functions use the same safety requirements, as
700
    // they are defined through the same macro: The caller ensures that the resumable is valid
701
    // in the current store. Also all other address types passed via the `State` must come from
702
    // the current store itself. Therefore, they are automatically valid in this store.
703
    unsafe {
704
1.00k
        instruction_fn(
705
1.00k
            state.wasm,
706
1.00k
            state.resumable,
707
1.00k
            state.current_sidetable,
708
1.00k
            state.store_inner,
709
1.00k
            state.modules,
710
1.00k
            state.current_module,
711
1.00k
            state.current_function_end_marker,
712
1.00k
        )
713
    }
714
1.00k
}
715
716
define_instruction!(
717
    super::fd_extensions_dispatch::<T>,
718
    fd_extensions_dispatch_mod,
719
    fuel_check = omit
720
);
721
/// # Safety
722
///
723
/// See [`State`] for more information.
724
#[inline(always)]
725
25.8k
pub unsafe fn fd_extensions_dispatch<T: Config>(
726
25.8k
    state: State,
727
25.8k
) -> Result<ControlFlow<InterpreterLoopOutcome>, RuntimeError> {
728
    // Should we call instruction hook here as well? Multibyte instruction
729
25.8k
    let second_instr = state.wasm.decode_var_u32().unwrap_validated();
730
731
    trace!(
732
        "Executing FD instruction {} at pc={}",
733
        crate::core::structure::instructions::fd_extension_instruction_to_str(second_instr),
734
        state.wasm.pc
735
    );
736
737
25.8k
    let instruction_fn = T::FD_DISPATCH_TABLE
738
25.8k
        .get(second_instr.into_usize())
739
25.8k
        .expect("the instruction to be valid because the code is validated");
740
741
    // SAFETY: All possible instruction handler functions use the same safety requirements, as
742
    // they are defined through the same macro: The caller ensures that the resumable is valid
743
    // in the current store. Also all other address types passed via the `State` must come from
744
    // the current store itself. Therefore, they are automatically valid in this store.
745
    unsafe {
746
25.8k
        instruction_fn(
747
25.8k
            state.wasm,
748
25.8k
            state.resumable,
749
25.8k
            state.current_sidetable,
750
25.8k
            state.store_inner,
751
25.8k
            state.modules,
752
25.8k
            state.current_module,
753
25.8k
            state.current_function_end_marker,
754
25.8k
        )
755
    }
756
25.8k
}