wasm_interp: create a block for each function and get tests working again

This commit is contained in:
Brian Carroll 2022-12-16 23:58:53 +00:00
parent caedb9060b
commit d51beb073f
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
6 changed files with 390 additions and 376 deletions

View file

@ -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]