mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
wasm_interp: create a block for each function and get tests working again
This commit is contained in:
parent
caedb9060b
commit
d51beb073f
@ -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],
|
||||
|
@ -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<Option<Value>, 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!();
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<DefaultImportDispatcher> {
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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 <condition>
|
||||
buf.push(OpCode::I32CONST as u8);
|
||||
buf.encode_i32(condition);
|
||||
|
||||
// i32.const <condition>
|
||||
buf.push(OpCode::I32CONST as u8);
|
||||
buf.encode_i32(condition);
|
||||
// if <blocktype>
|
||||
buf.push(OpCode::IF as u8);
|
||||
buf.push(ValueType::VOID as u8);
|
||||
|
||||
// if <blocktype>
|
||||
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 <condition>
|
||||
buf.push(OpCode::I32CONST as u8);
|
||||
buf.encode_i32(condition);
|
||||
|
||||
// i32.const <condition>
|
||||
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 <condition>
|
||||
buf.push(OpCode::I32CONST as u8);
|
||||
buf.encode_i32(condition);
|
||||
|
||||
// i32.const <condition>
|
||||
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]
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user