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