From 44e6fae29c2d0a99bb26e98e662a0743bad0204b Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 7 Jul 2020 09:48:17 -0700 Subject: [PATCH] wasmtime: Modify `WasmTy` and `WasmRet` for future `{extern,func}ref` support --- crates/wasmtime/src/func.rs | 604 ++++++++++++++++++++++++------------ 1 file changed, 406 insertions(+), 198 deletions(-) diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index cfa36a3b54..3a775ee55d 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -193,17 +193,26 @@ macro_rules! getters { unsafe extern "C" fn( *mut VMContext, *mut VMContext, - $($args,)* - ) -> R, + $( $args::Abi, )* + ) -> R::Abi, >(anyfunc.as_ref().func_ptr.as_ptr()); + let mut ret = None; - $(let $args = $args.into_abi();)* + + let weak_store = instance.store.weak(); + let weak_store = WeakStore(&weak_store); + + $( let $args = $args.into_abi_for_arg(weak_store); )* invoke_wasm_and_catch_traps(anyfunc.as_ref().vmctx, &instance.store, || { - ret = Some(fnptr(anyfunc.as_ref().vmctx, ptr::null_mut(), $($args,)*)); + ret = Some(fnptr( + anyfunc.as_ref().vmctx, + ptr::null_mut(), + $( $args, )* + )); })?; - Ok(ret.unwrap()) + Ok(R::from_abi(ret.unwrap(), weak_store)) } }) } @@ -783,6 +792,12 @@ pub(crate) fn invoke_wasm_and_catch_traps( } } +// Public (but hidden) wrapper around a `Weak` so that we can use it +// in public (but hidden) trait methods. +#[doc(hidden)] +#[derive(Clone, Copy)] +pub struct WeakStore<'a>(&'a Weak); + /// A trait implemented for types which can be arguments to closures passed to /// [`Func::wrap`] and friends. /// @@ -791,160 +806,40 @@ pub(crate) fn invoke_wasm_and_catch_traps( /// stable over time. /// /// For more information see [`Func::wrap`] -pub unsafe trait WasmTy: Copy { +pub unsafe trait WasmTy { + // The raw ABI representation of this type inside Wasm. + #[doc(hidden)] + type Abi: Copy; + + // Convert this value into its ABI representation, when passing a value into + // Wasm as an argument. + #[doc(hidden)] + fn into_abi_for_arg<'a>(self, store: WeakStore<'a>) -> Self::Abi; + + // Convert from the raw ABI representation back into `Self`, when receiving + // a value from Wasm. + // + // Safety: The abi value *must* have be valid for this type (e.g. for + // `externref`, it must be a valid raw `VMExternRef` pointer, not some + // random, dangling pointer). + #[doc(hidden)] + unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self; + + // Add this type to the given vec of expected valtypes. #[doc(hidden)] fn push(dst: &mut Vec); + + // Does the next valtype(s) match this type? #[doc(hidden)] fn matches(tys: impl Iterator) -> anyhow::Result<()>; + + // Load this type's raw ABI representation from an args array. #[doc(hidden)] - unsafe fn load(ptr: &mut *const u128) -> Self; + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi; + + // Store this type's raw ABI representation into an args array. #[doc(hidden)] - unsafe fn store(abi: Self, ptr: *mut u128); -} - -unsafe impl WasmTy for () { - fn push(_dst: &mut Vec) {} - fn matches(_tys: impl Iterator) -> anyhow::Result<()> { - Ok(()) - } - #[inline] - unsafe fn load(_ptr: &mut *const u128) -> Self {} - #[inline] - unsafe fn store(_abi: Self, _ptr: *mut u128) {} -} - -unsafe impl WasmTy for i32 { - fn push(dst: &mut Vec) { - dst.push(ValType::I32); - } - fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { - let next = tys.next(); - ensure!( - next == Some(ValType::I32), - "Type mismatch, expected i32, got {:?}", - next - ); - Ok(()) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - let ret = **ptr as Self; - *ptr = (*ptr).add(1); - return ret; - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - *ptr = abi as u128; - } -} - -unsafe impl WasmTy for u32 { - fn push(dst: &mut Vec) { - ::push(dst) - } - fn matches(tys: impl Iterator) -> anyhow::Result<()> { - ::matches(tys) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - ::load(ptr) as Self - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - ::store(abi as i32, ptr) - } -} - -unsafe impl WasmTy for i64 { - fn push(dst: &mut Vec) { - dst.push(ValType::I64); - } - fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { - let next = tys.next(); - ensure!( - next == Some(ValType::I64), - "Type mismatch, expected i64, got {:?}", - next - ); - Ok(()) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - let ret = **ptr as Self; - *ptr = (*ptr).add(1); - return ret; - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - *ptr = abi as u128; - } -} - -unsafe impl WasmTy for u64 { - fn push(dst: &mut Vec) { - ::push(dst) - } - fn matches(tys: impl Iterator) -> anyhow::Result<()> { - ::matches(tys) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - ::load(ptr) as Self - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - ::store(abi as i64, ptr) - } -} - -unsafe impl WasmTy for f32 { - fn push(dst: &mut Vec) { - dst.push(ValType::F32); - } - fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { - let next = tys.next(); - ensure!( - next == Some(ValType::F32), - "Type mismatch, expected f32, got {:?}", - next - ); - Ok(()) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - let ret = f32::from_bits(**ptr as u32); - *ptr = (*ptr).add(1); - return ret; - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - *ptr = abi.to_bits() as u128; - } -} - -unsafe impl WasmTy for f64 { - fn push(dst: &mut Vec) { - dst.push(ValType::F64); - } - fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { - let next = tys.next(); - ensure!( - next == Some(ValType::F64), - "Type mismatch, expected f64, got {:?}", - next - ); - Ok(()) - } - #[inline] - unsafe fn load(ptr: &mut *const u128) -> Self { - let ret = f64::from_bits(**ptr as u64); - *ptr = (*ptr).add(1); - return ret; - } - #[inline] - unsafe fn store(abi: Self, ptr: *mut u128) { - *ptr = abi.to_bits() as u128; - } + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128); } /// A trait implemented for types which can be returned from closures passed to @@ -956,64 +851,362 @@ unsafe impl WasmTy for f64 { /// /// For more information see [`Func::wrap`] pub unsafe trait WasmRet { + // Same as `WasmTy::Abi`. #[doc(hidden)] - type Abi; + type Abi: Copy; + + // Similar to `WasmTy::into_abi_for_arg` but used when host code is + // returning a value into Wasm, rather than host code passing an argument to + // a Wasm call. Unlike `into_abi_for_arg`, implementors of this method can + // raise traps, which means that callers must ensure that + // `invoke_wasm_and_catch_traps` is on the stack, and therefore this method + // is unsafe. + #[doc(hidden)] + unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi; + + // Same as `WasmTy::from_abi`. + #[doc(hidden)] + unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self; + + // Same as `WasmTy::push`. #[doc(hidden)] fn push(dst: &mut Vec); + + // Same as `WasmTy::matches`. #[doc(hidden)] fn matches(tys: impl Iterator) -> anyhow::Result<()>; + + // Same as `WasmTy::load_from_args`. #[doc(hidden)] - fn into_abi(self) -> Self::Abi; + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi; + + // Same as `WasmTy::store_to_args`. #[doc(hidden)] - unsafe fn store(abi: Self::Abi, ptr: *mut u128); + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128); } -unsafe impl WasmRet for T { - type Abi = T; - fn push(dst: &mut Vec) { - T::push(dst) - } +unsafe impl WasmTy for () { + type Abi = Self; - fn matches(tys: impl Iterator) -> anyhow::Result<()> { - T::matches(tys) + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {} + + #[inline] + unsafe fn from_abi<'a>(_abi: Self::Abi, _store: WeakStore<'a>) -> Self {} + + fn push(_dst: &mut Vec) {} + + fn matches(_tys: impl Iterator) -> anyhow::Result<()> { + Ok(()) } #[inline] - fn into_abi(self) -> Self::Abi { + unsafe fn load_from_args(_ptr: &mut *const u128) -> Self::Abi {} + + #[inline] + unsafe fn store_to_args(_abi: Self::Abi, _ptr: *mut u128) {} +} + +unsafe impl WasmTy for i32 { + type Abi = Self; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { self } #[inline] - unsafe fn store(abi: Self::Abi, ptr: *mut u128) { - T::store(abi, ptr); + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi + } + + fn push(dst: &mut Vec) { + dst.push(ValType::I32); + } + + fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { + let next = tys.next(); + ensure!( + next == Some(ValType::I32), + "Type mismatch, expected i32, got {:?}", + next + ); + Ok(()) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + let ret = **ptr as Self; + *ptr = (*ptr).add(1); + return ret; + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + *ptr = abi as u128; } } -unsafe impl WasmRet for Result { - type Abi = T; +unsafe impl WasmTy for u32 { + type Abi = ::Abi; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { + self as i32 + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi as Self + } + fn push(dst: &mut Vec) { - T::push(dst) + ::push(dst) } fn matches(tys: impl Iterator) -> anyhow::Result<()> { - T::matches(tys) + ::matches(tys) } #[inline] - fn into_abi(self) -> Self::Abi { + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + ::load_from_args(ptr) + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + ::store_to_args(abi, ptr) + } +} + +unsafe impl WasmTy for i64 { + type Abi = Self; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { + self + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi + } + + fn push(dst: &mut Vec) { + dst.push(ValType::I64); + } + + fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { + let next = tys.next(); + ensure!( + next == Some(ValType::I64), + "Type mismatch, expected i64, got {:?}", + next + ); + Ok(()) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + let ret = **ptr as Self; + *ptr = (*ptr).add(1); + return ret; + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + *ptr = abi as u128; + } +} + +unsafe impl WasmTy for u64 { + type Abi = ::Abi; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { + self as i64 + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi as Self + } + + fn push(dst: &mut Vec) { + ::push(dst) + } + + fn matches(tys: impl Iterator) -> anyhow::Result<()> { + ::matches(tys) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + ::load_from_args(ptr) + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + ::store_to_args(abi, ptr) + } +} + +unsafe impl WasmTy for f32 { + type Abi = Self; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { + self + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi + } + + fn push(dst: &mut Vec) { + dst.push(ValType::F32); + } + + fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { + let next = tys.next(); + ensure!( + next == Some(ValType::F32), + "Type mismatch, expected f32, got {:?}", + next + ); + Ok(()) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + let ret = f32::from_bits(**ptr as u32); + *ptr = (*ptr).add(1); + return ret; + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + *ptr = abi.to_bits() as u128; + } +} + +unsafe impl WasmTy for f64 { + type Abi = Self; + + #[inline] + fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi { + self + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, _store: WeakStore<'a>) -> Self { + abi + } + + fn push(dst: &mut Vec) { + dst.push(ValType::F64); + } + + fn matches(mut tys: impl Iterator) -> anyhow::Result<()> { + let next = tys.next(); + ensure!( + next == Some(ValType::F64), + "Type mismatch, expected f64, got {:?}", + next + ); + Ok(()) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + let ret = f64::from_bits(**ptr as u64); + *ptr = (*ptr).add(1); + return ret; + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + *ptr = abi.to_bits() as u128; + } +} + +unsafe impl WasmRet for T +where + T: WasmTy, +{ + type Abi = ::Abi; + + #[inline] + unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi { + ::into_abi_for_arg(self, store) + } + + #[inline] + unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self { + ::from_abi(abi, store) + } + + #[inline] + fn push(dst: &mut Vec) { + ::push(dst) + } + + #[inline] + fn matches(tys: impl Iterator) -> anyhow::Result<()> { + ::matches(tys) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + ::load_from_args(ptr) + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + ::store_to_args(abi, ptr) + } +} + +unsafe impl WasmRet for Result +where + T: WasmTy, +{ + type Abi = ::Abi; + + #[inline] + unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi { match self { - Ok(val) => return T::into_abi(val), + Ok(val) => return ::into_abi_for_arg(val, store), Err(trap) => handle_trap(trap), } - fn handle_trap(trap: Trap) -> ! { - unsafe { raise_user_trap(trap.into()) } + unsafe fn handle_trap(trap: Trap) -> ! { + raise_user_trap(trap.into()) } } #[inline] - unsafe fn store(abi: Self::Abi, ptr: *mut u128) { - T::store(abi, ptr); + unsafe fn from_abi<'a>(abi: Self::Abi, store: WeakStore<'a>) -> Self { + Ok(::from_abi(abi, store)) + } + + fn push(dst: &mut Vec) { + ::push(dst) + } + + fn matches(tys: impl Iterator) -> anyhow::Result<()> { + ::matches(tys) + } + + #[inline] + unsafe fn load_from_args(ptr: &mut *const u128) -> Self::Abi { + ::load_from_args(ptr) + } + + #[inline] + unsafe fn store_to_args(abi: Self::Abi, ptr: *mut u128) { + ::store_to_args(abi, ptr); } } @@ -1141,40 +1334,53 @@ macro_rules! impl_into_func { R: WasmRet, { fn into_func(self, store: &Store) -> Func { - // Note that this shim's ABI must match that expected by - // cranelift, since cranelift is generating raw function calls - // directly to this function. - unsafe extern "C" fn shim( + /// This shim is called by Wasm code, constructs a `Caller`, + /// calls the wrapped host function, and returns the translated + /// result back to Wasm. + /// + /// Note that this shim's ABI must *exactly* match that expected + /// by Cranelift, since Cranelift is generating raw function + /// calls directly to this function. + unsafe extern "C" fn wasm_to_host_shim( vmctx: *mut VMContext, caller_vmctx: *mut VMContext, - $($args: $args,)* + $( $args: $args::Abi, )* ) -> R::Abi where - F: Fn(Caller<'_>, $($args),*) -> R + 'static, - $($args: WasmTy,)* + F: Fn(Caller<'_>, $( $args ),*) -> R + 'static, + $( $args: WasmTy, )* R: WasmRet, { + let state = (*vmctx).host_state(); + // Double-check ourselves in debug mode, but we control + // the `Any` here so an unsafe downcast should also + // work. + debug_assert!(state.is::<(F, Weak)>()); + let (func, store) = &*(state as *const _ as *const (F, Weak)); + let weak_store = WeakStore(store); + let ret = { - let state = (*vmctx).host_state(); - // Double-check ourselves in debug mode, but we control - // the `Any` here so an unsafe downcast should also - // work. - debug_assert!(state.is::<(F, Weak)>()); - let (func, store) = &*(state as *const _ as *const (F, Weak)); panic::catch_unwind(AssertUnwindSafe(|| { func( Caller { store, caller_vmctx }, - $($args,)* + $( $args::from_abi($args, weak_store), )* ) })) }; match ret { - Ok(ret) => ret.into_abi(), + Ok(ret) => ret.into_abi_for_ret(weak_store), Err(panic) => wasmtime_runtime::resume_panic(panic), } } - unsafe extern "C" fn trampoline<$($args,)* R>( + /// This trampoline allows host code to indirectly call the + /// wrapped function (e.g. via `Func::call` on a `funcref` that + /// happens to reference our wrapped function). + /// + /// It reads the arguments out of the incoming `args` array, + /// calls the given function pointer, and then stores the result + /// back into the `args` array. + unsafe extern "C" fn host_trampoline<$($args,)* R>( callee_vmctx: *mut VMContext, caller_vmctx: *mut VMContext, ptr: *const VMFunctionBody, @@ -1189,14 +1395,14 @@ macro_rules! impl_into_func { unsafe extern "C" fn( *mut VMContext, *mut VMContext, - $($args,)* + $( $args::Abi, )* ) -> R::Abi, >(ptr); let mut _next = args as *const u128; - $(let $args = $args::load(&mut _next);)* - let ret = ptr(callee_vmctx, caller_vmctx, $($args),*); - R::store(ret, args); + $( let $args = $args::load_from_args(&mut _next); )* + let ret = ptr(callee_vmctx, caller_vmctx, $( $args ),*); + R::store_to_args(ret, args); } let mut _args = Vec::new(); @@ -1204,13 +1410,14 @@ macro_rules! impl_into_func { let mut ret = Vec::new(); R::push(&mut ret); let ty = FuncType::new(_args.into(), ret.into()); + let store_weak = store.weak(); - let trampoline = trampoline::<$($args,)* R>; + let trampoline = host_trampoline::<$($args,)* R>; let (instance, export) = unsafe { crate::trampoline::generate_raw_func_export( &ty, std::slice::from_raw_parts_mut( - shim:: as *mut _, + wasm_to_host_shim:: as *mut _, 0, ), trampoline, @@ -1219,6 +1426,7 @@ macro_rules! impl_into_func { ) .expect("failed to generate export") }; + Func { instance, export,