diff --git a/src/execution/execution_info.rs b/src/execution/execution_info.rs new file mode 100644 index 00000000..2d210619 --- /dev/null +++ b/src/execution/execution_info.rs @@ -0,0 +1,24 @@ +use crate::core::reader::types::FuncType; +use crate::core::reader::WasmReader; +use crate::execution::Store; + +/// ExecutionInfo is a compilation of relevant information needed by the [interpreter loop]( +/// crate::execution::interpreter_loop::run). The lifetime annotation `'r` represents that this structure needs to be +/// valid at least as long as the [RuntimeInstance](crate::execution::RuntimeInstance) that creates it. +pub struct ExecutionInfo<'r> { + pub wasm_bytecode: &'r [u8], + pub wasm_reader: WasmReader<'r>, + pub fn_types: &'r [FuncType], + pub store: &'r mut Store, +} + +impl<'r> ExecutionInfo<'r> { + pub fn new(wasm_bytecode: &'r [u8], fn_types: &'r [FuncType], store: &'r mut Store) -> Self { + ExecutionInfo { + wasm_bytecode, + wasm_reader: WasmReader::new(wasm_bytecode), + fn_types, + store, + } + } +} diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index b77a1c6f..8fcce005 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -21,8 +21,9 @@ use crate::{ WasmReadable, WasmReader, }, }, + execution::execution_info::ExecutionInfo, locals::Locals, - store::Store, + store::{FuncInst, Store}, value, value_stack::Stack, NumType, RuntimeError, ValType, Value, @@ -33,13 +34,13 @@ use crate::execution::hooks::HookSet; /// Interprets a functions. Parameters and return values are passed on the stack. pub(super) fn run( - wasm_bytecode: &[u8], - types: &[FuncType], - store: &mut Store, + modules: &mut [ExecutionInfo], + current_module: &mut usize, stack: &mut Stack, mut hooks: H, ) -> Result<(), RuntimeError> { - let func_inst = store + let func_inst = modules[*current_module] + .store .funcs .get(stack.current_stackframe().func_idx) .unwrap_validated() @@ -47,7 +48,7 @@ pub(super) fn run( .unwrap_validated(); // Start reading the function's instructions - let mut wasm = WasmReader::new(wasm_bytecode); + let mut wasm = &mut modules[*current_module].wasm_reader; // unwrap is sound, because the validation assures that the function points to valid subslice of the WASM binary wasm.move_start_to(func_inst.code_expr).unwrap(); @@ -56,7 +57,7 @@ pub(super) fn run( loop { // call the instruction hook #[cfg(feature = "hooks")] - hooks.instruction_hook(wasm_bytecode, wasm.pc); + hooks.instruction_hook(modules[*current_module].wasm_bytecode, wasm.pc); let first_instr_byte = wasm.read_u8().unwrap_validated(); @@ -79,8 +80,15 @@ pub(super) fn run( let func_to_call_idx = stack.current_stackframe().func_idx; - let func_to_call_inst = store.funcs.get(func_to_call_idx).unwrap_validated(); - let func_to_call_ty = types.get(func_to_call_inst.ty()).unwrap_validated(); + let func_to_call_inst = modules[*current_module] + .store + .funcs + .get(func_to_call_idx) + .unwrap_validated(); + let func_to_call_ty = modules[*current_module] + .fn_types + .get(func_to_call_inst.ty()) + .unwrap_validated(); let ret_vals = stack .pop_tail_iter(func_to_call_ty.returns.valtypes.len()) @@ -101,24 +109,32 @@ pub(super) fn run( CALL => { let func_to_call_idx = wasm.read_var_u32().unwrap_validated() as FuncIdx; - // TODO: if it is imported, defer to linking - let func_to_call_inst = store + let func_to_call_inst = modules[*current_module] + .store .funcs .get(func_to_call_idx) - .unwrap_validated() - .try_into_local() - .expect("TODO: call imported functions"); - let func_to_call_ty = types.get(func_to_call_inst.ty).unwrap_validated(); + .unwrap_validated(); + let func_to_call_ty = modules[*current_module] + .fn_types + .get(func_to_call_inst.ty()) + .unwrap_validated(); let params = stack.pop_tail_iter(func_to_call_ty.params.valtypes.len()); - let remaining_locals = func_to_call_inst.locals.iter().cloned(); trace!("Instruction: call [{func_to_call_idx:?}]"); - let locals = Locals::new(params, remaining_locals); - stack.push_stackframe(func_to_call_idx, func_to_call_ty, locals, wasm.pc); - wasm.move_start_to(func_to_call_inst.code_expr) - .unwrap_validated(); + match func_to_call_inst { + FuncInst::Local(local_func_inst) => { + let remaining_locals = local_func_inst.locals.iter().cloned(); + let locals = Locals::new(params, remaining_locals); + + stack.push_stackframe(func_to_call_idx, func_to_call_ty, locals, wasm.pc); + + wasm.move_start_to(local_func_inst.code_expr) + .unwrap_validated(); + } + FuncInst::Imported(imported_func_inst) => todo!(), + } } LOCAL_GET => { stack.get_local(wasm.read_var_u32().unwrap_validated() as LocalIdx); @@ -127,13 +143,25 @@ pub(super) fn run( LOCAL_TEE => stack.tee_local(wasm.read_var_u32().unwrap_validated() as LocalIdx), GLOBAL_GET => { let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx; - let global = store.globals.get(global_idx).unwrap_validated(); + let global = modules[*current_module] + .store + .globals + .get(global_idx) + .unwrap_validated(); + + // TODO: imported global stack.push_value(global.value); } GLOBAL_SET => { let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx; - let global = store.globals.get_mut(global_idx).unwrap_validated(); + let global = modules[*current_module] + .store + .globals + .get_mut(global_idx) + .unwrap_validated(); + + // TODO: imported global (?) ... can imported globals be set as mutable? global.value = stack.pop_value(global.global.ty.ty) } @@ -141,7 +169,12 @@ pub(super) fn run( let memarg = MemArg::read_unvalidated(&mut wasm); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + // TODO: how does this interact with imports? + let mem = modules[*current_module] + .store + .mems + .first() + .unwrap_validated(); // there is only one memory allowed as of now let data: u32 = { // The spec states that this should be a 33 bit integer @@ -167,7 +200,12 @@ pub(super) fn run( let memarg = MemArg::read_unvalidated(&mut wasm); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + // TODO: how does this interact with imports? + let mem = modules[*current_module] + .store + .mems + .first() + .unwrap_validated(); // there is only one memory allowed as of now let data: f32 = { // The spec states that this should be a 33 bit integer @@ -195,7 +233,12 @@ pub(super) fn run( let data_to_store: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let mem = store.mems.get_mut(0).unwrap_validated(); // there is only one memory allowed as of now + // TODO: How does this interact with imports? + let mem = modules[*current_module] + .store + .mems + .get_mut(0) + .unwrap_validated(); // there is only one memory allowed as of now // The spec states that this should be a 33 bit integer // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions @@ -216,7 +259,12 @@ pub(super) fn run( let data_to_store: f32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let mem = store.mems.get_mut(0).unwrap_validated(); // there is only one memory allowed as of now + // TODO: how does this interact with imports? + let mem = modules[*current_module] + .store + .mems + .get_mut(0) + .unwrap_validated(); // there is only one memory allowed as of now // The spec states that this should be a 33 bit integer // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions @@ -237,7 +285,12 @@ pub(super) fn run( let data_to_store: f64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let mem = store.mems.get_mut(0).unwrap_validated(); // there is only one memory allowed as of now + // TODO: how does this interact with imports? + let mem = modules[*current_module] + .store + .mems + .get_mut(0) + .unwrap_validated(); // there is only one memory allowed as of now // The spec states that this should be a 33 bit integer // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 8463bae2..ee3a3c10 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use const_interpreter_loop::run_const; +use execution_info::ExecutionInfo; use interpreter_loop::run; use locals::Locals; use store::{ImportedFuncInst, LocalFuncInst}; @@ -22,6 +23,7 @@ use crate::{RuntimeError, ValidationInfo}; // TODO pub(crate) mod assert_validated; mod const_interpreter_loop; +pub(crate) mod execution_info; pub mod hooks; mod interpreter_loop; pub(crate) mod locals; @@ -144,10 +146,11 @@ where stack.push_stackframe(func_idx, func_ty, locals, usize::MAX); // Run the interpreter + let execution_info = ExecutionInfo::new(self.wasm_bytecode, &self.types, &mut self.store); + run( - self.wasm_bytecode, - &self.types, - &mut self.store, + &mut [execution_info], + &mut 0, // todo! &mut stack, EmptyHookSet, )?; @@ -200,10 +203,11 @@ where stack.push_stackframe(func_idx, func_ty, locals, 0); // Run the interpreter + let execution_info = ExecutionInfo::new(self.wasm_bytecode, &self.types, &mut self.store); + run( - self.wasm_bytecode, - &self.types, - &mut self.store, + &mut [execution_info], + &mut 0, // todo! &mut stack, EmptyHookSet, )?;