Skip to content

Commit

Permalink
Allow Wasm module instantiation in host functions called from Wasmi's…
Browse files Browse the repository at this point in the history
… executor (#1116)

* add host call instantiation test

This test currently dead locks as expected.

* add len_params and len_results to HostFuncEntity

* replace uses of HostFuncEntity::ty_dedup

* no longer use FuncTypeRegistry in Wasmi executor

This finally resolves the dead lock when instantiating a Wasm module in a called host function.

* add better HostFuncEntity::new2 constructor

* use new HostFuncEntity::new2 constructor

* remove old HostFuncEntity::new constructor

* rename HostFuncEntity::new2 -> new

* remove commented out line of code

* enforce max params and results per FuncType

New enforced maximum parameter and result types per FuncType is 1000. This fits nicely in a u16 value which we will make use of later in the API.

* return u16 instead of usize in FuncType::len_{params,results}

This also make use of u16 in HostFuncEntity to inline cache len_{params,results} as an space optimization.
  • Loading branch information
Robbepop authored Jul 7, 2024
1 parent dd16c12 commit c7aa4cc
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 138 deletions.
13 changes: 6 additions & 7 deletions crates/wasmi/src/engine/executor/instrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ use crate::{
TableIdx,
UnaryInstr,
},
code_map::InstructionPtr,
code_map::{CodeMap, InstructionPtr},
executor::stack::{CallFrame, FrameRegisters, ValueStack},
DedupFuncType,
EngineResources,
},
memory::DataSegment,
module::DEFAULT_MEMORY_INDEX,
Expand Down Expand Up @@ -77,11 +76,11 @@ macro_rules! forward_return {
pub fn execute_instrs<'engine, T>(
store: &mut Store<T>,
stack: &'engine mut Stack,
res: &'engine EngineResources,
code_map: &'engine CodeMap,
) -> Result<(), Error> {
let instance = stack.calls.instance_expect();
let cache = CachedInstance::new(&mut store.inner, instance);
Executor::new(stack, res, cache).execute(store)
Executor::new(stack, code_map, cache).execute(store)
}

/// An execution context for executing a Wasmi function frame.
Expand All @@ -98,15 +97,15 @@ struct Executor<'engine> {
/// The static resources of an [`Engine`].
///
/// [`Engine`]: crate::Engine
res: &'engine EngineResources,
code_map: &'engine CodeMap,
}

impl<'engine> Executor<'engine> {
/// Creates a new [`Executor`] for executing a Wasmi function frame.
#[inline(always)]
pub fn new(
stack: &'engine mut Stack,
res: &'engine EngineResources,
code_map: &'engine CodeMap,
cache: CachedInstance,
) -> Self {
let frame = stack
Expand All @@ -123,7 +122,7 @@ impl<'engine> Executor<'engine> {
ip,
cache,
stack,
res,
code_map,
}
}

Expand Down
47 changes: 14 additions & 33 deletions crates/wasmi/src/engine/executor/instrs/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ use crate::{
bytecode::{FuncIdx, Instruction, Register, RegisterSpan, SignatureIdx, TableIdx},
code_map::InstructionPtr,
executor::stack::{CallFrame, FrameParams, ValueStack},
func_types::FuncTypeRegistry,
CompiledFunc,
CompiledFuncEntity,
DedupFuncType,
FuncParams,
},
func::{FuncEntity, HostFuncEntity},
Expand All @@ -22,17 +20,6 @@ use crate::{
use core::array;
use std::fmt;

impl FuncTypeRegistry {
/// Returns the maximum value of number of parameters and results for the given [`DedupFuncType`].
#[inline]
fn len_params_results(&self, func_ty: &DedupFuncType) -> (usize, usize) {
let (input_types, output_types) = self.resolve_func_type(func_ty).params_results();
let len_inputs = input_types.len();
let len_outputs = output_types.len();
(len_inputs, len_outputs)
}
}

/// Dispatches and executes the host function.
///
/// Returns the number of parameters and results of the called host function.
Expand All @@ -43,18 +30,18 @@ impl FuncTypeRegistry {
#[inline(always)]
pub fn dispatch_host_func<T>(
store: &mut Store<T>,
func_types: &FuncTypeRegistry,
value_stack: &mut ValueStack,
host_func: HostFuncEntity,
instance: Option<&Instance>,
) -> Result<(usize, usize), Error> {
let (len_inputs, len_outputs) = func_types.len_params_results(host_func.ty_dedup());
let max_inout = len_inputs.max(len_outputs);
) -> Result<(u16, u16), Error> {
let len_params = host_func.len_params();
let len_results = host_func.len_results();
let max_inout = len_params.max(len_results);
let values = value_stack.as_slice_mut();
let params_results = FuncParams::new(
values.split_at_mut(values.len() - max_inout).1,
len_inputs,
len_outputs,
values.split_at_mut(values.len() - usize::from(max_inout)).1,
usize::from(len_params),
usize::from(len_results),
);
let trampoline = store.resolve_trampoline(host_func.trampoline()).clone();
trampoline
Expand All @@ -65,9 +52,9 @@ pub fn dispatch_host_func<T>(
// called host function. Since the host function failed we
// need to clean up the temporary buffer values here.
// This is required for resumable calls to work properly.
value_stack.drop(max_inout);
value_stack.drop(usize::from(max_inout));
})?;
Ok((len_inputs, len_outputs))
Ok((len_params, len_results))
}

/// The kind of a function call.
Expand Down Expand Up @@ -302,7 +289,7 @@ impl<'engine> Executor<'engine> {
func: CompiledFunc,
mut instance: Option<Instance>,
) -> Result<(), Error> {
let func = self.res.code_map.get(Some(store.fuel_mut()), func)?;
let func = self.code_map.get(Some(store.fuel_mut()), func)?;
let mut called = self.dispatch_compiled_func::<C>(results, func)?;
match <C as CallContext>::KIND {
CallKind::Nested => {
Expand Down Expand Up @@ -498,8 +485,8 @@ impl<'engine> Executor<'engine> {
func: &Func,
host_func: HostFuncEntity,
) -> Result<(), Error> {
let (len_params, len_results) =
self.res.func_types.len_params_results(host_func.ty_dedup());
let len_params = usize::from(host_func.len_params());
let len_results = usize::from(host_func.len_results());
let max_inout = len_params.max(len_results);
let instance = *self.stack.calls.instance_expect();
// We have to reinstantiate the `self.sp` [`FrameRegisters`] since we just called
Expand Down Expand Up @@ -549,14 +536,8 @@ impl<'engine> Executor<'engine> {
store: &mut Store<T>,
host_func: HostFuncEntity,
instance: &Instance,
) -> Result<(usize, usize), Error> {
dispatch_host_func(
store,
&self.res.func_types,
&mut self.stack.values,
host_func,
Some(instance),
)
) -> Result<(u16, u16), Error> {
dispatch_host_func(store, &mut self.stack.values, host_func, Some(instance))
}

/// Executes an [`Instruction::CallIndirect0`].
Expand Down
46 changes: 19 additions & 27 deletions crates/wasmi/src/engine/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use crate::{
CallParams,
CallResults,
EngineInner,
EngineResources,
ResumableCallBase,
ResumableInvocation,
},
Expand All @@ -26,6 +25,8 @@ use crate::{
#[cfg(doc)]
use crate::engine::StackLimits;

use super::code_map::CodeMap;

mod cache;
mod instrs;
mod stack;
Expand All @@ -48,7 +49,7 @@ impl EngineInner {
where
Results: CallResults,
{
let res = self.res.read();
let res = self.code_map.read();
let mut stack = self.stacks.lock().reuse_or_new();
let results = EngineExecutor::new(&res, &mut stack)
.execute_root_func(ctx.store, func, params, results)
Expand Down Expand Up @@ -78,10 +79,10 @@ impl EngineInner {
Results: CallResults,
{
let store = ctx.store;
let res = self.res.read();
let code_map = self.code_map.read();
let mut stack = self.stacks.lock().reuse_or_new();
let results =
EngineExecutor::new(&res, &mut stack).execute_root_func(store, func, params, results);
let results = EngineExecutor::new(&code_map, &mut stack)
.execute_root_func(store, func, params, results);
match results {
Ok(results) => {
self.stacks.lock().recycle(stack);
Expand Down Expand Up @@ -126,10 +127,10 @@ impl EngineInner {
where
Results: CallResults,
{
let res = self.res.read();
let code_map = self.code_map.read();
let host_func = invocation.host_func();
let caller_results = invocation.caller_results();
let results = EngineExecutor::new(&res, &mut invocation.stack).resume_func(
let results = EngineExecutor::new(&code_map, &mut invocation.stack).resume_func(
ctx.store,
host_func,
params,
Expand Down Expand Up @@ -161,7 +162,7 @@ impl EngineInner {
#[derive(Debug)]
pub struct EngineExecutor<'engine> {
/// Shared and reusable generic engine resources.
res: &'engine EngineResources,
code_map: &'engine CodeMap,
/// The value and call stacks.
stack: &'engine mut Stack,
}
Expand All @@ -172,8 +173,8 @@ fn do_nothing<T>(_: &mut T) {}

impl<'engine> EngineExecutor<'engine> {
/// Creates a new [`EngineExecutor`] with the given [`StackLimits`].
fn new(res: &'engine EngineResources, stack: &'engine mut Stack) -> Self {
Self { res, stack }
fn new(code_map: &'engine CodeMap, stack: &'engine mut Stack) -> Self {
Self { code_map, stack }
}

/// Executes the given [`Func`] using the given `params`.
Expand Down Expand Up @@ -204,7 +205,6 @@ impl<'engine> EngineExecutor<'engine> {
let instance = *wasm_func.instance();
let compiled_func = wasm_func.func_body();
let compiled_func = self
.res
.code_map
.get(Some(store.inner.fuel_mut()), compiled_func)?;
let (mut uninit_params, offsets) = self
Expand All @@ -228,17 +228,15 @@ impl<'engine> EngineExecutor<'engine> {
FuncEntity::Host(host_func) => {
// The host function signature is required for properly
// adjusting, inspecting and manipulating the value stack.
let (input_types, output_types) = self
.res
.func_types
.resolve_func_type(host_func.ty_dedup())
.params_results();
// In case the host function returns more values than it takes
// we are required to extend the value stack.
let len_params = input_types.len();
let len_results = output_types.len();
let len_params = host_func.len_params();
let len_results = host_func.len_results();
let max_inout = len_params.max(len_results);
let uninit = self.stack.values.extend_by(max_inout, do_nothing)?;
let uninit = self
.stack
.values
.extend_by(usize::from(max_inout), do_nothing)?;
for (uninit, param) in uninit.iter_mut().zip(params.call_params()) {
uninit.write(param);
}
Expand Down Expand Up @@ -293,7 +291,7 @@ impl<'engine> EngineExecutor<'engine> {
/// When encountering a Wasm or host trap during execution.
#[inline(always)]
fn execute_func<T>(&mut self, store: &mut Store<T>) -> Result<(), Error> {
execute_instrs(store, self.stack, self.res)
execute_instrs(store, self.stack, self.code_map)
}

/// Convenience forwarder to [`dispatch_host_func`].
Expand All @@ -303,13 +301,7 @@ impl<'engine> EngineExecutor<'engine> {
store: &mut Store<T>,
host_func: HostFuncEntity,
) -> Result<(), Error> {
dispatch_host_func(
store,
&self.res.func_types,
&mut self.stack.values,
host_func,
None,
)?;
dispatch_host_func(store, &mut self.stack.values, host_func, None)?;
Ok(())
}

Expand Down
Loading

0 comments on commit c7aa4cc

Please sign in to comment.