wasm/execution/
interpreter_loop.rs

1//! This module solely contains the actual interpretation loop that matches instructions, interpreting the WASM bytecode
2//!
3//!
4//! # Note to Developer:
5//!
6//! 1. There must be only imports and one `impl` with one function (`run`) in it.
7//! 2. This module must not use the [`Error`](crate::core::error::Error) enum.
8//! 3. Instead, only the [`RuntimeError`] enum shall be used
9//!    - **not** to be confused with the [`Error`](crate::core::error::Error) enum's
10//!      [`Error::RuntimeError`](crate::Error::RuntimeError) variant, which as per 2., we don not
11//!      want
12
13use alloc::vec;
14use alloc::vec::Vec;
15use core::iter::zip;
16
17use crate::{
18    assert_validated::UnwrapValidatedExt,
19    core::{
20        indices::{DataIdx, FuncIdx, GlobalIdx, LabelIdx, LocalIdx, MemIdx, TableIdx, TypeIdx},
21        reader::{
22            types::{memarg::MemArg, BlockType},
23            WasmReadable, WasmReader,
24        },
25        sidetable::Sidetable,
26    },
27    locals::Locals,
28    store::DataInst,
29    value::{self, FuncAddr, Ref},
30    value_stack::Stack,
31    ElemInst, FuncInst, MemInst, ModuleInst, NumType, RefType, RuntimeError, TableInst, ValType,
32    Value,
33};
34
35#[cfg(feature = "hooks")]
36use crate::execution::hooks::HookSet;
37
38use super::store::Store;
39
40/// Interprets wasm native functions. Parameters and return values are passed on the stack.
41pub(super) fn run<H: HookSet>(
42    func_addr: usize,
43    stack: &mut Stack,
44    mut hooks: H,
45    store: &mut Store,
46) -> Result<(), RuntimeError> {
47    let mut current_func_addr = func_addr;
48    let func_inst = &store.functions[current_func_addr];
49    let FuncInst::WasmFunc(wasm_func_inst) = &func_inst else {
50        unreachable!(
51            "the interpreter loop shall only be executed with native wasm functions as root call"
52        );
53    };
54
55    let mut current_module_idx = wasm_func_inst.module_addr;
56
57    // Start reading the function's instructions
58    let wasm = &mut WasmReader::new(store.modules[current_module_idx].wasm_bytecode);
59
60    let mut current_sidetable: &Sidetable = &store.modules[current_module_idx].sidetable;
61    let mut stp = wasm_func_inst.stp;
62
63    // local variable for holding where the function code ends (last END instr address + 1) to avoid lookup at every END instr
64    let mut current_function_end_marker =
65        wasm_func_inst.code_expr.from() + wasm_func_inst.code_expr.len();
66
67    // unwrap is sound, because the validation assures that the function points to valid subslice of the WASM binary
68    wasm.move_start_to(wasm_func_inst.code_expr).unwrap();
69
70    use crate::core::reader::types::opcode::*;
71    loop {
72        // call the instruction hook
73        #[cfg(feature = "hooks")]
74        hooks.instruction_hook(store.modules[current_module_idx].wasm_bytecode, wasm.pc);
75
76        let first_instr_byte = wasm.read_u8().unwrap_validated();
77
78        #[cfg(debug_assertions)]
79        trace!(
80            "Executing instruction {}",
81            opcode_byte_to_str(first_instr_byte)
82        );
83
84        match first_instr_byte {
85            NOP => {
86                trace!("Instruction: NOP");
87            }
88            END => {
89                // There might be multiple ENDs in a single function. We want to
90                // exit only when the outermost block (aka function block) ends.
91                if wasm.pc != current_function_end_marker {
92                    continue;
93                }
94
95                let (maybe_return_func_addr, maybe_return_address, maybe_return_stp) =
96                    stack.pop_stackframe();
97
98                // We finished this entire invocation if there is no stackframe left. If there are
99                // one or more stack frames, we need to continue from where the callee was called
100                // fromn.
101                if stack.callframe_count() == 0 {
102                    break;
103                }
104
105                trace!("end of function reached, returning to previous stack frame");
106                current_func_addr = maybe_return_func_addr;
107                let FuncInst::WasmFunc(current_wasm_func_inst) =
108                    &store.functions[current_func_addr]
109                else {
110                    unreachable!("function addresses on the stack always correspond to native wasm functions")
111                };
112                current_module_idx = current_wasm_func_inst.module_addr;
113                wasm.full_wasm_binary = store.modules[current_module_idx].wasm_bytecode;
114                wasm.pc = maybe_return_address;
115                stp = maybe_return_stp;
116
117                current_sidetable = &store.modules[current_module_idx].sidetable;
118
119                current_function_end_marker = current_wasm_func_inst.code_expr.from()
120                    + current_wasm_func_inst.code_expr.len();
121
122                trace!("Instruction: END");
123            }
124            IF => {
125                wasm.read_var_u32().unwrap_validated();
126
127                let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
128
129                if test_val != 0 {
130                    stp += 1;
131                } else {
132                    do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
133                }
134                trace!("Instruction: IF");
135            }
136            ELSE => {
137                do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
138            }
139            BR_IF => {
140                wasm.read_var_u32().unwrap_validated();
141
142                let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
143
144                if test_val != 0 {
145                    do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
146                } else {
147                    stp += 1;
148                }
149                trace!("Instruction: BR_IF");
150            }
151            BR_TABLE => {
152                let label_vec = wasm
153                    .read_vec(|wasm| wasm.read_var_u32().map(|v| v as LabelIdx))
154                    .unwrap_validated();
155                wasm.read_var_u32().unwrap_validated();
156
157                // TODO is this correct?
158                let case_val_i32: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
159                let case_val = case_val_i32 as usize;
160
161                if case_val >= label_vec.len() {
162                    stp += label_vec.len();
163                } else {
164                    stp += case_val;
165                }
166
167                do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
168            }
169            BR => {
170                //skip n of BR n
171                wasm.read_var_u32().unwrap_validated();
172                do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
173            }
174            BLOCK | LOOP => {
175                BlockType::read_unvalidated(wasm);
176            }
177            RETURN => {
178                //same as BR, except no need to skip n of BR n
179                do_sidetable_control_transfer(wasm, stack, &mut stp, current_sidetable)?;
180            }
181            CALL => {
182                let local_func_idx = wasm.read_var_u32().unwrap_validated() as FuncIdx;
183                let FuncInst::WasmFunc(current_wasm_func_inst) =
184                    &store.functions[current_func_addr]
185                else {
186                    unreachable!()
187                };
188
189                let func_to_call_addr =
190                    store.modules[current_wasm_func_inst.module_addr].func_addrs[local_func_idx];
191
192                let func_to_call_ty = store.functions[func_to_call_addr].ty();
193
194                let params = stack.pop_tail_iter(func_to_call_ty.params.valtypes.len());
195                trace!("Instruction: call [{func_to_call_addr:?}]");
196
197                match &store.functions[func_to_call_addr] {
198                    FuncInst::HostFunc(host_func_to_call_inst) => {
199                        let returns = (host_func_to_call_inst.hostcode)(params.collect());
200
201                        // Verify that the return parameters match the host function parameters
202                        // since we have no validation guarantees for host functions
203                        if returns.len() != func_to_call_ty.returns.valtypes.len() {
204                            return Err(RuntimeError::HostFunctionSignatureMismatch);
205                        }
206                        for (value, ty) in zip(returns, func_to_call_ty.returns.valtypes) {
207                            if value.to_ty() != ty {
208                                return Err(RuntimeError::HostFunctionSignatureMismatch);
209                            }
210                            stack.push_value(value)?;
211                        }
212                    }
213                    FuncInst::WasmFunc(wasm_func_to_call_inst) => {
214                        let remaining_locals = wasm_func_to_call_inst.locals.iter().cloned();
215                        let locals = Locals::new(params, remaining_locals);
216
217                        stack.push_stackframe(
218                            current_func_addr,
219                            &func_to_call_ty,
220                            locals,
221                            wasm.pc,
222                            stp,
223                        )?;
224
225                        current_func_addr = func_to_call_addr;
226                        current_module_idx = wasm_func_to_call_inst.module_addr;
227                        wasm.full_wasm_binary = store.modules[current_module_idx].wasm_bytecode;
228                        wasm.move_start_to(wasm_func_to_call_inst.code_expr)
229                            .unwrap_validated();
230
231                        stp = wasm_func_to_call_inst.stp;
232                        current_sidetable = &store.modules[current_module_idx].sidetable;
233                        current_function_end_marker = wasm_func_to_call_inst.code_expr.from()
234                            + wasm_func_to_call_inst.code_expr.len();
235                    }
236                }
237                trace!("Instruction: CALL");
238            }
239
240            // TODO: fix push_stackframe, because the func idx that you get from the table is global func idx
241            CALL_INDIRECT => {
242                let given_type_idx = wasm.read_var_u32().unwrap_validated() as TypeIdx;
243                let table_idx = wasm.read_var_u32().unwrap_validated() as TableIdx;
244
245                let tab = &store.tables[store.modules[current_module_idx].table_addrs[table_idx]];
246                let func_ty = store.modules[current_module_idx]
247                    .types
248                    .get(given_type_idx)
249                    .unwrap_validated();
250
251                let i: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
252
253                let r = tab
254                    .elem
255                    .get(i as usize)
256                    .ok_or(RuntimeError::UndefinedTableIndex)
257                    .and_then(|r| {
258                        if r.is_null() {
259                            trace!("table_idx ({table_idx}) --- element index in table ({i})");
260                            Err(RuntimeError::UninitializedElement)
261                        } else {
262                            Ok(r)
263                        }
264                    })?;
265
266                let func_to_call_addr = match *r {
267                    Ref::Func(func_addr) => func_addr.addr.unwrap_validated(),
268                    Ref::Extern(_) => unreachable!(),
269                };
270
271                let func_to_call_ty = store.functions[func_to_call_addr].ty();
272                if *func_ty != func_to_call_ty {
273                    return Err(RuntimeError::SignatureMismatch);
274                }
275
276                let params = stack.pop_tail_iter(func_to_call_ty.params.valtypes.len());
277                trace!("Instruction: call [{func_to_call_addr:?}]");
278
279                match &store.functions[func_to_call_addr] {
280                    FuncInst::HostFunc(host_func_to_call_inst) => {
281                        let returns = (host_func_to_call_inst.hostcode)(params.collect());
282
283                        // Verify that the return parameters match the host function parameters
284                        // since we have no validation guarantees for host functions
285                        if returns.len() != func_to_call_ty.returns.valtypes.len() {
286                            return Err(RuntimeError::HostFunctionSignatureMismatch);
287                        }
288                        for (value, ty) in zip(returns, func_to_call_ty.returns.valtypes) {
289                            if value.to_ty() != ty {
290                                return Err(RuntimeError::HostFunctionSignatureMismatch);
291                            }
292                            stack.push_value(value)?;
293                        }
294                    }
295                    FuncInst::WasmFunc(wasm_func_to_call_inst) => {
296                        let remaining_locals = wasm_func_to_call_inst.locals.iter().cloned();
297                        let locals = Locals::new(params, remaining_locals);
298
299                        stack.push_stackframe(
300                            current_func_addr,
301                            &func_to_call_ty,
302                            locals,
303                            wasm.pc,
304                            stp,
305                        )?;
306
307                        current_func_addr = func_to_call_addr;
308                        current_module_idx = wasm_func_to_call_inst.module_addr;
309                        wasm.full_wasm_binary = store.modules[current_module_idx].wasm_bytecode;
310                        wasm.move_start_to(wasm_func_to_call_inst.code_expr)
311                            .unwrap_validated();
312
313                        stp = wasm_func_to_call_inst.stp;
314                        current_sidetable = &store.modules[current_module_idx].sidetable;
315                        current_function_end_marker = wasm_func_to_call_inst.code_expr.from()
316                            + wasm_func_to_call_inst.code_expr.len();
317                    }
318                }
319                trace!("Instruction: CALL_INDIRECT");
320            }
321            DROP => {
322                stack.drop_value();
323                trace!("Instruction: DROP");
324            }
325            SELECT => {
326                let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
327                let val2 = stack.pop_value_with_unknown_type();
328                let val1 = stack.pop_value_with_unknown_type();
329                if test_val != 0 {
330                    stack.push_value(val1)?;
331                } else {
332                    stack.push_value(val2)?;
333                }
334                trace!("Instruction: SELECT");
335            }
336            SELECT_T => {
337                let type_vec = wasm.read_vec(ValType::read).unwrap_validated();
338                let test_val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
339                let val2 = stack.pop_value(type_vec[0]);
340                let val1 = stack.pop_value(type_vec[0]);
341                if test_val != 0 {
342                    stack.push_value(val1)?;
343                } else {
344                    stack.push_value(val2)?;
345                }
346                trace!("Instruction: SELECT_T");
347            }
348            LOCAL_GET => {
349                let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx;
350                stack.get_local(local_idx)?;
351                trace!("Instruction: local.get {} [] -> [t]", local_idx);
352            }
353            LOCAL_SET => stack.set_local(wasm.read_var_u32().unwrap_validated() as LocalIdx),
354            LOCAL_TEE => stack.tee_local(wasm.read_var_u32().unwrap_validated() as LocalIdx),
355            GLOBAL_GET => {
356                let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx;
357                let global =
358                    &store.globals[store.modules[current_module_idx].global_addrs[global_idx]];
359
360                stack.push_value(global.value)?;
361
362                trace!(
363                    "Instruction: global.get '{}' [<GLOBAL>] -> [{:?}]",
364                    global_idx,
365                    global.value
366                );
367            }
368            GLOBAL_SET => {
369                let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx;
370                let global =
371                    &mut store.globals[store.modules[current_module_idx].global_addrs[global_idx]];
372                global.value = stack.pop_value(global.ty.ty);
373                trace!("Instruction: GLOBAL_SET");
374            }
375            TABLE_GET => {
376                let table_idx = wasm.read_var_u32().unwrap_validated() as TableIdx;
377                let tab = &store.tables[store.modules[current_module_idx].table_addrs[table_idx]];
378
379                let i: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
380
381                let val = tab
382                    .elem
383                    .get(i as usize)
384                    .ok_or(RuntimeError::TableAccessOutOfBounds)?;
385
386                stack.push_value((*val).into())?;
387                trace!(
388                    "Instruction: table.get '{}' [{}] -> [{}]",
389                    table_idx,
390                    i,
391                    val
392                );
393            }
394            TABLE_SET => {
395                let table_idx = wasm.read_var_u32().unwrap_validated() as TableIdx;
396
397                let tab =
398                    &mut store.tables[store.modules[current_module_idx].table_addrs[table_idx]];
399
400                let val: Ref = stack.pop_value(ValType::RefType(tab.ty.et)).into();
401                let i: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
402
403                tab.elem
404                    .get_mut(i as usize)
405                    .ok_or(RuntimeError::TableAccessOutOfBounds)
406                    .map(|r| *r = val)?;
407                trace!(
408                    "Instruction: table.set '{}' [{} {}] -> []",
409                    table_idx,
410                    i,
411                    val
412                )
413            }
414            UNREACHABLE => {
415                return Err(RuntimeError::ReachedUnreachable);
416            }
417            I32_LOAD => {
418                let memarg = MemArg::read_unvalidated(wasm);
419                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
420
421                let mem_inst = &store.memories[store.modules[current_module_idx].mem_addrs[0]];
422
423                let idx = calculate_mem_address(&memarg, relative_address)?;
424                let data = mem_inst.mem.load(idx)?;
425
426                stack.push_value(Value::I32(data))?;
427                trace!("Instruction: i32.load [{relative_address}] -> [{data}]");
428            }
429            I64_LOAD => {
430                let memarg = MemArg::read_unvalidated(wasm);
431                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
432
433                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
434
435                let idx = calculate_mem_address(&memarg, relative_address)?;
436                let data = mem.mem.load(idx)?;
437
438                stack.push_value(Value::I64(data))?;
439                trace!("Instruction: i64.load [{relative_address}] -> [{data}]");
440            }
441            F32_LOAD => {
442                let memarg = MemArg::read_unvalidated(wasm);
443                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
444
445                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
446
447                let idx = calculate_mem_address(&memarg, relative_address)?;
448                let data = mem.mem.load(idx)?;
449
450                stack.push_value(Value::F32(value::F32(data)))?;
451                trace!("Instruction: f32.load [{relative_address}] -> [{data}]");
452            }
453            F64_LOAD => {
454                let memarg = MemArg::read_unvalidated(wasm);
455                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
456
457                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
458
459                let idx = calculate_mem_address(&memarg, relative_address)?;
460                let data = mem.mem.load(idx)?;
461
462                stack.push_value(Value::F64(value::F64(data)))?;
463                trace!("Instruction: f64.load [{relative_address}] -> [{data}]");
464            }
465            I32_LOAD8_S => {
466                let memarg = MemArg::read_unvalidated(wasm);
467                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
468
469                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
470
471                let idx = calculate_mem_address(&memarg, relative_address)?;
472                let data: i8 = mem.mem.load(idx)?;
473
474                stack.push_value(Value::I32(data as u32))?;
475                trace!("Instruction: i32.load8_s [{relative_address}] -> [{data}]");
476            }
477            I32_LOAD8_U => {
478                let memarg = MemArg::read_unvalidated(wasm);
479                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
480
481                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
482
483                let idx = calculate_mem_address(&memarg, relative_address)?;
484                let data: u8 = mem.mem.load(idx)?;
485
486                stack.push_value(Value::I32(data as u32))?;
487                trace!("Instruction: i32.load8_u [{relative_address}] -> [{data}]");
488            }
489            I32_LOAD16_S => {
490                let memarg = MemArg::read_unvalidated(wasm);
491                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
492
493                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
494
495                let idx = calculate_mem_address(&memarg, relative_address)?;
496                let data: i16 = mem.mem.load(idx)?;
497
498                stack.push_value(Value::I32(data as u32))?;
499                trace!("Instruction: i32.load16_s [{relative_address}] -> [{data}]");
500            }
501            I32_LOAD16_U => {
502                let memarg = MemArg::read_unvalidated(wasm);
503                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
504
505                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
506
507                let idx = calculate_mem_address(&memarg, relative_address)?;
508                let data: u16 = mem.mem.load(idx)?;
509
510                stack.push_value(Value::I32(data as u32))?;
511                trace!("Instruction: i32.load16_u [{relative_address}] -> [{data}]");
512            }
513            I64_LOAD8_S => {
514                let memarg = MemArg::read_unvalidated(wasm);
515                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
516
517                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
518
519                let idx = calculate_mem_address(&memarg, relative_address)?;
520                let data: i8 = mem.mem.load(idx)?;
521
522                stack.push_value(Value::I64(data as u64))?;
523                trace!("Instruction: i64.load8_s [{relative_address}] -> [{data}]");
524            }
525            I64_LOAD8_U => {
526                let memarg = MemArg::read_unvalidated(wasm);
527                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
528
529                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
530
531                let idx = calculate_mem_address(&memarg, relative_address)?;
532                let data: u8 = mem.mem.load(idx)?;
533
534                stack.push_value(Value::I64(data as u64))?;
535                trace!("Instruction: i64.load8_u [{relative_address}] -> [{data}]");
536            }
537            I64_LOAD16_S => {
538                let memarg = MemArg::read_unvalidated(wasm);
539                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
540
541                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
542
543                let idx = calculate_mem_address(&memarg, relative_address)?;
544                let data: i16 = mem.mem.load(idx)?;
545
546                stack.push_value(Value::I64(data as u64))?;
547                trace!("Instruction: i64.load16_s [{relative_address}] -> [{data}]");
548            }
549            I64_LOAD16_U => {
550                let memarg = MemArg::read_unvalidated(wasm);
551                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
552
553                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
554
555                let idx = calculate_mem_address(&memarg, relative_address)?;
556                let data: u16 = mem.mem.load(idx)?;
557
558                stack.push_value(Value::I64(data as u64))?;
559                trace!("Instruction: i64.load16_u [{relative_address}] -> [{data}]");
560            }
561            I64_LOAD32_S => {
562                let memarg = MemArg::read_unvalidated(wasm);
563                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
564
565                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
566
567                let idx = calculate_mem_address(&memarg, relative_address)?;
568                let data: i32 = mem.mem.load(idx)?;
569
570                stack.push_value(Value::I64(data as u64))?;
571                trace!("Instruction: i64.load32_s [{relative_address}] -> [{data}]");
572            }
573            I64_LOAD32_U => {
574                let memarg = MemArg::read_unvalidated(wasm);
575                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
576
577                let mem = &store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
578
579                let idx = calculate_mem_address(&memarg, relative_address)?;
580                let data: u32 = mem.mem.load(idx)?;
581
582                stack.push_value(Value::I64(data as u64))?;
583                trace!("Instruction: i64.load32_u [{relative_address}] -> [{data}]");
584            }
585            I32_STORE => {
586                let memarg = MemArg::read_unvalidated(wasm);
587
588                let data_to_store: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
589                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
590
591                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
592
593                let idx = calculate_mem_address(&memarg, relative_address)?;
594                mem.mem.store(idx, data_to_store)?;
595
596                trace!("Instruction: i32.store [{relative_address} {data_to_store}] -> []");
597            }
598            I64_STORE => {
599                let memarg = MemArg::read_unvalidated(wasm);
600
601                let data_to_store: u64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
602                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
603
604                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
605
606                let idx = calculate_mem_address(&memarg, relative_address)?;
607                mem.mem.store(idx, data_to_store)?;
608
609                trace!("Instruction: i64.store [{relative_address} {data_to_store}] -> []");
610            }
611            F32_STORE => {
612                let memarg = MemArg::read_unvalidated(wasm);
613
614                let data_to_store: f32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
615                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
616
617                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
618
619                let idx = calculate_mem_address(&memarg, relative_address)?;
620                mem.mem.store(idx, data_to_store)?;
621
622                trace!("Instruction: f32.store [{relative_address} {data_to_store}] -> []");
623            }
624            F64_STORE => {
625                let memarg = MemArg::read_unvalidated(wasm);
626
627                let data_to_store: f64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
628                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
629
630                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]]; // there is only one memory allowed as of now
631
632                let idx = calculate_mem_address(&memarg, relative_address)?;
633                mem.mem.store(idx, data_to_store)?;
634
635                trace!("Instruction: f64.store [{relative_address} {data_to_store}] -> []");
636            }
637            I32_STORE8 => {
638                let memarg = MemArg::read_unvalidated(wasm);
639
640                let data_to_store: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
641                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
642
643                let wrapped_data: i8 = i8::from_le_bytes(
644                    data_to_store.rem_euclid(2_i32.pow(8)).to_le_bytes()[0..1]
645                        .try_into()
646                        .expect("array to be of length 1"),
647                );
648
649                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]];
650
651                let idx = calculate_mem_address(&memarg, relative_address)?;
652                mem.mem.store(idx, wrapped_data)?;
653
654                trace!("Instruction: i32.store8 [{relative_address} {wrapped_data}] -> []");
655            }
656            I32_STORE16 => {
657                let memarg = MemArg::read_unvalidated(wasm);
658
659                let data_to_store: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
660                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
661
662                let wrapped_data: i16 = i16::from_le_bytes(
663                    data_to_store.rem_euclid(2_i32.pow(16)).to_le_bytes()[0..2]
664                        .try_into()
665                        .expect("array to be of length 2"),
666                );
667
668                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]];
669
670                let idx = calculate_mem_address(&memarg, relative_address)?;
671                mem.mem.store(idx, wrapped_data)?;
672
673                trace!("Instruction: i32.store16 [{relative_address} {data_to_store}] -> []");
674            }
675            I64_STORE8 => {
676                let memarg = MemArg::read_unvalidated(wasm);
677
678                let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
679                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
680
681                let wrapped_data: i8 = i8::from_le_bytes(
682                    data_to_store.rem_euclid(2_i64.pow(8)).to_le_bytes()[0..1]
683                        .try_into()
684                        .expect("array to be of length 1"),
685                );
686
687                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]];
688
689                let idx = calculate_mem_address(&memarg, relative_address)?;
690                mem.mem.store(idx, wrapped_data)?;
691
692                trace!("Instruction: i64.store8 [{relative_address} {data_to_store}] -> []");
693            }
694            I64_STORE16 => {
695                let memarg = MemArg::read_unvalidated(wasm);
696
697                let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
698                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
699
700                let wrapped_data: i16 = i16::from_le_bytes(
701                    data_to_store.rem_euclid(2_i64.pow(16)).to_le_bytes()[0..2]
702                        .try_into()
703                        .expect("array to be of length 2"),
704                );
705
706                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]];
707
708                let idx = calculate_mem_address(&memarg, relative_address)?;
709                mem.mem.store(idx, wrapped_data)?;
710
711                trace!("Instruction: i64.store16 [{relative_address} {data_to_store}] -> []");
712            }
713            I64_STORE32 => {
714                let memarg = MemArg::read_unvalidated(wasm);
715
716                let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
717                let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
718
719                let wrapped_data: i32 = i32::from_le_bytes(
720                    data_to_store.rem_euclid(2_i64.pow(32)).to_le_bytes()[0..4]
721                        .try_into()
722                        .expect("array to be of length 4"),
723                );
724
725                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[0]];
726
727                let idx = calculate_mem_address(&memarg, relative_address)?;
728                mem.mem.store(idx, wrapped_data)?;
729
730                trace!("Instruction: i64.store32 [{relative_address} {data_to_store}] -> []");
731            }
732            MEMORY_SIZE => {
733                let mem_idx = wasm.read_u8().unwrap_validated() as usize;
734                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[mem_idx]];
735                let size = mem.size() as u32;
736                stack.push_value(Value::I32(size))?;
737                trace!("Instruction: memory.size [] -> [{}]", size);
738            }
739            MEMORY_GROW => {
740                let mem_idx = wasm.read_u8().unwrap_validated() as usize;
741                let mem = &mut store.memories[store.modules[current_module_idx].mem_addrs[mem_idx]];
742                let sz: u32 = mem.size() as u32;
743
744                let n: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
745
746                // TODO this instruction is non-deterministic w.r.t. spec, and can fail if the embedder wills it.
747                // for now we execute it always according to the following match expr.
748                // if the grow operation fails, err := Value::I32(2^32-1) is pushed to the stack per spec
749                let pushed_value = match mem.grow(n) {
750                    Ok(_) => sz,
751                    Err(_) => u32::MAX,
752                };
753                stack.push_value(Value::I32(pushed_value))?;
754                trace!("Instruction: memory.grow [{}] -> [{}]", n, pushed_value);
755            }
756            I32_CONST => {
757                let constant = wasm.read_var_i32().unwrap_validated();
758                trace!("Instruction: i32.const [] -> [{constant}]");
759                stack.push_value(constant.into())?;
760            }
761            F32_CONST => {
762                let constant = f32::from_bits(wasm.read_var_f32().unwrap_validated());
763                trace!("Instruction: f32.const [] -> [{constant:.7}]");
764                stack.push_value(constant.into())?;
765            }
766            I32_EQZ => {
767                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
768
769                let res = if v1 == 0 { 1 } else { 0 };
770
771                trace!("Instruction: i32.eqz [{v1}] -> [{res}]");
772                stack.push_value(res.into())?;
773            }
774            I32_EQ => {
775                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
776                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
777
778                let res = if v1 == v2 { 1 } else { 0 };
779
780                trace!("Instruction: i32.eq [{v1} {v2}] -> [{res}]");
781                stack.push_value(res.into())?;
782            }
783            I32_NE => {
784                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
785                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
786
787                let res = if v1 != v2 { 1 } else { 0 };
788
789                trace!("Instruction: i32.ne [{v1} {v2}] -> [{res}]");
790                stack.push_value(res.into())?;
791            }
792            I32_LT_S => {
793                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
794                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
795
796                let res = if v1 < v2 { 1 } else { 0 };
797
798                trace!("Instruction: i32.lt_s [{v1} {v2}] -> [{res}]");
799                stack.push_value(res.into())?;
800            }
801
802            I32_LT_U => {
803                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
804                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
805
806                let res = if (v1 as u32) < (v2 as u32) { 1 } else { 0 };
807
808                trace!("Instruction: i32.lt_u [{v1} {v2}] -> [{res}]");
809                stack.push_value(res.into())?;
810            }
811            I32_GT_S => {
812                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
813                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
814
815                let res = if v1 > v2 { 1 } else { 0 };
816
817                trace!("Instruction: i32.gt_s [{v1} {v2}] -> [{res}]");
818                stack.push_value(res.into())?;
819            }
820            I32_GT_U => {
821                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
822                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
823
824                let res = if (v1 as u32) > (v2 as u32) { 1 } else { 0 };
825
826                trace!("Instruction: i32.gt_u [{v1} {v2}] -> [{res}]");
827                stack.push_value(res.into())?;
828            }
829            I32_LE_S => {
830                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
831                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
832
833                let res = if v1 <= v2 { 1 } else { 0 };
834
835                trace!("Instruction: i32.le_s [{v1} {v2}] -> [{res}]");
836                stack.push_value(res.into())?;
837            }
838            I32_LE_U => {
839                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
840                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
841
842                let res = if (v1 as u32) <= (v2 as u32) { 1 } else { 0 };
843
844                trace!("Instruction: i32.le_u [{v1} {v2}] -> [{res}]");
845                stack.push_value(res.into())?;
846            }
847            I32_GE_S => {
848                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
849                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
850
851                let res = if v1 >= v2 { 1 } else { 0 };
852
853                trace!("Instruction: i32.ge_s [{v1} {v2}] -> [{res}]");
854                stack.push_value(res.into())?;
855            }
856            I32_GE_U => {
857                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
858                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
859
860                let res = if (v1 as u32) >= (v2 as u32) { 1 } else { 0 };
861
862                trace!("Instruction: i32.ge_u [{v1} {v2}] -> [{res}]");
863                stack.push_value(res.into())?;
864            }
865            I64_EQZ => {
866                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
867
868                let res = if v1 == 0 { 1 } else { 0 };
869
870                trace!("Instruction: i64.eqz [{v1}] -> [{res}]");
871                stack.push_value(res.into())?;
872            }
873            I64_EQ => {
874                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
875                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
876
877                let res = if v1 == v2 { 1 } else { 0 };
878
879                trace!("Instruction: i64.eq [{v1} {v2}] -> [{res}]");
880                stack.push_value(res.into())?;
881            }
882            I64_NE => {
883                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
884                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
885
886                let res = if v1 != v2 { 1 } else { 0 };
887
888                trace!("Instruction: i64.ne [{v1} {v2}] -> [{res}]");
889                stack.push_value(res.into())?;
890            }
891            I64_LT_S => {
892                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
893                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
894
895                let res = if v1 < v2 { 1 } else { 0 };
896
897                trace!("Instruction: i64.lt_s [{v1} {v2}] -> [{res}]");
898                stack.push_value(res.into())?;
899            }
900
901            I64_LT_U => {
902                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
903                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
904
905                let res = if (v1 as u64) < (v2 as u64) { 1 } else { 0 };
906
907                trace!("Instruction: i64.lt_u [{v1} {v2}] -> [{res}]");
908                stack.push_value(res.into())?;
909            }
910            I64_GT_S => {
911                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
912                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
913
914                let res = if v1 > v2 { 1 } else { 0 };
915
916                trace!("Instruction: i64.gt_s [{v1} {v2}] -> [{res}]");
917                stack.push_value(res.into())?;
918            }
919            I64_GT_U => {
920                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
921                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
922
923                let res = if (v1 as u64) > (v2 as u64) { 1 } else { 0 };
924
925                trace!("Instruction: i64.gt_u [{v1} {v2}] -> [{res}]");
926                stack.push_value(res.into())?;
927            }
928            I64_LE_S => {
929                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
930                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
931
932                let res = if v1 <= v2 { 1 } else { 0 };
933
934                trace!("Instruction: i64.le_s [{v1} {v2}] -> [{res}]");
935                stack.push_value(res.into())?;
936            }
937            I64_LE_U => {
938                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
939                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
940
941                let res = if (v1 as u64) <= (v2 as u64) { 1 } else { 0 };
942
943                trace!("Instruction: i64.le_u [{v1} {v2}] -> [{res}]");
944                stack.push_value(res.into())?;
945            }
946            I64_GE_S => {
947                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
948                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
949
950                let res = if v1 >= v2 { 1 } else { 0 };
951
952                trace!("Instruction: i64.ge_s [{v1} {v2}] -> [{res}]");
953                stack.push_value(res.into())?;
954            }
955            I64_GE_U => {
956                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
957                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
958
959                let res = if (v1 as u64) >= (v2 as u64) { 1 } else { 0 };
960
961                trace!("Instruction: i64.ge_u [{v1} {v2}] -> [{res}]");
962                stack.push_value(res.into())?;
963            }
964            F32_EQ => {
965                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
966                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
967
968                let res = if v1 == v2 { 1 } else { 0 };
969
970                trace!("Instruction: f32.eq [{v1} {v2}] -> [{res}]");
971                stack.push_value(res.into())?;
972            }
973            F32_NE => {
974                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
975                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
976
977                let res = if v1 != v2 { 1 } else { 0 };
978
979                trace!("Instruction: f32.ne [{v1} {v2}] -> [{res}]");
980                stack.push_value(res.into())?;
981            }
982            F32_LT => {
983                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
984                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
985
986                let res = if v1 < v2 { 1 } else { 0 };
987
988                trace!("Instruction: f32.lt [{v1} {v2}] -> [{res}]");
989                stack.push_value(res.into())?;
990            }
991            F32_GT => {
992                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
993                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
994
995                let res = if v1 > v2 { 1 } else { 0 };
996
997                trace!("Instruction: f32.gt [{v1} {v2}] -> [{res}]");
998                stack.push_value(res.into())?;
999            }
1000            F32_LE => {
1001                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1002                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1003
1004                let res = if v1 <= v2 { 1 } else { 0 };
1005
1006                trace!("Instruction: f32.le [{v1} {v2}] -> [{res}]");
1007                stack.push_value(res.into())?;
1008            }
1009            F32_GE => {
1010                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1011                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1012
1013                let res = if v1 >= v2 { 1 } else { 0 };
1014
1015                trace!("Instruction: f32.ge [{v1} {v2}] -> [{res}]");
1016                stack.push_value(res.into())?;
1017            }
1018
1019            F64_EQ => {
1020                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1021                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1022
1023                let res = if v1 == v2 { 1 } else { 0 };
1024
1025                trace!("Instruction: f64.eq [{v1} {v2}] -> [{res}]");
1026                stack.push_value(res.into())?;
1027            }
1028            F64_NE => {
1029                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1030                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1031
1032                let res = if v1 != v2 { 1 } else { 0 };
1033
1034                trace!("Instruction: f64.ne [{v1} {v2}] -> [{res}]");
1035                stack.push_value(res.into())?;
1036            }
1037            F64_LT => {
1038                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1039                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1040
1041                let res = if v1 < v2 { 1 } else { 0 };
1042
1043                trace!("Instruction: f64.lt [{v1} {v2}] -> [{res}]");
1044                stack.push_value(res.into())?;
1045            }
1046            F64_GT => {
1047                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1048                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1049
1050                let res = if v1 > v2 { 1 } else { 0 };
1051
1052                trace!("Instruction: f64.gt [{v1} {v2}] -> [{res}]");
1053                stack.push_value(res.into())?;
1054            }
1055            F64_LE => {
1056                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1057                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1058
1059                let res = if v1 <= v2 { 1 } else { 0 };
1060
1061                trace!("Instruction: f64.le [{v1} {v2}] -> [{res}]");
1062                stack.push_value(res.into())?;
1063            }
1064            F64_GE => {
1065                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1066                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1067
1068                let res = if v1 >= v2 { 1 } else { 0 };
1069
1070                trace!("Instruction: f64.ge [{v1} {v2}] -> [{res}]");
1071                stack.push_value(res.into())?;
1072            }
1073
1074            I32_CLZ => {
1075                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1076                let res = v1.leading_zeros() as i32;
1077
1078                trace!("Instruction: i32.clz [{v1}] -> [{res}]");
1079                stack.push_value(res.into())?;
1080            }
1081            I32_CTZ => {
1082                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1083                let res = v1.trailing_zeros() as i32;
1084
1085                trace!("Instruction: i32.ctz [{v1}] -> [{res}]");
1086                stack.push_value(res.into())?;
1087            }
1088            I32_POPCNT => {
1089                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1090                let res = v1.count_ones() as i32;
1091
1092                trace!("Instruction: i32.popcnt [{v1}] -> [{res}]");
1093                stack.push_value(res.into())?;
1094            }
1095            I64_CONST => {
1096                let constant = wasm.read_var_i64().unwrap_validated();
1097                trace!("Instruction: i64.const [] -> [{constant}]");
1098                stack.push_value(constant.into())?;
1099            }
1100            F64_CONST => {
1101                let constant = f64::from_bits(wasm.read_var_f64().unwrap_validated());
1102                trace!("Instruction: f64.const [] -> [{constant}]");
1103                stack.push_value(constant.into())?;
1104            }
1105            I32_ADD => {
1106                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1107                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1108                let res = v1.wrapping_add(v2);
1109
1110                trace!("Instruction: i32.add [{v1} {v2}] -> [{res}]");
1111                stack.push_value(res.into())?;
1112            }
1113            I32_SUB => {
1114                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1115                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1116                let res = v1.wrapping_sub(v2);
1117
1118                trace!("Instruction: i32.sub [{v1} {v2}] -> [{res}]");
1119                stack.push_value(res.into())?;
1120            }
1121            I32_MUL => {
1122                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1123                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1124                let res = v1.wrapping_mul(v2);
1125
1126                trace!("Instruction: i32.mul [{v1} {v2}] -> [{res}]");
1127                stack.push_value(res.into())?;
1128            }
1129            I32_DIV_S => {
1130                let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1131                let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1132
1133                if dividend == 0 {
1134                    return Err(RuntimeError::DivideBy0);
1135                }
1136                if divisor == i32::MIN && dividend == -1 {
1137                    return Err(RuntimeError::UnrepresentableResult);
1138                }
1139
1140                let res = divisor / dividend;
1141
1142                trace!("Instruction: i32.div_s [{divisor} {dividend}] -> [{res}]");
1143                stack.push_value(res.into())?;
1144            }
1145            I32_DIV_U => {
1146                let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1147                let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1148
1149                let dividend = dividend as u32;
1150                let divisor = divisor as u32;
1151
1152                if dividend == 0 {
1153                    return Err(RuntimeError::DivideBy0);
1154                }
1155
1156                let res = (divisor / dividend) as i32;
1157
1158                trace!("Instruction: i32.div_u [{divisor} {dividend}] -> [{res}]");
1159                stack.push_value(res.into())?;
1160            }
1161            I32_REM_S => {
1162                let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1163                let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1164
1165                if dividend == 0 {
1166                    return Err(RuntimeError::DivideBy0);
1167                }
1168
1169                let res = divisor.checked_rem(dividend);
1170                let res = res.unwrap_or_default();
1171
1172                trace!("Instruction: i32.rem_s [{divisor} {dividend}] -> [{res}]");
1173                stack.push_value(res.into())?;
1174            }
1175            I64_CLZ => {
1176                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1177                let res = v1.leading_zeros() as i64;
1178
1179                trace!("Instruction: i64.clz [{v1}] -> [{res}]");
1180                stack.push_value(res.into())?;
1181            }
1182            I64_CTZ => {
1183                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1184                let res = v1.trailing_zeros() as i64;
1185
1186                trace!("Instruction: i64.ctz [{v1}] -> [{res}]");
1187                stack.push_value(res.into())?;
1188            }
1189            I64_POPCNT => {
1190                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1191                let res = v1.count_ones() as i64;
1192
1193                trace!("Instruction: i64.popcnt [{v1}] -> [{res}]");
1194                stack.push_value(res.into())?;
1195            }
1196            I64_ADD => {
1197                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1198                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1199                let res = v1.wrapping_add(v2);
1200
1201                trace!("Instruction: i64.add [{v1} {v2}] -> [{res}]");
1202                stack.push_value(res.into())?;
1203            }
1204            I64_SUB => {
1205                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1206                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1207                let res = v1.wrapping_sub(v2);
1208
1209                trace!("Instruction: i64.sub [{v1} {v2}] -> [{res}]");
1210                stack.push_value(res.into())?;
1211            }
1212            I64_MUL => {
1213                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1214                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1215                let res = v1.wrapping_mul(v2);
1216
1217                trace!("Instruction: i64.mul [{v1} {v2}] -> [{res}]");
1218                stack.push_value(res.into())?;
1219            }
1220            I64_DIV_S => {
1221                let dividend: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1222                let divisor: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1223
1224                if dividend == 0 {
1225                    return Err(RuntimeError::DivideBy0);
1226                }
1227                if divisor == i64::MIN && dividend == -1 {
1228                    return Err(RuntimeError::UnrepresentableResult);
1229                }
1230
1231                let res = divisor / dividend;
1232
1233                trace!("Instruction: i64.div_s [{divisor} {dividend}] -> [{res}]");
1234                stack.push_value(res.into())?;
1235            }
1236            I64_DIV_U => {
1237                let dividend: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1238                let divisor: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1239
1240                let dividend = dividend as u64;
1241                let divisor = divisor as u64;
1242
1243                if dividend == 0 {
1244                    return Err(RuntimeError::DivideBy0);
1245                }
1246
1247                let res = (divisor / dividend) as i64;
1248
1249                trace!("Instruction: i64.div_u [{divisor} {dividend}] -> [{res}]");
1250                stack.push_value(res.into())?;
1251            }
1252            I64_REM_S => {
1253                let dividend: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1254                let divisor: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1255
1256                if dividend == 0 {
1257                    return Err(RuntimeError::DivideBy0);
1258                }
1259
1260                let res = divisor.checked_rem(dividend);
1261                let res = res.unwrap_or_default();
1262
1263                trace!("Instruction: i64.rem_s [{divisor} {dividend}] -> [{res}]");
1264                stack.push_value(res.into())?;
1265            }
1266            I64_REM_U => {
1267                let dividend: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1268                let divisor: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1269
1270                let dividend = dividend as u64;
1271                let divisor = divisor as u64;
1272
1273                if dividend == 0 {
1274                    return Err(RuntimeError::DivideBy0);
1275                }
1276
1277                let res = (divisor % dividend) as i64;
1278
1279                trace!("Instruction: i64.rem_u [{divisor} {dividend}] -> [{res}]");
1280                stack.push_value(res.into())?;
1281            }
1282            I64_AND => {
1283                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1284                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1285
1286                let res = v1 & v2;
1287
1288                trace!("Instruction: i64.and [{v1} {v2}] -> [{res}]");
1289                stack.push_value(res.into())?;
1290            }
1291            I64_OR => {
1292                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1293                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1294
1295                let res = v1 | v2;
1296
1297                trace!("Instruction: i64.or [{v1} {v2}] -> [{res}]");
1298                stack.push_value(res.into())?;
1299            }
1300            I64_XOR => {
1301                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1302                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1303
1304                let res = v1 ^ v2;
1305
1306                trace!("Instruction: i64.xor [{v1} {v2}] -> [{res}]");
1307                stack.push_value(res.into())?;
1308            }
1309            I64_SHL => {
1310                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1311                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1312
1313                let res = v1.wrapping_shl((v2 & 63) as u32);
1314
1315                trace!("Instruction: i64.shl [{v1} {v2}] -> [{res}]");
1316                stack.push_value(res.into())?;
1317            }
1318            I64_SHR_S => {
1319                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1320                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1321
1322                let res = v1.wrapping_shr((v2 & 63) as u32);
1323
1324                trace!("Instruction: i64.shr_s [{v1} {v2}] -> [{res}]");
1325                stack.push_value(res.into())?;
1326            }
1327            I64_SHR_U => {
1328                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1329                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1330
1331                let res = (v1 as u64).wrapping_shr((v2 & 63) as u32);
1332
1333                trace!("Instruction: i64.shr_u [{v1} {v2}] -> [{res}]");
1334                stack.push_value(res.into())?;
1335            }
1336            I64_ROTL => {
1337                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1338                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1339
1340                let res = v1.rotate_left((v2 & 63) as u32);
1341
1342                trace!("Instruction: i64.rotl [{v1} {v2}] -> [{res}]");
1343                stack.push_value(res.into())?;
1344            }
1345            I64_ROTR => {
1346                let v2: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1347                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1348
1349                let res = v1.rotate_right((v2 & 63) as u32);
1350
1351                trace!("Instruction: i64.rotr [{v1} {v2}] -> [{res}]");
1352                stack.push_value(res.into())?;
1353            }
1354            I32_REM_U => {
1355                let dividend: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1356                let divisor: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1357
1358                let dividend = dividend as u32;
1359                let divisor = divisor as u32;
1360
1361                if dividend == 0 {
1362                    return Err(RuntimeError::DivideBy0);
1363                }
1364
1365                let res = divisor.checked_rem(dividend);
1366                let res = res.unwrap_or_default() as i32;
1367
1368                trace!("Instruction: i32.rem_u [{divisor} {dividend}] -> [{res}]");
1369                stack.push_value(res.into())?;
1370            }
1371            I32_AND => {
1372                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1373                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1374                let res = v1 & v2;
1375
1376                trace!("Instruction: i32.and [{v1} {v2}] -> [{res}]");
1377                stack.push_value(res.into())?;
1378            }
1379            I32_OR => {
1380                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1381                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1382                let res = v1 | v2;
1383
1384                trace!("Instruction: i32.or [{v1} {v2}] -> [{res}]");
1385                stack.push_value(res.into())?;
1386            }
1387            I32_XOR => {
1388                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1389                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1390                let res = v1 ^ v2;
1391
1392                trace!("Instruction: i32.xor [{v1} {v2}] -> [{res}]");
1393                stack.push_value(res.into())?;
1394            }
1395            I32_SHL => {
1396                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1397                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1398                let res = v2.wrapping_shl(v1 as u32);
1399
1400                trace!("Instruction: i32.shl [{v2} {v1}] -> [{res}]");
1401                stack.push_value(res.into())?;
1402            }
1403            I32_SHR_S => {
1404                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1405                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1406
1407                let res = v2.wrapping_shr(v1 as u32);
1408
1409                trace!("Instruction: i32.shr_s [{v2} {v1}] -> [{res}]");
1410                stack.push_value(res.into())?;
1411            }
1412            I32_SHR_U => {
1413                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1414                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1415
1416                let res = (v2 as u32).wrapping_shr(v1 as u32) as i32;
1417
1418                trace!("Instruction: i32.shr_u [{v2} {v1}] -> [{res}]");
1419                stack.push_value(res.into())?;
1420            }
1421            I32_ROTL => {
1422                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1423                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1424
1425                let res = v2.rotate_left(v1 as u32);
1426
1427                trace!("Instruction: i32.rotl [{v2} {v1}] -> [{res}]");
1428                stack.push_value(res.into())?;
1429            }
1430            I32_ROTR => {
1431                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1432                let v2: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1433
1434                let res = v2.rotate_right(v1 as u32);
1435
1436                trace!("Instruction: i32.rotr [{v2} {v1}] -> [{res}]");
1437                stack.push_value(res.into())?;
1438            }
1439
1440            F32_ABS => {
1441                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1442                let res: value::F32 = v1.abs();
1443
1444                trace!("Instruction: f32.abs [{v1}] -> [{res}]");
1445                stack.push_value(res.into())?;
1446            }
1447            F32_NEG => {
1448                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1449                let res: value::F32 = v1.neg();
1450
1451                trace!("Instruction: f32.neg [{v1}] -> [{res}]");
1452                stack.push_value(res.into())?;
1453            }
1454            F32_CEIL => {
1455                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1456                let res: value::F32 = v1.ceil();
1457
1458                trace!("Instruction: f32.ceil [{v1}] -> [{res}]");
1459                stack.push_value(res.into())?;
1460            }
1461            F32_FLOOR => {
1462                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1463                let res: value::F32 = v1.floor();
1464
1465                trace!("Instruction: f32.floor [{v1}] -> [{res}]");
1466                stack.push_value(res.into())?;
1467            }
1468            F32_TRUNC => {
1469                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1470                let res: value::F32 = v1.trunc();
1471
1472                trace!("Instruction: f32.trunc [{v1}] -> [{res}]");
1473                stack.push_value(res.into())?;
1474            }
1475            F32_NEAREST => {
1476                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1477                let res: value::F32 = v1.nearest();
1478
1479                trace!("Instruction: f32.nearest [{v1}] -> [{res}]");
1480                stack.push_value(res.into())?;
1481            }
1482            F32_SQRT => {
1483                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1484                let res: value::F32 = v1.sqrt();
1485
1486                trace!("Instruction: f32.sqrt [{v1}] -> [{res}]");
1487                stack.push_value(res.into())?;
1488            }
1489            F32_ADD => {
1490                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1491                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1492                let res: value::F32 = v1 + v2;
1493
1494                trace!("Instruction: f32.add [{v1} {v2}] -> [{res}]");
1495                stack.push_value(res.into())?;
1496            }
1497            F32_SUB => {
1498                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1499                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1500                let res: value::F32 = v1 - v2;
1501
1502                trace!("Instruction: f32.sub [{v1} {v2}] -> [{res}]");
1503                stack.push_value(res.into())?;
1504            }
1505            F32_MUL => {
1506                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1507                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1508                let res: value::F32 = v1 * v2;
1509
1510                trace!("Instruction: f32.mul [{v1} {v2}] -> [{res}]");
1511                stack.push_value(res.into())?;
1512            }
1513            F32_DIV => {
1514                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1515                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1516                let res: value::F32 = v1 / v2;
1517
1518                trace!("Instruction: f32.div [{v1} {v2}] -> [{res}]");
1519                stack.push_value(res.into())?;
1520            }
1521            F32_MIN => {
1522                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1523                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1524                let res: value::F32 = v1.min(v2);
1525
1526                trace!("Instruction: f32.min [{v1} {v2}] -> [{res}]");
1527                stack.push_value(res.into())?;
1528            }
1529            F32_MAX => {
1530                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1531                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1532                let res: value::F32 = v1.max(v2);
1533
1534                trace!("Instruction: f32.max [{v1} {v2}] -> [{res}]");
1535                stack.push_value(res.into())?;
1536            }
1537            F32_COPYSIGN => {
1538                let v2: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1539                let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1540                let res: value::F32 = v1.copysign(v2);
1541
1542                trace!("Instruction: f32.copysign [{v1} {v2}] -> [{res}]");
1543                stack.push_value(res.into())?;
1544            }
1545
1546            F64_ABS => {
1547                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1548                let res: value::F64 = v1.abs();
1549
1550                trace!("Instruction: f64.abs [{v1}] -> [{res}]");
1551                stack.push_value(res.into())?;
1552            }
1553            F64_NEG => {
1554                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1555                let res: value::F64 = v1.neg();
1556
1557                trace!("Instruction: f64.neg [{v1}] -> [{res}]");
1558                stack.push_value(res.into())?;
1559            }
1560            F64_CEIL => {
1561                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1562                let res: value::F64 = v1.ceil();
1563
1564                trace!("Instruction: f64.ceil [{v1}] -> [{res}]");
1565                stack.push_value(res.into())?;
1566            }
1567            F64_FLOOR => {
1568                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1569                let res: value::F64 = v1.floor();
1570
1571                trace!("Instruction: f64.floor [{v1}] -> [{res}]");
1572                stack.push_value(res.into())?;
1573            }
1574            F64_TRUNC => {
1575                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1576                let res: value::F64 = v1.trunc();
1577
1578                trace!("Instruction: f64.trunc [{v1}] -> [{res}]");
1579                stack.push_value(res.into())?;
1580            }
1581            F64_NEAREST => {
1582                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1583                let res: value::F64 = v1.nearest();
1584
1585                trace!("Instruction: f64.nearest [{v1}] -> [{res}]");
1586                stack.push_value(res.into())?;
1587            }
1588            F64_SQRT => {
1589                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1590                let res: value::F64 = v1.sqrt();
1591
1592                trace!("Instruction: f64.sqrt [{v1}] -> [{res}]");
1593                stack.push_value(res.into())?;
1594            }
1595            F64_ADD => {
1596                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1597                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1598                let res: value::F64 = v1 + v2;
1599
1600                trace!("Instruction: f64.add [{v1} {v2}] -> [{res}]");
1601                stack.push_value(res.into())?;
1602            }
1603            F64_SUB => {
1604                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1605                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1606                let res: value::F64 = v1 - v2;
1607
1608                trace!("Instruction: f64.sub [{v1} {v2}] -> [{res}]");
1609                stack.push_value(res.into())?;
1610            }
1611            F64_MUL => {
1612                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1613                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1614                let res: value::F64 = v1 * v2;
1615
1616                trace!("Instruction: f64.mul [{v1} {v2}] -> [{res}]");
1617                stack.push_value(res.into())?;
1618            }
1619            F64_DIV => {
1620                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1621                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1622                let res: value::F64 = v1 / v2;
1623
1624                trace!("Instruction: f64.div [{v1} {v2}] -> [{res}]");
1625                stack.push_value(res.into())?;
1626            }
1627            F64_MIN => {
1628                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1629                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1630                let res: value::F64 = v1.min(v2);
1631
1632                trace!("Instruction: f64.min [{v1} {v2}] -> [{res}]");
1633                stack.push_value(res.into())?;
1634            }
1635            F64_MAX => {
1636                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1637                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1638                let res: value::F64 = v1.max(v2);
1639
1640                trace!("Instruction: f64.max [{v1} {v2}] -> [{res}]");
1641                stack.push_value(res.into())?;
1642            }
1643            F64_COPYSIGN => {
1644                let v2: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1645                let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1646                let res: value::F64 = v1.copysign(v2);
1647
1648                trace!("Instruction: f64.copysign [{v1} {v2}] -> [{res}]");
1649                stack.push_value(res.into())?;
1650            }
1651            I32_WRAP_I64 => {
1652                let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1653                let res: i32 = v as i32;
1654
1655                trace!("Instruction: i32.wrap_i64 [{v}] -> [{res}]");
1656                stack.push_value(res.into())?;
1657            }
1658            I32_TRUNC_F32_S => {
1659                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1660                if v.is_infinity() {
1661                    return Err(RuntimeError::UnrepresentableResult);
1662                }
1663                if v.is_nan() {
1664                    return Err(RuntimeError::BadConversionToInteger);
1665                }
1666                if v >= value::F32(2147483648.0) || v <= value::F32(-2147483904.0) {
1667                    return Err(RuntimeError::UnrepresentableResult);
1668                }
1669
1670                let res: i32 = v.as_i32();
1671
1672                trace!("Instruction: i32.trunc_f32_s [{v:.7}] -> [{res}]");
1673                stack.push_value(res.into())?;
1674            }
1675            I32_TRUNC_F32_U => {
1676                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1677                if v.is_infinity() {
1678                    return Err(RuntimeError::UnrepresentableResult);
1679                }
1680                if v.is_nan() {
1681                    return Err(RuntimeError::BadConversionToInteger);
1682                }
1683                if v >= value::F32(4294967296.0) || v <= value::F32(-1.0) {
1684                    return Err(RuntimeError::UnrepresentableResult);
1685                }
1686
1687                let res: i32 = v.as_u32() as i32;
1688
1689                trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]");
1690                stack.push_value(res.into())?;
1691            }
1692
1693            I32_TRUNC_F64_S => {
1694                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1695                if v.is_infinity() {
1696                    return Err(RuntimeError::UnrepresentableResult);
1697                }
1698                if v.is_nan() {
1699                    return Err(RuntimeError::BadConversionToInteger);
1700                }
1701                if v >= value::F64(2147483648.0) || v <= value::F64(-2147483649.0) {
1702                    return Err(RuntimeError::UnrepresentableResult);
1703                }
1704
1705                let res: i32 = v.as_i32();
1706
1707                trace!("Instruction: i32.trunc_f64_s [{v:.7}] -> [{res}]");
1708                stack.push_value(res.into())?;
1709            }
1710            I32_TRUNC_F64_U => {
1711                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1712                if v.is_infinity() {
1713                    return Err(RuntimeError::UnrepresentableResult);
1714                }
1715                if v.is_nan() {
1716                    return Err(RuntimeError::BadConversionToInteger);
1717                }
1718                if v >= value::F64(4294967296.0) || v <= value::F64(-1.0) {
1719                    return Err(RuntimeError::UnrepresentableResult);
1720                }
1721
1722                let res: i32 = v.as_u32() as i32;
1723
1724                trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]");
1725                stack.push_value(res.into())?;
1726            }
1727
1728            I64_EXTEND_I32_S => {
1729                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1730
1731                let res: i64 = v as i64;
1732
1733                trace!("Instruction: i64.extend_i32_s [{v}] -> [{res}]");
1734                stack.push_value(res.into())?;
1735            }
1736
1737            I64_EXTEND_I32_U => {
1738                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1739
1740                let res: i64 = v as u32 as i64;
1741
1742                trace!("Instruction: i64.extend_i32_u [{v}] -> [{res}]");
1743                stack.push_value(res.into())?;
1744            }
1745
1746            I64_TRUNC_F32_S => {
1747                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1748                if v.is_infinity() {
1749                    return Err(RuntimeError::UnrepresentableResult);
1750                }
1751                if v.is_nan() {
1752                    return Err(RuntimeError::BadConversionToInteger);
1753                }
1754                if v >= value::F32(9223372036854775808.0) || v <= value::F32(-9223373136366403584.0)
1755                {
1756                    return Err(RuntimeError::UnrepresentableResult);
1757                }
1758
1759                let res: i64 = v.as_i64();
1760
1761                trace!("Instruction: i64.trunc_f32_s [{v:.7}] -> [{res}]");
1762                stack.push_value(res.into())?;
1763            }
1764            I64_TRUNC_F32_U => {
1765                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1766                if v.is_infinity() {
1767                    return Err(RuntimeError::UnrepresentableResult);
1768                }
1769                if v.is_nan() {
1770                    return Err(RuntimeError::BadConversionToInteger);
1771                }
1772                if v >= value::F32(18446744073709551616.0) || v <= value::F32(-1.0) {
1773                    return Err(RuntimeError::UnrepresentableResult);
1774                }
1775
1776                let res: i64 = v.as_u64() as i64;
1777
1778                trace!("Instruction: i64.trunc_f32_u [{v:.7}] -> [{res}]");
1779                stack.push_value(res.into())?;
1780            }
1781
1782            I64_TRUNC_F64_S => {
1783                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1784                if v.is_infinity() {
1785                    return Err(RuntimeError::UnrepresentableResult);
1786                }
1787                if v.is_nan() {
1788                    return Err(RuntimeError::BadConversionToInteger);
1789                }
1790                if v >= value::F64(9223372036854775808.0) || v <= value::F64(-9223372036854777856.0)
1791                {
1792                    return Err(RuntimeError::UnrepresentableResult);
1793                }
1794
1795                let res: i64 = v.as_i64();
1796
1797                trace!("Instruction: i64.trunc_f64_s [{v:.17}] -> [{res}]");
1798                stack.push_value(res.into())?;
1799            }
1800            I64_TRUNC_F64_U => {
1801                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1802                if v.is_infinity() {
1803                    return Err(RuntimeError::UnrepresentableResult);
1804                }
1805                if v.is_nan() {
1806                    return Err(RuntimeError::BadConversionToInteger);
1807                }
1808                if v >= value::F64(18446744073709551616.0) || v <= value::F64(-1.0) {
1809                    return Err(RuntimeError::UnrepresentableResult);
1810                }
1811
1812                let res: i64 = v.as_u64() as i64;
1813
1814                trace!("Instruction: i64.trunc_f64_u [{v:.17}] -> [{res}]");
1815                stack.push_value(res.into())?;
1816            }
1817            F32_CONVERT_I32_S => {
1818                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1819                let res: value::F32 = value::F32(v as f32);
1820
1821                trace!("Instruction: f32.convert_i32_s [{v}] -> [{res}]");
1822                stack.push_value(res.into())?;
1823            }
1824            F32_CONVERT_I32_U => {
1825                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1826                let res: value::F32 = value::F32(v as u32 as f32);
1827
1828                trace!("Instruction: f32.convert_i32_u [{v}] -> [{res}]");
1829                stack.push_value(res.into())?;
1830            }
1831            F32_CONVERT_I64_S => {
1832                let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1833                let res: value::F32 = value::F32(v as f32);
1834
1835                trace!("Instruction: f32.convert_i64_s [{v}] -> [{res}]");
1836                stack.push_value(res.into())?;
1837            }
1838            F32_CONVERT_I64_U => {
1839                let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1840                let res: value::F32 = value::F32(v as u64 as f32);
1841
1842                trace!("Instruction: f32.convert_i64_u [{v}] -> [{res}]");
1843                stack.push_value(res.into())?;
1844            }
1845            F32_DEMOTE_F64 => {
1846                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1847                let res: value::F32 = v.as_f32();
1848
1849                trace!("Instruction: f32.demote_f64 [{v:.17}] -> [{res:.7}]");
1850                stack.push_value(res.into())?;
1851            }
1852            F64_CONVERT_I32_S => {
1853                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1854                let res: value::F64 = value::F64(v as f64);
1855
1856                trace!("Instruction: f64.convert_i32_s [{v}] -> [{res:.17}]");
1857                stack.push_value(res.into())?;
1858            }
1859            F64_CONVERT_I32_U => {
1860                let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1861                let res: value::F64 = value::F64(v as u32 as f64);
1862
1863                trace!("Instruction: f64.convert_i32_u [{v}] -> [{res:.17}]");
1864                stack.push_value(res.into())?;
1865            }
1866            F64_CONVERT_I64_S => {
1867                let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1868                let res: value::F64 = value::F64(v as f64);
1869
1870                trace!("Instruction: f64.convert_i64_s [{v}] -> [{res:.17}]");
1871                stack.push_value(res.into())?;
1872            }
1873            F64_CONVERT_I64_U => {
1874                let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1875                let res: value::F64 = value::F64(v as u64 as f64);
1876
1877                trace!("Instruction: f64.convert_i64_u [{v}] -> [{res:.17}]");
1878                stack.push_value(res.into())?;
1879            }
1880            F64_PROMOTE_F32 => {
1881                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1882                let res: value::F64 = v.as_f32();
1883
1884                trace!("Instruction: f64.promote_f32 [{v:.7}] -> [{res:.17}]");
1885                stack.push_value(res.into())?;
1886            }
1887            I32_REINTERPRET_F32 => {
1888                let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1889                let res: i32 = v.reinterpret_as_i32();
1890
1891                trace!("Instruction: i32.reinterpret_f32 [{v:.7}] -> [{res}]");
1892                stack.push_value(res.into())?;
1893            }
1894            I64_REINTERPRET_F64 => {
1895                let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1896                let res: i64 = v.reinterpret_as_i64();
1897
1898                trace!("Instruction: i64.reinterpret_f64 [{v:.17}] -> [{res}]");
1899                stack.push_value(res.into())?;
1900            }
1901            F32_REINTERPRET_I32 => {
1902                let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
1903                let res: value::F32 = value::F32::from_bits(v1 as u32);
1904
1905                trace!("Instruction: f32.reinterpret_i32 [{v1}] -> [{res:.7}]");
1906                stack.push_value(res.into())?;
1907            }
1908            F64_REINTERPRET_I64 => {
1909                let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
1910                let res: value::F64 = value::F64::from_bits(v1 as u64);
1911
1912                trace!("Instruction: f64.reinterpret_i64 [{v1}] -> [{res:.17}]");
1913                stack.push_value(res.into())?;
1914            }
1915            REF_NULL => {
1916                let reftype = RefType::read_unvalidated(wasm);
1917
1918                stack.push_value(Value::Ref(reftype.to_null_ref()))?;
1919                trace!("Instruction: ref.null '{:?}' -> [{:?}]", reftype, reftype);
1920            }
1921            REF_IS_NULL => {
1922                let rref = stack.pop_unknown_ref();
1923                let is_null = match rref {
1924                    Ref::Extern(rref) => rref.addr.is_none(),
1925                    Ref::Func(rref) => rref.addr.is_none(),
1926                };
1927
1928                let res = if is_null { 1 } else { 0 };
1929                trace!("Instruction: ref.is_null [{}] -> [{}]", rref, res);
1930                stack.push_value(Value::I32(res))?;
1931            }
1932            // https://webassembly.github.io/spec/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-ref-mathsf-ref-func-x
1933            REF_FUNC => {
1934                let func_idx = wasm.read_var_u32().unwrap_validated() as FuncIdx;
1935                stack.push_value(Value::Ref(Ref::Func(FuncAddr::new(Some(
1936                    store.modules[current_module_idx].func_addrs[func_idx],
1937                )))))?;
1938            }
1939            FC_EXTENSIONS => {
1940                // Should we call instruction hook here as well? Multibyte instruction
1941                let second_instr = wasm.read_var_u32().unwrap_validated();
1942
1943                use crate::core::reader::types::opcode::fc_extensions::*;
1944                match second_instr {
1945                    I32_TRUNC_SAT_F32_S => {
1946                        let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1947                        let res = {
1948                            if v1.is_nan() {
1949                                0
1950                            } else if v1.is_negative_infinity() {
1951                                i32::MIN
1952                            } else if v1.is_infinity() {
1953                                i32::MAX
1954                            } else {
1955                                v1.as_i32()
1956                            }
1957                        };
1958
1959                        trace!("Instruction: i32.trunc_sat_f32_s [{v1}] -> [{res}]");
1960                        stack.push_value(res.into())?;
1961                    }
1962                    I32_TRUNC_SAT_F32_U => {
1963                        let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
1964                        let res = {
1965                            if v1.is_nan() || v1.is_negative_infinity() {
1966                                0
1967                            } else if v1.is_infinity() {
1968                                u32::MAX as i32
1969                            } else {
1970                                v1.as_u32() as i32
1971                            }
1972                        };
1973
1974                        trace!("Instruction: i32.trunc_sat_f32_u [{v1}] -> [{res}]");
1975                        stack.push_value(res.into())?;
1976                    }
1977                    I32_TRUNC_SAT_F64_S => {
1978                        let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1979                        let res = {
1980                            if v1.is_nan() {
1981                                0
1982                            } else if v1.is_negative_infinity() {
1983                                i32::MIN
1984                            } else if v1.is_infinity() {
1985                                i32::MAX
1986                            } else {
1987                                v1.as_i32()
1988                            }
1989                        };
1990
1991                        trace!("Instruction: i32.trunc_sat_f64_s [{v1}] -> [{res}]");
1992                        stack.push_value(res.into())?;
1993                    }
1994                    I32_TRUNC_SAT_F64_U => {
1995                        let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
1996                        let res = {
1997                            if v1.is_nan() || v1.is_negative_infinity() {
1998                                0
1999                            } else if v1.is_infinity() {
2000                                u32::MAX as i32
2001                            } else {
2002                                v1.as_u32() as i32
2003                            }
2004                        };
2005
2006                        trace!("Instruction: i32.trunc_sat_f64_u [{v1}] -> [{res}]");
2007                        stack.push_value(res.into())?;
2008                    }
2009                    I64_TRUNC_SAT_F32_S => {
2010                        let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
2011                        let res = {
2012                            if v1.is_nan() {
2013                                0
2014                            } else if v1.is_negative_infinity() {
2015                                i64::MIN
2016                            } else if v1.is_infinity() {
2017                                i64::MAX
2018                            } else {
2019                                v1.as_i64()
2020                            }
2021                        };
2022
2023                        trace!("Instruction: i64.trunc_sat_f32_s [{v1}] -> [{res}]");
2024                        stack.push_value(res.into())?;
2025                    }
2026                    I64_TRUNC_SAT_F32_U => {
2027                        let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into();
2028                        let res = {
2029                            if v1.is_nan() || v1.is_negative_infinity() {
2030                                0
2031                            } else if v1.is_infinity() {
2032                                u64::MAX as i64
2033                            } else {
2034                                v1.as_u64() as i64
2035                            }
2036                        };
2037
2038                        trace!("Instruction: i64.trunc_sat_f32_u [{v1}] -> [{res}]");
2039                        stack.push_value(res.into())?;
2040                    }
2041                    I64_TRUNC_SAT_F64_S => {
2042                        let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
2043                        let res = {
2044                            if v1.is_nan() {
2045                                0
2046                            } else if v1.is_negative_infinity() {
2047                                i64::MIN
2048                            } else if v1.is_infinity() {
2049                                i64::MAX
2050                            } else {
2051                                v1.as_i64()
2052                            }
2053                        };
2054
2055                        trace!("Instruction: i64.trunc_sat_f64_s [{v1}] -> [{res}]");
2056                        stack.push_value(res.into())?;
2057                    }
2058                    I64_TRUNC_SAT_F64_U => {
2059                        let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into();
2060                        let res = {
2061                            if v1.is_nan() || v1.is_negative_infinity() {
2062                                0
2063                            } else if v1.is_infinity() {
2064                                u64::MAX as i64
2065                            } else {
2066                                v1.as_u64() as i64
2067                            }
2068                        };
2069
2070                        trace!("Instruction: i64.trunc_sat_f64_u [{v1}] -> [{res}]");
2071                        stack.push_value(res.into())?;
2072                    }
2073                    // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-init-x
2074                    // Copy a region from a data segment into memory
2075                    MEMORY_INIT => {
2076                        //  mappings:
2077                        //      n => number of bytes to copy
2078                        //      s => starting pointer in the data segment
2079                        //      d => destination address to copy to
2080                        let data_idx = wasm.read_var_u32().unwrap_validated() as DataIdx;
2081                        let mem_idx = wasm.read_u8().unwrap_validated() as usize;
2082
2083                        let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2084                        let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2085                        let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2086
2087                        memory_init(
2088                            &store.modules,
2089                            &mut store.memories,
2090                            &store.data,
2091                            current_module_idx,
2092                            data_idx,
2093                            mem_idx,
2094                            n,
2095                            s,
2096                            d,
2097                        )?;
2098                    }
2099                    DATA_DROP => {
2100                        let data_idx = wasm.read_var_u32().unwrap_validated() as DataIdx;
2101                        data_drop(
2102                            &store.modules,
2103                            &mut store.data,
2104                            current_module_idx,
2105                            data_idx,
2106                        )?;
2107                    }
2108                    // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-copy
2109                    MEMORY_COPY => {
2110                        //  mappings:
2111                        //      n => number of bytes to copy
2112                        //      s => source address to copy from
2113                        //      d => destination address to copy to
2114                        let (dst_idx, src_idx) = (
2115                            wasm.read_u8().unwrap_validated() as usize,
2116                            wasm.read_u8().unwrap_validated() as usize,
2117                        );
2118                        let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2119                        let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2120                        let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2121
2122                        let src_mem =
2123                            &store.memories[store.modules[current_module_idx].mem_addrs[src_idx]];
2124                        let dest_mem =
2125                            &store.memories[store.modules[current_module_idx].mem_addrs[dst_idx]];
2126
2127                        dest_mem
2128                            .mem
2129                            .copy(d as MemIdx, &src_mem.mem, s as MemIdx, n as MemIdx)?;
2130                        trace!("Instruction: memory.copy");
2131                    }
2132                    // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-fill
2133                    MEMORY_FILL => {
2134                        //  mappings:
2135                        //      n => number of bytes to update
2136                        //      val => the value to set each byte to (must be < 256)
2137                        //      d => the pointer to the region to update
2138                        let mem_idx = wasm.read_u8().unwrap_validated() as usize;
2139                        let mem = &mut store.memories
2140                            [store.modules[current_module_idx].mem_addrs[mem_idx]];
2141                        let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2142                        let val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2143
2144                        if !(0..=255).contains(&val) {
2145                            warn!("Value for memory.fill does not fit in a byte ({val})");
2146                        }
2147
2148                        let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2149
2150                        mem.mem.fill(d as usize, val as u8, n as usize)?;
2151
2152                        trace!("Instruction: memory.fill");
2153                    }
2154                    // https://webassembly.github.io/spec/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-init-x-y
2155                    // https://webassembly.github.io/spec/core/binary/instructions.html#table-instructions
2156                    // in binary format it seems that elemidx is first ???????
2157                    // this is ONLY for passive elements
2158                    TABLE_INIT => {
2159                        let elem_idx = wasm.read_var_u32().unwrap_validated() as usize;
2160                        let table_idx = wasm.read_var_u32().unwrap_validated() as usize;
2161
2162                        let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // size
2163                        let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // offset
2164                        let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // dst
2165
2166                        table_init(
2167                            &store.modules,
2168                            &mut store.tables,
2169                            &store.elements,
2170                            current_module_idx,
2171                            elem_idx,
2172                            table_idx,
2173                            n,
2174                            s,
2175                            d,
2176                        )?;
2177                    }
2178                    ELEM_DROP => {
2179                        let elem_idx = wasm.read_var_u32().unwrap_validated() as usize;
2180
2181                        elem_drop(
2182                            &store.modules,
2183                            &mut store.elements,
2184                            current_module_idx,
2185                            elem_idx,
2186                        )?;
2187                    }
2188                    // https://webassembly.github.io/spec/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-copy-x-y
2189                    TABLE_COPY => {
2190                        let table_x_idx = wasm.read_var_u32().unwrap_validated() as usize;
2191                        let table_y_idx = wasm.read_var_u32().unwrap_validated() as usize;
2192
2193                        let tab_x_elem_len = store.tables
2194                            [store.modules[current_module_idx].table_addrs[table_x_idx]]
2195                            .elem
2196                            .len();
2197                        let tab_y_elem_len = store.tables
2198                            [store.modules[current_module_idx].table_addrs[table_y_idx]]
2199                            .elem
2200                            .len();
2201
2202                        let n: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // size
2203                        let s: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // source
2204                        let d: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // destination
2205
2206                        let src_res = match s.checked_add(n) {
2207                            Some(res) => {
2208                                if res > tab_y_elem_len as u32 {
2209                                    return Err(RuntimeError::TableAccessOutOfBounds);
2210                                } else {
2211                                    res as usize
2212                                }
2213                            }
2214                            _ => return Err(RuntimeError::TableAccessOutOfBounds),
2215                        };
2216
2217                        let dst_res = match d.checked_add(n) {
2218                            Some(res) => {
2219                                if res > tab_x_elem_len as u32 {
2220                                    return Err(RuntimeError::TableAccessOutOfBounds);
2221                                } else {
2222                                    res as usize
2223                                }
2224                            }
2225                            _ => return Err(RuntimeError::TableAccessOutOfBounds),
2226                        };
2227
2228                        let dst = table_x_idx;
2229                        let src = table_y_idx;
2230
2231                        if table_x_idx == table_y_idx {
2232                            store.tables
2233                                [store.modules[current_module_idx].table_addrs[table_x_idx]]
2234                                .elem
2235                                .copy_within(s as usize..src_res, d as usize); // }
2236                        } else {
2237                            use core::cmp::Ordering::*;
2238                            let src = store.modules[current_module_idx].table_addrs[src];
2239                            let dst = store.modules[current_module_idx].table_addrs[dst];
2240
2241                            let (src_table, dst_table) = match dst.cmp(&src) {
2242                                Greater => {
2243                                    let (left, right) = store.tables.split_at_mut(dst);
2244                                    (&left[src], &mut right[0])
2245                                }
2246                                Less => {
2247                                    let (left, right) = store.tables.split_at_mut(src);
2248                                    (&right[0], &mut left[dst])
2249                                }
2250                                Equal => unreachable!(),
2251                            };
2252
2253                            dst_table.elem[d as usize..dst_res]
2254                                .copy_from_slice(&src_table.elem[s as usize..src_res]);
2255                        }
2256
2257                        trace!(
2258                            "Instruction: table.copy '{}' '{}' [{} {} {}] -> []",
2259                            table_x_idx,
2260                            table_y_idx,
2261                            d,
2262                            s,
2263                            n
2264                        );
2265                    }
2266                    TABLE_GROW => {
2267                        let table_idx = wasm.read_var_u32().unwrap_validated() as usize;
2268                        let tab = &mut store.tables
2269                            [store.modules[current_module_idx].table_addrs[table_idx]];
2270
2271                        let sz = tab.elem.len() as u32;
2272
2273                        let n: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2274                        let val = stack.pop_unknown_ref();
2275
2276                        // TODO this instruction is non-deterministic w.r.t. spec, and can fail if the embedder wills it.
2277                        // for now we execute it always according to the following match expr.
2278                        // if the grow operation fails, err := Value::I32(2^32-1) is pushed to the stack per spec
2279                        match tab.grow(n, val) {
2280                            Ok(_) => {
2281                                stack.push_value(Value::I32(sz))?;
2282                            }
2283                            Err(_) => {
2284                                stack.push_value(Value::I32(u32::MAX))?;
2285                            }
2286                        }
2287                    }
2288                    TABLE_SIZE => {
2289                        let table_idx = wasm.read_var_u32().unwrap_validated() as usize;
2290                        let tab = &mut store.tables
2291                            [store.modules[current_module_idx].table_addrs[table_idx]];
2292
2293                        let sz = tab.elem.len() as u32;
2294
2295                        stack.push_value(Value::I32(sz))?;
2296
2297                        trace!("Instruction: table.size '{}' [] -> [{}]", table_idx, sz);
2298                    }
2299                    TABLE_FILL => {
2300                        let table_idx = wasm.read_var_u32().unwrap_validated() as usize;
2301                        let tab = &mut store.tables
2302                            [store.modules[current_module_idx].table_addrs[table_idx]];
2303                        let ty = tab.ty.et;
2304
2305                        let n: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // len
2306                        let val: Ref = stack.pop_value(ValType::RefType(ty)).into();
2307                        let i: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); // dst
2308
2309                        let end = (i as usize)
2310                            .checked_add(n as usize)
2311                            .ok_or(RuntimeError::TableAccessOutOfBounds)?;
2312
2313                        tab.elem
2314                            .get_mut(i as usize..end)
2315                            .ok_or(RuntimeError::TableAccessOutOfBounds)?
2316                            .fill(val);
2317
2318                        trace!(
2319                            "Instruction table.fill '{}' [{} {} {}] -> []",
2320                            table_idx,
2321                            i,
2322                            val,
2323                            n
2324                        )
2325                    }
2326                    _ => unreachable!(),
2327                }
2328            }
2329
2330            I32_EXTEND8_S => {
2331                let mut v: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2332
2333                if v | 0xFF != 0xFF {
2334                    trace!("Number v ({}) not contained in 8 bits, truncating", v);
2335                    v &= 0xFF;
2336                }
2337
2338                let res = if v | 0x7F != 0x7F { v | 0xFFFFFF00 } else { v };
2339
2340                stack.push_value(res.into())?;
2341
2342                trace!("Instruction i32.extend8_s [{}] -> [{}]", v, res);
2343            }
2344            I32_EXTEND16_S => {
2345                let mut v: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into();
2346
2347                if v | 0xFFFF != 0xFFFF {
2348                    trace!("Number v ({}) not contained in 16 bits, truncating", v);
2349                    v &= 0xFFFF;
2350                }
2351
2352                let res = if v | 0x7FFF != 0x7FFF {
2353                    v | 0xFFFF0000
2354                } else {
2355                    v
2356                };
2357
2358                stack.push_value(res.into())?;
2359
2360                trace!("Instruction i32.extend16_s [{}] -> [{}]", v, res);
2361            }
2362            I64_EXTEND8_S => {
2363                let mut v: u64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
2364
2365                if v | 0xFF != 0xFF {
2366                    trace!("Number v ({}) not contained in 8 bits, truncating", v);
2367                    v &= 0xFF;
2368                }
2369
2370                let res = if v | 0x7F != 0x7F {
2371                    v | 0xFFFFFFFF_FFFFFF00
2372                } else {
2373                    v
2374                };
2375
2376                stack.push_value(res.into())?;
2377
2378                trace!("Instruction i64.extend8_s [{}] -> [{}]", v, res);
2379            }
2380            I64_EXTEND16_S => {
2381                let mut v: u64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
2382
2383                if v | 0xFFFF != 0xFFFF {
2384                    trace!("Number v ({}) not contained in 16 bits, truncating", v);
2385                    v &= 0xFFFF;
2386                }
2387
2388                let res = if v | 0x7FFF != 0x7FFF {
2389                    v | 0xFFFFFFFF_FFFF0000
2390                } else {
2391                    v
2392                };
2393
2394                stack.push_value(res.into())?;
2395
2396                trace!("Instruction i64.extend16_s [{}] -> [{}]", v, res);
2397            }
2398            I64_EXTEND32_S => {
2399                let mut v: u64 = stack.pop_value(ValType::NumType(NumType::I64)).into();
2400
2401                if v | 0xFFFF_FFFF != 0xFFFF_FFFF {
2402                    trace!("Number v ({}) not contained in 32 bits, truncating", v);
2403                    v &= 0xFFFF_FFFF;
2404                }
2405
2406                let res = if v | 0x7FFF_FFFF != 0x7FFF_FFFF {
2407                    v | 0xFFFFFFFF_00000000
2408                } else {
2409                    v
2410                };
2411
2412                stack.push_value(res.into())?;
2413
2414                trace!("Instruction i64.extend32_s [{}] -> [{}]", v, res);
2415            }
2416
2417            other => {
2418                trace!("Unknown instruction {other:#x}, skipping..");
2419            }
2420        }
2421    }
2422    Ok(())
2423}
2424
2425//helper function for avoiding code duplication at intraprocedural jumps
2426fn do_sidetable_control_transfer(
2427    wasm: &mut WasmReader,
2428    stack: &mut Stack,
2429    current_stp: &mut usize,
2430    current_sidetable: &Sidetable,
2431) -> Result<(), RuntimeError> {
2432    let sidetable_entry = &current_sidetable[*current_stp];
2433
2434    // TODO fix this corner cutting implementation
2435    let jump_vals = stack
2436        .pop_tail_iter(sidetable_entry.valcnt)
2437        .collect::<Vec<_>>();
2438    stack.pop_n_values(sidetable_entry.popcnt);
2439
2440    for val in jump_vals {
2441        stack.push_value(val)?;
2442    }
2443
2444    *current_stp = (*current_stp as isize + sidetable_entry.delta_stp) as usize;
2445    wasm.pc = (wasm.pc as isize + sidetable_entry.delta_pc) as usize;
2446
2447    Ok(())
2448}
2449
2450#[inline(always)]
2451fn calculate_mem_address(memarg: &MemArg, relative_address: u32) -> Result<usize, RuntimeError> {
2452    memarg
2453        .offset
2454        // The spec states that this should be a 33 bit integer, e.g. it is not legal to wrap if the
2455        // sum of offset and relative_address exceeds u32::MAX. To emulate this behavior, we use a
2456        // checked addition.
2457        // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions
2458        .checked_add(relative_address)
2459        .ok_or(RuntimeError::MemoryAccessOutOfBounds)?
2460        .try_into()
2461        .map_err(|_| RuntimeError::MemoryAccessOutOfBounds)
2462}
2463
2464//helpers for avoiding code duplication during module instantiation
2465#[inline(always)]
2466#[allow(clippy::too_many_arguments)]
2467pub(super) fn table_init(
2468    store_modules: &[ModuleInst],
2469    store_tables: &mut [TableInst],
2470    store_elements: &[ElemInst],
2471    current_module_idx: usize,
2472    elem_idx: usize,
2473    table_idx: usize,
2474    n: i32,
2475    s: i32,
2476    d: i32,
2477) -> Result<(), RuntimeError> {
2478    let tab_len = store_tables[store_modules[current_module_idx].table_addrs[table_idx]].len();
2479
2480    let tab = &mut store_tables[store_modules[current_module_idx].table_addrs[table_idx]];
2481
2482    // TODO is this spec compliant?
2483    let elem_len = store_modules[current_module_idx]
2484        .elem_addrs
2485        .get(elem_idx)
2486        .map(|elem_addr| store_elements[*elem_addr].len())
2487        .unwrap_or(0);
2488
2489    trace!(
2490        "Instruction: table.init '{}' '{}' [{} {} {}] -> []",
2491        elem_idx,
2492        table_idx,
2493        d,
2494        s,
2495        n
2496    );
2497
2498    let final_src_offset = (s as usize)
2499        .checked_add(n as usize)
2500        .filter(|&res| res <= elem_len)
2501        .ok_or(RuntimeError::TableAccessOutOfBounds)?;
2502
2503    (d as usize)
2504        .checked_add(n as usize)
2505        .filter(|&res| res <= tab_len)
2506        .ok_or(RuntimeError::TableAccessOutOfBounds)?;
2507
2508    let elem = &store_elements[store_modules[current_module_idx].elem_addrs[elem_idx]];
2509
2510    let dest = &mut tab.elem[d as usize..];
2511    let src = &elem.references[s as usize..final_src_offset];
2512    dest[..src.len()].copy_from_slice(src);
2513    Ok(())
2514}
2515
2516#[inline(always)]
2517pub(super) fn elem_drop(
2518    store_modules: &[ModuleInst],
2519    store_elements: &mut [ElemInst],
2520    current_module_idx: usize,
2521    elem_idx: usize,
2522) -> Result<(), RuntimeError> {
2523    // WARN: i'm not sure if this is okay or not
2524    store_elements[store_modules[current_module_idx].elem_addrs[elem_idx]].references = vec![];
2525    Ok(())
2526}
2527
2528#[inline(always)]
2529#[allow(clippy::too_many_arguments)]
2530pub(super) fn memory_init(
2531    store_modules: &[ModuleInst],
2532    store_memories: &mut [MemInst],
2533    store_data: &[DataInst],
2534    current_module_idx: usize,
2535    data_idx: usize,
2536    mem_idx: usize,
2537    n: i32,
2538    s: i32,
2539    d: i32,
2540) -> Result<(), RuntimeError> {
2541    let mem = &store_memories[store_modules[current_module_idx].mem_addrs[mem_idx]];
2542
2543    mem.mem.init(
2544        d as MemIdx,
2545        &store_data[store_modules[current_module_idx].data_addrs[data_idx]].data,
2546        s as MemIdx,
2547        n as MemIdx,
2548    )?;
2549
2550    trace!("Instruction: memory.init");
2551    Ok(())
2552}
2553
2554#[inline(always)]
2555pub(super) fn data_drop(
2556    store_modules: &[ModuleInst],
2557    store_data: &mut [DataInst],
2558    current_module_idx: usize,
2559    data_idx: usize,
2560) -> Result<(), RuntimeError> {
2561    // Here is debatable
2562    // If we were to be on par with the spec we'd have to use a DataInst struct
2563    // But since memory.init is specifically made for Passive data segments
2564    // I thought that using DataMode would be better because we can see if the
2565    // data segment is passive or active
2566
2567    // Also, we should set data to null here (empty), which we do using an empty init vec
2568    store_data[store_modules[current_module_idx].data_addrs[data_idx]] =
2569        DataInst { data: Vec::new() };
2570    Ok(())
2571}