Add API to statically assert signature of a Func (#955)

* Add API to statically assert signature of a `Func`

This commit add a family of APIs to `Func` named `getN` where `N` is the
number of arguments. Each function will attempt to statically assert the
signature of a `Func` and, if matching, returns a corresponding closure
which can be used to invoke the underlying function.

The purpose of this commit is to add a highly optimized way to enter a
wasm module, performing type checks up front and avoiding all the costs
of boxing and unboxing arguments within a `Val`. In general this should
be much more optimized than the previous `call` API for entering a wasm
module, if the signature is statically known.

* rustfmt

* Remove stray debugging
This commit is contained in:
Alex Crichton
2020-02-20 09:28:12 -06:00
committed by GitHub
parent b6be99c9e1
commit 80b095f2e2
8 changed files with 316 additions and 80 deletions

View File

@@ -8,7 +8,7 @@ use crate::jit_int::GdbJitImageRegistration;
use crate::memory::LinearMemory;
use crate::signalhandlers;
use crate::table::Table;
use crate::traphandlers::{wasmtime_call, Trap};
use crate::traphandlers::{catch_traps, Trap};
use crate::vmcontext::{
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport,
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
@@ -367,8 +367,15 @@ impl Instance {
};
// Make the call.
unsafe { wasmtime_call(callee_vmctx, self.vmctx_ptr(), callee_address) }
unsafe {
catch_traps(callee_vmctx, || {
mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
>(callee_address)(callee_vmctx, self.vmctx_ptr())
})
.map_err(InstantiationError::StartTrap)
}
}
/// Return the offset from the vmctx pointer to its containing Instance.

View File

@@ -44,7 +44,7 @@ pub use crate::mmap::Mmap;
pub use crate::sig_registry::SignatureRegistry;
pub use crate::trap_registry::{TrapDescription, TrapRegistration, TrapRegistry};
pub use crate::traphandlers::resume_panic;
pub use crate::traphandlers::{raise_user_trap, wasmtime_call, wasmtime_call_trampoline, Trap};
pub use crate::traphandlers::{catch_traps, raise_user_trap, wasmtime_call_trampoline, Trap};
pub use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition,
VMGlobalImport, VMInvokeArgument, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,

View File

@@ -9,23 +9,15 @@ use std::any::Any;
use std::cell::Cell;
use std::error::Error;
use std::fmt;
use std::mem;
use std::ptr;
use wasmtime_environ::ir;
extern "C" {
fn WasmtimeCallTrampoline(
fn RegisterSetjmp(
jmp_buf: *mut *const u8,
vmctx: *mut u8,
caller_vmctx: *mut u8,
trampoline: *const VMFunctionBody,
callee: *const VMFunctionBody,
values_vec: *mut u8,
) -> i32;
fn WasmtimeCall(
jmp_buf: *mut *const u8,
vmctx: *mut u8,
caller_vmctx: *mut u8,
callee: *const VMFunctionBody,
callback: extern "C" fn(*mut u8),
payload: *mut u8,
) -> i32;
fn Unwind(jmp_buf: *const u8) -> !;
}
@@ -154,33 +146,36 @@ pub unsafe fn wasmtime_call_trampoline(
callee: *const VMFunctionBody,
values_vec: *mut u8,
) -> Result<(), Trap> {
CallThreadState::new(vmctx).with(|cx| {
WasmtimeCallTrampoline(
cx.jmp_buf.as_ptr(),
vmctx as *mut u8,
caller_vmctx as *mut u8,
trampoline,
callee,
values_vec,
)
catch_traps(vmctx, || {
mem::transmute::<
_,
extern "C" fn(*mut VMContext, *mut VMContext, *const VMFunctionBody, *mut u8),
>(trampoline)(vmctx, caller_vmctx, callee, values_vec)
})
}
/// Call the wasm function pointed to by `callee`, which has no arguments or
/// return values.
pub unsafe fn wasmtime_call(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
callee: *const VMFunctionBody,
) -> Result<(), Trap> {
CallThreadState::new(vmctx).with(|cx| {
WasmtimeCall(
/// Catches any wasm traps that happen within the execution of `closure`,
/// returning them as a `Result`.
///
/// Highly unsafe since `closure` won't have any dtors run.
pub unsafe fn catch_traps<F>(vmctx: *mut VMContext, mut closure: F) -> Result<(), Trap>
where
F: FnMut(),
{
return CallThreadState::new(vmctx).with(|cx| {
RegisterSetjmp(
cx.jmp_buf.as_ptr(),
vmctx as *mut u8,
caller_vmctx as *mut u8,
callee,
call_closure::<F>,
&mut closure as *mut F as *mut u8,
)
})
});
extern "C" fn call_closure<F>(payload: *mut u8)
where
F: FnMut(),
{
unsafe { (*(payload as *mut F))() }
}
}
/// Temporary state stored on the stack which is registered in the `tls` module