diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f9b9a99ca4..8a0c33badc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## **[Unreleased]**
- [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder.
+- [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix.
- [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type.
- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types.
- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly.
diff --git a/index.html b/index.html
deleted file mode 100644
index 56b97fde0f6..00000000000
--- a/index.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/lib/interface-types/README.md b/lib/interface-types/README.md
new file mode 100644
index 00000000000..8dcf3ffc87e
--- /dev/null
+++ b/lib/interface-types/README.md
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Wasmer Interface Types
+
+Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
+compatible with WASI, Emscripten, Rust and Go. [Learn
+more](https://github.com/wasmerio/wasmer).
+
+This crate is an implementation of [the living WebAssembly Interface
+Types standard](https://github.com/WebAssembly/interface-types).
diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs
index 513ede7fbc0..ad4b5078edb 100644
--- a/lib/runtime-core/src/typed_func.rs
+++ b/lib/runtime-core/src/typed_func.rs
@@ -51,6 +51,8 @@ pub struct Wasm {
pub(crate) invoke_env: Option>,
}
+impl Kind for Wasm {}
+
impl Wasm {
/// Create new `Wasm` from given parts.
pub unsafe fn from_raw_parts(
@@ -70,7 +72,6 @@ impl Wasm {
/// by the host.
pub struct Host(());
-impl Kind for Wasm {}
impl Kind for Host {}
/// Represents a list of WebAssembly values.
@@ -110,14 +111,15 @@ pub trait WasmTypeList {
Rets: WasmTypeList;
}
-/// Empty trait to specify the kind of `ExternalFunction`: With or
+/// Empty trait to specify the kind of `HostFunction`: With or
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
/// `ImplicitVmCtx` structures.
///
-/// This type is never aimed to be used by a user. It is used by the
+/// This trait is never aimed to be used by a user. It is used by the
/// trait system to automatically generate an appropriate `wrap`
/// function.
-pub trait ExternalFunctionKind {}
+#[doc(hidden)]
+pub trait HostFunctionKind {}
/// This empty structure indicates that an external function must
/// contain an explicit `vm::Ctx` argument (at first position).
@@ -127,8 +129,11 @@ pub trait ExternalFunctionKind {}
/// x + 1
/// }
/// ```
+#[doc(hidden)]
pub struct ExplicitVmCtx {}
+impl HostFunctionKind for ExplicitVmCtx {}
+
/// This empty structure indicates that an external function has no
/// `vm::Ctx` argument (at first position). Its signature is:
///
@@ -139,14 +144,13 @@ pub struct ExplicitVmCtx {}
/// ```
pub struct ImplicitVmCtx {}
-impl ExternalFunctionKind for ExplicitVmCtx {}
-impl ExternalFunctionKind for ImplicitVmCtx {}
+impl HostFunctionKind for ImplicitVmCtx {}
/// Represents a function that can be converted to a `vm::Func`
/// (function pointer) that can be called within WebAssembly.
-pub trait ExternalFunction
+pub trait HostFunction
where
- Kind: ExternalFunctionKind,
+ Kind: HostFunctionKind,
Args: WasmTypeList,
Rets: WasmTypeList,
{
@@ -227,8 +231,8 @@ where
/// Creates a new `Func`.
pub fn new(func: F) -> Func<'a, Args, Rets, Host>
where
- Kind: ExternalFunctionKind,
- F: ExternalFunction,
+ Kind: HostFunctionKind,
+ F: HostFunction,
{
let (func, func_env) = func.to_raw();
@@ -310,6 +314,7 @@ macro_rules! impl_traits {
where
$( $x: WasmExternType ),*;
+ #[allow(unused_parens)]
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
where
$( $x: WasmExternType ),*
@@ -380,7 +385,8 @@ macro_rules! impl_traits {
}
}
- impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN
+ #[allow(unused_parens)]
+ impl< $( $x, )* Rets, Trap, FN > HostFunction for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
@@ -495,7 +501,8 @@ macro_rules! impl_traits {
}
}
- impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN
+ #[allow(unused_parens)]
+ impl< $( $x, )* Rets, Trap, FN > HostFunction for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
@@ -607,6 +614,7 @@ macro_rules! impl_traits {
}
}
+ #[allow(unused_parens)]
impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
where
$( $x: WasmExternType, )*
diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs
index 1b19bbc3d91..615964bb08e 100644
--- a/lib/runtime-core/src/types.rs
+++ b/lib/runtime-core/src/types.rs
@@ -104,44 +104,57 @@ where
{
/// Type for this `NativeWasmType`.
const TYPE: Type;
+
/// Convert from u64 bites to self.
fn from_binary(bits: u64) -> Self;
+
/// Convert self to u64 binary representation.
fn to_binary(self) -> u64;
}
unsafe impl NativeWasmType for i32 {
const TYPE: Type = Type::I32;
+
fn from_binary(bits: u64) -> Self {
bits as _
}
+
fn to_binary(self) -> u64 {
self as _
}
}
+
unsafe impl NativeWasmType for i64 {
const TYPE: Type = Type::I64;
+
fn from_binary(bits: u64) -> Self {
bits as _
}
+
fn to_binary(self) -> u64 {
self as _
}
}
+
unsafe impl NativeWasmType for f32 {
const TYPE: Type = Type::F32;
+
fn from_binary(bits: u64) -> Self {
f32::from_bits(bits as u32)
}
+
fn to_binary(self) -> u64 {
self.to_bits() as _
}
}
+
unsafe impl NativeWasmType for f64 {
const TYPE: Type = Type::F64;
+
fn from_binary(bits: u64) -> Self {
f64::from_bits(bits)
}
+
fn to_binary(self) -> u64 {
self.to_bits()
}
@@ -154,103 +167,41 @@ where
{
/// Native wasm type for this `WasmExternType`.
type Native: NativeWasmType;
+
/// Convert from given `Native` type to self.
fn from_native(native: Self::Native) -> Self;
+
/// Convert self to `Native` type.
fn to_native(self) -> Self::Native;
}
-unsafe impl WasmExternType for i8 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for u8 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for i16 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for u16 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for i32 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native
- }
- fn to_native(self) -> Self::Native {
- self
- }
-}
-unsafe impl WasmExternType for u32 {
- type Native = i32;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for i64 {
- type Native = i64;
- fn from_native(native: Self::Native) -> Self {
- native
- }
- fn to_native(self) -> Self::Native {
- self
- }
-}
-unsafe impl WasmExternType for u64 {
- type Native = i64;
- fn from_native(native: Self::Native) -> Self {
- native as _
- }
- fn to_native(self) -> Self::Native {
- self as _
- }
-}
-unsafe impl WasmExternType for f32 {
- type Native = f32;
- fn from_native(native: Self::Native) -> Self {
- native
- }
- fn to_native(self) -> Self::Native {
- self
- }
-}
-unsafe impl WasmExternType for f64 {
- type Native = f64;
- fn from_native(native: Self::Native) -> Self {
- native
- }
- fn to_native(self) -> Self::Native {
- self
- }
+macro_rules! wasm_extern_type {
+ ($type:ty => $native_type:ty) => {
+ unsafe impl WasmExternType for $type {
+ type Native = $native_type;
+
+ fn from_native(native: Self::Native) -> Self {
+ native as _
+ }
+
+ fn to_native(self) -> Self::Native {
+ self as _
+ }
+ }
+ };
}
+wasm_extern_type!(i8 => i32);
+wasm_extern_type!(u8 => i32);
+wasm_extern_type!(i16 => i32);
+wasm_extern_type!(u16 => i32);
+wasm_extern_type!(i32 => i32);
+wasm_extern_type!(u32 => i32);
+wasm_extern_type!(i64 => i64);
+wasm_extern_type!(u64 => i64);
+wasm_extern_type!(f32 => f32);
+wasm_extern_type!(f64 => f64);
+
// pub trait IntegerAtomic
// where
// Self: Sized
diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs
index 4c15a688c26..dbf20eed636 100644
--- a/lib/singlepass-backend/src/codegen_x64.rs
+++ b/lib/singlepass-backend/src/codegen_x64.rs
@@ -3,7 +3,6 @@
use crate::emitter_x64::*;
use crate::machine::*;
-use crate::protect_unix;
#[cfg(target_arch = "aarch64")]
use dynasmrt::aarch64::Assembler;
#[cfg(target_arch = "x86_64")]
@@ -28,7 +27,7 @@ use wasmer_runtime_core::{
},
cache::{Artifact, Error as CacheError},
codegen::*,
- fault::raw::register_preservation_trampoline,
+ fault::{self, raw::register_preservation_trampoline},
loader::CodeMemory,
memory::MemoryType,
module::{ModuleInfo, ModuleInner},
@@ -369,6 +368,11 @@ impl RunnableModule for X64ExecutionContext {
}
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option {
+ // Correctly unwinding from `catch_unsafe_unwind` on hardware exceptions depends
+ // on the signal handlers being installed. Here we call `ensure_sighandler` "statically"
+ // outside `invoke()`.
+ fault::ensure_sighandler();
+
unsafe extern "C" fn invoke(
_trampoline: Trampoline,
ctx: *mut vm::Ctx,
@@ -383,8 +387,9 @@ impl RunnableModule for X64ExecutionContext {
let args =
slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1);
- let ret = match protect_unix::call_protected(
+ let ret = match fault::catch_unsafe_unwind(
|| {
+ // Puts the arguments onto the stack and calls Wasm entry.
#[cfg(target_arch = "x86_64")]
{
let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect();
@@ -395,6 +400,9 @@ impl RunnableModule for X64ExecutionContext {
func.as_ptr(),
)
}
+
+ // FIXME: Currently we are doing a hack here to convert between native aarch64 and
+ // "emulated" x86 ABIs. Ideally, this should be done using handwritten assembly.
#[cfg(target_arch = "aarch64")]
{
struct CallCtx<'a> {
@@ -519,7 +527,7 @@ impl RunnableModule for X64ExecutionContext {
true
}
Err(err) => {
- *error_out = Some(err.0);
+ *error_out = Some(err);
false
}
};
@@ -545,8 +553,7 @@ impl RunnableModule for X64ExecutionContext {
}
unsafe fn do_early_trap(&self, data: Box) -> ! {
- protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
- protect_unix::trigger_trap();
+ fault::begin_unsafe_unwind(data);
}
fn get_code(&self) -> Option<&[u8]> {
@@ -1686,14 +1693,11 @@ impl X64FunctionCode {
Location::GPR(GPR::RSP),
);
- // FIXME: Possible dynasm bug. This is a workaround.
- // Using RSP as the source/destination operand of a `mov` instruction produces invalid code.
- a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
for (i, r) in used_xmms.iter().enumerate() {
a.emit_mov(
Size::S64,
Location::XMM(*r),
- Location::Memory(GPR::RCX, (i * 8) as i32),
+ Location::Memory(GPR::RSP, (i * 8) as i32),
);
}
for r in used_xmms.iter().rev() {
@@ -1771,37 +1775,26 @@ impl X64FunctionCode {
}
}
match *param {
- // Dynasm bug: RSP in memory operand does not work
- Location::Imm64(_) | Location::XMM(_) => {
- a.emit_mov(
- Size::S64,
- Location::GPR(GPR::RAX),
- Location::XMM(XMM::XMM0),
- );
- a.emit_mov(
- Size::S64,
- Location::GPR(GPR::RCX),
- Location::XMM(XMM::XMM1),
- );
- a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP));
- a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
- a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX));
- a.emit_mov(
- Size::S64,
- Location::GPR(GPR::RAX),
- Location::Memory(GPR::RCX, 0),
- );
- a.emit_mov(
- Size::S64,
- Location::XMM(XMM::XMM0),
- Location::GPR(GPR::RAX),
- );
+ Location::Imm64(_) => {
+ // Dummy value slot to be filled with `mov`.
+ a.emit_push(Size::S64, Location::GPR(GPR::RAX));
+
+ // Use R10 as the temporary register here, since it is callee-saved and not
+ // used by the callback `cb`.
+ a.emit_mov(Size::S64, *param, Location::GPR(GPR::R10));
a.emit_mov(
Size::S64,
- Location::XMM(XMM::XMM1),
- Location::GPR(GPR::RCX),
+ Location::GPR(GPR::R10),
+ Location::Memory(GPR::RSP, 0),
);
}
+ Location::XMM(_) => {
+ // Dummy value slot to be filled with `mov`.
+ a.emit_push(Size::S64, Location::GPR(GPR::RAX));
+
+ // XMM registers can be directly stored to memory.
+ a.emit_mov(Size::S64, *param, Location::Memory(GPR::RSP, 0));
+ }
_ => a.emit_push(Size::S64, *param),
}
}
@@ -1873,12 +1866,10 @@ impl X64FunctionCode {
// Restore XMMs.
if used_xmms.len() > 0 {
- // FIXME: Possible dynasm bug. This is a workaround.
- a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX));
for (i, r) in used_xmms.iter().enumerate() {
a.emit_mov(
Size::S64,
- Location::Memory(GPR::RDX, (i * 8) as i32),
+ Location::Memory(GPR::RSP, (i * 8) as i32),
Location::XMM(*r),
);
}
diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs
index 2afdaa7f57d..98339cca304 100644
--- a/lib/singlepass-backend/src/lib.rs
+++ b/lib/singlepass-backend/src/lib.rs
@@ -40,7 +40,6 @@ extern crate smallvec;
mod codegen_x64;
mod emitter_x64;
mod machine;
-pub mod protect_unix;
#[cfg(target_arch = "aarch64")]
mod translator_aarch64;
diff --git a/lib/singlepass-backend/src/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs
deleted file mode 100644
index 96f10748909..00000000000
--- a/lib/singlepass-backend/src/protect_unix.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-//! Installing signal handlers allows us to handle traps and out-of-bounds memory
-//! accesses that occur when runniing WebAssembly.
-//!
-//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
-//!
-//! When a WebAssembly module triggers any traps, we perform recovery here.
-//!
-//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling
-//! 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 std::any::Any;
-use std::cell::Cell;
-use wasmer_runtime_core::codegen::BreakpointMap;
-use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
-
-thread_local! {
- pub static TRAP_EARLY_DATA: Cell