wasm_interp: Handle calls to imports

This commit is contained in:
Brian Carroll 2022-12-01 20:41:38 +00:00
parent b10ac827f1
commit c866ce6b09
No known key found for this signature in database
GPG Key ID: 5C7B2EC4101703C0
6 changed files with 103 additions and 34 deletions

View File

@ -10,6 +10,7 @@ use roc_wasm_module::{Value, ValueType};
use crate::call_stack::CallStack;
use crate::value_stack::ValueStack;
use crate::ImportDispatcher;
pub enum Action {
Continue,
@ -17,7 +18,7 @@ pub enum Action {
}
#[derive(Debug)]
pub struct Instance<'a> {
pub struct Instance<'a, I: ImportDispatcher> {
/// Contents of the WebAssembly instance's memory
pub memory: Vec<'a, u8>,
/// Metadata for every currently-active function call
@ -34,12 +35,22 @@ pub struct Instance<'a> {
outermost_block: u32,
/// Signature indices (in the TypeSection) of all imported (non-WebAssembly) functions
import_signatures: Vec<'a, u32>,
/// Import dispatcher from user code
import_dispatcher: I,
/// Temporary storage for import arguments
import_arguments: Vec<'a, Value>,
/// temporary storage for output using the --debug option
debug_string: Option<String>,
}
impl<'a> Instance<'a> {
pub fn new<G>(arena: &'a Bump, memory_pages: u32, program_counter: usize, globals: G) -> Self
impl<'a, I: ImportDispatcher> Instance<'a, I> {
pub fn new<G>(
arena: &'a Bump,
memory_pages: u32,
program_counter: usize,
globals: G,
import_dispatcher: I,
) -> Self
where
G: IntoIterator<Item = Value>,
{
@ -53,6 +64,8 @@ impl<'a> Instance<'a> {
block_loop_addrs: Vec::new_in(arena),
outermost_block: 0,
import_signatures: Vec::new_in(arena),
import_dispatcher,
import_arguments: Vec::new_in(arena),
debug_string: Some(String::new()),
}
}
@ -60,6 +73,7 @@ impl<'a> Instance<'a> {
pub fn for_module(
arena: &'a Bump,
module: &WasmModule<'a>,
import_dispatcher: I,
is_debug_mode: bool,
) -> Result<Self, std::string::String> {
let mem_bytes = module.memory.min_bytes().map_err(|e| {
@ -101,6 +115,8 @@ impl<'a> Instance<'a> {
block_loop_addrs: Vec::new_in(arena),
outermost_block: 0,
import_signatures,
import_dispatcher,
import_arguments: Vec::new_in(arena),
debug_string,
})
}
@ -336,22 +352,51 @@ impl<'a> Instance<'a> {
let arg_type_bytes = module.types.look_up_arg_type_bytes(signature_index);
let return_addr = self.program_counter as u32;
self.program_counter = module.code.function_offsets[fn_index] as usize;
if fn_index < n_imports {
// Call an imported non-Wasm function
let mut import_fns = module.import.imports.iter().filter(|imp| imp.is_function());
let import = import_fns
.nth(fn_index)
.unwrap_or_else(|| unreachable!("Imported function {} not found", fn_index));
let return_block_depth = self.outermost_block;
self.outermost_block = self.block_loop_addrs.len() as u32;
self.import_arguments.clear();
self.import_arguments
.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));
self.import_arguments[i] = arg;
}
let _function_byte_length =
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
self.call_stack.push_frame(
return_addr,
return_block_depth,
arg_type_bytes,
&mut self.value_stack,
&module.code.bytes,
&mut self.program_counter,
);
let optional_return_val = self.import_dispatcher.dispatch(
import.module,
import.name,
&self.import_arguments,
&mut self.memory,
);
if let Some(return_val) = optional_return_val {
self.value_stack.push(return_val);
}
return;
} else {
// call an internal Wasm function
let return_addr = self.program_counter as u32;
self.program_counter = module.code.function_offsets[fn_index] as usize;
let return_block_depth = self.outermost_block;
self.outermost_block = self.block_loop_addrs.len() as u32;
let _function_byte_length =
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
self.call_stack.push_frame(
return_addr,
return_block_depth,
arg_type_bytes,
&mut self.value_stack,
&module.code.bytes,
&mut self.program_counter,
);
}
}
pub fn execute_next_instruction(&mut self, module: &WasmModule<'a>) -> Action {

View File

@ -24,3 +24,26 @@ pub trait ImportDispatcher {
memory: &mut [u8],
) -> Option<Value>;
}
pub const DEFAULT_IMPORTS: DefaultImportDispatcher = DefaultImportDispatcher {};
pub struct DefaultImportDispatcher {}
impl ImportDispatcher for DefaultImportDispatcher {
fn dispatch(
&mut self,
module_name: &str,
function_name: &str,
arguments: &[Value],
memory: &mut [u8],
) -> Option<Value> {
if module_name == wasi::MODULE_NAME {
wasi::dispatch(function_name, arguments, memory)
} else {
panic!(
"DefaultImportDispatcher does not implement {}.{}",
module_name, function_name
);
}
}
}

View File

@ -6,7 +6,7 @@ use std::fs;
use std::io;
use std::process;
use roc_wasm_interp::Instance;
use roc_wasm_interp::{Instance, DEFAULT_IMPORTS};
use roc_wasm_module::WasmModule;
pub const FLAG_FUNCTION: &str = "function";
@ -88,10 +88,11 @@ fn main() -> io::Result<()> {
// Create an execution instance
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap_or_else(|e| {
eprintln!("{}", e);
process::exit(2);
});
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode)
.unwrap_or_else(|e| {
eprintln!("{}", e);
process::exit(2);
});
// Run

View File

@ -1,14 +1,14 @@
use crate::Instance;
use crate::{DefaultImportDispatcher, Instance, DEFAULT_IMPORTS};
use bumpalo::{collections::Vec, Bump};
use roc_wasm_module::{
opcodes::OpCode, Export, ExportType, SerialBuffer, Signature, Value, ValueType, WasmModule,
};
pub fn default_state(arena: &Bump) -> Instance {
pub fn default_state(arena: &Bump) -> Instance<DefaultImportDispatcher> {
let pages = 1;
let program_counter = 0;
let globals = [];
Instance::new(arena, pages, program_counter, globals)
Instance::new(arena, pages, program_counter, globals, DEFAULT_IMPORTS)
}
pub fn const_value(buf: &mut Vec<'_, u8>, value: Value) {
@ -75,7 +75,7 @@ where
std::fs::write(&filename, outfile_buf).unwrap();
}
let mut inst = Instance::for_module(&arena, &module, true).unwrap();
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
let return_val = inst.call_export(&module, "test", []).unwrap().unwrap();

View File

@ -2,7 +2,7 @@
use bumpalo::{collections::Vec, Bump};
use roc_wasm_interp::test_utils::{const_value, create_exported_function_no_locals, default_state};
use roc_wasm_interp::{Action, Instance, ValueStack};
use roc_wasm_interp::{Action, Instance, ValueStack, DEFAULT_IMPORTS};
use roc_wasm_module::{
opcodes::OpCode, sections::ElementSegment, Export, ExportType, SerialBuffer, Serialize,
Signature, Value, ValueType, WasmModule,
@ -517,7 +517,7 @@ fn test_call_return_no_args() {
println!("Wrote to {}", filename);
}
let mut inst = Instance::for_module(&arena, &module, true).unwrap();
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
let return_val = inst
.call_export(&module, start_fn_name, [])
@ -656,7 +656,7 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value {
.unwrap();
}
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
inst.call_export(&module, start_fn_name, [])
.unwrap()
.unwrap()

View File

@ -1,5 +1,5 @@
use bumpalo::{collections::Vec, Bump};
use roc_wasm_interp::{test_utils::create_exported_function_no_locals, Instance};
use roc_wasm_interp::{test_utils::create_exported_function_no_locals, Instance, DEFAULT_IMPORTS};
use roc_wasm_module::{
opcodes::OpCode,
sections::{DataMode, DataSegment, MemorySection},
@ -16,7 +16,7 @@ fn test_currentmemory() {
module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE);
module.code.bytes.push(OpCode::CURRENTMEMORY as u8);
let mut state = Instance::new(&arena, pages, pc, []);
let mut state = Instance::new(&arena, pages, pc, [], DEFAULT_IMPORTS);
state.execute_next_instruction(&module);
assert_eq!(state.value_stack.pop(), Value::I32(3))
}
@ -34,7 +34,7 @@ fn test_growmemory() {
module.code.bytes.encode_i32(grow_pages);
module.code.bytes.push(OpCode::GROWMEMORY as u8);
let mut state = Instance::new(&arena, existing_pages, pc, []);
let mut state = Instance::new(&arena, existing_pages, pc, [], DEFAULT_IMPORTS);
state.execute_next_instruction(&module);
state.execute_next_instruction(&module);
assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize);
@ -76,7 +76,7 @@ fn test_load(load_op: OpCode, ty: ValueType, data: &[u8], addr: u32, offset: u32
std::fs::write("/tmp/roc/interp_load_test.wasm", outfile_buf).unwrap();
}
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
inst.call_export(&module, start_fn_name, [])
.unwrap()
.unwrap()
@ -273,7 +273,7 @@ fn test_store<'a>(
buf.append_u8(OpCode::END as u8);
});
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
inst.call_export(&module, start_fn_name, []).unwrap();
inst.memory