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