mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
wasm_interp: create a block for each function and get tests working again
This commit is contained in:
parent
caedb9060b
commit
d51beb073f
6 changed files with 390 additions and 376 deletions
|
@ -10,11 +10,11 @@ pub struct Frame {
|
||||||
pub fn_index: usize,
|
pub fn_index: usize,
|
||||||
/// Address in the code section where this frame returns to
|
/// Address in the code section where this frame returns to
|
||||||
pub return_addr: usize,
|
pub return_addr: usize,
|
||||||
/// Number of block scopes when this frame returns
|
/// Depth of the "function block" for this frame
|
||||||
pub return_block_depth: usize,
|
pub function_block_depth: usize,
|
||||||
/// Offset in the ValueStack where the locals begin
|
/// Offset in the ValueStack where the args & locals begin
|
||||||
pub locals_start: usize,
|
pub locals_start: usize,
|
||||||
/// Number of locals in the frame
|
/// Number of args & locals in the frame
|
||||||
pub locals_count: usize,
|
pub locals_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ impl Frame {
|
||||||
Frame {
|
Frame {
|
||||||
fn_index: 0,
|
fn_index: 0,
|
||||||
return_addr: 0,
|
return_addr: 0,
|
||||||
return_block_depth: 0,
|
function_block_depth: 0,
|
||||||
locals_start: 0,
|
locals_start: 0,
|
||||||
locals_count: 0,
|
locals_count: 0,
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ impl Frame {
|
||||||
pub fn enter(
|
pub fn enter(
|
||||||
fn_index: usize,
|
fn_index: usize,
|
||||||
return_addr: usize,
|
return_addr: usize,
|
||||||
return_block_depth: usize,
|
function_block_depth: usize,
|
||||||
arg_type_bytes: &[u8],
|
arg_type_bytes: &[u8],
|
||||||
code_bytes: &[u8],
|
code_bytes: &[u8],
|
||||||
value_stack: &mut ValueStack<'_>,
|
value_stack: &mut ValueStack<'_>,
|
||||||
|
@ -60,7 +60,7 @@ impl Frame {
|
||||||
Frame {
|
Frame {
|
||||||
fn_index,
|
fn_index,
|
||||||
return_addr,
|
return_addr,
|
||||||
return_block_depth,
|
function_block_depth,
|
||||||
locals_start,
|
locals_start,
|
||||||
locals_count,
|
locals_count,
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ impl Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn write_stack_trace(
|
pub fn write_stack_trace(
|
||||||
_current_frame: &Frame,
|
_current_frame: &Frame,
|
||||||
_previous_frames: &[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::{ExportType, WasmModule};
|
||||||
use roc_wasm_module::{Value, ValueType};
|
use roc_wasm_module::{Value, ValueType};
|
||||||
|
|
||||||
use crate::frame::{write_stack_trace, Frame};
|
use crate::frame::Frame;
|
||||||
use crate::value_stack::ValueStack;
|
use crate::value_stack::ValueStack;
|
||||||
use crate::{pc_to_fn_index, Error, ImportDispatcher};
|
use crate::{Error, ImportDispatcher};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
@ -295,10 +295,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
) -> Result<Option<Value>, String> {
|
) -> Result<Option<Value>, String> {
|
||||||
self.previous_frames.clear();
|
self.previous_frames.clear();
|
||||||
self.blocks.clear();
|
self.blocks.clear();
|
||||||
|
self.blocks.push(Block::Normal {
|
||||||
|
vstack: self.value_stack.depth(),
|
||||||
|
});
|
||||||
self.current_frame = Frame::enter(
|
self.current_frame = Frame::enter(
|
||||||
fn_index,
|
fn_index,
|
||||||
0, // return_addr
|
0, // return_addr
|
||||||
0, // return_block_depth
|
self.blocks.len(),
|
||||||
arg_type_bytes,
|
arg_type_bytes,
|
||||||
&module.code.bytes,
|
&module.code.bytes,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
|
@ -313,16 +316,16 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let file_offset = self.program_counter + module.code.section_offset as usize;
|
let file_offset = self.program_counter + module.code.section_offset as usize;
|
||||||
let mut message = e.to_string_at(file_offset);
|
let message = e.to_string_at(file_offset);
|
||||||
write_stack_trace(
|
// write_stack_trace(
|
||||||
&self.current_frame,
|
// &self.current_frame,
|
||||||
&self.previous_frames,
|
// &self.previous_frames,
|
||||||
self.module,
|
// self.module,
|
||||||
&self.value_stack,
|
// &self.value_stack,
|
||||||
self.program_counter,
|
// self.program_counter,
|
||||||
&mut message,
|
// &mut message,
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
return Err(message);
|
return Err(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -348,7 +351,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
fn do_return(&mut self) -> Action {
|
fn do_return(&mut self) -> Action {
|
||||||
let Frame {
|
let Frame {
|
||||||
return_addr,
|
return_addr,
|
||||||
return_block_depth,
|
function_block_depth,
|
||||||
locals_start,
|
locals_start,
|
||||||
..
|
..
|
||||||
} = self.current_frame;
|
} = self.current_frame;
|
||||||
|
@ -371,7 +374,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
self.value_stack.push(val);
|
self.value_stack.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.blocks.truncate(return_block_depth);
|
self.blocks.truncate(function_block_depth - 1);
|
||||||
self.program_counter = return_addr;
|
self.program_counter = return_addr;
|
||||||
|
|
||||||
if let Some(caller_frame) = self.previous_frames.pop() {
|
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()));
|
.extend(std::iter::repeat(Value::I64(0)).take(arg_type_bytes.len()));
|
||||||
for (i, type_byte) in arg_type_bytes.iter().copied().enumerate().rev() {
|
for (i, type_byte) in arg_type_bytes.iter().copied().enumerate().rev() {
|
||||||
let arg = self.value_stack.pop();
|
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;
|
self.import_arguments[i] = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,24 +529,26 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let return_addr = self.program_counter;
|
let return_addr = self.program_counter;
|
||||||
let return_block_depth = self.blocks.len();
|
|
||||||
|
|
||||||
// set PC to start of function bytes
|
// set PC to start of function bytes
|
||||||
let internal_fn_index = fn_index - self.import_count;
|
let internal_fn_index = fn_index - self.import_count;
|
||||||
self.program_counter = module.code.function_offsets[internal_fn_index] as usize;
|
self.program_counter = module.code.function_offsets[internal_fn_index] as usize;
|
||||||
// advance PC to the start of the local variable declarations
|
// advance PC to the start of the local variable declarations
|
||||||
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
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(
|
let mut swap_frame = Frame::enter(
|
||||||
fn_index,
|
fn_index,
|
||||||
return_addr,
|
return_addr,
|
||||||
return_block_depth,
|
function_block_depth,
|
||||||
arg_type_bytes,
|
arg_type_bytes,
|
||||||
&module.code.bytes,
|
&module.code.bytes,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
&mut self.program_counter,
|
&mut self.program_counter,
|
||||||
);
|
);
|
||||||
|
|
||||||
std::mem::swap(&mut swap_frame, &mut self.current_frame);
|
std::mem::swap(&mut swap_frame, &mut self.current_frame);
|
||||||
self.previous_frames.push(swap_frame);
|
self.previous_frames.push(swap_frame);
|
||||||
}
|
}
|
||||||
|
@ -636,7 +647,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
self.do_break(0, module);
|
self.do_break(0, module);
|
||||||
}
|
}
|
||||||
END => {
|
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
|
// implicit RETURN at end of function
|
||||||
action = self.do_return();
|
action = self.do_return();
|
||||||
implicit_return = true;
|
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 base = self.current_frame.locals_start + self.current_frame.locals_count;
|
||||||
let slice = self.value_stack.get_slice(base as usize);
|
let slice = self.value_stack.get_slice(base as usize);
|
||||||
eprintln!("{:06x} {:17} {:?}", file_offset, debug_string, slice);
|
eprintln!("{:06x} {:17} {:?}", file_offset, debug_string, slice);
|
||||||
if op_code == RETURN || (op_code == END && implicit_return) {
|
let is_return = op_code == RETURN || (op_code == END && implicit_return);
|
||||||
let fn_index = pc_to_fn_index(self.program_counter, module);
|
let is_program_end = self.program_counter == 0;
|
||||||
|
if is_return && !is_program_end {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"returning to function {} at pc={:06x}\n",
|
"returning to function {} at {:06x}\n",
|
||||||
fn_index, self.program_counter
|
self.current_frame.fn_index,
|
||||||
|
self.program_counter + self.module.code.section_offset as usize
|
||||||
);
|
);
|
||||||
} else if op_code == CALL || op_code == CALLINDIRECT {
|
} else if op_code == CALL || op_code == CALLINDIRECT {
|
||||||
eprintln!();
|
eprintln!();
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub use instance::Instance;
|
||||||
pub use wasi::{WasiDispatcher, WasiFile};
|
pub use wasi::{WasiDispatcher, WasiFile};
|
||||||
|
|
||||||
pub use roc_wasm_module::Value;
|
pub use roc_wasm_module::Value;
|
||||||
use roc_wasm_module::{ValueType, WasmModule};
|
use roc_wasm_module::ValueType;
|
||||||
|
|
||||||
pub trait ImportDispatcher {
|
pub trait ImportDispatcher {
|
||||||
/// Dispatch a call from WebAssembly to your own code, based on module and function name.
|
/// 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)
|
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 crate::{DefaultImportDispatcher, Instance};
|
||||||
use bumpalo::{collections::Vec, Bump};
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use roc_wasm_module::{
|
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> {
|
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_count += 1;
|
||||||
module.code.function_offsets.push(offset as u32);
|
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)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use super::{const_value, create_exported_function_no_locals, default_state};
|
|
||||||
use crate::frame::Frame;
|
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 bumpalo::{collections::Vec, Bump};
|
||||||
use roc_wasm_module::sections::{Import, ImportDesc};
|
use roc_wasm_module::sections::{Import, ImportDesc};
|
||||||
use roc_wasm_module::{
|
use roc_wasm_module::{
|
||||||
|
@ -119,163 +122,157 @@ fn test_if_else() {
|
||||||
fn test_if_else_help(condition: i32, expected: i32) {
|
fn test_if_else_help(condition: i32, expected: i32) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut module = WasmModule::new(&arena);
|
let mut module = WasmModule::new(&arena);
|
||||||
let buf = &mut module.code.bytes;
|
|
||||||
|
|
||||||
buf.push(1); // one group of the given type
|
let signature = Signature {
|
||||||
buf.push(1); // one local in the group
|
param_types: bumpalo::vec![in &arena],
|
||||||
buf.push(ValueType::I32 as u8);
|
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>
|
// if <blocktype>
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::IF as u8);
|
||||||
buf.encode_i32(condition);
|
buf.push(ValueType::VOID as u8);
|
||||||
|
|
||||||
// if <blocktype>
|
// i32.const 111
|
||||||
buf.push(OpCode::IF as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
buf.push(ValueType::VOID as u8);
|
buf.encode_i32(111);
|
||||||
|
|
||||||
// i32.const 111
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(111);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// else
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::ELSE as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// else
|
// i32.const 222
|
||||||
buf.push(OpCode::ELSE as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(222);
|
||||||
|
|
||||||
// i32.const 222
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(222);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// local.get 0
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::GETLOCAL as u8);
|
||||||
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.get 0
|
// end function
|
||||||
buf.push(OpCode::GETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
});
|
||||||
|
|
||||||
// end function
|
let is_debug_mode = false;
|
||||||
buf.push(OpCode::END as u8);
|
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);
|
assert_eq!(result, Value::I32(expected));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_br() {
|
fn test_br() {
|
||||||
|
let start_fn_name = "test";
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut state = default_state(&arena);
|
|
||||||
let mut module = WasmModule::new(&arena);
|
let mut module = WasmModule::new(&arena);
|
||||||
let buf = &mut module.code.bytes;
|
|
||||||
|
|
||||||
// (local i32)
|
let signature = Signature {
|
||||||
buf.encode_u32(1);
|
param_types: bumpalo::vec![in &arena],
|
||||||
buf.encode_u32(1);
|
ret_type: Some(ValueType::I32),
|
||||||
buf.push(ValueType::I32 as u8);
|
};
|
||||||
|
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
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(111);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// block ;; label = @1
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.encode_u32(0);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @1
|
// block ;; label = @2
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @2
|
// block ;; label = @3
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @3
|
// br 2 (;@1;)
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BR as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.encode_u32(2);
|
||||||
|
|
||||||
// br 2 (;@1;)
|
// i32.const 444
|
||||||
buf.push(OpCode::BR as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
buf.encode_u32(2);
|
buf.encode_i32(444);
|
||||||
|
|
||||||
// i32.const 444
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(444);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// i32.const 333
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(333);
|
||||||
|
|
||||||
// i32.const 333
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(333);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// i32.const 222
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(222);
|
||||||
|
|
||||||
// i32.const 222
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(222);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// local.get 0)
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::GETLOCAL as u8);
|
||||||
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.get 0)
|
buf.push(OpCode::END as u8);
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
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]
|
#[test]
|
||||||
|
@ -285,101 +282,101 @@ fn test_br_if() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_br_if_help(condition: i32, expected: i32) {
|
fn test_br_if_help(condition: i32, expected: i32) {
|
||||||
|
let start_fn_name = "test";
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut state = default_state(&arena);
|
|
||||||
let mut module = WasmModule::new(&arena);
|
let mut module = WasmModule::new(&arena);
|
||||||
let buf = &mut module.code.bytes;
|
|
||||||
|
|
||||||
// (local i32)
|
let signature = Signature {
|
||||||
buf.encode_u32(1);
|
param_types: bumpalo::vec![in &arena],
|
||||||
buf.encode_u32(1);
|
ret_type: Some(ValueType::I32),
|
||||||
buf.push(ValueType::I32 as u8);
|
};
|
||||||
|
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
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(111);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// block ;; label = @1
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.encode_u32(0);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @1
|
// block ;; label = @2
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @2
|
// block ;; label = @3
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @3
|
// i32.const <condition>
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.encode_i32(condition);
|
||||||
|
|
||||||
// i32.const <condition>
|
// br_if 2 (;@1;)
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::BRIF as u8);
|
||||||
buf.encode_i32(condition);
|
buf.encode_u32(2);
|
||||||
|
|
||||||
// br_if 2 (;@1;)
|
// i32.const 444
|
||||||
buf.push(OpCode::BRIF as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
buf.encode_u32(2);
|
buf.encode_i32(444);
|
||||||
|
|
||||||
// i32.const 444
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(444);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// i32.const 333
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(333);
|
||||||
|
|
||||||
// i32.const 333
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(333);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// i32.const 222
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(222);
|
||||||
|
|
||||||
// i32.const 222
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(222);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// end
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// local.get 0)
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::GETLOCAL as u8);
|
||||||
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.get 0)
|
buf.push(OpCode::END as u8);
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
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]
|
#[test]
|
||||||
|
@ -390,106 +387,104 @@ fn test_br_table() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_br_table_help(condition: i32, expected: i32) {
|
fn test_br_table_help(condition: i32, expected: i32) {
|
||||||
|
let start_fn_name = "test";
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut state = default_state(&arena);
|
|
||||||
let mut module = WasmModule::new(&arena);
|
let mut module = WasmModule::new(&arena);
|
||||||
let buf = &mut module.code.bytes;
|
|
||||||
|
|
||||||
// (local i32)
|
let signature = Signature {
|
||||||
buf.encode_u32(1);
|
param_types: bumpalo::vec![in &arena],
|
||||||
buf.encode_u32(1);
|
ret_type: Some(ValueType::I32),
|
||||||
buf.push(ValueType::I32 as u8);
|
};
|
||||||
|
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
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(111);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// block ;; label = @1
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.encode_u32(0);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @1
|
// block ;; label = @2
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @2
|
// block ;; label = @3
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::BLOCK as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.push(ValueType::VOID);
|
||||||
|
|
||||||
// block ;; label = @3
|
// i32.const <condition>
|
||||||
buf.push(OpCode::BLOCK as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
buf.push(ValueType::VOID);
|
buf.encode_i32(condition);
|
||||||
|
|
||||||
// i32.const <condition>
|
// br_table 0 1 2 (;@1;)
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::BRTABLE as u8);
|
||||||
buf.encode_i32(condition);
|
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;)
|
// end
|
||||||
buf.push(OpCode::BRTABLE as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(2); // number of non-fallback branches
|
|
||||||
buf.encode_u32(0);
|
|
||||||
buf.encode_u32(1);
|
|
||||||
buf.encode_u32(2);
|
|
||||||
|
|
||||||
// end
|
// i32.const 333
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(333);
|
||||||
|
|
||||||
// i32.const 333
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(333);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// br 1
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::BR as u8);
|
||||||
buf.encode_u32(0);
|
buf.encode_u32(1);
|
||||||
|
|
||||||
// br 1
|
// end
|
||||||
buf.push(OpCode::BR as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(1);
|
|
||||||
|
|
||||||
// end
|
// i32.const 222
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::I32CONST as u8);
|
||||||
|
buf.encode_i32(222);
|
||||||
|
|
||||||
// i32.const 222
|
// local.set 0
|
||||||
buf.push(OpCode::I32CONST as u8);
|
buf.push(OpCode::SETLOCAL as u8);
|
||||||
buf.encode_i32(222);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.set 0
|
// br 0
|
||||||
buf.push(OpCode::SETLOCAL as u8);
|
buf.push(OpCode::BR as u8);
|
||||||
buf.encode_u32(0);
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// br 0
|
// end
|
||||||
buf.push(OpCode::BR as u8);
|
buf.push(OpCode::END as u8);
|
||||||
buf.encode_u32(0);
|
|
||||||
|
|
||||||
// end
|
// local.get 0)
|
||||||
buf.push(OpCode::END as u8);
|
buf.push(OpCode::GETLOCAL as u8);
|
||||||
|
buf.encode_u32(0);
|
||||||
|
|
||||||
// local.get 0)
|
buf.push(OpCode::END as u8);
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
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 {
|
struct TestDispatcher {
|
||||||
|
@ -650,28 +645,22 @@ fn test_call_return_no_args() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_call_return_with_args() {
|
fn test_call_return_with_args() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut state = default_state(&arena);
|
|
||||||
let mut module = WasmModule::new(&arena);
|
let mut module = WasmModule::new(&arena);
|
||||||
|
|
||||||
// Function 0: calculate 2+2
|
// Function 0: calculate 2+2
|
||||||
let func0_offset = module.code.bytes.len() as u32;
|
let signature0 = Signature {
|
||||||
module.code.function_offsets.push(func0_offset);
|
param_types: bumpalo::vec![in &arena],
|
||||||
module.add_function_signature(Signature {
|
|
||||||
param_types: bumpalo::vec![in &arena;],
|
|
||||||
ret_type: Some(ValueType::I32),
|
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
|
// Function 1: add two numbers
|
||||||
let func1_offset = module.code.bytes.len() as u32;
|
let func1_offset = module.code.bytes.len() as u32;
|
||||||
|
@ -691,11 +680,24 @@ fn test_call_return_with_args() {
|
||||||
]
|
]
|
||||||
.serialize(&mut module.code.bytes);
|
.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]
|
#[test]
|
||||||
|
@ -769,11 +771,9 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value {
|
||||||
if false {
|
if false {
|
||||||
let mut outfile_buf = Vec::new_in(&arena);
|
let mut outfile_buf = Vec::new_in(&arena);
|
||||||
module.serialize(&mut outfile_buf);
|
module.serialize(&mut outfile_buf);
|
||||||
std::fs::write(
|
let filename = format!("/tmp/roc/call_indirect_{}_{}.wasm", table_index, elem_index);
|
||||||
format!("/tmp/roc/call_indirect_{}_{}.wasm", table_index, elem_index),
|
std::fs::write(&filename, outfile_buf).unwrap();
|
||||||
outfile_buf,
|
println!("\nWrote to {}\n", filename);
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut inst = Instance::for_module(
|
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) {
|
fn test_select_help(first: Value, second: Value, condition: i32, expected: Value) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let mut module = WasmModule::new(&arena);
|
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);
|
let mut inst =
|
||||||
const_value(buf, second);
|
Instance::for_module(&arena, &module, DefaultImportDispatcher::default(), false).unwrap();
|
||||||
const_value(buf, Value::I32(condition));
|
let result = inst.call_export("test", []).unwrap().unwrap();
|
||||||
buf.push(OpCode::SELECT as u8);
|
|
||||||
buf.push(OpCode::END as u8);
|
|
||||||
|
|
||||||
let mut inst = default_state(&arena);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -249,7 +249,6 @@ fn test_store<'a>(
|
||||||
offset: u32,
|
offset: u32,
|
||||||
value: Value,
|
value: Value,
|
||||||
) -> Vec<'a, u8> {
|
) -> Vec<'a, u8> {
|
||||||
let is_debug_mode = false;
|
|
||||||
let start_fn_name = "test";
|
let start_fn_name = "test";
|
||||||
|
|
||||||
module.memory = MemorySection::new(arena, MemorySection::PAGE_SIZE);
|
module.memory = MemorySection::new(arena, MemorySection::PAGE_SIZE);
|
||||||
|
@ -286,6 +285,7 @@ fn test_store<'a>(
|
||||||
buf.append_u8(OpCode::END as u8);
|
buf.append_u8(OpCode::END as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_debug_mode = false;
|
||||||
let mut inst = Instance::for_module(
|
let mut inst = Instance::for_module(
|
||||||
arena,
|
arena,
|
||||||
module,
|
module,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue