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