diff --git a/crates/wasm_interp/src/frame.rs b/crates/wasm_interp/src/frame.rs index 70919d9d04..71db1427bb 100644 --- a/crates/wasm_interp/src/frame.rs +++ b/crates/wasm_interp/src/frame.rs @@ -10,11 +10,11 @@ pub struct Frame { pub fn_index: usize, /// Address in the code section where this frame returns to pub return_addr: usize, - /// Number of block scopes when this frame returns - pub return_block_depth: usize, - /// Offset in the ValueStack where the locals begin + /// Depth of the "function block" for this frame + pub function_block_depth: usize, + /// Offset in the ValueStack where the args & locals begin pub locals_start: usize, - /// Number of locals in the frame + /// Number of args & locals in the frame pub locals_count: usize, } @@ -23,7 +23,7 @@ impl Frame { Frame { fn_index: 0, return_addr: 0, - return_block_depth: 0, + function_block_depth: 0, locals_start: 0, locals_count: 0, } @@ -32,7 +32,7 @@ impl Frame { pub fn enter( fn_index: usize, return_addr: usize, - return_block_depth: usize, + function_block_depth: usize, arg_type_bytes: &[u8], code_bytes: &[u8], value_stack: &mut ValueStack<'_>, @@ -60,7 +60,7 @@ impl Frame { Frame { fn_index, return_addr, - return_block_depth, + function_block_depth, locals_start, locals_count, } @@ -77,6 +77,7 @@ impl Frame { } } +#[allow(dead_code)] pub fn write_stack_trace( _current_frame: &Frame, _previous_frames: &[Frame], diff --git a/crates/wasm_interp/src/instance.rs b/crates/wasm_interp/src/instance.rs index 44f7d053b5..4a96ab04a8 100644 --- a/crates/wasm_interp/src/instance.rs +++ b/crates/wasm_interp/src/instance.rs @@ -8,9 +8,9 @@ use roc_wasm_module::sections::{ImportDesc, MemorySection}; use roc_wasm_module::{ExportType, WasmModule}; use roc_wasm_module::{Value, ValueType}; -use crate::frame::{write_stack_trace, Frame}; +use crate::frame::Frame; use crate::value_stack::ValueStack; -use crate::{pc_to_fn_index, Error, ImportDispatcher}; +use crate::{Error, ImportDispatcher}; #[derive(Debug)] pub enum Action { @@ -295,10 +295,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { ) -> Result, String> { self.previous_frames.clear(); self.blocks.clear(); + self.blocks.push(Block::Normal { + vstack: self.value_stack.depth(), + }); self.current_frame = Frame::enter( fn_index, 0, // return_addr - 0, // return_block_depth + self.blocks.len(), arg_type_bytes, &module.code.bytes, &mut self.value_stack, @@ -313,16 +316,16 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } Err(e) => { let file_offset = self.program_counter + module.code.section_offset as usize; - let mut message = e.to_string_at(file_offset); - write_stack_trace( - &self.current_frame, - &self.previous_frames, - self.module, - &self.value_stack, - self.program_counter, - &mut message, - ) - .unwrap(); + let message = e.to_string_at(file_offset); + // write_stack_trace( + // &self.current_frame, + // &self.previous_frames, + // self.module, + // &self.value_stack, + // self.program_counter, + // &mut message, + // ) + // .unwrap(); return Err(message); } }; @@ -348,7 +351,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { fn do_return(&mut self) -> Action { let Frame { return_addr, - return_block_depth, + function_block_depth, locals_start, .. } = self.current_frame; @@ -371,7 +374,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.value_stack.push(val); } - self.blocks.truncate(return_block_depth); + self.blocks.truncate(function_block_depth - 1); self.program_counter = return_addr; if let Some(caller_frame) = self.previous_frames.pop() { @@ -502,7 +505,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { .extend(std::iter::repeat(Value::I64(0)).take(arg_type_bytes.len())); for (i, type_byte) in arg_type_bytes.iter().copied().enumerate().rev() { let arg = self.value_stack.pop(); - assert_eq!(ValueType::from(arg), ValueType::from(type_byte)); + + let expected = ValueType::from(type_byte); + let actual = ValueType::from(arg); + if actual != expected { + return Err(Error::ValueStackType(expected, actual)); + } + self.import_arguments[i] = arg; } @@ -520,24 +529,26 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } } else { let return_addr = self.program_counter; - let return_block_depth = self.blocks.len(); - // set PC to start of function bytes let internal_fn_index = fn_index - self.import_count; self.program_counter = module.code.function_offsets[internal_fn_index] as usize; // advance PC to the start of the local variable declarations u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap(); + self.blocks.push(Block::Normal { + vstack: self.value_stack.depth(), + }); + let function_block_depth = self.blocks.len(); + let mut swap_frame = Frame::enter( fn_index, return_addr, - return_block_depth, + function_block_depth, arg_type_bytes, &module.code.bytes, &mut self.value_stack, &mut self.program_counter, ); - std::mem::swap(&mut swap_frame, &mut self.current_frame); self.previous_frames.push(swap_frame); } @@ -636,7 +647,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.do_break(0, module); } END => { - if self.blocks.len() == self.current_frame.return_block_depth { + if self.blocks.len() == self.current_frame.function_block_depth { // implicit RETURN at end of function action = self.do_return(); implicit_return = true; @@ -1646,11 +1657,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { let base = self.current_frame.locals_start + self.current_frame.locals_count; let slice = self.value_stack.get_slice(base as usize); eprintln!("{:06x} {:17} {:?}", file_offset, debug_string, slice); - if op_code == RETURN || (op_code == END && implicit_return) { - let fn_index = pc_to_fn_index(self.program_counter, module); + let is_return = op_code == RETURN || (op_code == END && implicit_return); + let is_program_end = self.program_counter == 0; + if is_return && !is_program_end { eprintln!( - "returning to function {} at pc={:06x}\n", - fn_index, self.program_counter + "returning to function {} at {:06x}\n", + self.current_frame.fn_index, + self.program_counter + self.module.code.section_offset as usize ); } else if op_code == CALL || op_code == CALLINDIRECT { eprintln!(); diff --git a/crates/wasm_interp/src/lib.rs b/crates/wasm_interp/src/lib.rs index 990beda1ea..ef9e59cbdd 100644 --- a/crates/wasm_interp/src/lib.rs +++ b/crates/wasm_interp/src/lib.rs @@ -9,7 +9,7 @@ pub use instance::Instance; pub use wasi::{WasiDispatcher, WasiFile}; pub use roc_wasm_module::Value; -use roc_wasm_module::{ValueType, WasmModule}; +use roc_wasm_module::ValueType; pub trait ImportDispatcher { /// Dispatch a call from WebAssembly to your own code, based on module and function name. @@ -100,22 +100,3 @@ impl From<(ValueType, ValueType)> for Error { Error::ValueStackType(expected, actual) } } - -// Determine which function the program counter is in -pub(crate) fn pc_to_fn_index(program_counter: usize, module: &WasmModule<'_>) -> usize { - if module.code.function_offsets.is_empty() { - 0 - } else { - // Find the first function that starts *after* the given program counter - let next_internal_fn_index = module - .code - .function_offsets - .iter() - .position(|o| *o as usize > program_counter) - .unwrap_or(module.code.function_offsets.len()); - // Go back 1 - let internal_fn_index = next_internal_fn_index - 1; - // Adjust for imports, whose indices come before the code section - module.import.imports.len() + internal_fn_index - } -} diff --git a/crates/wasm_interp/src/tests/mod.rs b/crates/wasm_interp/src/tests/mod.rs index 1563d2f73e..a78e4031cc 100644 --- a/crates/wasm_interp/src/tests/mod.rs +++ b/crates/wasm_interp/src/tests/mod.rs @@ -11,7 +11,8 @@ mod test_mem; use crate::{DefaultImportDispatcher, Instance}; use bumpalo::{collections::Vec, Bump}; use roc_wasm_module::{ - opcodes::OpCode, Export, ExportType, SerialBuffer, Signature, Value, ValueType, WasmModule, + opcodes::OpCode, Export, ExportType, SerialBuffer, Serialize, Signature, Value, ValueType, + WasmModule, }; pub fn default_state(arena: &Bump) -> Instance { @@ -126,3 +127,32 @@ pub fn create_exported_function_no_locals<'a, F>( module.code.function_count += 1; module.code.function_offsets.push(offset as u32); } + +pub fn create_exported_function_with_locals<'a, F>( + module: &mut WasmModule<'a>, + name: &'a str, + signature: Signature<'a>, + local_types: &[(u32, ValueType)], + write_instructions: F, +) where + F: FnOnce(&mut Vec<'a, u8>), +{ + let internal_fn_index = module.code.function_offsets.len(); + let fn_index = module.import.function_count() + internal_fn_index; + module.export.exports.push(Export { + name, + ty: ExportType::Func, + index: fn_index as u32, + }); + module.add_function_signature(signature); + + let offset = module.code.bytes.encode_padded_u32(0); + let start = module.code.bytes.len(); + local_types.serialize(&mut module.code.bytes); + write_instructions(&mut module.code.bytes); + let len = module.code.bytes.len() - start; + module.code.bytes.overwrite_padded_u32(offset, len as u32); + + module.code.function_count += 1; + module.code.function_offsets.push(offset as u32); +} diff --git a/crates/wasm_interp/src/tests/test_basics.rs b/crates/wasm_interp/src/tests/test_basics.rs index 1188d0e318..d2d3363cb2 100644 --- a/crates/wasm_interp/src/tests/test_basics.rs +++ b/crates/wasm_interp/src/tests/test_basics.rs @@ -1,8 +1,11 @@ #![cfg(test)] -use super::{const_value, create_exported_function_no_locals, default_state}; use crate::frame::Frame; -use crate::{instance::Action, DefaultImportDispatcher, ImportDispatcher, Instance}; +use crate::tests::{ + const_value, create_exported_function_no_locals, create_exported_function_with_locals, + default_state, +}; +use crate::{DefaultImportDispatcher, ImportDispatcher, Instance}; use bumpalo::{collections::Vec, Bump}; use roc_wasm_module::sections::{Import, ImportDesc}; use roc_wasm_module::{ @@ -119,163 +122,157 @@ fn test_if_else() { fn test_if_else_help(condition: i32, expected: i32) { let arena = Bump::new(); let mut module = WasmModule::new(&arena); - let buf = &mut module.code.bytes; - buf.push(1); // one group of the given type - buf.push(1); // one local in the group - buf.push(ValueType::I32 as u8); + let signature = Signature { + param_types: bumpalo::vec![in &arena], + ret_type: Some(ValueType::I32), + }; + let local_types = [(1, ValueType::I32)]; + create_exported_function_with_locals(&mut module, "test", signature, &local_types, |buf| { + // i32.const + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(condition); - // i32.const - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(condition); + // if + buf.push(OpCode::IF as u8); + buf.push(ValueType::VOID as u8); - // if - buf.push(OpCode::IF as u8); - buf.push(ValueType::VOID as u8); + // i32.const 111 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(111); - // i32.const 111 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(111); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // else + buf.push(OpCode::ELSE as u8); - // else - buf.push(OpCode::ELSE as u8); + // i32.const 222 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(222); - // i32.const 222 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(222); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // local.get 0 + buf.push(OpCode::GETLOCAL as u8); + buf.encode_u32(0); - // local.get 0 - buf.push(OpCode::GETLOCAL as u8); - buf.encode_u32(0); + // end function + buf.push(OpCode::END as u8); + }); - // end function - buf.push(OpCode::END as u8); + let is_debug_mode = false; + let mut inst = Instance::for_module( + &arena, + &module, + DefaultImportDispatcher::default(), + is_debug_mode, + ) + .unwrap(); + let result = inst.call_export("test", []).unwrap().unwrap(); - let mut state = default_state(&arena); - let fn_index = 0; - let return_addr = 0x1234; - let return_block_depth = 0; - let arg_type_bytes = &[]; - let frame = Frame::enter( - fn_index, - return_addr, - return_block_depth, - arg_type_bytes, - &buf, - &mut state.value_stack, - &mut state.program_counter, - ); - state.current_frame = frame; - - while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} - - assert_eq!(state.value_stack.pop_i32(), Ok(expected)); + assert_eq!(result, Value::I32(expected)); } #[test] fn test_br() { + let start_fn_name = "test"; let arena = Bump::new(); - let mut state = default_state(&arena); let mut module = WasmModule::new(&arena); - let buf = &mut module.code.bytes; - // (local i32) - buf.encode_u32(1); - buf.encode_u32(1); - buf.push(ValueType::I32 as u8); + let signature = Signature { + param_types: bumpalo::vec![in &arena], + ret_type: Some(ValueType::I32), + }; + let local_types = [(1, ValueType::I32)]; + create_exported_function_with_locals( + &mut module, + start_fn_name, + signature, + &local_types, + |buf| { + // i32.const 111 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(111); - // i32.const 111 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(111); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // block ;; label = @1 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @1 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @2 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @2 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @3 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @3 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // br 2 (;@1;) + buf.push(OpCode::BR as u8); + buf.encode_u32(2); - // br 2 (;@1;) - buf.push(OpCode::BR as u8); - buf.encode_u32(2); + // i32.const 444 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(444); - // i32.const 444 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(444); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 333 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(333); - // i32.const 333 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(333); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 222 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(222); - // i32.const 222 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(222); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // local.get 0) + buf.push(OpCode::GETLOCAL as u8); + buf.encode_u32(0); - // local.get 0) - buf.push(OpCode::GETLOCAL as u8); - buf.encode_u32(0); - - buf.push(OpCode::END as u8); - - let fn_index = 0; - let return_addr = 0x1234; - let return_block_depth = 0; - let arg_type_bytes = &[]; - let frame = Frame::enter( - fn_index, - return_addr, - return_block_depth, - arg_type_bytes, - &buf, - &mut state.value_stack, - &mut state.program_counter, + buf.push(OpCode::END as u8); + }, ); - state.current_frame = frame; - while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} + let is_debug_mode = false; + let mut inst = Instance::for_module( + &arena, + &module, + DefaultImportDispatcher::default(), + is_debug_mode, + ) + .unwrap(); + let result = inst.call_export(start_fn_name, []).unwrap().unwrap(); - assert_eq!(state.value_stack.pop(), Value::I32(111)) + assert_eq!(result, Value::I32(111)) } #[test] @@ -285,101 +282,101 @@ fn test_br_if() { } fn test_br_if_help(condition: i32, expected: i32) { + let start_fn_name = "test"; let arena = Bump::new(); - let mut state = default_state(&arena); let mut module = WasmModule::new(&arena); - let buf = &mut module.code.bytes; - // (local i32) - buf.encode_u32(1); - buf.encode_u32(1); - buf.push(ValueType::I32 as u8); + let signature = Signature { + param_types: bumpalo::vec![in &arena], + ret_type: Some(ValueType::I32), + }; + let local_types = [(1, ValueType::I32)]; + create_exported_function_with_locals( + &mut module, + start_fn_name, + signature, + &local_types, + |buf| { + // i32.const 111 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(111); - // i32.const 111 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(111); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // block ;; label = @1 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @1 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @2 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @2 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @3 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @3 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // i32.const + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(condition); - // i32.const - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(condition); + // br_if 2 (;@1;) + buf.push(OpCode::BRIF as u8); + buf.encode_u32(2); - // br_if 2 (;@1;) - buf.push(OpCode::BRIF as u8); - buf.encode_u32(2); + // i32.const 444 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(444); - // i32.const 444 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(444); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 333 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(333); - // i32.const 333 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(333); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 222 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(222); - // i32.const 222 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(222); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // local.get 0) + buf.push(OpCode::GETLOCAL as u8); + buf.encode_u32(0); - // local.get 0) - buf.push(OpCode::GETLOCAL as u8); - buf.encode_u32(0); - - buf.push(OpCode::END as u8); - - let fn_index = 0; - let return_addr = 0x1234; - let return_block_depth = 0; - let arg_type_bytes = &[]; - let frame = Frame::enter( - fn_index, - return_addr, - return_block_depth, - arg_type_bytes, - &buf, - &mut state.value_stack, - &mut state.program_counter, + buf.push(OpCode::END as u8); + }, ); - state.current_frame = frame; - while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} + let is_debug_mode = true; + let mut inst = Instance::for_module( + &arena, + &module, + DefaultImportDispatcher::default(), + is_debug_mode, + ) + .unwrap(); + let result = inst.call_export(start_fn_name, []).unwrap().unwrap(); - assert_eq!(state.value_stack.pop(), Value::I32(expected)) + assert_eq!(result, Value::I32(expected)) } #[test] @@ -390,106 +387,104 @@ fn test_br_table() { } fn test_br_table_help(condition: i32, expected: i32) { + let start_fn_name = "test"; let arena = Bump::new(); - let mut state = default_state(&arena); let mut module = WasmModule::new(&arena); - let buf = &mut module.code.bytes; - // (local i32) - buf.encode_u32(1); - buf.encode_u32(1); - buf.push(ValueType::I32 as u8); + let signature = Signature { + param_types: bumpalo::vec![in &arena], + ret_type: Some(ValueType::I32), + }; + let local_types = [(1, ValueType::I32)]; + create_exported_function_with_locals( + &mut module, + start_fn_name, + signature, + &local_types, + |buf| { + // i32.const 111 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(111); - // i32.const 111 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(111); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // block ;; label = @1 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @1 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @2 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @2 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // block ;; label = @3 + buf.push(OpCode::BLOCK as u8); + buf.push(ValueType::VOID); - // block ;; label = @3 - buf.push(OpCode::BLOCK as u8); - buf.push(ValueType::VOID); + // i32.const + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(condition); - // i32.const - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(condition); + // br_table 0 1 2 (;@1;) + buf.push(OpCode::BRTABLE as u8); + buf.encode_u32(2); // number of non-fallback branches + buf.encode_u32(0); + buf.encode_u32(1); + buf.encode_u32(2); - // br_table 0 1 2 (;@1;) - buf.push(OpCode::BRTABLE as u8); - buf.encode_u32(2); // number of non-fallback branches - buf.encode_u32(0); - buf.encode_u32(1); - buf.encode_u32(2); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 333 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(333); - // i32.const 333 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(333); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // br 1 + buf.push(OpCode::BR as u8); + buf.encode_u32(1); - // br 1 - buf.push(OpCode::BR as u8); - buf.encode_u32(1); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // i32.const 222 + buf.push(OpCode::I32CONST as u8); + buf.encode_i32(222); - // i32.const 222 - buf.push(OpCode::I32CONST as u8); - buf.encode_i32(222); + // local.set 0 + buf.push(OpCode::SETLOCAL as u8); + buf.encode_u32(0); - // local.set 0 - buf.push(OpCode::SETLOCAL as u8); - buf.encode_u32(0); + // br 0 + buf.push(OpCode::BR as u8); + buf.encode_u32(0); - // br 0 - buf.push(OpCode::BR as u8); - buf.encode_u32(0); + // end + buf.push(OpCode::END as u8); - // end - buf.push(OpCode::END as u8); + // local.get 0) + buf.push(OpCode::GETLOCAL as u8); + buf.encode_u32(0); - // local.get 0) - buf.push(OpCode::GETLOCAL as u8); - buf.encode_u32(0); - - buf.push(OpCode::END as u8); - - println!("{:02x?}", buf); - - let fn_index = 0; - let return_addr = 0x1234; - let return_block_depth = 0; - let arg_type_bytes = &[]; - let frame = Frame::enter( - fn_index, - return_addr, - return_block_depth, - arg_type_bytes, - &buf, - &mut state.value_stack, - &mut state.program_counter, + buf.push(OpCode::END as u8); + }, ); - state.current_frame = frame; - while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} + let is_debug_mode = false; + let mut inst = Instance::for_module( + &arena, + &module, + DefaultImportDispatcher::default(), + is_debug_mode, + ) + .unwrap(); + let result = inst.call_export(start_fn_name, []).unwrap().unwrap(); - assert_eq!(state.value_stack.pop(), Value::I32(expected)) + assert_eq!(result, Value::I32(expected)) } struct TestDispatcher { @@ -650,28 +645,22 @@ fn test_call_return_no_args() { #[test] fn test_call_return_with_args() { let arena = Bump::new(); - let mut state = default_state(&arena); let mut module = WasmModule::new(&arena); // Function 0: calculate 2+2 - let func0_offset = module.code.bytes.len() as u32; - module.code.function_offsets.push(func0_offset); - module.add_function_signature(Signature { - param_types: bumpalo::vec![in &arena;], + let signature0 = Signature { + param_types: bumpalo::vec![in &arena], ret_type: Some(ValueType::I32), + }; + create_exported_function_no_locals(&mut module, "two_plus_two", signature0, |buf| { + buf.push(OpCode::I32CONST as u8); + buf.push(2); + buf.push(OpCode::I32CONST as u8); + buf.push(2); + buf.push(OpCode::CALL as u8); + buf.push(1); + buf.push(OpCode::END as u8); }); - [ - 0, // no locals - OpCode::I32CONST as u8, - 2, - OpCode::I32CONST as u8, - 2, - OpCode::CALL as u8, - 1, - OpCode::END as u8, - ] - .serialize(&mut module.code.bytes); - let func0_first_instruction = func0_offset + 2; // skip function length and locals length // Function 1: add two numbers let func1_offset = module.code.bytes.len() as u32; @@ -691,11 +680,24 @@ fn test_call_return_with_args() { ] .serialize(&mut module.code.bytes); - state.program_counter = func0_first_instruction as usize; + let signature0 = Signature { + param_types: bumpalo::vec![in &arena; ValueType::I32, ValueType::I32], + ret_type: Some(ValueType::I32), + }; + create_exported_function_no_locals(&mut module, "add", signature0, |buf| { + buf.push(OpCode::GETLOCAL as u8); + buf.push(0); + buf.push(OpCode::GETLOCAL as u8); + buf.push(1); + buf.push(OpCode::I32ADD as u8); + buf.push(OpCode::END as u8); + }); - while let Ok(Action::Continue) = state.execute_next_instruction(&module) {} + let mut inst = + Instance::for_module(&arena, &module, DefaultImportDispatcher::default(), false).unwrap(); + let result = inst.call_export("two_plus_two", []).unwrap().unwrap(); - assert_eq!(state.value_stack.peek(), Value::I32(4)); + assert_eq!(result, Value::I32(4)); } #[test] @@ -769,11 +771,9 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value { if false { let mut outfile_buf = Vec::new_in(&arena); module.serialize(&mut outfile_buf); - std::fs::write( - format!("/tmp/roc/call_indirect_{}_{}.wasm", table_index, elem_index), - outfile_buf, - ) - .unwrap(); + let filename = format!("/tmp/roc/call_indirect_{}_{}.wasm", table_index, elem_index); + std::fs::write(&filename, outfile_buf).unwrap(); + println!("\nWrote to {}\n", filename); } let mut inst = Instance::for_module( @@ -798,36 +798,25 @@ fn test_select() { fn test_select_help(first: Value, second: Value, condition: i32, expected: Value) { let arena = Bump::new(); let mut module = WasmModule::new(&arena); - let buf = &mut module.code.bytes; - buf.push(0); // no locals + // Function 0: calculate 2+2 + let signature0 = Signature { + param_types: bumpalo::vec![in &arena], + ret_type: Some(ValueType::from(expected)), + }; + create_exported_function_no_locals(&mut module, "test", signature0, |buf| { + const_value(buf, first); + const_value(buf, second); + const_value(buf, Value::I32(condition)); + buf.push(OpCode::SELECT as u8); + buf.push(OpCode::END as u8); + }); - const_value(buf, first); - const_value(buf, second); - const_value(buf, Value::I32(condition)); - buf.push(OpCode::SELECT as u8); - buf.push(OpCode::END as u8); + let mut inst = + Instance::for_module(&arena, &module, DefaultImportDispatcher::default(), false).unwrap(); + let result = inst.call_export("test", []).unwrap().unwrap(); - let mut inst = default_state(&arena); - - let fn_index = 0; - let return_addr = 0x1234; - let return_block_depth = 0; - let arg_type_bytes = &[]; - let frame = Frame::enter( - fn_index, - return_addr, - return_block_depth, - arg_type_bytes, - &buf, - &mut inst.value_stack, - &mut inst.program_counter, - ); - inst.current_frame = frame; - - while let Ok(Action::Continue) = inst.execute_next_instruction(&module) {} - - assert_eq!(inst.value_stack.pop(), expected); + assert_eq!(result, expected); } #[test] diff --git a/crates/wasm_interp/src/tests/test_mem.rs b/crates/wasm_interp/src/tests/test_mem.rs index 66ccc3f559..1f08dce8ff 100644 --- a/crates/wasm_interp/src/tests/test_mem.rs +++ b/crates/wasm_interp/src/tests/test_mem.rs @@ -249,7 +249,6 @@ fn test_store<'a>( offset: u32, value: Value, ) -> Vec<'a, u8> { - let is_debug_mode = false; let start_fn_name = "test"; module.memory = MemorySection::new(arena, MemorySection::PAGE_SIZE); @@ -286,6 +285,7 @@ fn test_store<'a>( buf.append_u8(OpCode::END as u8); }); + let is_debug_mode = false; let mut inst = Instance::for_module( arena, module,