Remove an indirect function call in Func::new (#3293)

This commit optimizes the runtime execution of `Func::new` by removing
an indirect function call that happens whenever a host function is
called. This indirection was generally done to prevent monomoprhizing a
lot into consumer code but the few extra functions this makes
monomorphic are fairly small, and in general wasm->host call performance
is pretty important.

While not a massive win this is expected to improve codegen, especially
because with the indirect call removed the compiler should now be able
to prove more often when a `Func::new` closure doesn't panic or return
an error.
This commit is contained in:
Alex Crichton
2021-09-03 13:40:51 -05:00
committed by GitHub
parent 36b7e81979
commit 50ce19a4a4
2 changed files with 22 additions and 24 deletions

View File

@@ -1894,11 +1894,11 @@ impl HostFunc {
let ty_clone = ty.clone();
// Create a trampoline that converts raw u128 values to `Val`
let func = Box::new(move |caller_vmctx, values_vec: *mut u128| unsafe {
let func = move |caller_vmctx, values_vec: *mut u128| unsafe {
Caller::with(caller_vmctx, |caller| {
Func::invoke(caller, &ty_clone, values_vec, &func)
})
});
};
let (instance, trampoline) = crate::trampoline::create_function(&ty, func, engine)
.expect("failed to create function");

View File

@@ -12,17 +12,19 @@ use wasmtime_runtime::{
OnDemandInstanceAllocator, VMContext, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
struct TrampolineState {
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync>,
struct TrampolineState<F> {
func: F,
#[allow(dead_code)]
code_memory: CodeMemory,
}
unsafe extern "C" fn stub_fn(
unsafe extern "C" fn stub_fn<F>(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
values_vec: *mut u128,
) {
) where
F: Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + 'static,
{
// Here we are careful to use `catch_unwind` to ensure Rust panics don't
// unwind past us. The primary reason for this is that Rust considers it UB
// to unwind past an `extern "C"` function. Here we are in an `extern "C"`
@@ -37,7 +39,13 @@ unsafe extern "C" fn stub_fn(
// have any. To prevent leaks we avoid having any local destructors by
// avoiding local variables.
let result = panic::catch_unwind(AssertUnwindSafe(|| {
call_stub(vmctx, caller_vmctx, values_vec)
// Double-check ourselves in debug mode, but we control
// the `Any` here so an unsafe downcast should also
// work.
let state = (*vmctx).host_state();
debug_assert!(state.is::<TrampolineState<F>>());
let state = &*(state as *const _ as *const TrampolineState<F>);
(state.func)(caller_vmctx, values_vec)
}));
match result {
@@ -55,31 +63,21 @@ unsafe extern "C" fn stub_fn(
// platforms.
Err(panic) => wasmtime_runtime::resume_panic(panic),
}
unsafe fn call_stub(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
values_vec: *mut u128,
) -> Result<(), Trap> {
let instance = InstanceHandle::from_vmctx(vmctx);
let state = &instance
.host_state()
.downcast_ref::<TrampolineState>()
.expect("state");
(state.func)(caller_vmctx, values_vec)
}
}
#[cfg(compiler)]
pub fn create_function(
pub fn create_function<F>(
ft: &FuncType,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync>,
func: F,
engine: &Engine,
) -> Result<(InstanceHandle, VMTrampoline)> {
) -> Result<(InstanceHandle, VMTrampoline)>
where
F: Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync + 'static,
{
let mut obj = engine.compiler().object()?;
let (t1, t2) = engine.compiler().emit_trampoline_obj(
ft.as_wasm_func_type(),
stub_fn as usize,
stub_fn::<F> as usize,
&mut obj,
)?;
let obj = MmapVec::from_obj(obj)?;