Coverage Report

Created: 2026-07-03 16:00

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::reader::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
    unreachable_validated, AddrVec, DataAddr, ElemAddr, FuncAddr, MemAddr, ModuleAddr,
35
    RuntimeError, Store, 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
0
define_instruction_fn! {unset, fuel_check = omit, |Args { .. }| {
87
    // Access T to circumvent warning that it is unused by this function. #[allow] does not work for
88
    // macros.
89
0
    let _ = T::DISPATCH_TABLE;
90
0
    unreachable_validated!();
91
}}
92
93
/// Interprets wasm native functions. Wasm parameters and Wasm return values are passed on the stack.
94
/// Returns `Ok(ControlFlow::Continue(()))` in case execution successfully terminates, `Ok(Some(required_fuel))` if execution
95
/// terminates due to insufficient fuel, indicating how much fuel is required to resume with `required_fuel`,
96
/// and `[Error::RuntimeError]` otherwise.
97
///
98
/// # Safety
99
///
100
/// The given resumable must be valid in the given [`Store`].
101
#[inline(never)]
102
54.7k
pub(super) unsafe fn run<T: Config>(
103
54.7k
    resumable: &mut WasmResumable,
104
54.7k
    store: &mut Store<T>,
105
54.7k
) -> Result<InterpreterLoopOutcome, RuntimeError> {
106
54.7k
    let current_func_addr = resumable.current_func_addr;
107
54.7k
    let pc = resumable.pc;
108
    // SAFETY: The caller ensures that the resumable and thus also its function
109
    // address is valid in the current store.
110
54.7k
    let func_inst = unsafe { store.inner.functions.get(current_func_addr) };
111
54.7k
    let FuncInst::WasmFunc(wasm_func_inst) = &func_inst else {
112
0
        unreachable!(
113
            "the interpreter loop shall only be executed with native wasm functions as root call"
114
        );
115
    };
116
54.7k
    let mut current_module = wasm_func_inst.module_addr;
117
118
    // Start reading the function's instructions
119
    // SAFETY: This module address was just read from the current store. Every
120
    // store guarantees all addresses contained in it to be valid within itself.
121
54.7k
    let module = unsafe { store.modules.get(current_module) };
122
54.7k
    let wasm_bytecode = module.wasm_bytecode;
123
54.7k
    let wasm = &mut WasmDecoder::new(wasm_bytecode);
124
125
54.7k
    let mut current_sidetable: &Sidetable = &module.sidetable;
126
127
54.7k
    let mut current_function_end_marker =
128
54.7k
        wasm_func_inst.code_expr.from() + wasm_func_inst.code_expr.len();
129
130
54.7k
    let store_inner = &mut store.inner;
131
132
    // local variable for holding where the function code ends (last END instr address + 1) to avoid lookup at every END instr
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
        #[cfg(debug_assertions)]
145
16.4M
        trace!(
146
            "Executing instruction {}",
147
            crate::instructions::instruction_byte_to_str(first_instr_byte)
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 `Args` 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
931k
    memarg
201
931k
        .offset
202
        // The spec states that this should be a 33 bit integer, e.g. it is not legal to wrap if the
203
        // sum of offset and relative_address exceeds u32::MAX. To emulate this behavior, we use a
204
        // checked addition.
205
        // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions
206
931k
        .checked_add(relative_address)
207
931k
        .ok_or(TrapError::MemoryOrDataAccessOutOfBounds)
?43
208
931k
        .try_into()
209
931k
        .map_err(|_| 
TrapError::MemoryOrDataAccessOutOfBounds0
.
into0
())
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
473
    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(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
223
    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
pub(crate) struct Args<'a, 'sidetable, 'wasm, 'other, 'resumable> {
408
    wasm: &'a mut WasmDecoder<'wasm>,
409
    resumable: &'resumable mut WasmResumable,
410
    current_sidetable: &'a mut &'sidetable Sidetable,
411
    store_inner: &'other mut StoreInner,
412
    modules: &'sidetable AddrVec<ModuleAddr, ModuleInst<'wasm>>,
413
    current_module: &'a mut ModuleAddr,
414
    current_function_end_marker: &'a mut usize,
415
}
416
417
macro_rules! define_instruction_fn {
418
    ($name:ident, fuel_check = omit, $contents:expr) => {
419
        /// # Safety
420
        ///
421
        /// The given [`WasmResumable`](crate::execution::resumable::WasmResumable) and all address
422
        /// types contained in the [`Args`](crate::execution::instructions::Args) must be valid
423
        /// in the [`StoreInner`](crate::execution::runtime_structure::store::StoreInner) that is also contained in the
424
        /// [`Args`](crate::execution::instructions::Args).
425
        // Disable inlining to inspect the emitted code of individual instruction handlers:
426
        // #[inline(never)]
427
16.5M
        pub(crate) unsafe fn $name<'wasm, 'modules, T: $crate::execution::config::Config>(
428
16.5M
            wasm: &mut $crate::core::decoding::reader::WasmDecoder<'wasm>,
429
16.5M
            resumable: &mut $crate::execution::resumable::WasmResumable,
430
16.5M
            current_sidetable: &mut &'modules $crate::core::sidetable::Sidetable,
431
16.5M
            store_inner: &mut $crate::execution::runtime_structure::store::StoreInner,
432
16.5M
            modules: &'modules $crate::execution::runtime_structure::addresses::AddrVec<
433
16.5M
                $crate::execution::runtime_structure::addresses::ModuleAddr,
434
16.5M
                $crate::execution::runtime_structure::module_instances::ModuleInst<'wasm>,
435
16.5M
            >,
436
16.5M
            current_module: &mut $crate::execution::runtime_structure::addresses::ModuleAddr,
437
16.5M
            current_function_end_marker: &mut usize,
438
16.5M
        ) -> Result<
439
16.5M
            core::ops::ControlFlow<$crate::execution::instructions::InterpreterLoopOutcome>,
440
16.5M
            $crate::RuntimeError,
441
16.5M
        > {
442
16.5M
            let args = $crate::execution::instructions::Args {
443
16.5M
                store_inner,
444
16.5M
                modules,
445
16.5M
                wasm,
446
16.5M
                current_module,
447
16.5M
                current_function_end_marker,
448
16.5M
                current_sidetable,
449
16.5M
                resumable,
450
16.5M
            };
451
452
16.5M
            $contents(args)
453
16.5M
        }
454
    };
455
456
    ($name:ident, fuel_check = flat($instruction:expr), $contents:expr) => {
457
        define_instruction_fn! {
458
            $name,
459
            fuel_check = omit,
460
16.4M
            |args: $crate::execution::instructions::Args| {
461
53
                if let core::ops::ControlFlow::Break(outcome) =
462
16.4M
                    $crate::execution::instructions::decrement_fuel(
463
16.4M
                        T::get_flat_cost($instruction),
464
16.4M
                        &mut args.resumable.maybe_fuel,
465
16.4M
                    )
466
                {
467
53
                    return Ok(core::ops::ControlFlow::Break(outcome));
468
16.4M
                }
469
470
16.4M
                $contents(args)
471
16.4M
            }
472
        }
473
    };
474
475
    ($name: ident, fuel_check = flat_fc($instruction: expr), $contents:expr) => {
476
        define_instruction_fn! {
477
            $name,
478
            fuel_check = omit,
479
26.2k
            |args: $crate::execution::instructions::Args| {
480
0
                if let core::ops::ControlFlow::Break(outcome) =
481
26.2k
                    $crate::execution::instructions::decrement_fuel(
482
26.2k
                        T::get_fc_extension_flat_cost($instruction),
483
26.2k
                        &mut args.resumable.maybe_fuel,
484
26.2k
                    )
485
                {
486
0
                    return Ok(core::ops::ControlFlow::Break(outcome));
487
26.2k
                }
488
489
26.2k
                $contents(args)
490
26.2k
            }
491
        }
492
    };
493
494
    ($name: ident, fuel_check = flat_fd($instruction: expr), $contents:expr) => {
495
        define_instruction_fn! {
496
            $name,
497
            fuel_check = omit,
498
            |args: $crate::execution::instructions::Args| {
499
                if let core::ops::ControlFlow::Break(outcome) =
500
                    $crate::execution::instructions::decrement_fuel(
501
                        T::get_fd_extension_flat_cost($instruction),
502
                        &mut args.resumable.maybe_fuel,
503
                    )
504
                {
505
                    return Ok(core::ops::ControlFlow::Break(outcome));
506
                }
507
508
                $contents(args)
509
            }
510
        }
511
    };
512
}
513
514
pub(crate) use define_instruction_fn;
515
516
#[inline(always)]
517
16.4M
fn decrement_fuel(cost: u64, maybe_fuel: &mut Option<u64>) -> ControlFlow<InterpreterLoopOutcome> {
518
16.4M
    if let Some(
fuel275
) = maybe_fuel {
519
275
        if *fuel >= cost {
520
222
            *fuel -= cost;
521
222
        } else {
522
53
            return ControlFlow::Break(InterpreterLoopOutcome::OutOfFuel {
523
53
                required_fuel: NonZeroU64::new(cost - *fuel)
524
53
                    .expect("the last check guarantees that the current fuel is smaller than cost"),
525
53
            });
526
        }
527
16.4M
    }
528
529
16.4M
    ControlFlow::Continue(())
530
16.4M
}
531
532
1.00k
define_instruction_fn! {fc_extensions, fuel_check = omit, |args: Args| {
533
    // should we call instruction hook here as well? multibyte instruction
534
1.00k
    let second_instr = args.wasm.decode_var_u32().unwrap_validated();
535
536
1.00k
    let instruction_fn = T::FC_DISPATCH_TABLE
537
1.00k
        .get(second_instr.into_usize())
538
1.00k
        .expect("the instruction to be valid because the code is validated");
539
540
    // SAFETY: All possible instruction handler functions use the same safety requirements, as
541
    // they are defined through the same macro: The caller ensures that the resumable is valid
542
    // in the current store. Also all other address types passed via the `Args` must come from
543
    // the current store itself. Therefore, they are automatically valid in this store.
544
    unsafe {
545
1.00k
        instruction_fn(
546
1.00k
            args.wasm,
547
1.00k
            args.resumable,
548
1.00k
            args.current_sidetable,
549
1.00k
            args.store_inner,
550
1.00k
            args.modules,
551
1.00k
            args.current_module,
552
1.00k
            args.current_function_end_marker,
553
1.00k
        )
554
    }
555
1.00k
}}
556
557
25.8k
define_instruction_fn! {fd_extensions, fuel_check = omit, |args: Args| {
558
    // Should we call instruction hook here as well? Multibyte instruction
559
25.8k
    let second_instr = args.wasm.decode_var_u32().unwrap_validated();
560
561
25.8k
    let instruction_fn = T::FD_DISPATCH_TABLE
562
25.8k
        .get(second_instr.into_usize())
563
25.8k
        .expect("the instruction to be valid because the code is validated");
564
565
    // SAFETY: All possible instruction handler functions use the same safety requirements, as
566
    // they are defined through the same macro: The caller ensures that the resumable is valid
567
    // in the current store. Also all other address types passed via the `Args` must come from
568
    // the current store itself. Therefore, they are automatically valid in this store.
569
    unsafe {
570
25.8k
        instruction_fn(
571
25.8k
            args.wasm,
572
25.8k
            args.resumable,
573
25.8k
            args.current_sidetable,
574
25.8k
            args.store_inner,
575
25.8k
            args.modules,
576
25.8k
            args.current_module,
577
25.8k
            args.current_function_end_marker,
578
25.8k
        )
579
    }
580
25.8k
}}