Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor vm #2281

Merged
merged 13 commits into from
May 3, 2021
175 changes: 29 additions & 146 deletions lib/api/src/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,10 @@ use std::sync::Arc;
use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata};
use wasmer_vm::{
raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr,
VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMExportFunction, VMFuncRef, VMFunctionBody,
VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody,
VMFunctionEnvironment, VMFunctionKind, VMTrampoline,
};

/// A function defined in the Wasm module
#[derive(Clone, PartialEq, MemoryUsage)]
pub struct WasmFunctionDefinition {
// Address of the trampoline to do the call.
#[loupe(skip)]
pub(crate) trampoline: VMTrampoline,
}

/// A function defined in the Host
#[derive(Clone, PartialEq, MemoryUsage)]
pub struct HostFunctionDefinition {
/// If the host function has a custom environment attached
pub(crate) has_env: bool,
}

/// The inner helper
#[derive(Clone, PartialEq, MemoryUsage)]
pub enum FunctionDefinition {
/// A function defined in the Wasm side
Wasm(WasmFunctionDefinition),
/// A function defined in the Host side
Host(HostFunctionDefinition),
}

/// A WebAssembly `function` instance.
///
/// A function instance is the runtime representation of a function.
Expand All @@ -66,7 +42,6 @@ pub enum FunctionDefinition {
#[derive(Clone, PartialEq, MemoryUsage)]
pub struct Function {
pub(crate) store: Store,
pub(crate) definition: FunctionDefinition,
pub(crate) exported: ExportFunction,
}

Expand Down Expand Up @@ -135,6 +110,8 @@ where
(env, metadata)
}

impl WasmerEnv for WithoutEnv {}

impl Function {
/// Creates a new host `Function` (dynamic) with the provided signature.
///
Expand Down Expand Up @@ -174,60 +151,9 @@ impl Function {
FT: Into<FunctionType>,
F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync,
{
let ty: FunctionType = ty.into();
let dynamic_ctx: VMDynamicFunctionContext<DynamicFunctionWithoutEnv> =
VMDynamicFunctionContext::from_context(DynamicFunctionWithoutEnv {
func: Arc::new(func),
store: store.clone(),
function_type: ty.clone(),
});
// We don't yet have the address with the Wasm ABI signature.
// The engine linker will replace the address with one pointing to a
// generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody;
let host_env = Box::into_raw(Box::new(dynamic_ctx)) as *mut _;
let vmctx = VMFunctionEnvironment { host_env };
let host_env_clone_fn: fn(*mut c_void) -> *mut c_void = |ptr| {
let duped_env: VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = unsafe {
let ptr: *mut VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = ptr as _;
let item: &VMDynamicFunctionContext<DynamicFunctionWithoutEnv> = &*ptr;
item.clone()
};
Box::into_raw(Box::new(duped_env)) as _
};
let host_env_drop_fn: fn(*mut c_void) = |ptr: *mut c_void| {
unsafe {
Box::from_raw(ptr as *mut VMDynamicFunctionContext<DynamicFunctionWithoutEnv>)
};
};

Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }),
exported: ExportFunction {
metadata: Some(Arc::new(
// # Safety
// - All these functions work on all threads
// - The host env is `Send`.
unsafe {
ExportFunctionMetadata::new(
host_env,
None,
host_env_clone_fn,
host_env_drop_fn,
)
},
)),
vm_function: VMExportFunction {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
signature: ty,
call_trampoline: None,
instance_ref: None,
},
},
}
let wrapped_func =
move |_env: &WithoutEnv, args: &[Val]| -> Result<Vec<Val>, RuntimeError> { func(args) };
Self::new_with_env(store, ty, WithoutEnv, wrapped_func)
}

/// Creates a new host `Function` (dynamic) with the provided signature and environment.
Expand Down Expand Up @@ -282,22 +208,22 @@ impl Function {
Env: Sized + WasmerEnv + 'static,
{
let ty: FunctionType = ty.into();
let dynamic_ctx: VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>> =
VMDynamicFunctionContext::from_context(DynamicFunctionWithEnv {
let dynamic_ctx: VMDynamicFunctionContext<DynamicFunction<Env>> =
VMDynamicFunctionContext::from_context(DynamicFunction {
env: Box::new(env),
func: Arc::new(func),
store: store.clone(),
function_type: ty.clone(),
});

let import_init_function_ptr: for<'a> fn(&'a mut _, &'a _) -> Result<(), _> =
|env: &mut VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>>,
|env: &mut VMDynamicFunctionContext<DynamicFunction<Env>>,
instance: &crate::Instance| {
Env::init_with_instance(&mut *env.ctx.env, instance)
};

let (host_env, metadata) = build_export_function_metadata::<
VMDynamicFunctionContext<DynamicFunctionWithEnv<Env>>,
VMDynamicFunctionContext<DynamicFunction<Env>>,
>(dynamic_ctx, import_init_function_ptr);

// We don't yet have the address with the Wasm ABI signature.
Expand All @@ -308,10 +234,9 @@ impl Function {

Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
vm_function: VMFunction {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
Expand Down Expand Up @@ -359,13 +284,11 @@ impl Function {

Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: false }),

exported: ExportFunction {
// TODO: figure out what's going on in this function: it takes an `Env`
// param but also marks itself as not having an env
metadata: None,
vm_function: VMExportFunction {
vm_function: VMFunction {
address,
vmctx,
signature,
Expand Down Expand Up @@ -421,10 +344,9 @@ impl Function {

Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
vm_function: VMFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
Expand Down Expand Up @@ -469,10 +391,9 @@ impl Function {

Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
metadata: Some(Arc::new(metadata)),
vm_function: VMExportFunction {
vm_function: VMFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
Expand Down Expand Up @@ -512,7 +433,7 @@ impl Function {

fn call_wasm(
&self,
func: &WasmFunctionDefinition,
trampoline: VMTrampoline,
params: &[Val],
results: &mut [Val],
) -> Result<(), RuntimeError> {
Expand Down Expand Up @@ -560,7 +481,7 @@ impl Function {
if let Err(error) = unsafe {
wasmer_call_trampoline(
self.exported.vm_function.vmctx,
func.trampoline,
trampoline,
self.exported.vm_function.address,
values_vec.as_mut_ptr() as *mut u8,
)
Expand Down Expand Up @@ -649,34 +570,19 @@ impl Function {
/// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]);
/// ```
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, RuntimeError> {
let mut results = vec![Val::null(); self.result_arity()];

match &self.definition {
FunctionDefinition::Wasm(wasm) => {
self.call_wasm(&wasm, params, &mut results)?;
}
// TODO: we can trivially hit this, look into it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably shouldn't remove this comment unless it's been fixed by this PR

_ => unimplemented!("The function definition isn't supported for the moment"),
if let Some(trampoline) = self.exported.vm_function.call_trampoline {
let mut results = vec![Val::null(); self.result_arity()];
self.call_wasm(trampoline, params, &mut results)?;
return Ok(results.into_boxed_slice());
}

Ok(results.into_boxed_slice())
unimplemented!("The function definition isn't supported for the moment");
}

pub(crate) fn from_vm_export(store: &Store, wasmer_export: ExportFunction) -> Self {
if let Some(trampoline) = wasmer_export.vm_function.call_trampoline {
Self {
store: store.clone(),
definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }),
exported: wasmer_export,
}
} else {
Self {
store: store.clone(),
definition: FunctionDefinition::Host(HostFunctionDefinition {
has_env: !wasmer_export.vm_function.vmctx.is_null(),
}),
exported: wasmer_export,
}
Self {
store: store.clone(),
exported: wasmer_export,
}
}

Expand Down Expand Up @@ -798,11 +704,7 @@ impl Function {
}
}

Ok(NativeFunc::new(
self.store.clone(),
self.exported.clone(),
self.definition.clone(),
))
Ok(NativeFunc::new(self.store.clone(), self.exported.clone()))
}

#[track_caller]
Expand Down Expand Up @@ -840,27 +742,7 @@ pub(crate) trait VMDynamicFunction: Send + Sync {
fn store(&self) -> &Store;
}

#[derive(Clone)]
pub(crate) struct DynamicFunctionWithoutEnv {
#[allow(clippy::type_complexity)]
func: Arc<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static + Send + Sync>,
function_type: FunctionType,
store: Store,
}

impl VMDynamicFunction for DynamicFunctionWithoutEnv {
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError> {
(*self.func)(&args)
}
fn function_type(&self) -> &FunctionType {
&self.function_type
}
fn store(&self) -> &Store {
&self.store
}
}

pub(crate) struct DynamicFunctionWithEnv<Env>
pub(crate) struct DynamicFunction<Env>
where
Env: Sized + 'static + Send + Sync,
{
Expand All @@ -871,7 +753,7 @@ where
env: Box<Env>,
}

impl<Env: Sized + Clone + 'static + Send + Sync> Clone for DynamicFunctionWithEnv<Env> {
impl<Env: Sized + Clone + 'static + Send + Sync> Clone for DynamicFunction<Env> {
fn clone(&self) -> Self {
Self {
env: self.env.clone(),
Expand All @@ -882,7 +764,7 @@ impl<Env: Sized + Clone + 'static + Send + Sync> Clone for DynamicFunctionWithEn
}
}

impl<Env> VMDynamicFunction for DynamicFunctionWithEnv<Env>
impl<Env> VMDynamicFunction for DynamicFunction<Env>
where
Env: Sized + 'static + Send + Sync,
{
Expand Down Expand Up @@ -1285,6 +1167,7 @@ mod inner {

/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does not have an environment.
#[derive(Clone)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloning an empty struct? Why? Is the struct just used in the type system? Or is it compared by pointer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's required for WasmerEnv to be clonable

pub struct WithoutEnv;

impl HostFunctionKind for WithoutEnv {}
Expand Down
Loading