From 683ea7110cfe1910a6e00186acdacf3ba99113ce Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 17 Oct 2019 17:36:02 +0200 Subject: [PATCH 01/27] feat(runtime-core) Support closures for imported functions. The trampoline and the invoke function takes a pointer to a function environment (`*mut vm::FuncEnv`) instead of a context (`*mut vm::Ctx`). The function environment `vm::FuncEnv` is an opaque pointer that can be of 2 kinds: Either a closure environment pointer, or `vm::Ctx`. --- lib/runtime-core/src/backing.rs | 31 ++--- lib/runtime-core/src/import.rs | 2 +- lib/runtime-core/src/instance.rs | 45 ++++--- lib/runtime-core/src/macros.rs | 2 +- lib/runtime-core/src/table/anyfunc.rs | 16 ++- lib/runtime-core/src/typed_func.rs | 183 ++++++++++++++------------ lib/runtime-core/src/vm.rs | 37 +++--- 7 files changed, 169 insertions(+), 147 deletions(-) diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index ff21e9195f1..55c334e7d94 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -365,13 +365,12 @@ impl LocalBacking { table.anyfunc_direct_access_mut(|elements| { for (i, &func_index) in init.elements.iter().enumerate() { let sig_index = module.info.func_assoc[func_index]; - // let signature = &module.info.signatures[sig_index]; let signature = SigRegistry .lookup_signature_ref(&module.info.signatures[sig_index]); let sig_id = vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(&module.info) { + let (func, env) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .runnable_module @@ -379,16 +378,17 @@ impl LocalBacking { .unwrap() .as_ptr() as *const vm::Func, - vmctx, + vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, vmctx } = + let vm::ImportedFunc { func, env } = imports.vm_functions[imported_func_index]; - (func, vmctx) + + (func, env) } }; - elements[init_base + i] = vm::Anyfunc { func, ctx, sig_id }; + elements[init_base + i] = vm::Anyfunc { func, env, sig_id }; } }); } @@ -400,11 +400,10 @@ impl LocalBacking { let sig_index = module.info.func_assoc[func_index]; let signature = SigRegistry .lookup_signature_ref(&module.info.signatures[sig_index]); - // let signature = &module.info.signatures[sig_index]; let sig_id = vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(&module.info) { + let (func, env) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .runnable_module @@ -412,16 +411,17 @@ impl LocalBacking { .unwrap() .as_ptr() as *const vm::Func, - vmctx, + vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, vmctx } = + let vm::ImportedFunc { func, env } = imports.vm_functions[imported_func_index]; - (func, vmctx) + + (func, env) } }; - elements[init_base + i] = vm::Anyfunc { func, ctx, sig_id }; + elements[init_base + i] = vm::Anyfunc { func, env, sig_id }; } }); } @@ -564,6 +564,7 @@ fn import_functions( let import = imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name)); + match import { Some(Export::Function { func, @@ -573,10 +574,10 @@ fn import_functions( if *expected_sig == *signature { functions.push(vm::ImportedFunc { func: func.inner(), - vmctx: match ctx { + env: match ctx { Context::External(ctx) => ctx, Context::Internal => vmctx, - }, + } as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` }); } else { link_errors.push(LinkError::IncorrectImportSignature { @@ -606,7 +607,7 @@ fn import_functions( if imports.allow_missing_functions { functions.push(vm::ImportedFunc { func: ::std::ptr::null(), - vmctx: ::std::ptr::null_mut(), + env: ::std::ptr::null_mut(), }); } else { link_errors.push(LinkError::ImportNotFound { diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 9d86205b156..6d13f40521c 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -40,7 +40,7 @@ impl IsExport for Export { /// }, /// }; /// -/// fn foo(_: &mut Ctx, n: i32) -> i32 { +/// fn foo(n: i32) -> i32 { /// n /// } /// ``` diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 85e9f8f768b..364edb179cd 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -108,11 +108,13 @@ impl Instance { .unwrap(), }; - let ctx_ptr = match start_index.local_or_import(&instance.module.info) { - LocalOrImport::Local(_) => instance.inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - instance.inner.import_backing.vm_functions[imported_func_index].vmctx + let env_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(_) => { + NonNull::new(instance.inner.vmctx).map(|pointer| pointer.cast()) } + LocalOrImport::Import(imported_func_index) => NonNull::new( + instance.inner.import_backing.vm_functions[imported_func_index].env, + ), }; let sig_index = *instance @@ -129,7 +131,7 @@ impl Instance { .expect("wasm trampoline"); let start_func: Func<(), (), Wasm> = - unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) }; + unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, env_ptr) }; start_func.call()?; } @@ -193,13 +195,6 @@ impl Instance { })?; } - let ctx = match func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(_) => self.inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - self.inner.import_backing.vm_functions[imported_func_index].vmctx - } - }; - let func_wasm_inner = self .module .runnable_module @@ -218,8 +213,17 @@ impl Instance { .unwrap(), }; + let env_ptr = match func_index.local_or_import(&self.module.info) { + LocalOrImport::Local(_) => { + NonNull::new(self.inner.vmctx).map(|pointer| pointer.cast()) + } + LocalOrImport::Import(imported_func_index) => { + NonNull::new(self.inner.import_backing.vm_functions[imported_func_index].env) + } + }; + let typed_func: Func = - unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) }; + unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, env_ptr) }; Ok(typed_func) } else { @@ -449,13 +453,12 @@ impl InstanceInner { let imported_func = &self.import_backing.vm_functions[imported_func_index]; ( imported_func.func as *const _, - Context::External(imported_func.vmctx), + Context::External(imported_func.env as _), // cast `*mut vm::Ctx` to `*mut vm::Func` ) } }; let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]); - // let signature = &module.info.signatures[sig_index]; (unsafe { FuncPointer::new(func_ptr) }, ctx, signature) } @@ -572,10 +575,10 @@ fn call_func_with_index( } }; - let ctx_ptr = match func_index.local_or_import(info) { - LocalOrImport::Local(_) => local_ctx, + let env_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(_) => NonNull::new(local_ctx).map(|pointer| pointer.cast()), LocalOrImport::Import(imported_func_index) => { - import_backing.vm_functions[imported_func_index].vmctx + NonNull::new(import_backing.vm_functions[imported_func_index].env) } }; @@ -583,11 +586,11 @@ fn call_func_with_index( .get_trampoline(info, sig_index) .expect("wasm trampoline"); - call_func_with_index_inner(ctx_ptr, func_ptr, signature, wasm, args, rets) + call_func_with_index_inner(env_ptr, func_ptr, signature, wasm, args, rets) } pub(crate) fn call_func_with_index_inner( - ctx_ptr: *mut vm::Ctx, + env_ptr: Option>, func_ptr: NonNull, signature: &FuncSig, wasm: Wasm, @@ -651,7 +654,7 @@ pub(crate) fn call_func_with_index_inner( let success = invoke( trampoline, - ctx_ptr, + env_ptr, func_ptr, raw_args.as_ptr(), result_space, diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index 6dbb93ca09c..d5a81f7d4c4 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -69,7 +69,7 @@ macro_rules! func { /// }, /// }; /// -/// fn foo(_: &mut Ctx, n: i32) -> i32 { +/// fn foo(n: i32) -> i32 { /// n /// } /// ``` diff --git a/lib/runtime-core/src/table/anyfunc.rs b/lib/runtime-core/src/table/anyfunc.rs index 4a63c8f6249..74bb0c22710 100644 --- a/lib/runtime-core/src/table/anyfunc.rs +++ b/lib/runtime-core/src/table/anyfunc.rs @@ -7,11 +7,12 @@ use crate::{ vm, }; -use std::{ptr, sync::Arc}; +use std::sync::Arc; enum AnyfuncInner<'a> { Host { ptr: *const vm::Func, + env: *mut vm::FuncEnv, signature: Arc, }, Managed(DynFunc<'a>), @@ -22,13 +23,14 @@ pub struct Anyfunc<'a> { } impl<'a> Anyfunc<'a> { - pub unsafe fn new(func: *const vm::Func, signature: Sig) -> Self + pub unsafe fn new(func: *const vm::Func, env: *mut vm::FuncEnv, signature: Sig) -> Self where Sig: Into>, { Self { inner: AnyfuncInner::Host { ptr: func as _, + env, signature: signature.into(), }, } @@ -99,13 +101,17 @@ impl AnyfuncTable { pub fn set(&mut self, index: u32, element: Anyfunc) -> Result<(), ()> { if let Some(slot) = self.backing.get_mut(index as usize) { let anyfunc = match element.inner { - AnyfuncInner::Host { ptr, signature } => { + AnyfuncInner::Host { + ptr, + env, + signature, + } => { let sig_index = SigRegistry.lookup_sig_index(signature); let sig_id = vm::SigId(sig_index.index() as u32); vm::Anyfunc { func: ptr, - ctx: ptr::null_mut(), + env, sig_id, } } @@ -115,7 +121,7 @@ impl AnyfuncTable { vm::Anyfunc { func: func.raw(), - ctx: func.instance_inner.vmctx, + env: func.instance_inner.vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` sig_id, } } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 74318747c2f..74ea3d966eb 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -3,16 +3,10 @@ use crate::{ export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, - vm::{self, Ctx}, + vm, }; use std::{ - any::Any, - convert::Infallible, - ffi::c_void, - fmt, - marker::PhantomData, - mem, panic, - ptr::{self, NonNull}, + any::Any, convert::Infallible, ffi::c_void, fmt, marker::PhantomData, mem, panic, ptr::NonNull, sync::Arc, }; @@ -52,10 +46,12 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} -pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); +pub type Trampoline = + unsafe extern "C" fn(Option>, NonNull, *const u64, *mut u64); + pub type Invoke = unsafe extern "C" fn( Trampoline, - *mut Ctx, + Option>, NonNull, *const u64, *mut u64, @@ -122,9 +118,9 @@ pub trait WasmTypeList { /// directly in the code, see the `Func:call` implementation. unsafe fn call( self, - f: NonNull, + func: NonNull, wasm: Wasm, - ctx: *mut Ctx, + env: Option>, ) -> Result where Rets: WasmTypeList; @@ -137,7 +133,7 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(&self) -> NonNull; + fn to_raw(self) -> (NonNull, Option>); } pub trait TrapEarly @@ -172,8 +168,8 @@ where /// Represents a function that can be used by WebAssembly. pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, - f: NonNull, - ctx: *mut Ctx, + func: NonNull, + env: Option>, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -187,19 +183,19 @@ where { pub(crate) unsafe fn from_raw_parts( inner: Wasm, - f: NonNull, - ctx: *mut Ctx, + func: NonNull, + env: Option>, ) -> Func<'a, Args, Rets, Wasm> { Func { inner, - f, - ctx, + func, + env, _phantom: PhantomData, } } pub fn get_vm_func(&self) -> NonNull { - self.f + self.func } } @@ -208,14 +204,16 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Host> + pub fn new(func: F) -> Func<'a, Args, Rets, Host> where F: ExternalFunction, { + let (func, env) = func.to_raw(); + Func { inner: Host(()), - f: f.to_raw(), - ctx: ptr::null_mut(), + func, + env, _phantom: PhantomData, } } @@ -267,7 +265,7 @@ impl WasmTypeList for Infallible { self, _: NonNull, _: Wasm, - _: *mut Ctx, + _: Option>, ) -> Result where Rets: WasmTypeList, @@ -311,9 +309,9 @@ where #[allow(non_snake_case)] unsafe fn call( self, - f: NonNull, + func: NonNull, wasm: Wasm, - ctx: *mut Ctx, + env: Option>, ) -> Result where Rets: WasmTypeList, @@ -326,8 +324,8 @@ where if (wasm.invoke)( wasm.trampoline, - ctx, - f, + env, + func, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, @@ -352,7 +350,7 @@ where Rets: WasmTypeList, { pub fn call(&self, a: A) -> Result { - unsafe { ::call(a, self.f, self.inner, self.ctx) } + unsafe { ::call(a, self.func, self.inner, self.env) } } } @@ -403,9 +401,9 @@ macro_rules! impl_traits { #[allow(non_snake_case)] unsafe fn call( self, - f: NonNull, + func: NonNull, wasm: Wasm, - ctx: *mut Ctx, + env: Option>, ) -> Result where Rets: WasmTypeList @@ -419,8 +417,8 @@ macro_rules! impl_traits { if (wasm.invoke)( wasm.trampoline, - ctx, - f, + env, + func, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, @@ -443,56 +441,62 @@ macro_rules! impl_traits { $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn(&mut Ctx $( , $x )*) -> Trap, + FN: Fn( $( $x ),* ) -> Trap + 'static, { #[allow(non_snake_case)] - fn to_raw(&self) -> NonNull { - if mem::size_of::() == 0 { - /// This is required for the llvm backend to be able to unwind through this function. - #[cfg_attr(nightly, unwind(allowed))] - extern fn wrap<$( $x, )* Rets, Trap, FN>( - ctx: &mut Ctx $( , $x: <$x as WasmExternType>::Native )* - ) -> Rets::CStruct - where - $( $x: WasmExternType, )* - Rets: WasmTypeList, - Trap: TrapEarly, - FN: Fn(&mut Ctx $( , $x )*) -> Trap, - { - let f: FN = unsafe { mem::transmute_copy(&()) }; - - let err = match panic::catch_unwind( - panic::AssertUnwindSafe( - || { - f(ctx $( , WasmExternType::from_native($x) )* ).report() - } - ) - ) { - Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => { - let b: Box<_> = err.into(); - b as Box - }, - Err(err) => err, - }; - - unsafe { - (&*ctx.module).runnable_module.do_early_trap(err) - } - } - - NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap() + fn to_raw(self) -> (NonNull, Option>) { + let env: Option> = if mem::size_of::() != 0 { + NonNull::new(Box::leak(Box::new(self))).map(|pointer| pointer.cast()) } else { - assert_eq!( - mem::size_of::(), - mem::size_of::(), - "you cannot use a closure that captures state for `Func`." - ); - - NonNull::new(unsafe { - ::std::mem::transmute_copy::<_, *mut vm::Func>(self) - }).unwrap() + None + }; + + /// This is required for the llvm backend to be able to unwind through this function. + #[cfg_attr(nightly, unwind(allowed))] + extern fn wrap<$( $x, )* Rets, Trap, FN>( + env: *mut vm::FuncEnv + $( , $x: <$x as WasmExternType>::Native )* + ) -> Rets::CStruct + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn( $( $x ),* ) -> Trap + 'static, + { + let func: &FN = if mem::size_of::() != 0 { + unsafe { &*(env as *const FN) } + } else { + unsafe { mem::transmute(&()) } + }; + + let _err = match panic::catch_unwind( + panic::AssertUnwindSafe( + || { + func( $( WasmExternType::from_native($x) ),* ).report() + } + ) + ) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, + Err(err) => err, + }; + + /* + unsafe { + (&*ctx.module).runnable_module.do_early_trap(err) + } + */ + eprintln!("early trap ahhh"); + ::std::process::exit(1) } + + ( + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(), + env, + ) } } @@ -507,9 +511,9 @@ macro_rules! impl_traits { unsafe { <( $( $x ),* ) as WasmTypeList>::call( ( $( $x ),* ), - self.f, + self.func, self.inner, - self.ctx + self.env, ) } } @@ -547,8 +551,12 @@ where Inner: Kind, { fn to_export(&self) -> Export { - let func = unsafe { FuncPointer::new(self.f.as_ptr()) }; - let ctx = Context::Internal; + let func = unsafe { FuncPointer::new(self.func.as_ptr()) }; + let ctx = if let Some(ptr) = self.env { + Context::External(ptr.cast().as_ptr()) + } else { + Context::Internal + }; let signature = Arc::new(FuncSig::new(Args::types(), Rets::types())); Export::Function { @@ -562,24 +570,25 @@ where #[cfg(test)] mod tests { use super::*; + #[test] fn test_call() { - fn foo(_ctx: &mut Ctx, a: i32, b: i32) -> (i32, i32) { + fn foo(a: i32, b: i32) -> (i32, i32) { (a, b) } - let _f = Func::new(foo); + let _ = Func::new(foo); } #[test] fn test_imports() { use crate::{func, imports}; - fn foo(_ctx: &mut Ctx, a: i32) -> i32 { + fn foo(a: i32) -> i32 { a } - let _import_object = imports! { + let _ = imports! { "env" => { "foo" => func!(foo), }, diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index fd8cebbec9a..c7025ed923e 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -406,7 +406,7 @@ impl Ctx { ) -> CallResult> { let anyfunc_table = unsafe { &*((**self.internal.tables).table as *mut crate::table::AnyfuncTable) }; - let Anyfunc { func, ctx, sig_id } = anyfunc_table.backing[index.index()]; + let Anyfunc { func, env, sig_id } = anyfunc_table.backing[index.index()]; let signature = SigRegistry.lookup_signature(unsafe { std::mem::transmute(sig_id.0) }); let mut rets = vec![]; @@ -422,7 +422,7 @@ impl Ctx { }; call_func_with_index_inner( - ctx, + NonNull::new(env), NonNull::new(func as *mut _).unwrap(), &signature, wasm, @@ -498,22 +498,25 @@ impl Ctx { } } -enum InnerFunc {} /// Used to provide type safety (ish) for passing around function pointers. -/// The typesystem ensures this cannot be dereferenced since an -/// empty enum cannot actually exist. #[repr(C)] -pub struct Func(InnerFunc); +pub struct Func { + _private: [u8; 0], +} + +#[repr(C)] +pub struct FuncEnv { + _private: [u8; 0], +} -/// An imported function, which contains the vmctx that owns this function. +/// An imported function, which contains an environment. #[derive(Debug, Clone)] #[repr(C)] pub struct ImportedFunc { pub func: *const Func, - pub vmctx: *mut Ctx, + pub env: *mut FuncEnv, } -// manually implemented because ImportedFunc contains raw pointers directly; `Func` is marked Send (But `Ctx` actually isn't! (TODO: review this, shouldn't `Ctx` be Send?)) unsafe impl Send for ImportedFunc {} impl ImportedFunc { @@ -522,7 +525,7 @@ impl ImportedFunc { 0 * (mem::size_of::() as u8) } - pub fn offset_vmctx() -> u8 { + pub fn offset_env() -> u8 { 1 * (mem::size_of::() as u8) } @@ -624,7 +627,7 @@ pub struct SigId(pub u32); #[repr(C)] pub struct Anyfunc { pub func: *const Func, - pub ctx: *mut Ctx, + pub env: *mut FuncEnv, pub sig_id: SigId, } @@ -635,7 +638,7 @@ impl Anyfunc { pub fn null() -> Self { Self { func: ptr::null(), - ctx: ptr::null_mut(), + env: ptr::null_mut(), sig_id: SigId(u32::max_value()), } } @@ -645,7 +648,7 @@ impl Anyfunc { 0 * (mem::size_of::() as u8) } - pub fn offset_vmctx() -> u8 { + pub fn offset_env() -> u8 { 1 * (mem::size_of::() as u8) } @@ -745,8 +748,8 @@ mod vm_offset_tests { ); assert_eq!( - ImportedFunc::offset_vmctx() as usize, - offset_of!(ImportedFunc => vmctx).get_byte_offset(), + ImportedFunc::offset_env() as usize, + offset_of!(ImportedFunc => env).get_byte_offset(), ); } @@ -792,8 +795,8 @@ mod vm_offset_tests { ); assert_eq!( - Anyfunc::offset_vmctx() as usize, - offset_of!(Anyfunc => ctx).get_byte_offset(), + Anyfunc::offset_env() as usize, + offset_of!(Anyfunc => env).get_byte_offset(), ); assert_eq!( From 534dfeca9514b658169f2f35b1f3c0d4e103ed33 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 17 Oct 2019 23:16:38 +0200 Subject: [PATCH 02/27] feat(cranelift-backend) Support closures for imported functions. This patch makes the crate compatible with previous commits on `runtime-core`. Notably: The `crate::trampoline::Trampoline` type is removed since `wasmer_runtime_core::typed_func::Trampoline` exists. --- lib/clif-backend/src/code.rs | 60 ++++++++------------------ lib/clif-backend/src/resolver.rs | 3 +- lib/clif-backend/src/signal/mod.rs | 10 ++--- lib/clif-backend/src/signal/unix.rs | 14 +++--- lib/clif-backend/src/signal/windows.rs | 50 +++++++++++---------- lib/clif-backend/src/trampoline.rs | 13 ++---- 6 files changed, 66 insertions(+), 84 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index b2ccc66520a..5130b91a6db 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -782,23 +782,12 @@ impl FuncEnvironment for FunctionEnvironment { vm::Anyfunc::offset_func() as i32, ); - let vmctx_ptr = { - let loaded_vmctx_ptr = pos.ins().load( - ptr_type, - mflags, - entry_addr, - vm::Anyfunc::offset_vmctx() as i32, - ); - - let argument_vmctx_ptr = pos - .func - .special_param(ir::ArgumentPurpose::VMContext) - .expect("missing vmctx parameter"); - - // If the loaded vmctx ptr is zero, use the caller vmctx, else use the callee (loaded) vmctx. - pos.ins() - .select(loaded_vmctx_ptr, loaded_vmctx_ptr, argument_vmctx_ptr) - }; + let env_ptr = pos.ins().load( + ptr_type, + mflags, + entry_addr, + vm::Anyfunc::offset_env() as i32, + ); let found_sig = pos.ins().load( ir::types::I32, @@ -821,16 +810,6 @@ impl FuncEnvironment for FunctionEnvironment { }); pos.ins().symbol_value(ir::types::I64, sig_index_global) - - // let dynamic_sigindices_array_ptr = pos.ins().load( - // ptr_type, - // mflags, - - // ) - - // let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64); - - // self.env.deduplicated[clif_sig_index] }; let not_equal_flags = pos.ins().ifcmp(found_sig, expected_sig); @@ -842,16 +821,15 @@ impl FuncEnvironment for FunctionEnvironment { ); // Build a value list for the indirect call instruction containing the call_args - // and the vmctx parameter. + // and the env parameter. let mut args = Vec::with_capacity(call_args.len() + 1); - args.push(vmctx_ptr); + args.push(env_ptr); args.extend(call_args.iter().cloned()); Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args)) } /// Generates a call IR with `callee` and `call_args` and inserts it at `pos` - /// TODO: add support for imported functions fn translate_call( &mut self, mut pos: FuncCursor, @@ -865,13 +843,13 @@ impl FuncEnvironment for FunctionEnvironment { match callee_index.local_or_import(&self.module_info.read().unwrap()) { LocalOrImport::Local(local_function_index) => { // this is an internal function - let vmctx = pos + let env = pos .func .special_param(ir::ArgumentPurpose::VMContext) .expect("missing vmctx parameter"); let mut args = Vec::with_capacity(call_args.len() + 1); - args.push(vmctx); + args.push(env); args.extend(call_args.iter().cloned()); let sig_ref = pos.func.dfg.ext_funcs[callee].signature; @@ -881,7 +859,7 @@ impl FuncEnvironment for FunctionEnvironment { let function_array_ptr = pos.ins().load( ptr_type, mflags, - vmctx, + env, vm::Ctx::offset_local_functions() as i32, ); @@ -897,10 +875,10 @@ impl FuncEnvironment for FunctionEnvironment { } LocalOrImport::Import(imported_func_index) => { // this is an imported function - let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext); + let env = pos.func.create_global_value(ir::GlobalValueData::VMContext); let imported_funcs = pos.func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, + base: env, offset: (vm::Ctx::offset_imported_funcs() as i32).into(), global_type: ptr_type, readonly: true, @@ -923,25 +901,25 @@ impl FuncEnvironment for FunctionEnvironment { readonly: true, }); - let imported_vmctx_addr = pos.func.create_global_value(ir::GlobalValueData::Load { + let imported_env_addr = pos.func.create_global_value(ir::GlobalValueData::Load { base: imported_func_struct_addr, - offset: (vm::ImportedFunc::offset_vmctx() as i32).into(), + offset: (vm::ImportedFunc::offset_env() as i32).into(), global_type: ptr_type, readonly: true, }); let imported_func_addr = pos.ins().global_value(ptr_type, imported_func_addr); - let imported_vmctx_addr = pos.ins().global_value(ptr_type, imported_vmctx_addr); + let imported_env_addr = pos.ins().global_value(ptr_type, imported_env_addr); let sig_ref = pos.func.dfg.ext_funcs[callee].signature; let mut args = Vec::with_capacity(call_args.len() + 1); - args.push(imported_vmctx_addr); + args.push(imported_env_addr); args.extend(call_args.iter().cloned()); Ok(pos .ins() - .call_indirect(sig_ref, imported_func_addr, &args[..])) + .call_indirect(sig_ref, imported_func_addr, args.as_slice())) } } } @@ -1086,7 +1064,7 @@ impl FunctionEnvironment { Converter(sig_index).into() } - /// Creates a signature with VMContext as the last param + /// Creates a signature with VMContext as the first param pub fn generate_signature( &self, clif_sig_index: cranelift_wasm::SignatureIndex, diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 7a868f35e46..f543b48298a 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -49,7 +49,7 @@ fn lookup_func( let offset = *map.get(local_func_index)?; let ptr = unsafe { memory.as_ptr().add(offset) }; - NonNull::new(ptr).map(|nonnull| nonnull.cast()) + NonNull::new(ptr).map(|ptr| ptr.cast()) } #[allow(dead_code)] @@ -62,6 +62,7 @@ pub struct FuncResolverBuilder { } pub struct NoopStackmapSink {} + impl StackmapSink for NoopStackmapSink { fn add_stackmap(&mut self, _: u32, _: Stackmap) {} } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 3facce2ad68..78b9b65c1dd 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -6,7 +6,7 @@ use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ backend::RunnableModule, module::ModuleInfo, - typed_func::{Wasm, WasmTrapInfo}, + typed_func::{Trampoline, Wasm, WasmTrapInfo}, types::{LocalFuncIndex, SigIndex}, vm, }; @@ -59,8 +59,8 @@ impl RunnableModule for Caller { fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { unsafe extern "C" fn invoke( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), - ctx: *mut vm::Ctx, + trampoline: Trampoline, + env: Option>, func: NonNull, args: *const u64, rets: *mut u64, @@ -73,12 +73,12 @@ impl RunnableModule for Caller { #[cfg(not(target_os = "windows"))] let res = call_protected(handler_data, || { // Leap of faith. - trampoline(ctx, func, args, rets); + trampoline(env, func, args, rets); }); // the trampoline is called from C on windows #[cfg(target_os = "windows")] - let res = call_protected(handler_data, trampoline, ctx, func, args, rets); + let res = call_protected(handler_data, trampoline, env, func, args, rets); match res { Err(err) => { diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 39f3aa893c6..06df736cc04 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -9,15 +9,19 @@ //! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here //! unless you have memory unsafety elsewhere in your code. //! -use crate::relocation::{TrapCode, TrapData}; -use crate::signal::{CallProtError, HandlerData}; +use crate::{ + relocation::{TrapCode, TrapData}, + signal::{CallProtError, HandlerData}, +}; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, }; -use std::cell::{Cell, UnsafeCell}; -use std::ptr; -use std::sync::Once; +use std::{ + cell::{Cell, UnsafeCell}, + ptr, + sync::Once, +}; use wasmer_runtime_core::typed_func::WasmTrapInfo; extern "C" fn signal_trap_handler( diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index d755cd575d2..36b5656b397 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -1,24 +1,30 @@ -use crate::relocation::{TrapCode, TrapData}; -use crate::signal::{CallProtError, HandlerData}; -use crate::trampoline::Trampoline; -use std::cell::Cell; -use std::ffi::c_void; -use std::ptr::{self, NonNull}; -use wasmer_runtime_core::typed_func::WasmTrapInfo; -use wasmer_runtime_core::vm::Ctx; -use wasmer_runtime_core::vm::Func; +use crate::{ + relocation::{TrapCode, TrapData}, + signal::{CallProtError, HandlerData}, +}; +use std::{ + cell::Cell, + ffi::c_void, + ptr::{self, NonNull}, +}; +use wasmer_runtime_core::{ + typed_func::{Trampoline, WasmTrapInfo}, + vm, +}; use wasmer_win_exception_handler::CallProtectedData; pub use wasmer_win_exception_handler::_call_protected; -use winapi::shared::minwindef::DWORD; -use winapi::um::minwinbase::{ - EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, - EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, - EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, - EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, - EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, - EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, - EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, - EXCEPTION_STACK_OVERFLOW, +use winapi::{ + shared::minwindef::DWORD, + um::minwinbase::{ + EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, + EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, + EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT, + EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_FLT_STACK_CHECK, + EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_ILLEGAL_INSTRUCTION, + EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_INVALID_HANDLE, + EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, EXCEPTION_POSSIBLE_DEADLOCK, + EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_STACK_OVERFLOW, + }, }; thread_local! { @@ -28,8 +34,8 @@ thread_local! { pub fn call_protected( handler_data: &HandlerData, trampoline: Trampoline, - ctx: *mut Ctx, - func: NonNull, + env: Option>, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, ) -> Result<(), CallProtError> { @@ -39,7 +45,7 @@ pub fn call_protected( // return Err(RuntimeError::User { msg }); // } - let result = _call_protected(trampoline, ctx, func, param_vec, return_vec); + let result = _call_protected(trampoline, env, func, param_vec, return_vec); if let Ok(_) = result { return Ok(()); diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index a6fc6572bac..6f5655a304b 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -7,12 +7,12 @@ use cranelift_codegen::{ isa, Context, }; use std::collections::HashMap; -use std::{iter, mem, ptr::NonNull}; +use std::{iter, mem}; use wasmer_runtime_core::{ backend::sys::{Memory, Protect}, module::{ExportIndex, ModuleInfo}, + typed_func::Trampoline, types::{FuncSig, SigIndex, Type}, - vm, }; struct NullRelocSink {} @@ -28,8 +28,6 @@ impl RelocSink for NullRelocSink { fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {} } -pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64); - pub struct Trampolines { memory: Memory, offsets: HashMap, @@ -37,12 +35,6 @@ pub struct Trampolines { impl Trampolines { pub fn from_trampoline_cache(cache: TrampolineCache) -> Self { - // pub struct TrampolineCache { - // #[serde(with = "serde_bytes")] - // code: Vec, - // offsets: HashMap, - // } - let mut memory = Memory::with_size(cache.code.len()).unwrap(); unsafe { memory.protect(.., Protect::ReadWrite).unwrap(); @@ -173,6 +165,7 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function { let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1); args_vec.push(vmctx_ptr); + for (index, wasm_ty) in func_sig.params().iter().enumerate() { let mem_flags = ir::MemFlags::trusted(); From 3ecf2f25b982289957df00ffcb9c3d878940c66b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 17 Oct 2019 23:48:32 +0200 Subject: [PATCH 03/27] feat(win-exception-handler) Use `wasmer_runtime_core::typed_func::Trampoline`. This patch uses the definition of `Trampoline` given in `runtime-core` instead of copying it. This patch also makes the C code more clear regarding what is opaque. `wasmer_instance_context_t` is also renamed to `funcenv_t` to avoid confusion with the Wasmer runtime C API. --- .../exception_handling/exception_handling.c | 19 +++++++-------- .../exception_handling/exception_handling.h | 23 ++++++++++--------- .../src/exception_handling.rs | 17 ++++++++------ 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/win-exception-handler/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c index c3ecbab1d74..47ad2809f90 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.c +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -49,13 +49,14 @@ static void removeExceptionHandler() { exceptionHandlerInstalled = FALSE; } -uint8_t callProtected(trampoline_t trampoline, - const struct wasmer_instance_context_t* ctx, - const struct func_t* func, - const uint64_t* param_vec, - uint64_t* return_vec, - struct call_protected_result_t* out_result) { - +uint8_t callProtected( + trampoline_t trampoline, + const funcenv_t* func_env, + const func_t* func, + const uint64_t* param_vec, + uint64_t* return_vec, + call_protected_result_t* out_result +) { // install exception handler if (exceptionHandlerInstalled == FALSE) { exceptionHandlerInstalled = TRUE; @@ -68,7 +69,7 @@ uint8_t callProtected(trampoline_t trampoline, { // save the stack pointer savedStackPointer = get_callee_frame_address(); - trampoline(ctx, func, param_vec, return_vec); + trampoline(func_env, func, param_vec, return_vec); out_result->code = 0; out_result->exception_address = 0; out_result->instruction_pointer = 0; @@ -86,4 +87,4 @@ uint8_t callProtected(trampoline_t trampoline, removeExceptionHandler(); return FALSE; -} \ No newline at end of file +} diff --git a/lib/win-exception-handler/exception_handling/exception_handling.h b/lib/win-exception-handler/exception_handling/exception_handling.h index f00227600f1..b06ac97273a 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.h +++ b/lib/win-exception-handler/exception_handling/exception_handling.h @@ -3,23 +3,24 @@ #include -struct func_t; -struct wasmer_instance_context_t; +typedef struct func_t func_t; +typedef struct funcenv_t funcenv_t; -typedef void(*trampoline_t)(struct wasmer_instance_context_t*, const struct func_t*, const uint64_t*, uint64_t*); +typedef void(*trampoline_t)(const funcenv_t*, const func_t*, const uint64_t*, uint64_t*); -struct call_protected_result_t { +typedef struct call_protected_result_t { uint64_t code; uint64_t exception_address; uint64_t instruction_pointer; -}; +} call_protected_result_t; uint8_t callProtected( - trampoline_t trampoline, - const struct wasmer_instance_context_t* ctx, - const struct func_t* func, - const uint64_t* param_vec, - uint64_t* return_vec, - struct call_protected_result_t* out_result); + trampoline_t trampoline, + const funcenv_t* func_env, + const func_t* func, + const uint64_t* param_vec, + uint64_t* return_vec, + call_protected_result_t* out_result +); #endif //WASMER_EXCEPTION_HANDLING_H diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index c448392b0a3..fde22068b40 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -1,7 +1,9 @@ use std::ptr::NonNull; -use wasmer_runtime_core::vm::{Ctx, Func}; +use wasmer_runtime_core::{ + typed_func::Trampoline, + vm::{self, Ctx}, +}; -type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); type CallProtectedResult = Result<(), CallProtectedData>; #[repr(C)] @@ -15,8 +17,8 @@ extern "C" { #[link_name = "callProtected"] pub fn __call_protected( trampoline: Trampoline, - ctx: *mut Ctx, - func: NonNull, + env: Option>, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, out_result: *mut CallProtectedData, @@ -25,8 +27,8 @@ extern "C" { pub fn _call_protected( trampoline: Trampoline, - ctx: *mut Ctx, - func: NonNull, + env: Option>, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, ) -> CallProtectedResult { @@ -38,13 +40,14 @@ pub fn _call_protected( let result = unsafe { __call_protected( trampoline, - ctx, + env, func, param_vec, return_vec, &mut out_result, ) }; + if result == 1 { Ok(()) } else { From b7fa66a7ecdf9a29862a0fdec20609bdf8e83930 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 00:11:10 +0200 Subject: [PATCH 04/27] feat(llvm-backend) Use `wasmer_runtime_core` trampoline and invoke definitions. This patch updates the LLVM backend to the previous commits that update `wasmer-runtime-core` to support closures as imported functions. The definitions of `Trampoline` and `Invoke` have changed. Use the definitions from `runtime-core` instead of copying it. --- lib/llvm-backend/cpp/object_loader.hh | 6 +++--- lib/llvm-backend/src/backend.rs | 8 ++++---- lib/llvm-backend/src/stackmap.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index bc9b9ab6717..121ea39a904 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -285,12 +285,12 @@ void module_delete(WasmModule *module) { delete module; } unsafe_unwind(new BreakpointException(callback)); } -bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func, +bool invoke_trampoline(trampoline_t trampoline, void *func_env, void *func, void *params, void *results, WasmTrapType *trap_out, box_any_t *user_error, void *invoke_env) noexcept { try { - catch_unwind([trampoline, ctx, func, params, results]() { - trampoline(ctx, func, params, results); + catch_unwind([trampoline, func_env, func, params, results]() { + trampoline(func_env, func, params, results); }); return true; } catch (std::unique_ptr &e) { diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 682bd72782b..1017cf5a374 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -28,7 +28,7 @@ use wasmer_runtime_core::{ module::ModuleInfo, state::ModuleStateMap, structures::TypedIndex, - typed_func::{Wasm, WasmTrapInfo}, + typed_func::{Trampoline, Wasm, WasmTrapInfo}, types::{LocalFuncIndex, SigIndex}, vm, vmcalls, }; @@ -58,8 +58,8 @@ extern "C" { #[allow(improper_ctypes)] fn invoke_trampoline( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), - vmctx_ptr: *mut vm::Ctx, + trampoline: Trampoline, + env_ptr: Option>, func_ptr: NonNull, params: *const u64, results: *mut u64, @@ -409,7 +409,7 @@ impl RunnableModule for LLVMBackend { fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { let trampoline: unsafe extern "C" fn( - *mut vm::Ctx, + Option>, NonNull, *const u64, *mut u64, diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs index a56c3c6a38d..a9be2242060 100644 --- a/lib/llvm-backend/src/stackmap.rs +++ b/lib/llvm-backend/src/stackmap.rs @@ -161,7 +161,7 @@ impl StackmapEntry { ValueSemantic::ImportedFuncCtx(idx) => MachineValue::VmctxDeref(vec![ Ctx::offset_imported_funcs() as usize, vm::ImportedFunc::size() as usize * idx - + vm::ImportedFunc::offset_vmctx() as usize, + + vm::ImportedFunc::offset_env() as usize, 0, ]), ValueSemantic::DynamicSigindice(idx) => { From 861865a859782bdcc8b021f745f54b39a3e3bfc4 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 17:39:58 +0200 Subject: [PATCH 05/27] feat(runtime-core) `vm::Ctx` is explicitely passed to `Invoke`. --- lib/runtime-core/src/backing.rs | 26 +++++++---- lib/runtime-core/src/instance.rs | 52 ++++++++++++++-------- lib/runtime-core/src/table/anyfunc.rs | 5 +-- lib/runtime-core/src/typed_func.rs | 64 +++++++++++++++++---------- lib/runtime-core/src/vm.rs | 19 +++++--- 5 files changed, 104 insertions(+), 62 deletions(-) diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 55c334e7d94..1a17582f165 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -370,7 +370,7 @@ impl LocalBacking { let sig_id = vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, env) = match func_index.local_or_import(&module.info) { + let (func, func_env) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .runnable_module @@ -381,14 +381,18 @@ impl LocalBacking { vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, env } = + let vm::ImportedFunc { func, func_env } = imports.vm_functions[imported_func_index]; - (func, env) + (func, func_env) } }; - elements[init_base + i] = vm::Anyfunc { func, env, sig_id }; + elements[init_base + i] = vm::Anyfunc { + func, + func_env, + sig_id, + }; } }); } @@ -414,14 +418,18 @@ impl LocalBacking { vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, env } = + let vm::ImportedFunc { func, func_env } = imports.vm_functions[imported_func_index]; - (func, env) + (func, func_env) } }; - elements[init_base + i] = vm::Anyfunc { func, env, sig_id }; + elements[init_base + i] = vm::Anyfunc { + func, + func_env: env, + sig_id, + }; } }); } @@ -574,7 +582,7 @@ fn import_functions( if *expected_sig == *signature { functions.push(vm::ImportedFunc { func: func.inner(), - env: match ctx { + func_env: match ctx { Context::External(ctx) => ctx, Context::Internal => vmctx, } as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` @@ -607,7 +615,7 @@ fn import_functions( if imports.allow_missing_functions { functions.push(vm::ImportedFunc { func: ::std::ptr::null(), - env: ::std::ptr::null_mut(), + func_env: ::std::ptr::null_mut(), }); } else { link_errors.push(LinkError::ImportNotFound { diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 364edb179cd..5ebee48833e 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -113,7 +113,7 @@ impl Instance { NonNull::new(instance.inner.vmctx).map(|pointer| pointer.cast()) } LocalOrImport::Import(imported_func_index) => NonNull::new( - instance.inner.import_backing.vm_functions[imported_func_index].env, + instance.inner.import_backing.vm_functions[imported_func_index].func_env, ), }; @@ -130,8 +130,14 @@ impl Instance { .get_trampoline(&instance.module.info, sig_index) .expect("wasm trampoline"); - let start_func: Func<(), (), Wasm> = - unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, env_ptr) }; + let start_func: Func<(), (), Wasm> = unsafe { + Func::from_raw_parts( + wasm_trampoline, + func_ptr, + env_ptr, + NonNull::new(instance.inner.vmctx), + ) + }; start_func.call()?; } @@ -213,17 +219,23 @@ impl Instance { .unwrap(), }; - let env_ptr = match func_index.local_or_import(&self.module.info) { + let func_env_ptr = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => { NonNull::new(self.inner.vmctx).map(|pointer| pointer.cast()) } - LocalOrImport::Import(imported_func_index) => { - NonNull::new(self.inner.import_backing.vm_functions[imported_func_index].env) - } + LocalOrImport::Import(imported_func_index) => NonNull::new( + self.inner.import_backing.vm_functions[imported_func_index].func_env, + ), }; - let typed_func: Func = - unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, env_ptr) }; + let typed_func: Func = unsafe { + Func::from_raw_parts( + func_wasm_inner, + func_ptr, + func_env_ptr, + NonNull::new(self.inner.vmctx), + ) + }; Ok(typed_func) } else { @@ -349,7 +361,7 @@ impl Instance { &self.module.info, &*self.module.runnable_module, &self.inner.import_backing, - self.inner.vmctx, + NonNull::new(self.inner.vmctx), func_index, params, &mut results, @@ -453,7 +465,7 @@ impl InstanceInner { let imported_func = &self.import_backing.vm_functions[imported_func_index]; ( imported_func.func as *const _, - Context::External(imported_func.env as _), // cast `*mut vm::Ctx` to `*mut vm::Func` + Context::External(imported_func.func_env as _), // cast `*mut vm::Ctx` to `*mut vm::Func` ) } }; @@ -554,7 +566,7 @@ fn call_func_with_index( info: &ModuleInfo, runnable: &dyn RunnableModule, import_backing: &ImportBacking, - local_ctx: *mut vm::Ctx, + vmctx: Option>, func_index: FuncIndex, args: &[Value], rets: &mut Vec, @@ -575,10 +587,10 @@ fn call_func_with_index( } }; - let env_ptr = match func_index.local_or_import(info) { - LocalOrImport::Local(_) => NonNull::new(local_ctx).map(|pointer| pointer.cast()), + let func_env_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(_) => vmctx.map(|pointer| pointer.cast()), LocalOrImport::Import(imported_func_index) => { - NonNull::new(import_backing.vm_functions[imported_func_index].env) + NonNull::new(import_backing.vm_functions[imported_func_index].func_env) } }; @@ -586,11 +598,12 @@ fn call_func_with_index( .get_trampoline(info, sig_index) .expect("wasm trampoline"); - call_func_with_index_inner(env_ptr, func_ptr, signature, wasm, args, rets) + call_func_with_index_inner(vmctx, func_env_ptr, func_ptr, signature, wasm, args, rets) } pub(crate) fn call_func_with_index_inner( - env_ptr: Option>, + vmctx: Option>, + func_env_ptr: Option>, func_ptr: NonNull, signature: &FuncSig, wasm: Wasm, @@ -654,7 +667,8 @@ pub(crate) fn call_func_with_index_inner( let success = invoke( trampoline, - env_ptr, + vmctx, + func_env_ptr, func_ptr, raw_args.as_ptr(), result_space, @@ -768,7 +782,7 @@ impl<'a> DynFunc<'a> { &self.module.info, &*self.module.runnable_module, &self.instance_inner.import_backing, - self.instance_inner.vmctx, + NonNull::new(self.instance_inner.vmctx), self.func_index, params, &mut results, diff --git a/lib/runtime-core/src/table/anyfunc.rs b/lib/runtime-core/src/table/anyfunc.rs index 74bb0c22710..f76ba0b4e49 100644 --- a/lib/runtime-core/src/table/anyfunc.rs +++ b/lib/runtime-core/src/table/anyfunc.rs @@ -6,7 +6,6 @@ use crate::{ types::{FuncSig, TableDescriptor}, vm, }; - use std::sync::Arc; enum AnyfuncInner<'a> { @@ -111,7 +110,7 @@ impl AnyfuncTable { vm::Anyfunc { func: ptr, - env, + func_env: env, sig_id, } } @@ -121,7 +120,7 @@ impl AnyfuncTable { vm::Anyfunc { func: func.raw(), - env: func.instance_inner.vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` + func_env: func.instance_inner.vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` sig_id, } } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 74ea3d966eb..a8084bb08d2 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -46,18 +46,23 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} -pub type Trampoline = - unsafe extern "C" fn(Option>, NonNull, *const u64, *mut u64); +pub type Trampoline = unsafe extern "C" fn( + func_env: Option>, + func: NonNull, + args: *const u64, + rets: *mut u64, +); pub type Invoke = unsafe extern "C" fn( - Trampoline, - Option>, - NonNull, - *const u64, - *mut u64, - *mut WasmTrapInfo, - *mut Option>, - Option>, + trampoline: Trampoline, + vmctx: Option>, + func_env: Option>, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + extra: Option>, ) -> bool; /// TODO(lachlan): Naming TBD. @@ -119,8 +124,9 @@ pub trait WasmTypeList { unsafe fn call( self, func: NonNull, + func_env: Option>, + vmctx: Option>, wasm: Wasm, - env: Option>, ) -> Result where Rets: WasmTypeList; @@ -169,7 +175,8 @@ where pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, func: NonNull, - env: Option>, + func_env: Option>, + vmctx: Option>, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -184,12 +191,14 @@ where pub(crate) unsafe fn from_raw_parts( inner: Wasm, func: NonNull, - env: Option>, + func_env: Option>, + vmctx: Option>, ) -> Func<'a, Args, Rets, Wasm> { Func { inner, func, - env, + func_env, + vmctx, _phantom: PhantomData, } } @@ -208,12 +217,13 @@ where where F: ExternalFunction, { - let (func, env) = func.to_raw(); + let (func, func_env) = func.to_raw(); Func { inner: Host(()), func, - env, + func_env, + vmctx: None, _phantom: PhantomData, } } @@ -264,8 +274,9 @@ impl WasmTypeList for Infallible { unsafe fn call( self, _: NonNull, - _: Wasm, _: Option>, + _: Option>, + _: Wasm, ) -> Result where Rets: WasmTypeList, @@ -310,8 +321,9 @@ where unsafe fn call( self, func: NonNull, + func_env: Option>, + vmctx: Option>, wasm: Wasm, - env: Option>, ) -> Result where Rets: WasmTypeList, @@ -324,7 +336,8 @@ where if (wasm.invoke)( wasm.trampoline, - env, + vmctx, + func_env, func, args.as_ptr(), rets.as_mut().as_mut_ptr(), @@ -350,7 +363,7 @@ where Rets: WasmTypeList, { pub fn call(&self, a: A) -> Result { - unsafe { ::call(a, self.func, self.inner, self.env) } + unsafe { ::call(a, self.func, self.func_env, self.vmctx, self.inner) } } } @@ -402,8 +415,9 @@ macro_rules! impl_traits { unsafe fn call( self, func: NonNull, + func_env: Option>, + vmctx: Option>, wasm: Wasm, - env: Option>, ) -> Result where Rets: WasmTypeList @@ -417,7 +431,8 @@ macro_rules! impl_traits { if (wasm.invoke)( wasm.trampoline, - env, + vmctx, + func_env, func, args.as_ptr(), rets.as_mut().as_mut_ptr(), @@ -512,8 +527,9 @@ macro_rules! impl_traits { <( $( $x ),* ) as WasmTypeList>::call( ( $( $x ),* ), self.func, + self.func_env, + self.vmctx, self.inner, - self.env, ) } } @@ -552,7 +568,7 @@ where { fn to_export(&self) -> Export { let func = unsafe { FuncPointer::new(self.func.as_ptr()) }; - let ctx = if let Some(ptr) = self.env { + let ctx = if let Some(ptr) = self.func_env { Context::External(ptr.cast().as_ptr()) } else { Context::Internal diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index c7025ed923e..2e5c3c19ffe 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -406,7 +406,11 @@ impl Ctx { ) -> CallResult> { let anyfunc_table = unsafe { &*((**self.internal.tables).table as *mut crate::table::AnyfuncTable) }; - let Anyfunc { func, env, sig_id } = anyfunc_table.backing[index.index()]; + let Anyfunc { + func, + func_env, + sig_id, + } = anyfunc_table.backing[index.index()]; let signature = SigRegistry.lookup_signature(unsafe { std::mem::transmute(sig_id.0) }); let mut rets = vec![]; @@ -422,7 +426,8 @@ impl Ctx { }; call_func_with_index_inner( - NonNull::new(env), + None, + NonNull::new(func_env), NonNull::new(func as *mut _).unwrap(), &signature, wasm, @@ -514,7 +519,7 @@ pub struct FuncEnv { #[repr(C)] pub struct ImportedFunc { pub func: *const Func, - pub env: *mut FuncEnv, + pub func_env: *mut FuncEnv, } unsafe impl Send for ImportedFunc {} @@ -525,7 +530,7 @@ impl ImportedFunc { 0 * (mem::size_of::() as u8) } - pub fn offset_env() -> u8 { + pub fn offset_func_env() -> u8 { 1 * (mem::size_of::() as u8) } @@ -627,7 +632,7 @@ pub struct SigId(pub u32); #[repr(C)] pub struct Anyfunc { pub func: *const Func, - pub env: *mut FuncEnv, + pub func_env: *mut FuncEnv, pub sig_id: SigId, } @@ -638,7 +643,7 @@ impl Anyfunc { pub fn null() -> Self { Self { func: ptr::null(), - env: ptr::null_mut(), + func_env: ptr::null_mut(), sig_id: SigId(u32::max_value()), } } @@ -648,7 +653,7 @@ impl Anyfunc { 0 * (mem::size_of::() as u8) } - pub fn offset_env() -> u8 { + pub fn offset_func_env() -> u8 { 1 * (mem::size_of::() as u8) } From d6459e28783a94c46c6d4024be56a91f18198e4d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 17:40:36 +0200 Subject: [PATCH 06/27] feat(singlepass-backend) Use `wasmer_runtime_core` trampoline and invoke definitions. This patch updates the Singlepass backend to the previous commits that update `wasmer-runtime-core` to support closures as imported functions. The definitions of `Trampoline` and `Invoke` have changed. Use the definitions from `wasmer-runtime-core` instead of copying it. --- lib/singlepass-backend/src/codegen_x64.rs | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index c2543b987d8..79d2047e1ae 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,6 +10,7 @@ use std::ptr::NonNull; use std::{ any::Any, collections::{BTreeMap, HashMap}, + ptr, sync::{Arc, RwLock}, }; use wasmer_runtime_core::{ @@ -255,17 +256,13 @@ impl RunnableModule for X64ExecutionContext { } fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { - use std::ffi::c_void; - use wasmer_runtime_core::typed_func::WasmTrapInfo; + use std::{ffi::c_void, mem, slice}; + use wasmer_runtime_core::typed_func::{Trampoline, WasmTrapInfo}; unsafe extern "C" fn invoke( - _trampoline: unsafe extern "C" fn( - *mut vm::Ctx, - NonNull, - *const u64, - *mut u64, - ), - ctx: *mut vm::Ctx, + _trampoline: Trampoline, + vmctx: Option>, + func_env: Option>, func: NonNull, args: *const u64, rets: *mut u64, @@ -273,21 +270,24 @@ impl RunnableModule for X64ExecutionContext { user_error: *mut Option>, num_params_plus_one: Option>, ) -> bool { - let rm: &Box = &(&*(*ctx).module).runnable_module; + let vmctx: &vm::Ctx = vmctx + .map(|pointer| &*pointer.as_ptr()) + .expect("vmctx is null"); + let rm: &Box = &(*(vmctx.module)).runnable_module; let execution_context = - ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); - let args = std::slice::from_raw_parts( - args, - num_params_plus_one.unwrap().as_ptr() as usize - 1, - ); + let args = + slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1); let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); let ret = match protect_unix::call_protected( || { CONSTRUCT_STACK_AND_CALL_WASM( args_reverse.as_ptr(), args_reverse.as_ptr().offset(args_reverse.len() as isize), - ctx, + func_env + .map(|pointer| pointer.cast().as_ptr()) + .unwrap_or_else(ptr::null_mut), func.as_ptr(), ) }, @@ -311,7 +311,7 @@ impl RunnableModule for X64ExecutionContext { } unsafe extern "C" fn dummy_trampoline( - _: *mut vm::Ctx, + _: Option>, _: NonNull, _: *const u64, _: *mut u64, From b66b016389cff844df202d33bf2f56f05fe4cb7b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 17:43:06 +0200 Subject: [PATCH 07/27] feat(clif-backend) Update the `invoke` definition. --- lib/clif-backend/src/code.rs | 4 ++-- lib/clif-backend/src/signal/mod.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 5130b91a6db..0ec270dfcf1 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -786,7 +786,7 @@ impl FuncEnvironment for FunctionEnvironment { ptr_type, mflags, entry_addr, - vm::Anyfunc::offset_env() as i32, + vm::Anyfunc::offset_func_env() as i32, ); let found_sig = pos.ins().load( @@ -903,7 +903,7 @@ impl FuncEnvironment for FunctionEnvironment { let imported_env_addr = pos.func.create_global_value(ir::GlobalValueData::Load { base: imported_func_struct_addr, - offset: (vm::ImportedFunc::offset_env() as i32).into(), + offset: (vm::ImportedFunc::offset_func_env() as i32).into(), global_type: ptr_type, readonly: true, }); diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 78b9b65c1dd..197757bfe02 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -60,7 +60,8 @@ impl RunnableModule for Caller { fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { unsafe extern "C" fn invoke( trampoline: Trampoline, - env: Option>, + _vmctx: Option>, + func_env: Option>, func: NonNull, args: *const u64, rets: *mut u64, @@ -73,12 +74,12 @@ impl RunnableModule for Caller { #[cfg(not(target_os = "windows"))] let res = call_protected(handler_data, || { // Leap of faith. - trampoline(env, func, args, rets); + trampoline(func_env, func, args, rets); }); // the trampoline is called from C on windows #[cfg(target_os = "windows")] - let res = call_protected(handler_data, trampoline, env, func, args, rets); + let res = call_protected(handler_data, trampoline, func_env, func, args, rets); match res { Err(err) => { From 32880710c094e86cd7070e781c876cf6781fb396 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 17:43:46 +0200 Subject: [PATCH 08/27] feat(llvm-backend) Update the `invoke_trampoline` definition. --- lib/llvm-backend/cpp/object_loader.hh | 4 ++-- lib/llvm-backend/src/backend.rs | 1 + lib/llvm-backend/src/stackmap.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index 121ea39a904..1e75f548489 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -285,9 +285,9 @@ void module_delete(WasmModule *module) { delete module; } unsafe_unwind(new BreakpointException(callback)); } -bool invoke_trampoline(trampoline_t trampoline, void *func_env, void *func, +bool invoke_trampoline(trampoline_t trampoline, void *_vmctx, void *func_env, void *func, void *params, void *results, WasmTrapType *trap_out, - box_any_t *user_error, void *invoke_env) noexcept { + box_any_t *user_error, void *_invoke_env) noexcept { try { catch_unwind([trampoline, func_env, func, params, results]() { trampoline(func_env, func, params, results); diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 1017cf5a374..360dc03ead2 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -59,6 +59,7 @@ extern "C" { #[allow(improper_ctypes)] fn invoke_trampoline( trampoline: Trampoline, + vmctx: Option>, env_ptr: Option>, func_ptr: NonNull, params: *const u64, diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs index a9be2242060..3942e294549 100644 --- a/lib/llvm-backend/src/stackmap.rs +++ b/lib/llvm-backend/src/stackmap.rs @@ -161,7 +161,7 @@ impl StackmapEntry { ValueSemantic::ImportedFuncCtx(idx) => MachineValue::VmctxDeref(vec![ Ctx::offset_imported_funcs() as usize, vm::ImportedFunc::size() as usize * idx - + vm::ImportedFunc::offset_env() as usize, + + vm::ImportedFunc::offset_func_env() as usize, 0, ]), ValueSemantic::DynamicSigindice(idx) => { From 356ea60b8dce16182af2353df048d4590c659080 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 18 Oct 2019 17:48:55 +0200 Subject: [PATCH 09/27] test(Makefile) Run `spectests-singlepass` on rustc nightly. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 58b85dbb1a5..9572a340636 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ generate: generate-spectests generate-emtests generate-wasitests # Spectests spectests-singlepass: - cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture + cargo +nightly test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture spectests-cranelift: cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture From 9e80125b689bc9452746488c1f8e2c845d3c0779 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 21 Oct 2019 22:03:29 +0200 Subject: [PATCH 10/27] fix(runtime-core) Remove dead code. It appears this code is also generated by the `impl_traits` macro. For a reason I ignore, `rustc` seems to think they are different. --- lib/runtime-core/src/typed_func.rs | 82 ------------------------------ 1 file changed, 82 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index a8084bb08d2..f89d3038434 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -285,88 +285,6 @@ impl WasmTypeList for Infallible { } } -impl WasmTypeList for (A,) -where - A: WasmExternType, -{ - type CStruct = S1; - type RetArray = [u64; 1]; - - fn from_ret_array(array: Self::RetArray) -> Self { - (WasmExternType::from_native(NativeWasmType::from_binary( - array[0], - )),) - } - - fn empty_ret_array() -> Self::RetArray { - [0u64] - } - - fn from_c_struct(c_struct: Self::CStruct) -> Self { - let S1(a) = c_struct; - (WasmExternType::from_native(a),) - } - - fn into_c_struct(self) -> Self::CStruct { - #[allow(unused_parens, non_snake_case)] - let (a,) = self; - S1(WasmExternType::to_native(a)) - } - - fn types() -> &'static [Type] { - &[A::Native::TYPE] - } - - #[allow(non_snake_case)] - unsafe fn call( - self, - func: NonNull, - func_env: Option>, - vmctx: Option>, - wasm: Wasm, - ) -> Result - where - Rets: WasmTypeList, - { - let (a,) = self; - let args = [a.to_native().to_binary()]; - let mut rets = Rets::empty_ret_array(); - let mut trap = WasmTrapInfo::Unknown; - let mut user_error = None; - - if (wasm.invoke)( - wasm.trampoline, - vmctx, - func_env, - func, - args.as_ptr(), - rets.as_mut().as_mut_ptr(), - &mut trap, - &mut user_error, - wasm.invoke_env, - ) { - Ok(Rets::from_ret_array(rets)) - } else { - if let Some(data) = user_error { - Err(RuntimeError::Error { data }) - } else { - Err(RuntimeError::Trap { - msg: trap.to_string().into(), - }) - } - } - } -} - -impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> -where - Rets: WasmTypeList, -{ - pub fn call(&self, a: A) -> Result { - unsafe { ::call(a, self.func, self.func_env, self.vmctx, self.inner) } - } -} - macro_rules! impl_traits { ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { #[repr($repr)] From d16c7650c657d0f00f82fdd3f0835c33ad2692c5 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 22 Oct 2019 00:04:36 +0200 Subject: [PATCH 11/27] test(runtime-core) Fix function names. --- lib/runtime-core/src/vm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2e5c3c19ffe..0ce78487ff3 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -753,8 +753,8 @@ mod vm_offset_tests { ); assert_eq!( - ImportedFunc::offset_env() as usize, - offset_of!(ImportedFunc => env).get_byte_offset(), + ImportedFunc::offset_func_env() as usize, + offset_of!(ImportedFunc => func_env).get_byte_offset(), ); } @@ -800,8 +800,8 @@ mod vm_offset_tests { ); assert_eq!( - Anyfunc::offset_env() as usize, - offset_of!(Anyfunc => env).get_byte_offset(), + Anyfunc::offset_func_env() as usize, + offset_of!(Anyfunc => func_env).get_byte_offset(), ); assert_eq!( From 07acb4592fc44f2fd5bad963c06e7075752ffdb7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 22 Oct 2019 00:05:11 +0200 Subject: [PATCH 12/27] feat(runtime-core) !temp Support optional `&mut vm::Ctx`. --- lib/runtime-core/src/typed_func.rs | 106 +++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index f89d3038434..7e62b3f62b9 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -132,10 +132,19 @@ pub trait WasmTypeList { Rets: WasmTypeList; } +pub trait ExternalFunctionKind {} + +pub struct ExplicitVmCtx(); +pub struct ClosedEnvironment(); + +impl ExternalFunctionKind for ExplicitVmCtx {} +impl ExternalFunctionKind for ClosedEnvironment {} + /// Represents a function that can be converted to a `vm::Func` /// (function pointer) that can be called within WebAssembly. -pub trait ExternalFunction +pub trait ExternalFunction where + Kind: ExternalFunctionKind, Args: WasmTypeList, Rets: WasmTypeList, { @@ -213,9 +222,10 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(func: F) -> Func<'a, Args, Rets, Host> + pub fn new(func: F) -> Func<'a, Args, Rets, Host> where - F: ExternalFunction, + Kind: ExternalFunctionKind, + F: ExternalFunction, { let (func, func_env) = func.to_raw(); @@ -342,7 +352,7 @@ macro_rules! impl_traits { { #[allow(unused_parens)] let ( $( $x ),* ) = self; - let args = [ $( $x.to_native().to_binary()),* ]; + let args = [ $( $x.to_native().to_binary() ),* ]; let mut rets = Rets::empty_ret_array(); let mut trap = WasmTrapInfo::Unknown; let mut user_error = None; @@ -369,7 +379,28 @@ macro_rules! impl_traits { } } - impl< $( $x, )* Rets, Trap, FN > ExternalFunction<( $( $x ),* ), Rets> for FN + impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + { + #[allow(non_snake_case)] + pub fn call(&self, $( $x: $x, )* ) -> Result { + #[allow(unused_parens)] + unsafe { + <( $( $x ),* ) as WasmTypeList>::call( + ( $( $x ),* ), + self.func, + self.func_env, + self.vmctx, + self.inner, + ) + } + } + } + + // Generic implementation for `Fn` (without `&mut Ctx` as first argument). + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN where $( $x: WasmExternType, )* Rets: WasmTypeList, @@ -384,7 +415,7 @@ macro_rules! impl_traits { None }; - /// This is required for the llvm backend to be able to unwind through this function. + /// This is required for the LLVM backend to be able to unwind through this function. #[cfg_attr(nightly, unwind(allowed))] extern fn wrap<$( $x, )* Rets, Trap, FN>( env: *mut vm::FuncEnv @@ -419,11 +450,12 @@ macro_rules! impl_traits { /* unsafe { - (&*ctx.module).runnable_module.do_early_trap(err) + let vmctx: &mut vm::Ctx = unsafe { &mut *(env as *mut vm::Ctx) }; + (&*vmctx.module).runnable_module.do_early_trap(err) } - */ + */ eprintln!("early trap ahhh"); - ::std::process::exit(1) + ::std::process::exit(1); } ( @@ -433,23 +465,59 @@ macro_rules! impl_traits { } } - impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> + // Specific implementation for `Fn` (with a `&mut Ctx` as first argument). + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN where $( $x: WasmExternType, )* Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn( &mut vm::Ctx $( , $x )* ) -> Trap + 'static, { #[allow(non_snake_case)] - pub fn call(&self, $( $x: $x, )* ) -> Result { - #[allow(unused_parens)] - unsafe { - <( $( $x ),* ) as WasmTypeList>::call( - ( $( $x ),* ), - self.func, - self.func_env, - self.vmctx, - self.inner, + fn to_raw(self) -> (NonNull, Option>) { + if mem::size_of::() == 0 { + /// This is required for the LLVM backend to be able to unwind through this function. + #[cfg_attr(nightly, unwind(allowed))] + extern fn wrap<$( $x, )* Rets, Trap, FN>( + vmctx: &mut vm::Ctx + $( , $x: <$x as WasmExternType>::Native )* + ) -> Rets::CStruct + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn( &mut vm::Ctx $( , $x )* ) -> Trap + 'static, + { + let func: FN = unsafe { mem::transmute_copy(&()) }; + + let err = match panic::catch_unwind( + panic::AssertUnwindSafe( + || { + func(vmctx $( , WasmExternType::from_native($x) )* ).report() + } + ) + ) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, + Err(err) => err, + }; + + unsafe { + (&*vmctx.module).runnable_module.do_early_trap(err) + } + } + + ( + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(), + None, ) + } else { + panic!("yolo"); } + } } }; From 7868926e5f2530232b2c95b87b298f720ef8faec Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 23 Oct 2019 16:35:39 +0200 Subject: [PATCH 13/27] test(runtime-core) Create the `wasmer-runtime-core-tests` crate. --- Cargo.lock | 11 ++++ Cargo.toml | 1 + lib/runtime-core-tests/Cargo.toml | 21 +++++++ lib/runtime-core-tests/src/lib.rs | 9 +++ lib/runtime-core-tests/tests/imports.rs | 84 +++++++++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 lib/runtime-core-tests/Cargo.toml create mode 100644 lib/runtime-core-tests/src/lib.rs create mode 100644 lib/runtime-core-tests/tests/imports.rs diff --git a/Cargo.lock b/Cargo.lock index 86c0406594c..be2d0da2615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1668,6 +1668,17 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasmer-runtime-core-tests" +version = "0.7.0" +dependencies = [ + "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.8.0", + "wasmer-llvm-backend 0.8.0", + "wasmer-runtime-core 0.8.0", + "wasmer-singlepass-backend 0.8.0", +] + [[package]] name = "wasmer-singlepass-backend" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 4d619059e35..afa35be1f18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "lib/singlepass-backend", "lib/runtime", "lib/runtime-core", + "lib/runtime-core-tests", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml new file mode 100644 index 00000000000..edf72d75ae7 --- /dev/null +++ b/lib/runtime-core-tests/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "wasmer-runtime-core-tests" +version = "0.7.0" +description = "Tests for the Wasmer runtime core crate" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +publish = false + +[dependencies] +wabt = "0.9.1" +wasmer-runtime-core = { path = "../runtime-core", version = "0.8" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.8", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.8", optional = true } + +[features] +default = ["backend-cranelift"] +backend-cranelift = ["wasmer-clif-backend"] +backend-singlepass = ["wasmer-singlepass-backend"] +backend-llvm = ["wasmer-llvm-backend"] \ No newline at end of file diff --git a/lib/runtime-core-tests/src/lib.rs b/lib/runtime-core-tests/src/lib.rs new file mode 100644 index 00000000000..d923dff3190 --- /dev/null +++ b/lib/runtime-core-tests/src/lib.rs @@ -0,0 +1,9 @@ +pub use wabt::wat2wasm; +use wasmer_runtime_core::backend::Compiler; + +#[cfg(feature = "backend-cranelift")] +pub fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + + CraneliftCompiler::new() +} diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs new file mode 100644 index 00000000000..4c50f2aa866 --- /dev/null +++ b/lib/runtime-core-tests/tests/imports.rs @@ -0,0 +1,84 @@ +use wasmer_runtime_core::{ + compile_with, imports, memory::Memory, typed_func::Func, types::MemoryDescriptor, units::Pages, + vm, +}; +use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; + +#[test] +fn imported_functions_forms() { + let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); + let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); + let memory_descriptor = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); + let memory = Memory::new(memory_descriptor).unwrap(); + + const SHIFT: i32 = 10; + memory.view()[0].set(SHIFT); + + let import_object = imports! { + "env" => { + "memory" => memory.clone(), + "callback1_fn" => Func::new(callback1_fn), + "callback1_closure" => Func::new(|n: i32| -> Result { + Ok(n + 1) + }), + "callback1_fn_with_vmctx" => Func::new(callback1_fn_with_vmctx), + "callback1_closure_with_env" => Func::new(move |n: i32| -> Result { + let shift: i32 = memory.view()[0].get(); + + Ok(shift + n + 1) + }), + }, + }; + let instance = module.instantiate(&import_object).unwrap(); + + macro_rules! call_and_assert { + ($function:ident, $expected_value:expr) => { + let $function: Func = instance.func(stringify!($function)).unwrap(); + + let result = $function.call(1); + + assert_eq!( + result, $expected_value, + concat!("Expected right when calling `", stringify!($function), "`.") + ); + }; + } + + call_and_assert!(function1_fn, Ok(2)); + call_and_assert!(function1_closure, Ok(2)); + call_and_assert!(function1_fn_with_vmctx, Ok(2 + SHIFT)); + call_and_assert!(function1_closure_with_env, Ok(2 + SHIFT)); +} + +fn callback1_fn(n: i32) -> Result { + Ok(n + 1) +} + +fn callback1_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { + let memory = vmctx.memory(0); + let shift: i32 = memory.view()[0].get(); + + Ok(shift + n + 1) +} + +static MODULE: &str = r#" +(module + (type $type (func (param i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (import "env" "callback1_fn" (func $callback1_fn (type $type))) + (import "env" "callback1_closure" (func $callback1_closure (type $type))) + (import "env" "callback1_fn_with_vmctx" (func $callback1_fn_with_vmctx (type $type))) + (import "env" "callback1_closure_with_env" (func $callback1_closure_with_env (type $type))) + (func (export "function1_fn") (type $type) + get_local 0 + call $callback1_fn) + (func (export "function1_closure") (type $type) + get_local 0 + call $callback1_closure) + (func (export "function1_fn_with_vmctx") (type $type) + get_local 0 + call $callback1_fn_with_vmctx) + (func (export "function1_closure_with_env") (type $type) + get_local 0 + call $callback1_closure_with_env)) +"#; From 95541d27b5092262d826238c260465822dbaef6e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 23 Oct 2019 16:41:37 +0200 Subject: [PATCH 14/27] test(runtime-core) Test against the LLVM and Singlepass backends. --- lib/runtime-core-tests/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/runtime-core-tests/src/lib.rs b/lib/runtime-core-tests/src/lib.rs index d923dff3190..96f7e6265b8 100644 --- a/lib/runtime-core-tests/src/lib.rs +++ b/lib/runtime-core-tests/src/lib.rs @@ -7,3 +7,15 @@ pub fn get_compiler() -> impl Compiler { CraneliftCompiler::new() } + +#[cfg(feature = "backend-singlepass")] +pub fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} + +#[cfg(feature = "backend-llvm")] +pub fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} From 9baa11e8032c1a89060f98282338c0709ca532ec Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 23 Oct 2019 17:06:31 +0200 Subject: [PATCH 15/27] feat(runtime-core) Implement trap for closure and `fn` without vmctx/env. --- lib/runtime-core-tests/tests/imports.rs | 70 ++++++++++++++++++++++--- lib/runtime-core/src/typed_func.rs | 8 +-- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 4c50f2aa866..e781ad661cb 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -1,6 +1,6 @@ use wasmer_runtime_core::{ - compile_with, imports, memory::Memory, typed_func::Func, types::MemoryDescriptor, units::Pages, - vm, + compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func, + types::MemoryDescriptor, units::Pages, vm, }; use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; @@ -17,16 +17,26 @@ fn imported_functions_forms() { let import_object = imports! { "env" => { "memory" => memory.clone(), + + // Without vmctx or env. "callback1_fn" => Func::new(callback1_fn), "callback1_closure" => Func::new(|n: i32| -> Result { Ok(n + 1) }), + + // With vmctx or env. "callback1_fn_with_vmctx" => Func::new(callback1_fn_with_vmctx), "callback1_closure_with_env" => Func::new(move |n: i32| -> Result { let shift: i32 = memory.view()[0].get(); Ok(shift + n + 1) }), + + // Trap without vmctx or env. + "callback1_fn_trap" => Func::new(callback1_fn_trap), + "callback1_closure_trap" => Func::new(|_n: i32| -> Result { + Err("bar") + }), }, }; let instance = module.instantiate(&import_object).unwrap(); @@ -37,10 +47,32 @@ fn imported_functions_forms() { let result = $function.call(1); - assert_eq!( - result, $expected_value, - concat!("Expected right when calling `", stringify!($function), "`.") - ); + match (result, $expected_value) { + (Ok(value), expected_value) => assert_eq!( + Ok(value), + expected_value, + concat!("Expected right when calling `", stringify!($function), "`.") + ), + ( + Err(RuntimeError::Error { data }), + Err(RuntimeError::Error { + data: expected_data, + }), + ) => { + if let (Some(data), Some(expected_data)) = ( + data.downcast_ref::<&str>(), + expected_data.downcast_ref::<&str>(), + ) { + assert_eq!( + data, expected_data, + concat!("Expected right when calling `", stringify!($function), "`.") + ) + } else { + assert!(false, "Unexpected error, cannot compare it.") + } + } + _ => assert!(false, "Unexpected assertion."), + } }; } @@ -48,6 +80,18 @@ fn imported_functions_forms() { call_and_assert!(function1_closure, Ok(2)); call_and_assert!(function1_fn_with_vmctx, Ok(2 + SHIFT)); call_and_assert!(function1_closure_with_env, Ok(2 + SHIFT)); + call_and_assert!( + function1_fn_trap, + Err(RuntimeError::Error { + data: Box::new("foo") + }) + ); + call_and_assert!( + function1_closure_trap, + Err(RuntimeError::Error { + data: Box::new("bar") + }) + ); } fn callback1_fn(n: i32) -> Result { @@ -61,6 +105,10 @@ fn callback1_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { Ok(shift + n + 1) } +fn callback1_fn_trap(_n: i32) -> Result { + Err("foo") +} + static MODULE: &str = r#" (module (type $type (func (param i32) (result i32))) @@ -69,6 +117,8 @@ static MODULE: &str = r#" (import "env" "callback1_closure" (func $callback1_closure (type $type))) (import "env" "callback1_fn_with_vmctx" (func $callback1_fn_with_vmctx (type $type))) (import "env" "callback1_closure_with_env" (func $callback1_closure_with_env (type $type))) + (import "env" "callback1_fn_trap" (func $callback1_fn_trap (type $type))) + (import "env" "callback1_closure_trap" (func $callback1_closure_trap (type $type))) (func (export "function1_fn") (type $type) get_local 0 call $callback1_fn) @@ -80,5 +130,11 @@ static MODULE: &str = r#" call $callback1_fn_with_vmctx) (func (export "function1_closure_with_env") (type $type) get_local 0 - call $callback1_closure_with_env)) + call $callback1_closure_with_env) + (func (export "function1_fn_trap") (type $type) + get_local 0 + call $callback1_fn_trap) + (func (export "function1_closure_trap") (type $type) + get_local 0 + call $callback1_closure_trap)) "#; diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 7e62b3f62b9..7d955e998f1 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -433,7 +433,7 @@ macro_rules! impl_traits { unsafe { mem::transmute(&()) } }; - let _err = match panic::catch_unwind( + let err = match panic::catch_unwind( panic::AssertUnwindSafe( || { func( $( WasmExternType::from_native($x) ),* ).report() @@ -448,14 +448,10 @@ macro_rules! impl_traits { Err(err) => err, }; - /* unsafe { - let vmctx: &mut vm::Ctx = unsafe { &mut *(env as *mut vm::Ctx) }; + let vmctx: &mut vm::Ctx = &mut *(env as *mut vm::Ctx); (&*vmctx.module).runnable_module.do_early_trap(err) } - */ - eprintln!("early trap ahhh"); - ::std::process::exit(1); } ( From 73175d9216bc2b65b3aff78dcc4996cedb5c8951 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 12:52:59 +0200 Subject: [PATCH 16/27] feat(runtime-core) Handle the case of a closure with an environment + vmctx. --- lib/runtime-core/src/typed_func.rs | 76 +++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 7d955e998f1..5133412816d 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -409,13 +409,20 @@ macro_rules! impl_traits { { #[allow(non_snake_case)] fn to_raw(self) -> (NonNull, Option>) { - let env: Option> = if mem::size_of::() != 0 { - NonNull::new(Box::leak(Box::new(self))).map(|pointer| pointer.cast()) - } else { - None - }; + let env: Option> = + // `FN` is a function pointer, or a closure + // _without_ a captured environment. + if mem::size_of::() == 0 { + None + } + // `FN` is a closure _with_ a captured + // environment. Grab it. + else { + NonNull::new(Box::leak(Box::new(self))).map(|pointer| pointer.cast()) + }; - /// This is required for the LLVM backend to be able to unwind through this function. + // This is required for the LLVM backend to be able to + // unwind through this function. #[cfg_attr(nightly, unwind(allowed))] extern fn wrap<$( $x, )* Rets, Trap, FN>( env: *mut vm::FuncEnv @@ -427,11 +434,27 @@ macro_rules! impl_traits { Trap: TrapEarly, FN: Fn( $( $x ),* ) -> Trap + 'static, { - let func: &FN = if mem::size_of::() != 0 { - unsafe { &*(env as *const FN) } - } else { - unsafe { mem::transmute(&()) } - }; + let (func, vmctx): (&FN, Option<&mut vm::Ctx>) = + // `FN` is a function pointer, or a closure + // _without_ a captured + // environment. Consequently `env` holds a + // pointer to `vm::Ctx`. + if mem::size_of::() == 0 { + ( + unsafe { mem::transmute(&()) }, + Some(unsafe { &mut *(env as *mut vm::Ctx) }), + ) + } + // `FN` is a closure _with_ a captured + // environment. `env` effectively represents + // the closure environment, and we don't have + // a pointer to `vm::Ctx`. + else { + ( + unsafe { &*(env as *const FN) }, + None + ) + }; let err = match panic::catch_unwind( panic::AssertUnwindSafe( @@ -448,9 +471,14 @@ macro_rules! impl_traits { Err(err) => err, }; - unsafe { - let vmctx: &mut vm::Ctx = &mut *(env as *mut vm::Ctx); - (&*vmctx.module).runnable_module.do_early_trap(err) + match vmctx { + Some(vmctx) => { + unsafe { (&*vmctx.module).runnable_module.do_early_trap(err) } + }, + None => { + eprintln!("yolo"); + ::std::process::exit(1) + } } } @@ -471,8 +499,11 @@ macro_rules! impl_traits { { #[allow(non_snake_case)] fn to_raw(self) -> (NonNull, Option>) { + // `FN` is a function pointer, or a closure _without_ + // a captured environment. if mem::size_of::() == 0 { - /// This is required for the LLVM backend to be able to unwind through this function. + // This is required for the LLVM backend to be + // able to unwind through this function. #[cfg_attr(nightly, unwind(allowed))] extern fn wrap<$( $x, )* Rets, Trap, FN>( vmctx: &mut vm::Ctx @@ -510,10 +541,19 @@ macro_rules! impl_traits { NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(), None, ) - } else { - panic!("yolo"); } - + // `FN` is a closure _with_ a captured + // environment. Since it also has a `&mut vm::Ctx` as + // its first argument, `vm::FuncEnv` has 2 pointers to + // hold, and it is impossible (it represents only 1 + // pointer). It is OK-ish to panic: It happens during + // runtime (at compile-time would be far better) but + // it happens when `Func::new` is called, so when the + // import object is built, which happens before the + // module instantiation and its execution. + else { + panic!("A closure cannot capture its environment while having `&mut vm::Ctx` as its first argument."); + } } } }; From 0e681bebf9fba058643d796f10e6fb0688d09d99 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 12:53:48 +0200 Subject: [PATCH 17/27] test(runtime-core) Write more tests. --- lib/runtime-core-tests/Cargo.toml | 2 +- lib/runtime-core-tests/tests/imports.rs | 163 +++++++++++++++++------- 2 files changed, 116 insertions(+), 49 deletions(-) diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index edf72d75ae7..66e7b1de96a 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.7.0" +version = "0.8.0" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index e781ad661cb..b9be4361903 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -6,10 +6,53 @@ use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; #[test] fn imported_functions_forms() { + const MODULE: &str = r#" +(module + (type $type (func (param i32) (result i32))) + (import "env" "memory" (memory 1 1)) + (import "env" "callback_fn" (func $callback_fn (type $type))) + (import "env" "callback_closure" (func $callback_closure (type $type))) + (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) + (import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type))) + (import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type))) + (import "env" "callback_fn_trap" (func $callback_fn_trap (type $type))) + (import "env" "callback_closure_trap" (func $callback_closure_trap (type $type))) + (import "env" "callback_fn_trap_with_vmctx" (func $callback_fn_trap_with_vmctx (type $type))) + (import "env" "callback_closure_trap_with_env" (func $callback_closure_trap_with_env (type $type))) + (func (export "function_fn") (type $type) + get_local 0 + call $callback_fn) + (func (export "function_closure") (type $type) + get_local 0 + call $callback_closure) + (func (export "function_fn_with_vmctx") (type $type) + get_local 0 + call $callback_fn_with_vmctx) + (func (export "function_closure_with_vmctx") (type $type) + get_local 0 + call $callback_closure_with_vmctx) + (func (export "function_closure_with_env") (type $type) + get_local 0 + call $callback_closure_with_env) + (func (export "function_fn_trap") (type $type) + get_local 0 + call $callback_fn_trap) + (func (export "function_closure_trap") (type $type) + get_local 0 + call $callback_closure_trap) + (func (export "function_fn_trap_with_vmctx") (type $type) + get_local 0 + call $callback_fn_trap_with_vmctx) + (func (export "function_closure_trap_with_env") (type $type) + get_local 0 + call $callback_closure_trap_with_env)) +"#; + let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); let memory_descriptor = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); let memory = Memory::new(memory_descriptor).unwrap(); + let memory2 = memory.clone(); const SHIFT: i32 = 10; memory.view()[0].set(SHIFT); @@ -19,23 +62,37 @@ fn imported_functions_forms() { "memory" => memory.clone(), // Without vmctx or env. - "callback1_fn" => Func::new(callback1_fn), - "callback1_closure" => Func::new(|n: i32| -> Result { + "callback_fn" => Func::new(callback_fn), + "callback_closure" => Func::new(|n: i32| -> Result { Ok(n + 1) }), // With vmctx or env. - "callback1_fn_with_vmctx" => Func::new(callback1_fn_with_vmctx), - "callback1_closure_with_env" => Func::new(move |n: i32| -> Result { + "callback_fn_with_vmctx" => Func::new(callback_fn_with_vmctx), + "callback_closure_with_vmctx" => Func::new(|vmctx: &mut vm::Ctx, n: i32| -> Result { + let memory = vmctx.memory(0); + let shift: i32 = memory.view()[0].get(); + + Ok(shift + n + 1) + }), + "callback_closure_with_env" => Func::new(move |n: i32| -> Result { let shift: i32 = memory.view()[0].get(); Ok(shift + n + 1) }), // Trap without vmctx or env. - "callback1_fn_trap" => Func::new(callback1_fn_trap), - "callback1_closure_trap" => Func::new(|_n: i32| -> Result { - Err("bar") + "callback_fn_trap" => Func::new(callback_fn_trap), + "callback_closure_trap" => Func::new(|n: i32| -> Result { + Err(format!("bar {}", SHIFT + n)) + }), + + // Trap with vmctx or env. + "callback_fn_trap_with_vmctx" => Func::new(callback_fn_trap_with_vmctx), + "callback_closure_trap_with_env" => Func::new(move |n: i32| -> Result { + let shift: i32 = memory2.view()[0].get(); + + Err(format!("qux {}", shift + n + 1)) }), }, }; @@ -67,6 +124,14 @@ fn imported_functions_forms() { data, expected_data, concat!("Expected right when calling `", stringify!($function), "`.") ) + } else if let (Some(data), Some(expected_data)) = ( + data.downcast_ref::(), + expected_data.downcast_ref::(), + ) { + assert_eq!( + data, expected_data, + concat!("Expected right when calling `", stringify!($function), "`.") + ) } else { assert!(false, "Unexpected error, cannot compare it.") } @@ -76,65 +141,67 @@ fn imported_functions_forms() { }; } - call_and_assert!(function1_fn, Ok(2)); - call_and_assert!(function1_closure, Ok(2)); - call_and_assert!(function1_fn_with_vmctx, Ok(2 + SHIFT)); - call_and_assert!(function1_closure_with_env, Ok(2 + SHIFT)); + call_and_assert!(function_fn, Ok(2)); + call_and_assert!(function_closure, Ok(2)); + call_and_assert!(function_fn_with_vmctx, Ok(2 + SHIFT)); + call_and_assert!(function_closure_with_vmctx, Ok(2 + SHIFT)); + call_and_assert!(function_closure_with_env, Ok(2 + SHIFT)); call_and_assert!( - function1_fn_trap, + function_fn_trap, Err(RuntimeError::Error { - data: Box::new("foo") + data: Box::new(format!("foo {}", 1)) }) ); call_and_assert!( - function1_closure_trap, + function_closure_trap, Err(RuntimeError::Error { - data: Box::new("bar") + data: Box::new(format!("bar {}", 1 + SHIFT)) }) ); + call_and_assert!( + function_fn_trap_with_vmctx, + Err(RuntimeError::Error { + data: Box::new(format!("baz {}", 2 + SHIFT)) + }) + ); + /* + call_and_assert!( + function_closure_trap_with_env, + Err(RuntimeError::Error { + data: Box::new(format!("qux {}", 2 + SHIFT)) + }) + ); + */ +} + +#[test] +#[should_panic( + expected = "A closure cannot capture its environment while having `&mut vm::Ctx` as its first argument." +)] +#[allow(non_snake_case)] +fn invalid_imported_functions_form__closure_with_vmctx_and_env() { + let p = 1; + let _func = Func::new(move |_vmctx: &mut vm::Ctx, n: i32| -> Result { Ok(n + p) }); } -fn callback1_fn(n: i32) -> Result { +fn callback_fn(n: i32) -> Result { Ok(n + 1) } -fn callback1_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { +fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { let memory = vmctx.memory(0); let shift: i32 = memory.view()[0].get(); Ok(shift + n + 1) } -fn callback1_fn_trap(_n: i32) -> Result { - Err("foo") +fn callback_fn_trap(n: i32) -> Result { + Err(format!("foo {}", n)) } -static MODULE: &str = r#" -(module - (type $type (func (param i32) (result i32))) - (import "env" "memory" (memory 1 1)) - (import "env" "callback1_fn" (func $callback1_fn (type $type))) - (import "env" "callback1_closure" (func $callback1_closure (type $type))) - (import "env" "callback1_fn_with_vmctx" (func $callback1_fn_with_vmctx (type $type))) - (import "env" "callback1_closure_with_env" (func $callback1_closure_with_env (type $type))) - (import "env" "callback1_fn_trap" (func $callback1_fn_trap (type $type))) - (import "env" "callback1_closure_trap" (func $callback1_closure_trap (type $type))) - (func (export "function1_fn") (type $type) - get_local 0 - call $callback1_fn) - (func (export "function1_closure") (type $type) - get_local 0 - call $callback1_closure) - (func (export "function1_fn_with_vmctx") (type $type) - get_local 0 - call $callback1_fn_with_vmctx) - (func (export "function1_closure_with_env") (type $type) - get_local 0 - call $callback1_closure_with_env) - (func (export "function1_fn_trap") (type $type) - get_local 0 - call $callback1_fn_trap) - (func (export "function1_closure_trap") (type $type) - get_local 0 - call $callback1_closure_trap)) -"#; +fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { + let memory = vmctx.memory(0); + let shift: i32 = memory.view()[0].get(); + + Err(format!("baz {}", shift + n + 1)) +} From 61c4eb4bde7edb430e4bb9add807352d9da02875 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 13:17:31 +0200 Subject: [PATCH 18/27] test(runtime-core) Build `Func::new` with different functions of various arities. --- lib/runtime-core/src/typed_func.rs | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 5133412816d..abff94b39f1 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -609,6 +609,54 @@ where mod tests { use super::*; + macro_rules! test_func_arity_n { + ($test_name:ident, $($x:ident),*) => { + #[test] + fn $test_name() { + use crate::vm; + + fn without_vmctx($($x: i32),*) -> i32 { + vec![$($x),*].iter().sum() + } + + fn with_vmctx(_: &mut vm::Ctx, $($x: i32),*) -> i32 { + vec![$($x),*].iter().sum() + } + + let _func = Func::new(without_vmctx); + let _func = Func::new(with_vmctx); + let _func = Func::new(|$($x: i32),*| -> i32 { + vec![$($x),*].iter().sum() + }); + let _func = Func::new(|_: &mut vm::Ctx, $($x: i32),*| -> i32 { + vec![$($x),*].iter().sum() + }); + } + } + } + + #[test] + fn test_func_arity_0() { + fn foo() -> i32 { + 0 + } + + let _ = Func::new(foo); + } + + test_func_arity_n!(test_func_arity_1, a); + test_func_arity_n!(test_func_arity_2, a, b); + test_func_arity_n!(test_func_arity_3, a, b, c); + test_func_arity_n!(test_func_arity_4, a, b, c, d); + test_func_arity_n!(test_func_arity_5, a, b, c, d, e); + test_func_arity_n!(test_func_arity_6, a, b, c, d, e, f); + test_func_arity_n!(test_func_arity_7, a, b, c, d, e, f, g); + test_func_arity_n!(test_func_arity_8, a, b, c, d, e, f, g, h); + test_func_arity_n!(test_func_arity_9, a, b, c, d, e, f, g, h, i); + test_func_arity_n!(test_func_arity_10, a, b, c, d, e, f, g, h, i, j); + test_func_arity_n!(test_func_arity_11, a, b, c, d, e, f, g, h, i, j, k); + test_func_arity_n!(test_func_arity_12, a, b, c, d, e, f, g, h, i, j, k, l); + #[test] fn test_call() { fn foo(a: i32, b: i32) -> (i32, i32) { From 9f6e796d7f29f6341c7ea5f059ed1d9f940e64e2 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:28:33 +0200 Subject: [PATCH 19/27] feat(singlepass-backend) Replace `vm::Ctx` by `vm::FuncEnv` when it makes sense. --- lib/singlepass-backend/src/codegen_x64.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 79d2047e1ae..ea5b1426c46 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,7 +10,7 @@ use std::ptr::NonNull; use std::{ any::Any, collections::{BTreeMap, HashMap}, - ptr, + mem, ptr, sync::{Arc, RwLock}, }; use wasmer_runtime_core::{ @@ -39,7 +39,7 @@ use wasmparser::{MemoryImmediate, Operator, Type as WpType, TypeOrFuncType as Wp lazy_static! { /// Performs a System V call to `target` with [stack_top..stack_base] as the argument list, from right to left. - static ref CONSTRUCT_STACK_AND_CALL_WASM: unsafe extern "C" fn (stack_top: *const u64, stack_base: *const u64, ctx: *mut vm::Ctx, target: *const vm::Func) -> u64 = { + static ref CONSTRUCT_STACK_AND_CALL_WASM: unsafe extern "C" fn (stack_top: *const u64, stack_base: *const u64, func_env: *mut vm::FuncEnv, target: *const vm::Func) -> u64 = { let mut assembler = Assembler::new().unwrap(); let offset = assembler.offset(); dynasm!( @@ -57,7 +57,7 @@ lazy_static! { ; mov r13, rdx ; mov r12, rcx - ; mov rdi, r13 // ctx + ; mov rdi, r13 // func_env ; sub r14, 8 ; cmp r14, r15 @@ -119,8 +119,8 @@ lazy_static! { ; ret ); let buf = assembler.finalize().unwrap(); - let ret = unsafe { ::std::mem::transmute(buf.ptr(offset)) }; - ::std::mem::forget(buf); + let ret = unsafe { mem::transmute(buf.ptr(offset)) }; + mem::forget(buf); ret }; } @@ -256,7 +256,7 @@ impl RunnableModule for X64ExecutionContext { } fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { - use std::{ffi::c_void, mem, slice}; + use std::{ffi::c_void, slice}; use wasmer_runtime_core::typed_func::{Trampoline, WasmTrapInfo}; unsafe extern "C" fn invoke( @@ -286,7 +286,7 @@ impl RunnableModule for X64ExecutionContext { args_reverse.as_ptr(), args_reverse.as_ptr().offset(args_reverse.len() as isize), func_env - .map(|pointer| pointer.cast().as_ptr()) + .map(|pointer| pointer.as_ptr()) .unwrap_or_else(ptr::null_mut), func.as_ptr(), ) From ee04f6eba992058affda4a303adf53521a3c5447 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:29:35 +0200 Subject: [PATCH 20/27] test(runtime-core) Print more debugging info when a test fails. --- lib/runtime-core-tests/tests/imports.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index b9be4361903..73e05580c7f 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -136,7 +136,15 @@ fn imported_functions_forms() { assert!(false, "Unexpected error, cannot compare it.") } } - _ => assert!(false, "Unexpected assertion."), + (result, expected_value) => assert!( + false, + format!( + "Unexpected assertion for `{}`: left = `{:?}`, right = `{:?}`.", + stringify!($function), + result, + expected_value + ) + ), } }; } From 4ee381b5b2101739e5e65d4a68eff3cee5c89171 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:30:31 +0200 Subject: [PATCH 21/27] fix(spectests) Various small fixes. --- lib/spectests/examples/test.rs | 1 - lib/spectests/tests/spectest.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/spectests/examples/test.rs b/lib/spectests/examples/test.rs index 8ce199695fc..006fc1397d2 100644 --- a/lib/spectests/examples/test.rs +++ b/lib/spectests/examples/test.rs @@ -1,5 +1,4 @@ use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::{backend::Compiler, import::ImportObject, Instance}; fn main() { diff --git a/lib/spectests/tests/spectest.rs b/lib/spectests/tests/spectest.rs index 62bfc7d5381..a49e866e1fd 100644 --- a/lib/spectests/tests/spectest.rs +++ b/lib/spectests/tests/spectest.rs @@ -721,7 +721,7 @@ mod tests { CommandKind::AssertUninstantiable { module: _, message: _, - } => println!("AssertUninstantiable not yet implmented "), + } => println!("AssertUninstantiable not yet implemented "), CommandKind::AssertExhaustion { action, message: _ } => { match action { Action::Invoke { From df9c1e6d3180b98874db22eed9c433e722591b62 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:31:24 +0200 Subject: [PATCH 22/27] chore(make) Test `wasmer-runtime-core-tests` for all backends. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 9572a340636..b4eeff188c1 100644 --- a/Makefile +++ b/Makefile @@ -89,12 +89,15 @@ wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llv # Backends singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasitests-singlepass cargo test -p wasmer-singlepass-backend --release + cargo test -p wasmer-runtime-core-tests --no-default-features --features backend-singlepass cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift cargo test -p wasmer-clif-backend --release + cargo test -p wasmer-runtime-core-tests llvm: spectests-llvm emtests-llvm wasitests-llvm cargo test -p wasmer-llvm-backend --release + cargo test -p wasmer-runtime-core-tests --no-default-features --features backend-llvm # All tests From 3e277d353de2d1029a1148057edabb651dbfd555 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:31:55 +0200 Subject: [PATCH 23/27] chore(make) Exclude `wasmer-runtime-core-tests` from `test-rest`. --- Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b4eeff188c1..f3e839b5a95 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,20 @@ test-capi: capi cargo test -p wasmer-runtime-c-api --release test-rest: - cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests + cargo test --release \ + --all \ + --exclude wasmer-runtime-c-api \ + --exclude wasmer-emscripten \ + --exclude wasmer-spectests \ + --exclude wasmer-wasi \ + --exclude wasmer-middleware-common \ + --exclude wasmer-middleware-common-tests \ + --exclude wasmer-singlepass-backend \ + --exclude wasmer-clif-backend \ + --exclude wasmer-llvm-backend \ + --exclude wasmer-wasi-tests \ + --exclude wasmer-emscripten-tests \ + --exclude wasmer-runtime-core-tests circleci-clean: @if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi; From 21d28a054d4305fa0036c3f083b42041d2abdd95 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 24 Oct 2019 15:32:21 +0200 Subject: [PATCH 24/27] chore(cargo) Update the lockfile. --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index be2d0da2615..e6c0365392b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1670,7 +1670,7 @@ dependencies = [ [[package]] name = "wasmer-runtime-core-tests" -version = "0.7.0" +version = "0.8.0" dependencies = [ "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.8.0", From 15e2915bf2297260b1192db8a1e48a5f9a62f9f8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 25 Oct 2019 00:16:12 +0200 Subject: [PATCH 25/27] chore(runtime-core) Use `std::{fmt, ptr}`. --- lib/runtime-core/src/backing.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 1a17582f165..5b78587fb51 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -15,14 +15,14 @@ use crate::{ }, vm, }; -use std::{fmt::Debug, slice}; +use std::{fmt, fmt::Debug, ptr, slice}; pub const INTERNALS_SIZE: usize = 256; pub(crate) struct Internals(pub(crate) [u64; INTERNALS_SIZE]); impl Debug for Internals { - fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "Internals({:?})", &self.0[..]) } } @@ -535,7 +535,6 @@ impl ImportBacking { memories, tables, globals, - vm_functions, vm_memories, vm_tables, @@ -614,8 +613,8 @@ fn import_functions( None => { if imports.allow_missing_functions { functions.push(vm::ImportedFunc { - func: ::std::ptr::null(), - func_env: ::std::ptr::null_mut(), + func: ptr::null(), + func_env: ptr::null_mut(), }); } else { link_errors.push(LinkError::ImportNotFound { From d168e050712501dcdfb88ed56c6fbbc548563dbe Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 25 Oct 2019 00:20:38 +0200 Subject: [PATCH 26/27] chore(runtime-core) Factorize code to remove duplicate code. --- lib/runtime-core/src/instance.rs | 104 +++++++++++++++---------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 5ebee48833e..ee3b3137c75 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -96,26 +96,26 @@ impl Instance { // We know that the start function takes no arguments and returns no values. // Therefore, we can call it without doing any signature checking, etc. - let func_ptr = match start_index.local_or_import(&instance.module.info) { - LocalOrImport::Local(local_func_index) => instance - .module - .runnable_module - .get_func(&instance.module.info, local_func_index) - .unwrap(), - LocalOrImport::Import(import_func_index) => NonNull::new( - instance.inner.import_backing.vm_functions[import_func_index].func as *mut _, - ) - .unwrap(), - }; - - let env_ptr = match start_index.local_or_import(&instance.module.info) { - LocalOrImport::Local(_) => { - NonNull::new(instance.inner.vmctx).map(|pointer| pointer.cast()) - } - LocalOrImport::Import(imported_func_index) => NonNull::new( - instance.inner.import_backing.vm_functions[imported_func_index].func_env, - ), - }; + let (func_ptr, func_env_ptr): (_, Option>) = + match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(local_func_index) => ( + instance + .module + .runnable_module + .get_func(&instance.module.info, local_func_index) + .unwrap(), + NonNull::new(instance.inner.vmctx).map(|pointer| pointer.cast()), + ), + LocalOrImport::Import(imported_func_index) => { + let imported_func = + &instance.inner.import_backing.vm_functions[imported_func_index]; + + ( + NonNull::new(imported_func.func as *mut _).unwrap(), + NonNull::new(imported_func.func_env), + ) + } + }; let sig_index = *instance .module @@ -134,7 +134,7 @@ impl Instance { Func::from_raw_parts( wasm_trampoline, func_ptr, - env_ptr, + func_env_ptr, NonNull::new(instance.inner.vmctx), ) }; @@ -207,25 +207,24 @@ impl Instance { .get_trampoline(&self.module.info, sig_index) .unwrap(); - let func_ptr = match func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(local_func_index) => self - .module - .runnable_module - .get_func(&self.module.info, local_func_index) - .unwrap(), - LocalOrImport::Import(import_func_index) => NonNull::new( - self.inner.import_backing.vm_functions[import_func_index].func as *mut _, - ) - .unwrap(), - }; + let (func_ptr, func_env_ptr): (_, Option>) = match func_index + .local_or_import(&self.module.info) + { + LocalOrImport::Local(local_func_index) => ( + self.module + .runnable_module + .get_func(&self.module.info, local_func_index) + .unwrap(), + NonNull::new(self.inner.vmctx).map(|pointer| pointer.cast()), + ), + LocalOrImport::Import(import_func_index) => { + let imported_func = &self.inner.import_backing.vm_functions[import_func_index]; - let func_env_ptr = match func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(_) => { - NonNull::new(self.inner.vmctx).map(|pointer| pointer.cast()) + ( + NonNull::new(imported_func.func as *mut _).unwrap(), + NonNull::new(imported_func.func_env), + ) } - LocalOrImport::Import(imported_func_index) => NonNull::new( - self.inner.import_backing.vm_functions[imported_func_index].func_env, - ), }; let typed_func: Func = unsafe { @@ -463,6 +462,7 @@ impl InstanceInner { ), LocalOrImport::Import(imported_func_index) => { let imported_func = &self.import_backing.vm_functions[imported_func_index]; + ( imported_func.func as *const _, Context::External(imported_func.func_env as _), // cast `*mut vm::Ctx` to `*mut vm::Func` @@ -578,21 +578,21 @@ fn call_func_with_index( let signature = &info.signatures[sig_index]; - let func_ptr = match func_index.local_or_import(info) { - LocalOrImport::Local(local_func_index) => { - runnable.get_func(info, local_func_index).unwrap() - } - LocalOrImport::Import(import_func_index) => { - NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap() - } - }; + let (func_ptr, func_env_ptr): (_, Option>) = + match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => ( + runnable.get_func(info, local_func_index).unwrap(), + vmctx.map(|pointer| pointer.cast()), + ), + LocalOrImport::Import(import_func_index) => { + let imported_func = &import_backing.vm_functions[import_func_index]; - let func_env_ptr = match func_index.local_or_import(info) { - LocalOrImport::Local(_) => vmctx.map(|pointer| pointer.cast()), - LocalOrImport::Import(imported_func_index) => { - NonNull::new(import_backing.vm_functions[imported_func_index].func_env) - } - }; + ( + NonNull::new(imported_func.func as *mut _).unwrap(), + NonNull::new(imported_func.func_env), + ) + } + }; let wasm = runnable .get_trampoline(info, sig_index) From c428a74e82c15d6a145eea1c6fe6e29ef1266fe8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 25 Oct 2019 00:23:53 +0200 Subject: [PATCH 27/27] feat(runtime-core) `export::Context::External` now holds a `vm::FuncEnv`. --- lib/runtime-core/src/backing.rs | 10 +++++----- lib/runtime-core/src/export.rs | 2 +- lib/runtime-core/src/instance.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 5b78587fb51..a6472feb0e1 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -378,7 +378,7 @@ impl LocalBacking { .unwrap() .as_ptr() as *const vm::Func, - vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` + vmctx as *mut vm::FuncEnv, ), LocalOrImport::Import(imported_func_index) => { let vm::ImportedFunc { func, func_env } = @@ -415,7 +415,7 @@ impl LocalBacking { .unwrap() .as_ptr() as *const vm::Func, - vmctx as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` + vmctx as *mut vm::FuncEnv, ), LocalOrImport::Import(imported_func_index) => { let vm::ImportedFunc { func, func_env } = @@ -582,9 +582,9 @@ fn import_functions( functions.push(vm::ImportedFunc { func: func.inner(), func_env: match ctx { - Context::External(ctx) => ctx, - Context::Internal => vmctx, - } as _, // cast `*mut vm::Ctx` to `*mut vm::FuncEnv` + Context::External(func_env) => func_env, + Context::Internal => vmctx as *mut vm::FuncEnv, + }, }); } else { link_errors.push(LinkError::IncorrectImportSignature { diff --git a/lib/runtime-core/src/export.rs b/lib/runtime-core/src/export.rs index 7960d76e699..55c456286a4 100644 --- a/lib/runtime-core/src/export.rs +++ b/lib/runtime-core/src/export.rs @@ -7,7 +7,7 @@ use std::sync::Arc; #[derive(Debug, Copy, Clone)] pub enum Context { - External(*mut vm::Ctx), + External(*mut vm::FuncEnv), Internal, } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index ee3b3137c75..f7522a7e7b6 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -418,7 +418,7 @@ impl InstanceInner { Export::Function { func, ctx: match ctx { - Context::Internal => Context::External(self.vmctx), + Context::Internal => Context::External(self.vmctx as *mut vm::FuncEnv), ctx @ Context::External(_) => ctx, }, signature, @@ -465,7 +465,7 @@ impl InstanceInner { ( imported_func.func as *const _, - Context::External(imported_func.func_env as _), // cast `*mut vm::Ctx` to `*mut vm::Func` + Context::External(imported_func.func_env), ) } };