Refactor the internals of Func to remove layers of indirection (#1363)
* Remove `WrappedCallable` indirection At this point `Func` has evolved quite a bit since inception and the `WrappedCallable` trait I don't believe is needed any longer. This should help clean up a few entry points by having fewer traits in play. * Remove the `Callable` trait This commit removes the `wasmtime::Callable` trait, changing the signature of `Func::new` to take an appropriately typed `Fn`. Additionally the function now always takes `&Caller` like `Func::wrap` optionally can, to empower `Func::new` to have the same capabilities of `Func::wrap`. * Add a test for an already-fixed issue Closes #849 * rustfmt * Update more locations for `Callable` * rustfmt * Remove a stray leading borrow * Review feedback * Remove unneeded `wasmtime_call_trampoline` shim
This commit is contained in:
@@ -1,214 +0,0 @@
|
||||
use crate::runtime::Store;
|
||||
use crate::trampoline::generate_func_export;
|
||||
use crate::trap::Trap;
|
||||
use crate::types::FuncType;
|
||||
use crate::values::Val;
|
||||
use std::cmp::max;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use wasmtime_runtime::{ExportFunction, InstanceHandle, VMTrampoline};
|
||||
|
||||
/// A trait representing a function that can be imported and called from inside
|
||||
/// WebAssembly.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use wasmtime::Val;
|
||||
///
|
||||
/// struct TimesTwo;
|
||||
///
|
||||
/// impl wasmtime::Callable for TimesTwo {
|
||||
/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), wasmtime::Trap> {
|
||||
/// let mut value = params[0].unwrap_i32();
|
||||
/// value *= 2;
|
||||
/// results[0] = value.into();
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main () -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// // Simple module that imports our host function ("times_two") and re-exports
|
||||
/// // it as "run".
|
||||
/// let wat = r#"
|
||||
/// (module
|
||||
/// (func $times_two (import "" "times_two") (param i32) (result i32))
|
||||
/// (func
|
||||
/// (export "run")
|
||||
/// (param i32)
|
||||
/// (result i32)
|
||||
/// (local.get 0)
|
||||
/// (call $times_two))
|
||||
/// )
|
||||
/// "#;
|
||||
///
|
||||
/// // Initialise environment and our module.
|
||||
/// let store = wasmtime::Store::default();
|
||||
/// let module = wasmtime::Module::new(&store, wat)?;
|
||||
///
|
||||
/// // Define the type of the function we're going to call.
|
||||
/// let times_two_type = wasmtime::FuncType::new(
|
||||
/// // Parameters
|
||||
/// Box::new([wasmtime::ValType::I32]),
|
||||
/// // Results
|
||||
/// Box::new([wasmtime::ValType::I32])
|
||||
/// );
|
||||
///
|
||||
/// // Build a reference to the "times_two" function that can be used.
|
||||
/// let times_two_function =
|
||||
/// wasmtime::Func::new(&store, times_two_type, std::rc::Rc::new(TimesTwo));
|
||||
///
|
||||
/// // Create module instance that imports our function
|
||||
/// let instance = wasmtime::Instance::new(
|
||||
/// &module,
|
||||
/// &[times_two_function.into()]
|
||||
/// )?;
|
||||
///
|
||||
/// // Get "run" function from the exports.
|
||||
/// let run_function = instance.exports()[0].func().unwrap();
|
||||
///
|
||||
/// // Borrow and call "run". Returning any error message from Wasm as a string.
|
||||
/// let original = 5i32;
|
||||
/// let results = run_function
|
||||
/// .call(&[original.into()])
|
||||
/// .map_err(|trap| trap.to_string())?;
|
||||
///
|
||||
/// // Compare that the results returned matches what we expect.
|
||||
/// assert_eq!(original * 2, results[0].unwrap_i32());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait Callable {
|
||||
/// What is called when the function is invoked in WebAssembly.
|
||||
/// `params` is an immutable list of parameters provided to the function.
|
||||
/// `results` is mutable list of results to be potentially set by your
|
||||
/// function. Produces a `Trap` if the function encounters any errors.
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
|
||||
}
|
||||
|
||||
pub(crate) trait WrappedCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle;
|
||||
fn wasmtime_function(&self) -> &ExportFunction;
|
||||
}
|
||||
|
||||
pub(crate) struct WasmtimeFn {
|
||||
store: Store,
|
||||
instance: InstanceHandle,
|
||||
export: ExportFunction,
|
||||
trampoline: VMTrampoline,
|
||||
}
|
||||
|
||||
impl WasmtimeFn {
|
||||
pub fn new(
|
||||
store: &Store,
|
||||
instance: InstanceHandle,
|
||||
export: ExportFunction,
|
||||
trampoline: VMTrampoline,
|
||||
) -> WasmtimeFn {
|
||||
WasmtimeFn {
|
||||
store: store.clone(),
|
||||
instance,
|
||||
export,
|
||||
trampoline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for WasmtimeFn {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
|
||||
let f = self.wasmtime_function();
|
||||
let signature = self
|
||||
.store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.lookup(f.signature)
|
||||
.expect("missing signature");
|
||||
if signature.params.len() - 2 != params.len() {
|
||||
return Err(Trap::new(format!(
|
||||
"expected {} arguments, got {}",
|
||||
signature.params.len() - 2,
|
||||
params.len()
|
||||
)));
|
||||
}
|
||||
if signature.returns.len() != results.len() {
|
||||
return Err(Trap::new(format!(
|
||||
"expected {} results, got {}",
|
||||
signature.returns.len(),
|
||||
results.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut values_vec = vec![0; max(params.len(), results.len())];
|
||||
|
||||
// Store the argument values into `values_vec`.
|
||||
let param_tys = signature.params.iter().skip(2);
|
||||
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
|
||||
if arg.ty().get_wasmtime_type() != Some(ty.value_type) {
|
||||
return Err(Trap::new("argument type mismatch"));
|
||||
}
|
||||
unsafe {
|
||||
arg.write_value_to(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the trampoline.
|
||||
if let Err(error) = unsafe {
|
||||
wasmtime_runtime::wasmtime_call_trampoline(
|
||||
f.vmctx,
|
||||
ptr::null_mut(),
|
||||
self.trampoline,
|
||||
f.address,
|
||||
values_vec.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
} {
|
||||
return Err(Trap::from_jit(error));
|
||||
}
|
||||
|
||||
// Load the return values out of `values_vec`.
|
||||
for (index, abi_param) in signature.returns.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
|
||||
results[index] = Val::read_value_from(ptr, abi_param.value_type);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_function(&self) -> &ExportFunction {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeCallable {
|
||||
callable: Rc<dyn Callable + 'static>,
|
||||
instance: InstanceHandle,
|
||||
export: ExportFunction,
|
||||
}
|
||||
|
||||
impl NativeCallable {
|
||||
pub(crate) fn new(callable: Rc<dyn Callable + 'static>, ft: &FuncType, store: &Store) -> Self {
|
||||
let (instance, export) =
|
||||
generate_func_export(ft, &callable, store).expect("generated func");
|
||||
NativeCallable {
|
||||
callable,
|
||||
instance,
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for NativeCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
|
||||
self.callable.call(params, results)
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_function(&self) -> &ExportFunction {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::callable::{NativeCallable, WasmtimeFn, WrappedCallable};
|
||||
use crate::{Callable, Extern, FuncType, Memory, Store, Trap, Val, ValType};
|
||||
use crate::{Extern, FuncType, Memory, Store, Trap, Val, ValType};
|
||||
use anyhow::{ensure, Context as _};
|
||||
use std::cmp::max;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||
use wasmtime_runtime::{ExportFunction, VMTrampoline};
|
||||
|
||||
/// A WebAssembly function which can be called.
|
||||
///
|
||||
@@ -100,19 +100,6 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// use std::rc::Rc;
|
||||
///
|
||||
/// struct Double;
|
||||
///
|
||||
/// impl Callable for Double {
|
||||
/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
|
||||
/// let mut value = params[0].unwrap_i32();
|
||||
/// value *= 2;
|
||||
/// results[0] = value.into();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// let store = Store::default();
|
||||
///
|
||||
@@ -122,7 +109,12 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||
/// Box::new([wasmtime::ValType::I32]),
|
||||
/// Box::new([wasmtime::ValType::I32])
|
||||
/// );
|
||||
/// let double = Func::new(&store, double_type, Rc::new(Double));
|
||||
/// let double = Func::new(&store, double_type, |_, params, results| {
|
||||
/// let mut value = params[0].unwrap_i32();
|
||||
/// value *= 2;
|
||||
/// results[0] = value.into();
|
||||
/// Ok(())
|
||||
/// });
|
||||
///
|
||||
/// let module = Module::new(
|
||||
/// &store,
|
||||
@@ -144,8 +136,10 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
||||
#[derive(Clone)]
|
||||
pub struct Func {
|
||||
store: Store,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
instance: InstanceHandle,
|
||||
export: ExportFunction,
|
||||
ty: FuncType,
|
||||
trampoline: VMTrampoline,
|
||||
}
|
||||
|
||||
macro_rules! getters {
|
||||
@@ -213,15 +207,76 @@ impl Func {
|
||||
/// * `ty` - the signature of this function, used to indicate what the
|
||||
/// inputs and outputs are, which must be WebAssembly types.
|
||||
///
|
||||
/// * `callable` - a type implementing the [`Callable`] trait which
|
||||
/// is the implementation of this `Func` value.
|
||||
/// * `func` - the native code invoked whenever this `Func` will be called.
|
||||
/// This closure is provided a [`Caller`] as its first argument to learn
|
||||
/// information about the caller, and then it's passed a list of
|
||||
/// parameters as a slice along with a mutable slice of where to write
|
||||
/// results.
|
||||
///
|
||||
/// Note that the implementation of `callable` must adhere to the `ty`
|
||||
/// Note that the implementation of `func` must adhere to the `ty`
|
||||
/// signature given, error or traps may occur if it does not respect the
|
||||
/// `ty` signature.
|
||||
pub fn new(store: &Store, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
|
||||
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
///
|
||||
/// Additionally note that this is quite a dynamic function since signatures
|
||||
/// are not statically known. For a more performant `Func` it's recommended
|
||||
/// to use [`Func::wrap`] if you can because with statically known
|
||||
/// signatures the engine can optimize the implementation much more.
|
||||
pub fn new(
|
||||
store: &Store,
|
||||
ty: FuncType,
|
||||
func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + 'static,
|
||||
) -> Self {
|
||||
let store_clone = store.clone();
|
||||
let ty_clone = ty.clone();
|
||||
|
||||
// Create our actual trampoline function which translates from a bunch
|
||||
// of bit patterns on the stack to actual instances of `Val` being
|
||||
// passed to the given function.
|
||||
let func = Box::new(move |caller_vmctx, values_vec: *mut u128| {
|
||||
// We have a dynamic guarantee that `values_vec` has the right
|
||||
// number of arguments and the right types of arguments. As a result
|
||||
// we should be able to safely run through them all and read them.
|
||||
let mut args = Vec::with_capacity(ty_clone.params().len());
|
||||
for (i, ty) in ty_clone.params().iter().enumerate() {
|
||||
unsafe {
|
||||
args.push(Val::read_value_from(values_vec.add(i), ty));
|
||||
}
|
||||
}
|
||||
let mut returns = vec![Val::null(); ty_clone.results().len()];
|
||||
func(
|
||||
Caller {
|
||||
store: &store_clone,
|
||||
caller_vmctx,
|
||||
},
|
||||
&args,
|
||||
&mut returns,
|
||||
)?;
|
||||
|
||||
// Unlike our arguments we need to dynamically check that the return
|
||||
// values produced are correct. There could be a bug in `func` that
|
||||
// produces the wrong number or wrong types of values, and we need
|
||||
// to catch that here.
|
||||
for (i, (ret, ty)) in returns.iter_mut().zip(ty_clone.results()).enumerate() {
|
||||
if ret.ty() != *ty {
|
||||
return Err(Trap::new(
|
||||
"function attempted to return an incompatible value",
|
||||
));
|
||||
}
|
||||
unsafe {
|
||||
ret.write_value_to(values_vec.add(i));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
let (instance, export, trampoline) =
|
||||
crate::trampoline::generate_func_export(&ty, func, store).expect("generated func");
|
||||
Func {
|
||||
store: store.clone(),
|
||||
ty,
|
||||
instance,
|
||||
export,
|
||||
trampoline,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Func` from the given Rust closure.
|
||||
@@ -414,18 +469,6 @@ impl Func {
|
||||
func.into_func(store)
|
||||
}
|
||||
|
||||
fn from_wrapped(
|
||||
store: &Store,
|
||||
ty: FuncType,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
) -> Func {
|
||||
Func {
|
||||
store: store.clone(),
|
||||
callable,
|
||||
ty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying wasm type that this `Func` has.
|
||||
pub fn ty(&self) -> &FuncType {
|
||||
&self.ty
|
||||
@@ -451,26 +494,71 @@ impl Func {
|
||||
/// This function should not panic unless the underlying function itself
|
||||
/// initiates a panic.
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Trap> {
|
||||
for param in params {
|
||||
if !param.comes_from_same_store(&self.store) {
|
||||
// We need to perform a dynamic check that the arguments given to us
|
||||
// match the signature of this function and are appropriate to pass to
|
||||
// this function. This involves checking to make sure we have the right
|
||||
// number and types of arguments as well as making sure everything is
|
||||
// from the same `Store`.
|
||||
if self.ty.params().len() != params.len() {
|
||||
return Err(Trap::new(format!(
|
||||
"expected {} arguments, got {}",
|
||||
self.ty.params().len(),
|
||||
params.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut values_vec = vec![0; max(params.len(), self.ty.results().len())];
|
||||
|
||||
// Store the argument values into `values_vec`.
|
||||
let param_tys = self.ty.params().iter();
|
||||
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
|
||||
if arg.ty() != *ty {
|
||||
return Err(Trap::new("argument type mismatch"));
|
||||
}
|
||||
if !arg.comes_from_same_store(&self.store) {
|
||||
return Err(Trap::new(
|
||||
"cross-`Store` values are not currently supported",
|
||||
));
|
||||
}
|
||||
unsafe {
|
||||
arg.write_value_to(slot);
|
||||
}
|
||||
}
|
||||
let mut results = vec![Val::null(); self.result_arity()];
|
||||
self.callable.call(params, &mut results)?;
|
||||
Ok(results.into_boxed_slice())
|
||||
|
||||
// Call the trampoline.
|
||||
if let Err(error) = unsafe {
|
||||
wasmtime_runtime::catch_traps(self.export.vmctx, || {
|
||||
(self.trampoline)(
|
||||
self.export.vmctx,
|
||||
ptr::null_mut(),
|
||||
self.export.address,
|
||||
values_vec.as_mut_ptr(),
|
||||
)
|
||||
})
|
||||
} {
|
||||
return Err(Trap::from_jit(error));
|
||||
}
|
||||
|
||||
// Load the return values out of `values_vec`.
|
||||
let mut results = Vec::with_capacity(self.ty.results().len());
|
||||
for (index, ty) in self.ty.results().iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
results.push(Val::read_value_from(ptr, ty));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results.into())
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_function(&self) -> &wasmtime_runtime::ExportFunction {
|
||||
self.callable.wasmtime_function()
|
||||
&self.export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_function(
|
||||
export: wasmtime_runtime::ExportFunction,
|
||||
store: &Store,
|
||||
instance_handle: InstanceHandle,
|
||||
instance: InstanceHandle,
|
||||
) -> Self {
|
||||
// Signatures should always be registered in the store's registry of
|
||||
// shared signatures, so we should be able to unwrap safely here.
|
||||
@@ -489,12 +577,17 @@ impl Func {
|
||||
// Each function signature in a module should have a trampoline stored
|
||||
// on that module as well, so unwrap the result here since otherwise
|
||||
// it's a bug in wasmtime.
|
||||
let trampoline = instance_handle
|
||||
let trampoline = instance
|
||||
.trampoline(export.signature)
|
||||
.expect("failed to retrieve trampoline from module");
|
||||
|
||||
let callable = WasmtimeFn::new(store, instance_handle, export, trampoline);
|
||||
Func::from_wrapped(store, ty, Rc::new(callable))
|
||||
Func {
|
||||
instance,
|
||||
export,
|
||||
trampoline,
|
||||
ty,
|
||||
store: store.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
getters! {
|
||||
@@ -998,8 +1091,13 @@ macro_rules! impl_into_func {
|
||||
Box::new((self, store_clone)),
|
||||
)
|
||||
.expect("failed to generate export");
|
||||
let callable = Rc::new(WasmtimeFn::new(store, instance, export, trampoline));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
Func {
|
||||
store: store.clone(),
|
||||
ty,
|
||||
instance,
|
||||
export,
|
||||
trampoline,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#![deny(missing_docs, intra_doc_link_resolution_failure)]
|
||||
|
||||
mod callable;
|
||||
mod externals;
|
||||
mod frame_info;
|
||||
mod func;
|
||||
@@ -21,7 +20,6 @@ mod trap;
|
||||
mod types;
|
||||
mod values;
|
||||
|
||||
pub use crate::callable::Callable;
|
||||
pub use crate::externals::*;
|
||||
pub use crate::frame_info::FrameInfo;
|
||||
pub use crate::func::*;
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
//! Support for a calling of an imported function.
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{Callable, FuncType, Store, Trap, Val};
|
||||
use crate::{FuncType, Store, Trap};
|
||||
use anyhow::{bail, Result};
|
||||
use std::any::Any;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::rc::Rc;
|
||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||
use wasmtime_environ::ir::types;
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::isa::TargetIsa;
|
||||
use wasmtime_environ::wasm::FuncIndex;
|
||||
use wasmtime_environ::{
|
||||
ir, settings, CompiledFunction, CompiledFunctionUnwindInfo, Export, Module,
|
||||
};
|
||||
@@ -26,22 +23,15 @@ use wasmtime_jit::{native, CodeMemory};
|
||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody, VMTrampoline};
|
||||
|
||||
struct TrampolineState {
|
||||
func: Rc<dyn Callable + 'static>,
|
||||
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
|
||||
#[allow(dead_code)]
|
||||
code_memory: CodeMemory,
|
||||
}
|
||||
|
||||
impl TrampolineState {
|
||||
fn new(func: Rc<dyn Callable + 'static>, code_memory: CodeMemory) -> Self {
|
||||
TrampolineState { func, code_memory }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn stub_fn(
|
||||
vmctx: *mut VMContext,
|
||||
_caller_vmctx: *mut VMContext,
|
||||
call_id: u32,
|
||||
values_vec: *mut i128,
|
||||
caller_vmctx: *mut VMContext,
|
||||
values_vec: *mut u128,
|
||||
) {
|
||||
// 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
|
||||
@@ -56,7 +46,9 @@ unsafe extern "C" fn stub_fn(
|
||||
// below will trigger a longjmp, which won't run local destructors if we
|
||||
// have any. To prevent leaks we avoid having any local destructors by
|
||||
// avoiding local variables.
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| call_stub(vmctx, call_id, values_vec)));
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
call_stub(vmctx, caller_vmctx, values_vec)
|
||||
}));
|
||||
|
||||
match result {
|
||||
Ok(Ok(())) => {}
|
||||
@@ -76,54 +68,23 @@ unsafe extern "C" fn stub_fn(
|
||||
|
||||
unsafe fn call_stub(
|
||||
vmctx: *mut VMContext,
|
||||
call_id: u32,
|
||||
values_vec: *mut i128,
|
||||
caller_vmctx: *mut VMContext,
|
||||
values_vec: *mut u128,
|
||||
) -> Result<(), Trap> {
|
||||
let instance = InstanceHandle::from_vmctx(vmctx);
|
||||
|
||||
let (args, returns_len) = {
|
||||
let module = instance.module_ref();
|
||||
let signature =
|
||||
&module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]];
|
||||
|
||||
let mut args = Vec::new();
|
||||
for i in 2..signature.params.len() {
|
||||
args.push(Val::read_value_from(
|
||||
values_vec.offset(i as isize - 2),
|
||||
signature.params[i].value_type,
|
||||
))
|
||||
}
|
||||
(args, signature.returns.len())
|
||||
};
|
||||
|
||||
let mut returns = vec![Val::null(); returns_len];
|
||||
let state = &instance
|
||||
.host_state()
|
||||
.downcast_ref::<TrampolineState>()
|
||||
.expect("state");
|
||||
state.func.call(&args, &mut returns)?;
|
||||
|
||||
let module = instance.module_ref();
|
||||
let signature =
|
||||
&module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]];
|
||||
for (i, ret) in returns.iter_mut().enumerate() {
|
||||
if ret.ty().get_wasmtime_type() != Some(signature.returns[i].value_type) {
|
||||
return Err(Trap::new(
|
||||
"`Callable` attempted to return an incompatible value",
|
||||
));
|
||||
}
|
||||
ret.write_value_to(values_vec.add(i));
|
||||
}
|
||||
Ok(())
|
||||
(state.func)(caller_vmctx, values_vec)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a Callable.
|
||||
/// Create a trampoline for invoking a function.
|
||||
fn make_trampoline(
|
||||
isa: &dyn TargetIsa,
|
||||
code_memory: &mut CodeMemory,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
call_id: u32,
|
||||
signature: &ir::Signature,
|
||||
) -> *mut [VMFunctionBody] {
|
||||
// Mostly reverse copy of the similar method from wasmtime's
|
||||
@@ -140,9 +101,6 @@ fn make_trampoline(
|
||||
// Add the caller `vmctx` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add the `call_id` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(types::I32));
|
||||
|
||||
// Add the `values_vec` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
@@ -188,14 +146,8 @@ fn make_trampoline(
|
||||
let block_params = builder.func.dfg.block_params(block0);
|
||||
let vmctx_ptr_val = block_params[0];
|
||||
let caller_vmctx_ptr_val = block_params[1];
|
||||
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
|
||||
|
||||
let callee_args = vec![
|
||||
vmctx_ptr_val,
|
||||
caller_vmctx_ptr_val,
|
||||
call_id_val,
|
||||
values_vec_ptr_val,
|
||||
];
|
||||
let callee_args = vec![vmctx_ptr_val, caller_vmctx_ptr_val, values_vec_ptr_val];
|
||||
|
||||
let new_sig = builder.import_signature(stub_sig);
|
||||
|
||||
@@ -249,9 +201,9 @@ fn make_trampoline(
|
||||
|
||||
pub fn create_handle_with_function(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
|
||||
store: &Store,
|
||||
) -> Result<InstanceHandle> {
|
||||
) -> Result<(InstanceHandle, VMTrampoline)> {
|
||||
let isa = {
|
||||
let isa_builder = native::builder();
|
||||
let flag_builder = settings::builder();
|
||||
@@ -277,13 +229,7 @@ pub fn create_handle_with_function(
|
||||
module
|
||||
.exports
|
||||
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||
let trampoline = make_trampoline(
|
||||
isa.as_ref(),
|
||||
&mut code_memory,
|
||||
&mut fn_builder_ctx,
|
||||
func_id.index() as u32,
|
||||
&sig,
|
||||
);
|
||||
let trampoline = make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig);
|
||||
finished_functions.push(trampoline);
|
||||
|
||||
// ... and then we also need a trampoline with the standard "trampoline ABI"
|
||||
@@ -304,7 +250,7 @@ pub fn create_handle_with_function(
|
||||
// code memory (makes it executable) and ensuring all our various bits of
|
||||
// state make it into the instance constructors.
|
||||
code_memory.publish();
|
||||
let trampoline_state = TrampolineState::new(func.clone(), code_memory);
|
||||
let trampoline_state = TrampolineState { func, code_memory };
|
||||
create_handle(
|
||||
module,
|
||||
store,
|
||||
@@ -312,6 +258,7 @@ pub fn create_handle_with_function(
|
||||
trampolines,
|
||||
Box::new(trampoline_state),
|
||||
)
|
||||
.map(|instance| (instance, trampoline))
|
||||
}
|
||||
|
||||
pub unsafe fn create_handle_with_raw_function(
|
||||
|
||||
@@ -10,23 +10,23 @@ use self::func::create_handle_with_function;
|
||||
use self::global::create_global;
|
||||
use self::memory::create_handle_with_memory;
|
||||
use self::table::create_handle_with_table;
|
||||
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
|
||||
use crate::{FuncType, GlobalType, MemoryType, Store, TableType, Trap, Val};
|
||||
use anyhow::Result;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
use wasmtime_runtime::{VMFunctionBody, VMTrampoline};
|
||||
use wasmtime_runtime::{VMContext, VMFunctionBody, VMTrampoline};
|
||||
|
||||
pub fn generate_func_export(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
|
||||
store: &Store,
|
||||
) -> Result<(
|
||||
wasmtime_runtime::InstanceHandle,
|
||||
wasmtime_runtime::ExportFunction,
|
||||
VMTrampoline,
|
||||
)> {
|
||||
let instance = create_handle_with_function(ft, func, store)?;
|
||||
let (instance, trampoline) = create_handle_with_function(ft, func, store)?;
|
||||
match instance.lookup("trampoline").expect("trampoline export") {
|
||||
wasmtime_runtime::Export::Function(f) => Ok((instance, f)),
|
||||
wasmtime_runtime::Export::Function(f) => Ok((instance, f, trampoline)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::r#ref::AnyRef;
|
||||
use crate::{Func, Store, ValType};
|
||||
use anyhow::{bail, Result};
|
||||
use std::ptr;
|
||||
use wasmtime_environ::ir;
|
||||
|
||||
/// Possible runtime values that a WebAssembly module can either consume or
|
||||
/// produce.
|
||||
@@ -81,7 +80,7 @@ impl Val {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn write_value_to(&self, p: *mut i128) {
|
||||
pub(crate) unsafe fn write_value_to(&self, p: *mut u128) {
|
||||
match self {
|
||||
Val::I32(i) => ptr::write(p as *mut i32, *i),
|
||||
Val::I64(i) => ptr::write(p as *mut i64, *i),
|
||||
@@ -92,13 +91,13 @@ impl Val {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn read_value_from(p: *const i128, ty: ir::Type) -> Val {
|
||||
pub(crate) unsafe fn read_value_from(p: *const u128, ty: &ValType) -> Val {
|
||||
match ty {
|
||||
ir::types::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||
ir::types::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||
ir::types::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||
ir::types::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||
ir::types::I8X16 => Val::V128(ptr::read(p as *const u128)),
|
||||
ValType::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||
ValType::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||
ValType::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||
ValType::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||
ValType::V128 => Val::V128(ptr::read(p as *const u128)),
|
||||
_ => unimplemented!("Val::read_value_from"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user