Merge branch 'master' into feat-deprecated-runtime-core

This commit is contained in:
Ivan Enderlin
2020-06-25 09:51:15 +02:00
256 changed files with 13268 additions and 1925 deletions

39
Cargo.lock generated
View File

@@ -160,6 +160,15 @@ dependencies = [
"serde",
]
[[package]]
name = "build-deps"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f14468960818ce4f3e3553c32d524446687884f8e7af5d3e252331d8a87e43"
dependencies = [
"glob",
]
[[package]]
name = "bumpalo"
version = "3.4.0"
@@ -1794,18 +1803,6 @@ dependencies = [
"target-lexicon",
]
[[package]]
name = "test-utils"
version = "1.0.0-alpha.1"
dependencies = [
"wasmer",
"wasmer-compiler",
"wasmer-compiler-cranelift",
"wasmer-compiler-llvm",
"wasmer-compiler-singlepass",
"wasmer-engine-jit",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@@ -2088,6 +2085,7 @@ dependencies = [
"anyhow",
"atty",
"blake3",
"build-deps",
"bytesize",
"cfg-if",
"colored",
@@ -2098,7 +2096,6 @@ dependencies = [
"rustc_version",
"structopt",
"test-generator",
"test-utils",
"wasm-common",
"wasmer",
"wasmer-cache",
@@ -2297,7 +2294,6 @@ dependencies = [
"cfg-if",
"leb128",
"libloading 0.6.2",
"object",
"serde",
"serde_bytes",
"tempfile",
@@ -2305,9 +2301,20 @@ dependencies = [
"wasm-common",
"wasmer-compiler",
"wasmer-engine",
"wasmer-object",
"wasmer-runtime",
]
[[package]]
name = "wasmer-object"
version = "1.0.0-alpha.1"
dependencies = [
"object",
"thiserror",
"wasm-common",
"wasmer-compiler",
]
[[package]]
name = "wasmer-runtime"
version = "1.0.0-alpha.1"
@@ -2361,8 +2368,12 @@ name = "wasmer-wast"
version = "1.0.0-alpha.1"
dependencies = [
"anyhow",
"serde",
"tempfile",
"thiserror",
"typetag",
"wasmer",
"wasmer-wasi",
"wast",
]

View File

@@ -59,6 +59,7 @@ exclude = [
[build-dependencies]
test-generator = { path = "tests/lib/test-generator" }
build-deps = "0.1.4"
anyhow = "1.0"
glob = "0.3"
rustc_version = "0.2"
@@ -68,7 +69,6 @@ anyhow = "1.0"
blake3 = "0.3"
criterion = "0.3"
lazy_static = "1.4"
test-utils = { path = "tests/lib/test-utils" }
wasmer-engine-dummy = { path = "tests/lib/engine-dummy" }
[features]
@@ -107,17 +107,14 @@ experimental-io-devices = [
]
singlepass = [
"wasmer-compiler-singlepass",
"test-utils/singlepass",
"compiler",
]
cranelift = [
"wasmer-compiler-cranelift",
"test-utils/cranelift",
"compiler",
]
llvm = [
"wasmer-compiler-llvm",
"test-utils/llvm",
"compiler",
]

View File

@@ -111,6 +111,7 @@ test-packages:
cargo test -p wasmer --release
cargo test -p wasmer-runtime --release
cargo test -p wasm-common --release
cargo test -p wasmer-wasi --release
test-capi-singlepass: build-capi-singlepass
cargo test --manifest-path lib/c-api/Cargo.toml --release \
@@ -126,6 +127,9 @@ test-capi-llvm: build-capi-llvm
test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-emscripten
test-wasi-unit:
cargo test --manifest-path lib/wasi/Cargo.toml --release
#############
# Packaging #
#############

View File

@@ -1,8 +1,7 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use test_utils::get_compiler_config_from_str;
use wasmer::*;
use wasmer_engine_jit::JITEngine;
use wasmer_engine_jit::JIT;
static BASIC_WAT: &str = r#"(module
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
@@ -151,19 +150,19 @@ pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Cr
fn run_static_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")]
{
let store = test_utils::get_default_llvm_store();
let store = Store::new(&JIT::new(&wasmer_compiler_llvm::LLVM::new()).engine());
run_basic_static_function(&store, "llvm", c);
}
#[cfg(feature = "cranelift")]
{
let store = test_utils::get_default_cranelift_store();
let store = Store::new(&JIT::new(&wasmer_compiler_cranelift::Cranelift::new()).engine());
run_basic_static_function(&store, "cranelift", c);
}
#[cfg(feature = "singlepass")]
{
let store = test_utils::get_default_singlepass_store();
let store = Store::new(&JIT::new(&wasmer_compiler_singlepass::Singlepass::new()).engine());
run_basic_static_function(&store, "singlepass", c);
}
}
@@ -171,19 +170,19 @@ fn run_static_benchmarks(c: &mut Criterion) {
fn run_dynamic_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")]
{
let store = test_utils::get_default_llvm_store();
let store = Store::new(&JIT::new(&wasmer_compiler_llvm::LLVM::new()).engine());
run_basic_dynamic_function(&store, "llvm", c);
}
#[cfg(feature = "cranelift")]
{
let store = test_utils::get_default_cranelift_store();
let store = Store::new(&JIT::new(&wasmer_compiler_cranelift::Cranelift::new()).engine());
run_basic_dynamic_function(&store, "cranelift", c);
}
#[cfg(feature = "singlepass")]
{
let store = test_utils::get_default_singlepass_store();
let store = Store::new(&JIT::new(&wasmer_compiler_singlepass::Singlepass::new()).engine());
run_basic_dynamic_function(&store, "singlepass", c);
}
}

View File

@@ -9,28 +9,34 @@ use std::fs;
use std::path::PathBuf;
use std::process::Command;
use test_generator::{
build_ignores_from_textfile, test_directory, test_directory_module, wast_processor,
with_features, with_test_module, Testsuite,
build_ignores_from_textfile, test_directory, test_directory_module, wasi_processor,
wast_processor, with_features, with_test_module, Testsuite,
};
fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=tests/ignores.txt");
// As rerun-if-changed doesn't support globs, we use another crate
// to check changes in directories.
build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/unstable/*")
.expect("Can't get directory");
build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/snapshot1/*")
.expect("Can't get directory");
let out_dir = PathBuf::from(
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
);
let ignores = build_ignores_from_textfile("tests/ignores.txt".into())?;
let compilers = ["singlepass", "cranelift", "llvm"];
// Spectests test generation
{
let mut spectests = Testsuite {
buffer: String::new(),
path: vec![],
ignores,
ignores: ignores.clone(),
};
let backends = vec!["singlepass", "cranelift", "llvm"];
with_features(&mut spectests, &backends, |mut spectests| {
with_features(&mut spectests, &compilers, |mut spectests| {
with_test_module(&mut spectests, "spec", |spectests| {
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
test_directory_module(
@@ -56,6 +62,38 @@ fn main() -> anyhow::Result<()> {
// Note: We need drop because we don't want to run `unwrap` or `expect` as
// the command might fail, but we don't care about it's result.
drop(Command::new("rustfmt").arg(&spectests_output).status());
}
// Wasitest test generation
{
let mut wasitests = Testsuite {
buffer: String::new(),
path: vec![],
ignores,
};
let wasi_versions = ["unstable", "snapshot1"];
with_features(&mut wasitests, &compilers, |mut wasitests| {
with_test_module(&mut wasitests, "wasitests", |wasitests| {
for wasi_version in &wasi_versions {
with_test_module(wasitests, wasi_version, |wasitests| {
let _wasi_tests = test_directory(
wasitests,
format!("tests/wasi-wast/wasi/{}", wasi_version),
wasi_processor,
)?;
Ok(())
})?;
}
Ok(())
})?;
Ok(())
})?;
let wasitests_output = out_dir.join("generated_wasitests.rs");
fs::write(&wasitests_output, wasitests.buffer)?;
drop(Command::new("rustfmt").arg(&wasitests_output).status());
}
Ok(())
}

View File

@@ -40,7 +40,7 @@ anyhow = "1.0"
maintenance = { status = "actively-developed" }
[features]
default = ["wat", "cranelift", "jit"]
default = ["wat", "default-cranelift", "default-jit"]
compiler = [
"wasmer-engine-jit/compiler",
"wasmer-engine-native/compiler",
@@ -66,3 +66,26 @@ llvm = [
"wasmer-compiler-llvm",
"compiler",
]
default-compiler = []
default-engine = []
default-singlepass = [
"singlepass",
"default-compiler"
]
default-cranelift = [
"cranelift",
"default-compiler"
]
default-llvm = [
"llvm",
"default-compiler"
]
default-jit = [
"jit",
"default-engine"
]
default-native = [
"native",
"default-engine"
]

View File

@@ -1,13 +1,13 @@
use crate::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace;
use crate::native::NativeFunc;
use crate::WasmTypeList;
use indexmap::IndexMap;
use std::{
iter::{ExactSizeIterator, FromIterator},
sync::Arc,
};
use thiserror::Error;
use wasm_common::WasmTypeList;
use wasmer_runtime::Export;
/// The `ExportError` can happen when trying to get a specific
@@ -127,7 +127,7 @@ impl Exports {
{
self.get_function(name)?
.native()
.ok_or(ExportError::IncompatibleType)
.map_err(|_| ExportError::IncompatibleType)
}
/// Get an export as an `Extern`.

View File

@@ -5,12 +5,14 @@ use crate::types::Val;
use crate::FunctionType;
use crate::NativeFunc;
use crate::RuntimeError;
pub use inner::{HostFunction, WasmExternType, WasmTypeList};
use inner::{WithEnv, WithoutEnv};
use std::cell::RefCell;
use std::cmp::max;
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use wasmer_runtime::{
wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext,
VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
raise_user_trap, resume_panic, wasmer_call_trampoline, Export, ExportFunction,
VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
VMTrampoline,
};
/// A function defined in the Wasm module
@@ -47,10 +49,13 @@ pub struct Function {
}
impl Function {
/// Creates a new `Func` with the given parameters.
/// Creates a new `Function` that is:
///
/// * `store` - a global cache to store information in
/// * `func` - the function.
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
/// unique _statically declared type_. The outputs can be
/// wrapped in a `Result`.
/// 2. Independent, i.e. the function _does not_ receive an
/// environment argument.
pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Self
where
F: HostFunction<Args, Rets, WithoutEnv, Env>,
@@ -58,10 +63,11 @@ impl Function {
Rets: WasmTypeList,
Env: Sized + 'static,
{
let func: wasm_common::Func<Args, Rets> = wasm_common::Func::new(func);
let address = func.address() as *const VMFunctionBody;
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address() as *const VMFunctionBody;
let vmctx = std::ptr::null_mut() as *mut _ as *mut VMContext;
let signature = func.ty();
let signature = function.ty();
Self {
store: store.clone(),
owned_by_store: true,
@@ -75,6 +81,53 @@ impl Function {
}
}
/// Creates a new `Function` that is:
///
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
/// unique statically declared type. The outputs can be wrapped
/// in a `Result`.
/// 2. Dependent, i.e. the function _does_ receive an environment
/// argument (given by `env`).
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: Env, func: F) -> Self
where
F: HostFunction<Args, Rets, WithEnv, Env>,
Args: WasmTypeList,
Rets: WasmTypeList,
Env: Sized + 'static,
{
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address();
// TODO: We need to refactor the Function context.
// Right now is structured as it's always a `VMContext`. However, only
// Wasm-defined functions have a `VMContext`.
// In the case of Host-defined functions `VMContext` is whatever environment
// the user want to attach to the function.
let box_env = Box::new(env);
let vmctx = Box::into_raw(box_env) as *mut _ as *mut VMContext;
let signature = function.ty();
Self {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
signature,
},
}
}
/// Creates a new `Function` that is:
///
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
/// slice of `Val` (the set of all Wasm values), and all
/// outputs are stored in a vector of `Val`, wrapped in a
/// `Result`.
/// 2. Independent, i.e. the function _does not_ receive an
/// environment argument.
#[allow(clippy::cast_ptr_alignment)]
pub fn new_dynamic<F>(store: &Store, ty: &FunctionType, func: F) -> Self
where
@@ -89,6 +142,7 @@ impl Function {
// generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self {
store: store.clone(),
owned_by_store: true,
@@ -102,6 +156,14 @@ impl Function {
}
}
/// Creates a new `Function` that is:
///
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
/// slice of `Val` (the set of all Wasm values), and all
/// outputs are stored in a vector of `Val`, wrapped in a
/// `Result`.
/// 2. Dependent, i.e. the function _does_ receive an environment
/// argument (given by `env`).
#[allow(clippy::cast_ptr_alignment)]
pub fn new_dynamic_env<F, Env>(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self
where
@@ -118,6 +180,7 @@ impl Function {
// generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self {
store: store.clone(),
owned_by_store: true,
@@ -131,41 +194,6 @@ impl Function {
}
}
/// Creates a new `Func` with the given parameters.
///
/// * `store` - a global cache to store information in.
/// * `env` - the function environment.
/// * `func` - the function.
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: Env, func: F) -> Self
where
F: HostFunction<Args, Rets, WithEnv, Env>,
Args: WasmTypeList,
Rets: WasmTypeList,
Env: Sized + 'static,
{
let func: wasm_common::Func<Args, Rets> = wasm_common::Func::new(func);
let address = func.address() as *const VMFunctionBody;
// TODO: We need to refactor the Function context.
// Right now is structured as it's always a `VMContext`. However, only
// Wasm-defined functions have a `VMContext`.
// In the case of Host-defined functions `VMContext` is whatever environment
// the user want to attach to the function.
let box_env = Box::new(env);
let vmctx = Box::into_raw(box_env) as *mut _ as *mut VMContext;
let signature = func.ty();
Self {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
exported: ExportFunction {
address,
kind: VMFunctionKind::Static,
vmctx,
signature,
},
}
}
/// Returns the underlying type of this function.
pub fn ty(&self) -> &FunctionType {
&self.exported.signature
@@ -300,22 +328,42 @@ impl Function {
}
}
pub fn native<'a, Args, Rets>(&self) -> Option<NativeFunc<'a, Args, Rets>>
/// Transform this WebAssembly function into a function with the
/// native ABI. See `NativeFunc` to learn more.
pub fn native<'a, Args, Rets>(&self) -> Result<NativeFunc<'a, Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
// type check
if self.exported.signature.params() != Args::wasm_types() {
// todo: error param types don't match
return None;
{
let expected = self.exported.signature.params();
let given = Args::wasm_types();
if expected != given {
return Err(RuntimeError::new(format!(
"given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
given,
expected,
)));
}
if self.exported.signature.results() != Rets::wasm_types() {
// todo: error result types don't match
return None;
}
Some(NativeFunc::new(
{
let expected = self.exported.signature.results();
let given = Rets::wasm_types();
if expected != given {
// todo: error result types don't match
return Err(RuntimeError::new(format!(
"given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
Ok(NativeFunc::new(
self.store.clone(),
self.exported.address,
self.exported.vmctx,
@@ -441,8 +489,607 @@ impl<T: VMDynamicFunction> VMDynamicFunctionCall<T> for VMDynamicFunctionContext
match result {
Ok(Ok(())) => {}
Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)),
Err(panic) => wasmer_runtime::resume_panic(panic),
Ok(Err(trap)) => raise_user_trap(Box::new(trap)),
Err(panic) => resume_panic(panic),
}
}
}
/// This private inner module contains the low-level implementation
/// for `Function` and its siblings.
mod inner {
use std::convert::Infallible;
use std::error::Error;
use std::marker::PhantomData;
use std::panic::{self, AssertUnwindSafe};
use wasm_common::{FunctionType, NativeWasmType, Type};
use wasmer_runtime::{raise_user_trap, resume_panic, VMFunctionBody};
/// A trait to represent a wasm extern type.
pub unsafe trait WasmExternType: Copy
where
Self: Sized,
{
/// 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;
}
macro_rules! wasm_extern_type {
( $( $type:ty => $native_type:ty ),* ) => {
$(
#[allow(clippy::use_self)]
unsafe impl WasmExternType for $type {
type Native = $native_type;
#[inline]
fn from_native(native: Self::Native) -> Self {
native as _
}
#[inline]
fn to_native(self) -> Self::Native {
self as _
}
}
)*
};
}
wasm_extern_type!(
i8 => i32,
u8 => i32,
i16 => i32,
u16 => i32,
i32 => i32,
u32 => i32,
i64 => i64,
u64 => i64,
f32 => f32,
f64 => f64
);
/// The `WasmTypeList` trait represents a tuple (list) of Wasm
/// typed values. It is used to get low-level representation of
/// such a tuple.
pub trait WasmTypeList {
/// The C type (a struct) that can hold/represent all the
/// represented values.
type CStruct;
/// The array type that can hold all the represented values.
///
/// Note that all values are stored in their binary form.
type Array: AsMut<[i128]>;
/// Constructs `Self` based on an array of values.
fn from_array(array: Self::Array) -> Self;
/// Builds and returns an array of type `Array` from a tuple
/// (list) of values.
fn into_array(self) -> Self::Array;
/// Allocates and return an empty array of type `Array` that
/// will hold a tuple (list) of values, usually to hold the
/// returned values of a WebAssembly function call.
fn empty_array() -> Self::Array;
/// Builds a tuple (list) of values from a C struct of type
/// `CStruct`.
fn from_c_struct(c_struct: Self::CStruct) -> Self;
/// Builds and returns a C struct of type `CStruct` from a
/// tuple (list) of values.
fn into_c_struct(self) -> Self::CStruct;
/// Get the Wasm types for the tuple (list) of currently
/// represented values.
fn wasm_types() -> &'static [Type];
}
/// The `IntoResult` trait turns a `WasmTypeList` into a
/// `Result<WasmTypeList, Self::Error>`.
///
/// It is mostly used to turn result values of a Wasm function
/// call into a `Result`.
pub trait IntoResult<T>
where
T: WasmTypeList,
{
/// The error type for this trait.
type Error: Error + Sync + Send + 'static;
/// Transforms `Self` into a `Result`.
fn into_result(self) -> Result<T, Self::Error>;
}
impl<T> IntoResult<T> for T
where
T: WasmTypeList,
{
// `T` is not a `Result`, it's already a value, so no error
// can be built.
type Error = Infallible;
fn into_result(self) -> Result<Self, Infallible> {
Ok(self)
}
}
impl<T, E> IntoResult<T> for Result<T, E>
where
T: WasmTypeList,
E: Error + Sync + Send + 'static,
{
type Error = E;
fn into_result(self) -> Self {
self
}
}
/// The `HostFunction` trait represents the set of functions that
/// can be used as host function. To uphold this statement, it is
/// necessary for a function to be transformed into a pointer to
/// `VMFunctionBody`.
pub trait HostFunction<Args, Rets, Kind, T>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
T: Sized,
Self: Sized,
{
/// Get the pointer to the function body.
fn function_body_ptr(self) -> *const VMFunctionBody;
}
/// Empty trait to specify the kind of `HostFunction`: With or
/// without an environment.
///
/// This trait is never aimed to be used by a user. It is used by
/// the trait system to automatically generate the appropriate
/// host functions.
#[doc(hidden)]
pub trait HostFunctionKind {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does have an environment.
pub struct WithEnv;
impl HostFunctionKind for WithEnv {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does not have an environment.
pub struct WithoutEnv;
impl HostFunctionKind for WithoutEnv {}
/// Represents a low-level Wasm static host function. See
/// `super::Function::new` and `super::Function::new_env` to learn
/// more.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Function<Args = (), Rets = ()> {
address: *const VMFunctionBody,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for Function<Args, Rets> {}
impl<Args, Rets> Function<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
/// Creates a new `Function`.
pub fn new<F, T, E>(function: F) -> Self
where
F: HostFunction<Args, Rets, T, E>,
T: HostFunctionKind,
E: Sized,
{
Self {
address: function.function_body_ptr(),
_phantom: PhantomData,
}
}
/// Get the function type of this `Function`.
pub fn ty(&self) -> FunctionType {
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
}
/// Get the address of this `Function`.
pub fn address(&self) -> *const VMFunctionBody {
self.address
}
}
macro_rules! impl_host_function {
( [$c_struct_representation:ident]
$c_struct_name:ident,
$( $x:ident ),* ) => {
/// A structure with a C-compatible representation that can hold a set of Wasm values.
/// This type is used by `WasmTypeList::CStruct`.
#[repr($c_struct_representation)]
pub struct $c_struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
where
$( $x: WasmExternType ),*;
// Implement `WasmTypeList` for a specific tuple.
#[allow(unused_parens, dead_code)]
impl< $( $x ),* >
WasmTypeList
for
( $( $x ),* )
where
$( $x: WasmExternType ),*
{
type CStruct = $c_struct_name< $( $x ),* >;
type Array = [i128; count_idents!( $( $x ),* )];
fn from_array(array: Self::Array) -> Self {
// Unpack items of the array.
#[allow(non_snake_case)]
let [ $( $x ),* ] = array;
// Build the tuple.
(
$(
WasmExternType::from_native(NativeWasmType::from_binary($x))
),*
)
}
fn into_array(self) -> Self::Array {
// Unpack items of the tuple.
#[allow(non_snake_case)]
let ( $( $x ),* ) = self;
// Build the array.
[
$(
WasmExternType::to_native($x).to_binary()
),*
]
}
fn empty_array() -> Self::Array {
// Build an array initialized with `0`.
[0; count_idents!( $( $x ),* )]
}
fn from_c_struct(c_struct: Self::CStruct) -> Self {
// Unpack items of the C structure.
#[allow(non_snake_case)]
let $c_struct_name( $( $x ),* ) = c_struct;
(
$(
WasmExternType::from_native($x)
),*
)
}
#[allow(unused_parens, non_snake_case)]
fn into_c_struct(self) -> Self::CStruct {
// Unpack items of the tuple.
let ( $( $x ),* ) = self;
// Build the C structure.
$c_struct_name(
$(
WasmExternType::to_native($x)
),*
)
}
fn wasm_types() -> &'static [Type] {
&[
$(
$x::Native::WASM_TYPE
),*
]
}
}
// Implement `HostFunction` for a function that has the same arity than the tuple.
// This specific function has no environment.
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Func >
HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()>
for
Func
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn($( $x , )*) -> RetsAsResult + 'static + Send,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn( $( $x ),* ) -> RetsAsResult + 'static
{
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func( $( WasmExternType::from_native($x) ),* ).into_result()
}));
match result {
Ok(Ok(result)) => return result.into_c_struct(),
Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) },
Err(panic) => unsafe { resume_panic(panic) },
}
}
func_wrapper::<$( $x, )* Rets, RetsAsResult, Self> as *const VMFunctionBody
}
}
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Env, Func >
HostFunction<( $( $x ),* ), Rets, WithEnv, Env>
for
Func
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: Sized,
Func: Fn(&mut Env, $( $x , )*) -> RetsAsResult + Send + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( env: &mut Env, $( $x: $x::Native, )* ) -> Rets::CStruct
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Env: Sized,
Func: Fn(&mut Env, $( $x ),* ) -> RetsAsResult + 'static
{
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func(env, $( WasmExternType::from_native($x) ),* ).into_result()
}));
match result {
Ok(Ok(result)) => return result.into_c_struct(),
Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) },
Err(panic) => unsafe { resume_panic(panic) },
}
}
func_wrapper::<$( $x, )* Rets, RetsAsResult, Env, Self> as *const VMFunctionBody
}
}
};
}
// Black-magic to count the number of identifiers at compile-time.
macro_rules! count_idents {
( $($idents:ident),* ) => {
{
#[allow(dead_code, non_camel_case_types)]
enum Idents { $( $idents, )* __CountIdentsLast }
const COUNT: usize = Idents::__CountIdentsLast as usize;
COUNT
}
};
}
// Here we go! Let's generate all the C struct, `WasmTypeList`
// implementations and `HostFunction` implementations.
impl_host_function!([C] S0,);
impl_host_function!([transparent] S1, A1);
impl_host_function!([C] S2, A1, A2);
impl_host_function!([C] S3, A1, A2, A3);
impl_host_function!([C] S4, A1, A2, A3, A4);
impl_host_function!([C] S5, A1, A2, A3, A4, A5);
impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);
// Implement `WasmTypeList` on `Infallible`, which means that
// `Infallible` can be used as a returned type of a host function
// to express that it doesn't return.
impl WasmTypeList for Infallible {
type CStruct = Self;
type Array = [i128; 0];
fn from_array(_: Self::Array) -> Self {
unreachable!()
}
fn into_array(self) -> Self::Array {
[]
}
fn empty_array() -> Self::Array {
unreachable!()
}
fn from_c_struct(_: Self::CStruct) -> Self {
unreachable!()
}
fn into_c_struct(self) -> Self::CStruct {
unreachable!()
}
fn wasm_types() -> &'static [Type] {
&[]
}
}
#[cfg(test)]
mod test_wasm_type_list {
use super::*;
use wasm_common::Type;
#[test]
fn test_from_array() {
assert_eq!(<()>::from_array([]), ());
assert_eq!(<i32>::from_array([1]), (1i32));
assert_eq!(<(i32, i64)>::from_array([1, 2]), (1i32, 2i64));
assert_eq!(
<(i32, i64, f32, f64)>::from_array([
1,
2,
(3.1f32).to_bits().into(),
(4.2f64).to_bits().into()
]),
(1, 2, 3.1f32, 4.2f64)
);
}
#[test]
fn test_into_array() {
assert_eq!(().into_array(), []);
assert_eq!((1).into_array(), [1]);
assert_eq!((1i32, 2i64).into_array(), [1, 2]);
assert_eq!(
(1i32, 2i32, 3.1f32, 4.2f64).into_array(),
[1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]
);
}
#[test]
fn test_empty_array() {
assert_eq!(<()>::empty_array().len(), 0);
assert_eq!(<i32>::empty_array().len(), 1);
assert_eq!(<(i32, i64)>::empty_array().len(), 2);
}
#[test]
fn test_from_c_struct() {
assert_eq!(<()>::from_c_struct(S0()), ());
assert_eq!(<i32>::from_c_struct(S1(1)), (1i32));
assert_eq!(<(i32, i64)>::from_c_struct(S2(1, 2)), (1i32, 2i64));
assert_eq!(
<(i32, i64, f32, f64)>::from_c_struct(S4(1, 2, 3.1, 4.2)),
(1i32, 2i64, 3.1f32, 4.2f64)
);
}
#[test]
fn test_wasm_types_for_uni_values() {
assert_eq!(<i32>::wasm_types(), [Type::I32]);
assert_eq!(<i64>::wasm_types(), [Type::I64]);
assert_eq!(<f32>::wasm_types(), [Type::F32]);
assert_eq!(<f64>::wasm_types(), [Type::F64]);
}
#[test]
fn test_wasm_types_for_multi_values() {
assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]);
assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]);
assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]);
assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]);
assert_eq!(
<(i32, i64, f32, f64)>::wasm_types(),
[Type::I32, Type::I64, Type::F32, Type::F64]
);
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod test_function {
use super::*;
use wasm_common::Type;
fn func() {}
fn func__i32() -> i32 {
0
}
fn func_i32(_a: i32) {}
fn func_i32__i32(a: i32) -> i32 {
a * 2
}
fn func_i32_i32__i32(a: i32, b: i32) -> i32 {
a + b
}
fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) {
(a, b)
}
fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) {
(b, a)
}
#[test]
fn test_function_types() {
assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![]));
assert_eq!(
Function::new(func__i32).ty(),
FunctionType::new(vec![], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32).ty(),
FunctionType::new(vec![Type::I32], vec![])
);
assert_eq!(
Function::new(func_i32__i32).ty(),
FunctionType::new(vec![Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
);
assert_eq!(
Function::new(func_i32_i32__i32_i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
);
assert_eq!(
Function::new(func_f32_i32__i32_f32).ty(),
FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32])
);
}
#[test]
fn test_function_pointer() {
let f = Function::new(func_i32__i32);
let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) };
assert_eq!(function(0, 3), 6);
}
}
}

View File

@@ -19,7 +19,7 @@ pub struct Memory {
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Result<Memory, MemoryError> {
let tunables = store.engine().tunables();
let tunables = store.tunables();
let memory_plan = tunables.memory_plan(ty);
let memory = tunables.create_memory(memory_plan)?;

View File

@@ -3,7 +3,7 @@ mod global;
mod memory;
mod table;
pub use self::function::Function;
pub use self::function::{Function, HostFunction, WasmExternType, WasmTypeList};
pub use self::global::Global;
pub use self::memory::Memory;
pub use self::table::Table;

View File

@@ -33,7 +33,7 @@ impl Table {
/// All the elements in the table will be set to the `init` value.
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
let item = init.into_checked_anyfunc(store)?;
let tunables = store.engine().tunables();
let tunables = store.tunables();
let table_plan = tunables.table_plan(ty);
let table = tunables
.create_table(table_plan)

View File

@@ -18,7 +18,9 @@ mod types;
mod utils;
pub use crate::exports::{ExportError, Exportable, Exports};
pub use crate::externals::{Extern, Function, Global, Memory, Table};
pub use crate::externals::{
Extern, Function, Global, HostFunction, Memory, Table, WasmExternType, WasmTypeList,
};
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::instance::Instance;
pub use crate::memory_view::{Atomically, MemoryView};
@@ -34,11 +36,10 @@ pub use crate::types::{
};
pub use crate::types::{Val as Value, ValType as Type};
pub use crate::utils::is_wasm;
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
pub use wasm_common::{
Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WasmExternType, WasmTypeList,
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES,
WASM_PAGE_SIZE,
};
#[cfg(feature = "compiler")]
pub use wasmer_compiler::CompilerConfig;
@@ -56,37 +57,38 @@ pub use wat::parse_bytes as wat2wasm;
// The compilers are mutually exclusive
#[cfg(any(
all(feature = "llvm", any(feature = "cranelift", feature = "singlepass")),
all(feature = "cranelift", feature = "singlepass")
all(
feature = "default-llvm",
any(feature = "default-cranelift", feature = "default-singlepass")
),
all(feature = "default-cranelift", feature = "default-singlepass")
))]
compile_error!(
r#"The `singlepass`, `cranelift` and `llvm` features are mutually exclusive.
If you wish to use more than one compiler, you can simply import it from it's own crate. Eg.:
r#"The `default-singlepass`, `default-cranelift` and `default-llvm` features are mutually exclusive.
If you wish to use more than one compiler, you can simply create the own store. Eg.:
```
use wasmer::{Store, Engine};
use wasmer_compiler_singlepass::SinglepassConfig;
use wasmer::{Store, JIT, Singlepass};
// TODO: update this, this is now out of date:
let engine = Engine::new(SinglepassConfig::default());
let store = Store::new_config(&engine);
let engine = JIT::new(&Singlepass::default()).engine();
let store = Store::new(&engine);
```"#
);
#[cfg(feature = "singlepass")]
pub use wasmer_compiler_singlepass::SinglepassConfig;
pub use wasmer_compiler_singlepass::Singlepass;
#[cfg(feature = "cranelift")]
pub use wasmer_compiler_cranelift::CraneliftConfig;
pub use wasmer_compiler_cranelift::Cranelift;
#[cfg(feature = "llvm")]
pub use wasmer_compiler_llvm::LLVMConfig;
pub use wasmer_compiler_llvm::LLVM;
#[cfg(feature = "jit")]
pub use wasmer_engine_jit::JITEngine;
pub use wasmer_engine_jit::{JITArtifact, JITEngine, JIT};
#[cfg(feature = "native")]
pub use wasmer_engine_native::NativeEngine;
pub use wasmer_engine_native::{Native, NativeArtifact, NativeEngine};
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -107,7 +107,6 @@ impl Memory for LinearMemory {
/// of wasm pages.
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError> {
// Optimization of memory.grow 0 calls.
let delta: Pages = delta.into();
let mut mmap = self.mmap.borrow_mut();
if delta.0 == 0 {
return Ok(mmap.size);

View File

@@ -156,7 +156,7 @@ impl Module {
}
fn compile(store: &Store, binary: &[u8]) -> Result<Self, CompileError> {
let artifact = store.engine().compile(binary)?;
let artifact = store.engine().compile(binary, store.tunables())?;
Ok(Self::from_artifact(store, artifact))
}
@@ -261,11 +261,9 @@ impl Module {
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
unsafe {
let instance_handle = self.artifact.instantiate(
self.store.engine().tunables(),
resolver,
Box::new(()),
)?;
let instance_handle =
self.artifact
.instantiate(self.store.tunables(), resolver, Box::new(()))?;
// After the instance handle is created, we need to initialize
// the data, call the start function and so. However, if any

View File

@@ -13,9 +13,9 @@ use crate::externals::function::{
FunctionDefinition, HostFunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithEnv,
VMDynamicFunctionWithoutEnv, WasmFunctionDefinition,
};
use crate::{Function, FunctionType, RuntimeError, Store};
use crate::{Function, FunctionType, RuntimeError, Store, WasmExternType, WasmTypeList};
use std::panic::{catch_unwind, AssertUnwindSafe};
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
use wasm_common::NativeWasmType;
use wasmer_runtime::{
ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
};

View File

@@ -6,10 +6,9 @@
//! Therefore, you should use this abstraction whenever possible to avoid memory
//! related bugs when implementing an ABI.
use crate::externals::Memory;
use wasm_common::{ValueType, WasmExternType};
use crate::{externals::Memory, WasmExternType};
use std::{cell::Cell, fmt, marker::PhantomData, mem};
use wasm_common::ValueType;
/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
/// to get access to methods

View File

@@ -1,7 +1,7 @@
#[cfg(all(feature = "compiler", feature = "engine"))]
use crate::tunables::Tunables;
#[cfg(all(feature = "compiler", feature = "engine"))]
use wasmer_compiler::CompilerConfig;
use wasmer_engine::Tunables as BaseTunables;
use std::sync::Arc;
use wasmer_engine::Engine;
@@ -9,11 +9,35 @@ use wasmer_engine::Engine;
#[derive(Clone)]
pub struct Store {
engine: Arc<dyn Engine + Send + Sync>,
tunables: Arc<dyn BaseTunables + Send + Sync>,
}
impl Store {
pub fn new(engine: Arc<dyn Engine + Send + Sync>) -> Store {
Store { engine }
pub fn new<E>(engine: &E) -> Store
where
E: Engine + ?Sized,
{
Store {
engine: engine.cloned(),
tunables: Arc::new(Tunables::for_target(engine.target())),
}
}
pub fn new_with_tunables<E>(
engine: &E,
tunables: impl BaseTunables + Send + Sync + 'static,
) -> Store
where
E: Engine + ?Sized,
{
Store {
engine: engine.cloned(),
tunables: Arc::new(tunables),
}
}
pub fn tunables(&self) -> &dyn BaseTunables {
self.tunables.as_ref()
}
pub fn engine(&self) -> &Arc<dyn Engine + Send + Sync> {
@@ -32,40 +56,49 @@ impl PartialEq for Store {
}
// We only implement default if we have assigned a default compiler and engine
#[cfg(all(feature = "compiler", feature = "engine"))]
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
impl Default for Store {
fn default() -> Store {
// We store them on a function that returns to make
// sure this function doesn't emit a compile error even if
// more than one compiler is enabled.
#[allow(unreachable_code)]
fn get_config() -> Box<dyn CompilerConfig + Send + Sync> {
#[cfg(feature = "cranelift")]
return Box::new(wasmer_compiler_cranelift::CraneliftConfig::default());
#[cfg(feature = "llvm")]
return Box::new(wasmer_compiler_llvm::LLVMConfig::default());
#[cfg(feature = "singlepass")]
return Box::new(wasmer_compiler_singlepass::SinglepassConfig::default());
fn get_config() -> impl CompilerConfig + Send + Sync {
cfg_if::cfg_if! {
if #[cfg(feature = "default-cranelift")] {
wasmer_compiler_cranelift::Cranelift::default()
} else if #[cfg(feature = "default-llvm")] {
wasmer_compiler_llvm::LLVM::default()
} else if #[cfg(feature = "default-singlepass")] {
wasmer_compiler_singlepass::Singlepass::default()
} else {
compile_error!("No default compiler chosen")
}
}
}
#[allow(unreachable_code)]
fn get_engine(
config: Box<dyn CompilerConfig + Send + Sync>,
) -> Arc<dyn Engine + Send + Sync> {
let tunables = Tunables::for_target(config.target().triple());
#[cfg(feature = "jit")]
return Arc::new(wasmer_engine_jit::JITEngine::new(config, tunables));
#[cfg(feature = "native")]
return Arc::new(wasmer_engine_native::NativeEngine::new(config, tunables));
fn get_engine(config: impl CompilerConfig + Send + Sync) -> impl Engine + Send + Sync {
cfg_if::cfg_if! {
if #[cfg(feature = "default-jit")] {
wasmer_engine_jit::JIT::new(&config)
.engine()
} else if #[cfg(feature = "default-native")] {
wasmer_engine_native::Native::new(&config)
.engine()
} else {
compile_error!("No default engine chosen")
}
}
}
let config = get_config();
let engine = get_engine(config);
Store::new(engine)
let tunables = Tunables::for_target(engine.target());
Store {
engine: Arc::new(engine),
tunables: Arc::new(tunables),
}
}
}

View File

@@ -4,7 +4,8 @@ use crate::{MemoryType, Pages, TableType};
use more_asserts::assert_ge;
use std::cmp::min;
use std::sync::Arc;
use target_lexicon::{OperatingSystem, PointerWidth, Triple, HOST};
use target_lexicon::{OperatingSystem, PointerWidth};
use wasmer_compiler::Target;
use wasmer_engine::Tunables as BaseTunables;
use wasmer_runtime::MemoryError;
use wasmer_runtime::{Memory, MemoryPlan, MemoryStyle, Table, TablePlan, TableStyle};
@@ -24,7 +25,8 @@ pub struct Tunables {
impl Tunables {
/// Get the `Tunables` for a specific Target
pub fn for_target(triple: &Triple) -> Self {
pub fn for_target(target: &Target) -> Self {
let triple = target.triple();
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) =
match pointer_width {
@@ -102,9 +104,3 @@ impl BaseTunables for Tunables {
Ok(Arc::new(LinearTable::new(&plan)?))
}
}
impl Default for Tunables {
fn default() -> Self {
Self::for_target(&HOST)
}
}

View File

@@ -4,7 +4,6 @@ use libc::c_uchar;
use std::path::PathBuf;
use std::ptr;
use std::str;
use std::sync::Arc;
use wasmer::{Memory, MemoryType, NamedResolver};
use wasmer_wasi as wasi;
@@ -217,7 +216,7 @@ pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wa
// this API will now leak a `Memory`
let memory_type = MemoryType::new(0, None, false);
let memory = Memory::new(store, memory_type).expect("create memory");
wasi_env.set_memory(Arc::new(memory));
wasi_env.set_memory(memory);
// TODO(mark): review lifetime of `Memory` here
let import_object_inner: Box<dyn NamedResolver> = Box::new(
wasi::generate_import_object_from_env(store, wasi_env, wasi::WasiVersion::Latest),

View File

@@ -195,7 +195,7 @@ pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a
lazy_static! {
pub(crate) static ref GLOBAL_STORE: wasmer::Store =
wasmer::Store::new(crate::wasm_c_api::wasm_engine_new().inner);
wasmer::Store::new(&*crate::wasm_c_api::wasm_engine_new().inner);
}
pub(crate) fn get_global_store() -> &'static wasmer::Store {

View File

@@ -7,15 +7,13 @@ use std::ptr::{self, NonNull};
use std::slice;
use std::sync::Arc;
#[cfg(feature = "engine")]
use wasmer::Tunables;
use wasmer::{
Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, Instance,
Memory, MemoryType, Module, Mutability, OrderedResolver, Pages, RuntimeError, Store, Table,
TableType, Val, ValType,
};
#[cfg(feature = "jit")]
use wasmer_engine_jit::JITEngine;
use wasmer_engine_jit::JIT;
use crate::error::update_last_error;
@@ -58,11 +56,11 @@ cfg_if! {
fn get_default_compiler_config() -> Box<dyn CompilerConfig> {
cfg_if! {
if #[cfg(feature = "cranelift")] {
Box::new(wasmer_compiler_cranelift::CraneliftConfig::default())
Box::new(wasmer_compiler_cranelift::Cranelift::default())
} else if #[cfg(feature = "llvm")] {
Box::new(wasmer_compiler_llvm::LLVMConfig::default())
Box::new(wasmer_compiler_llvm::LLVM::default())
} else if #[cfg(feature = "singlepass")] {
Box::new(wasmer_compiler_singlepass::SinglepassConfig::default())
Box::new(wasmer_compiler_singlepass::Singlepass::default())
} else {
compile_error!("Please enable one of the compiler backends")
}
@@ -72,8 +70,7 @@ cfg_if! {
#[no_mangle]
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
let compiler_config: Box<dyn CompilerConfig> = get_default_compiler_config();
let tunables = Tunables::default();
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::new(compiler_config, tunables));
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::new(&*compiler_config).engine());
Box::new(wasm_engine_t { inner: engine })
}
}
@@ -81,8 +78,7 @@ cfg_if! {
// Headless JIT
#[no_mangle]
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
let tunables = Tunables::default();
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::headless(tunables));
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::headless().engine());
Box::new(wasm_engine_t { inner: engine })
}
}
@@ -299,7 +295,7 @@ pub unsafe extern "C" fn wasm_store_new(
) -> Option<NonNull<wasm_store_t>> {
let wasm_engine_ptr = wasm_engine_ptr?;
let wasm_engine = wasm_engine_ptr.as_ref();
let store = Store::new(wasm_engine.inner.clone());
let store = Store::new(&*wasm_engine.inner);
Some(NonNull::new_unchecked(
Box::into_raw(Box::new(store)) as *mut wasm_store_t
))

View File

@@ -3,7 +3,28 @@
This is the `wasmer-compiler-cranelift` crate, which contains a
compiler implementation based on Cranelift.
We recommend using this compiler only for development proposes.
## Usage
First, add this crate into your `Cargo.toml` dependencies:
```toml
wasmer-compiler-cranelift = "1.0.0-alpha.1"
```
And then:
```rust
use wasmer::{Store, JIT};
use wasmer_compiler_cranelift::Cranelift;
let compiler = Cranelift::new();
// Put it into an engine and add it to the store
let store = Store::new(&JIT::new(&compiler).engine());
```
## When to use Cranelift
We recommend using this compiler crate **only for development proposes**.
For production we recommend using `wasmer-compiler-llvm` as it offers
a much better runtime speed (50% faster on average).

View File

@@ -1,7 +1,7 @@
//! Support for compiling with Cranelift.
use crate::address_map::get_function_address_map;
use crate::config::CraneliftConfig;
use crate::config::Cranelift;
#[cfg(feature = "unwind")]
use crate::dwarf::WriterRelocate;
use crate::func_environ::{get_func_name, FuncEnvironment};
@@ -15,73 +15,54 @@ use crate::translator::{
};
use cranelift_codegen::ir;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, isa, Context};
use cranelift_codegen::{binemit, Context};
#[cfg(feature = "unwind")]
use gimli::write::{Address, EhFrame, FrameTable};
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex, TableIndex,
};
use wasm_common::{FunctionIndex, LocalFunctionIndex, SignatureIndex};
use wasmer_compiler::CompileError;
use wasmer_compiler::{CallingConvention, CompilerConfig, ModuleTranslationState, Target};
use wasmer_compiler::{CallingConvention, ModuleTranslationState, Target};
use wasmer_compiler::{
Compilation, CompiledFunction, CompiledFunctionFrameInfo, CompiledFunctionUnwindInfo, Compiler,
Dwarf, FunctionBody, FunctionBodyData, SectionIndex,
Compilation, CompileModuleInfo, CompiledFunction, CompiledFunctionFrameInfo,
CompiledFunctionUnwindInfo, Compiler, Dwarf, FunctionBody, FunctionBodyData, SectionIndex,
};
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
/// optimizing it and then translating to assembly.
pub struct CraneliftCompiler {
isa: Box<dyn isa::TargetIsa>,
config: CraneliftConfig,
config: Cranelift,
}
impl CraneliftCompiler {
/// Creates a new Cranelift compiler
pub fn new(config: &CraneliftConfig) -> Self {
let isa = config.isa();
pub fn new(config: &Cranelift) -> Self {
Self {
isa,
config: config.clone(),
}
}
/// Retrieves the starget ISA
fn isa(&self) -> &dyn isa::TargetIsa {
&*self.isa
}
/// Gets the WebAssembly features for this Compiler
pub fn config(&self) -> &CraneliftConfig {
pub fn config(&self) -> &Cranelift {
&self.config
}
}
impl Compiler for CraneliftCompiler {
/// Gets the WebAssembly features for this Compiler
fn features(&self) -> &Features {
self.config.features()
}
/// Gets the target associated to the Cranelift ISA.
fn target(&self) -> &Target {
self.config.target()
}
/// Compile the module using Cranelift, producing a compilation result with
/// associated relocations.
fn compile_module(
&self,
module: &ModuleInfo,
target: &Target,
compile_info: &CompileModuleInfo,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
let isa = self.isa();
let isa = self.config().isa(target);
let frontend_config = isa.frontend_config();
let memory_plans = &compile_info.memory_plans;
let table_plans = &compile_info.table_plans;
let module = &compile_info.module;
let signatures = module
.signatures
.iter()
@@ -92,7 +73,7 @@ impl Compiler for CraneliftCompiler {
#[cfg(feature = "unwind")]
let dwarf_frametable = {
use std::sync::{Arc, Mutex};
match self.target().triple().default_calling_convention() {
match target.triple().default_calling_convention() {
Ok(CallingConvention::SystemV) => {
match isa.create_systemv_cie() {
Some(cie) => {
@@ -144,17 +125,17 @@ impl Compiler for CraneliftCompiler {
let mut stackmap_sink = binemit::NullStackmapSink {};
context
.compile_and_emit(
isa,
&*isa,
&mut code_buf,
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
.map_err(|error| {
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
CompileError::Codegen(pretty_error(&context.func, Some(&*isa), error))
})?;
let unwind_info = match compiled_function_unwind_info(isa, &context)? {
let unwind_info = match compiled_function_unwind_info(&*isa, &context)? {
#[cfg(feature = "unwind")]
CraneliftUnwindInfo::FDE(fde) => {
if let Some((dwarf_frametable, cie_id)) = &dwarf_frametable {
@@ -181,7 +162,7 @@ impl Compiler for CraneliftCompiler {
other => other.maybe_into_to_windows_unwind(),
};
let address_map = get_function_address_map(&context, input, code_buf.len(), isa);
let address_map = get_function_address_map(&context, input, code_buf.len(), &*isa);
// We transform the Cranelift JumpTable's into compiler JumpTables
let func_jt_offsets = transform_jump_table(context.func.jt_offsets);
@@ -207,9 +188,7 @@ impl Compiler for CraneliftCompiler {
let (custom_sections, dwarf) = {
let mut custom_sections = PrimaryMap::new();
let dwarf = if let Some((dwarf_frametable, _cie_id)) = dwarf_frametable {
let mut eh_frame = EhFrame(WriterRelocate::new(
self.target().triple().endianness().ok(),
));
let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
dwarf_frametable
.lock()
.unwrap()
@@ -234,7 +213,7 @@ impl Compiler for CraneliftCompiler {
.collect::<Vec<_>>()
.par_iter()
.map_init(FunctionBuilderContext::new, |mut cx, sig| {
make_trampoline_function_call(&*self.isa, &mut cx, sig)
make_trampoline_function_call(&*isa, &mut cx, sig)
})
.collect::<Result<Vec<FunctionBody>, CompileError>>()?
.into_iter()
@@ -248,7 +227,7 @@ impl Compiler for CraneliftCompiler {
.collect::<Vec<_>>()
.par_iter()
.map_init(FunctionBuilderContext::new, |mut cx, func_type| {
make_trampoline_dynamic_function(&*self.isa, &offsets, &mut cx, &func_type)
make_trampoline_dynamic_function(&*isa, &offsets, &mut cx, &func_type)
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()

View File

@@ -3,8 +3,7 @@ use cranelift_codegen::isa::{lookup, TargetIsa};
use cranelift_codegen::settings::{self, Configurable};
use std::sync::Arc;
use wasmer_compiler::{
Architecture, Compiler, CompilerConfig, CpuFeature, Features, FunctionMiddlewareGenerator,
Target,
Architecture, Compiler, CompilerConfig, CpuFeature, FunctionMiddlewareGenerator, Target,
};
// Runtime Environment
@@ -29,48 +28,61 @@ pub enum OptLevel {
/// This structure exposed a builder-like interface and is primarily consumed by
/// [`Engine::new()`]
#[derive(Clone)]
pub struct CraneliftConfig {
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub enable_nan_canonicalization: bool,
/// Should the Cranelift verifier be enabled.
///
/// The verifier assures that the generated Cranelift IR is valid.
pub enable_verifier: bool,
pub struct Cranelift {
enable_nan_canonicalization: bool,
enable_verifier: bool,
enable_simd: bool,
enable_pic: bool,
/// The optimization levels when optimizing the IR.
pub opt_level: OptLevel,
features: Features,
target: Target,
opt_level: OptLevel,
/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
}
impl CraneliftConfig {
impl Cranelift {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new(features: Features, target: Target) -> Self {
pub fn new() -> Self {
Self {
enable_nan_canonicalization: false,
enable_verifier: false,
opt_level: OptLevel::Speed,
enable_pic: false,
features,
target,
enable_simd: false,
middlewares: vec![],
}
}
/// Generates the ISA for the current target
pub fn isa(&self) -> Box<dyn TargetIsa> {
let target = self.target();
/// Should the Cranelift verifier be enabled.
///
/// The verifier assures that the generated Cranelift IR is valid.
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
self.enable_verifier = enable;
self
}
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
self.enable_nan_canonicalization = enable;
self
}
/// Enable SIMD support.
pub fn enable_simd(&mut self, enable: bool) -> &mut Self {
self.enable_simd = enable;
self
}
/// The optimization levels when optimizing the IR.
pub fn opt_level(&mut self, opt_level: OptLevel) -> &mut Self {
self.opt_level = opt_level;
self
}
/// Generates the ISA for the provided target
pub fn isa(&self, target: &Target) -> Box<dyn TargetIsa> {
let mut builder =
lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
// Cpu Features
@@ -124,7 +136,7 @@ impl CraneliftConfig {
builder.finish(self.flags())
}
/// Generates the flags for the current target
/// Generates the flags for the compiler
pub fn flags(&self) -> settings::Flags {
let mut flags = settings::builder();
@@ -148,7 +160,7 @@ impl CraneliftConfig {
.set("enable_verifier", enable_verifier)
.expect("should be valid flag");
let opt_level = if self.features.simd {
let opt_level = if self.enable_simd {
"none"
} else {
match self.opt_level {
@@ -162,7 +174,7 @@ impl CraneliftConfig {
.set("opt_level", opt_level)
.expect("should be valid flag");
let enable_simd = if self.features.simd { "true" } else { "false" };
let enable_simd = if self.enable_simd { "true" } else { "false" };
flags
.set("enable_simd", enable_simd)
.expect("should be valid flag");
@@ -180,22 +192,11 @@ impl CraneliftConfig {
}
}
impl CompilerConfig for CraneliftConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features {
&self.features
}
impl CompilerConfig for Cranelift {
fn enable_pic(&mut self) {
self.enable_pic = true;
}
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Transform it into the compiler
fn compiler(&self) -> Box<dyn Compiler + Send> {
Box::new(CraneliftCompiler::new(&self))
@@ -207,8 +208,8 @@ impl CompilerConfig for CraneliftConfig {
}
}
impl Default for CraneliftConfig {
impl Default for Cranelift {
fn default() -> Self {
Self::new(Default::default(), Default::default())
Self::new()
}
}

View File

@@ -57,7 +57,7 @@ mod trampoline;
mod translator;
pub use crate::compiler::CraneliftCompiler;
pub use crate::config::CraneliftConfig;
pub use crate::config::Cranelift;
pub use crate::debug::{ModuleInfoMemoryOffset, ModuleInfoVmctxInfo, ValueLabelsRanges};
pub use crate::trampoline::make_trampoline_function_call;

View File

@@ -1290,12 +1290,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
}
Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder);
let extracted = builder.ins().extractlane(vector, lane.clone());
let extracted = builder.ins().extractlane(vector, *lane);
state.push1(builder.ins().sextend(I32, extracted))
}
Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder);
let extracted = builder.ins().extractlane(vector, lane.clone());
let extracted = builder.ins().extractlane(vector, *lane);
state.push1(builder.ins().uextend(I32, extracted));
// On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so
// uextend could be elided; for now, uextend is needed for Cranelift's type checks to
@@ -1306,7 +1306,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::F32x4ExtractLane { lane }
| Operator::F64x2ExtractLane { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder);
state.push1(builder.ins().extractlane(vector, lane.clone()))
state.push1(builder.ins().extractlane(vector, *lane))
}
Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
let (vector, replacement) = state.pop2();

View File

@@ -11,7 +11,7 @@ use super::code_translator::{bitcast_arguments, translate_operator, wasm_param_t
use super::func_environ::{FuncEnvironment, ReturnMode};
use super::func_state::FuncTranslationState;
use super::translation_utils::get_vmctx_value_label;
use crate::config::CraneliftConfig;
use crate::config::Cranelift;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing;
@@ -69,7 +69,7 @@ impl FuncTranslator {
func: &mut ir::Function,
environ: &mut FE,
local_function_index: LocalFunctionIndex,
config: &CraneliftConfig,
config: &Cranelift,
) -> WasmResult<()> {
let mut reader = MiddlewareBinaryReader::new_with_offset(code, code_offset);
reader.set_middleware_chain(

View File

@@ -24,7 +24,7 @@ impl CraneliftUnwindInfo {
/// main users of this function)
pub fn maybe_into_to_windows_unwind(self) -> Option<CompiledFunctionUnwindInfo> {
match self {
CraneliftUnwindInfo::WindowsX64(unwind_info) => {
Self::WindowsX64(unwind_info) => {
Some(CompiledFunctionUnwindInfo::WindowsX64(unwind_info))
}
_ => None,

View File

@@ -3,6 +3,27 @@
This is the `wasmer-compiler-llvm` crate, which contains a
compiler implementation based on LLVM.
## Usage
First, add this crate into your `Cargo.toml` dependencies:
```toml
wasmer-compiler-llvm = "1.0.0-alpha.1"
```
And then:
```rust
use wasmer::{Store, JIT};
use wasmer_compiler_llvm::LLVM;
let compiler = LLVM::new();
// Put it into an engine and add it to the store
let store = Store::new(&JIT::new(&compiler).engine());
```
## When to use LLVM
We recommend using LLVM as the default compiler when running WebAssembly
files on any **production** system, as it offers maximum peformance near
to native speeds.

View File

@@ -1,61 +1,52 @@
use crate::config::LLVMConfig;
use crate::config::LLVM;
use crate::trampoline::FuncTrampoline;
use crate::translator::FuncTranslator;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
use wasm_common::Features;
use wasm_common::{LocalFunctionIndex, MemoryIndex, TableIndex};
use wasm_common::LocalFunctionIndex;
use wasmer_compiler::{
Compilation, CompileError, Compiler, CompilerConfig, FunctionBodyData, ModuleTranslationState,
RelocationTarget, SectionIndex, Target,
Compilation, CompileError, CompileModuleInfo, Compiler, CustomSection, CustomSectionProtection,
Dwarf, FunctionBodyData, ModuleTranslationState, RelocationTarget, SectionBody, SectionIndex,
Target,
};
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
//use std::sync::{Arc, Mutex};
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
/// optimizing it and then translating to assembly.
pub struct LLVMCompiler {
config: LLVMConfig,
config: LLVM,
}
impl LLVMCompiler {
/// Creates a new LLVM compiler
pub fn new(config: &LLVMConfig) -> LLVMCompiler {
pub fn new(config: &LLVM) -> LLVMCompiler {
LLVMCompiler {
config: config.clone(),
}
}
/// Gets the WebAssembly features for this Compiler
fn config(&self) -> &LLVMConfig {
/// Gets the config for this Compiler
fn config(&self) -> &LLVM {
&self.config
}
}
impl Compiler for LLVMCompiler {
/// Gets the WebAssembly features for this Compiler
fn features(&self) -> &Features {
self.config.features()
}
/// Gets the target associated to this Compiler.
fn target(&self) -> &Target {
self.config.target()
}
/// Compile the module using LLVM, producing a compilation result with
/// associated relocations.
fn compile_module<'data, 'module>(
&self,
module: &'module ModuleInfo,
target: &Target,
compile_info: &'module CompileModuleInfo,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
//let data = Arc::new(Mutex::new(0));
let mut func_names = SecondaryMap::new();
let memory_plans = &compile_info.memory_plans;
let table_plans = &compile_info.table_plans;
let module = &compile_info.module;
// TODO: merge constants in sections.
@@ -67,15 +58,22 @@ impl Compiler for LLVMCompiler {
.unwrap_or_else(|| format!("fn{}", func_index.index()));
}
let mut module_custom_sections = PrimaryMap::new();
let mut frame_section_bytes = vec![];
let mut frame_section_relocations = vec![];
let functions = function_body_inputs
.into_iter()
.collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
.par_iter()
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
.map_init(
|| {
let target_machine = self.config().target_machine(target);
FuncTranslator::new(target_machine)
},
|func_translator, (i, input)| {
// TODO: remove (to serialize)
//let mut data = data.lock().unwrap();
func_translator.translate(
module,
&module,
module_translation,
i,
input,
@@ -84,12 +82,13 @@ impl Compiler for LLVMCompiler {
&table_plans,
&func_names,
)
})
},
)
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.map(|(mut compiled_function, function_custom_sections)| {
.map(|mut compiled_function| {
let first_section = module_custom_sections.len() as u32;
for (_, custom_section) in function_custom_sections.iter() {
for (section_index, custom_section) in compiled_function.custom_sections.iter() {
// TODO: remove this call to clone()
let mut custom_section = custom_section.clone();
for mut reloc in &mut custom_section.relocations {
@@ -99,27 +98,74 @@ impl Compiler for LLVMCompiler {
)
}
}
if compiled_function
.eh_frame_section_indices
.contains(&section_index)
{
let offset = frame_section_bytes.len() as u32;
for mut reloc in &mut custom_section.relocations {
reloc.offset += offset;
}
frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
frame_section_relocations.extend(custom_section.relocations);
// TODO: we do this to keep the count right, remove it.
module_custom_sections.push(CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::new_with_vec(vec![]),
relocations: vec![],
});
} else {
module_custom_sections.push(custom_section);
}
for mut reloc in &mut compiled_function.relocations {
}
for mut reloc in &mut compiled_function.compiled_function.relocations {
if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
reloc.reloc_target = RelocationTarget::CustomSection(
SectionIndex::from_u32(first_section + index.as_u32()),
)
}
}
compiled_function
compiled_function.compiled_function
})
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
let dwarf = if !frame_section_bytes.is_empty() {
let dwarf = Some(Dwarf::new(SectionIndex::from_u32(
module_custom_sections.len() as u32,
)));
// Terminating zero-length CIE.
frame_section_bytes.extend(vec![
0x00, 0x00, 0x00, 0x00, // Length
0x00, 0x00, 0x00, 0x00, // CIE ID
0x10, // Version (must be 1)
0x00, // Augmentation data
0x00, // Code alignment factor
0x00, // Data alignment factor
0x00, // Return address register
0x00, 0x00, 0x00, // Padding to a multiple of 4 bytes
]);
module_custom_sections.push(CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::new_with_vec(frame_section_bytes),
relocations: frame_section_relocations,
});
dwarf
} else {
None
};
let function_call_trampolines = module
.signatures
.values()
.collect::<Vec<_>>()
.par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, sig| {
func_trampoline.trampoline(sig, self.config())
})
.map_init(
|| {
let target_machine = self.config().target_machine(target);
FuncTrampoline::new(target_machine)
},
|func_trampoline, sig| func_trampoline.trampoline(sig, self.config()),
)
.collect::<Vec<_>>()
.into_iter()
.collect::<Result<PrimaryMap<_, _>, CompileError>>()?;
@@ -128,9 +174,15 @@ impl Compiler for LLVMCompiler {
.imported_function_types()
.collect::<Vec<_>>()
.par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, func_type| {
.map_init(
|| {
let target_machine = self.config().target_machine(target);
FuncTrampoline::new(target_machine)
},
|func_trampoline, func_type| {
func_trampoline.dynamic_trampoline(&func_type, self.config())
})
},
)
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.collect::<PrimaryMap<_, _>>();
@@ -140,7 +192,7 @@ impl Compiler for LLVMCompiler {
module_custom_sections,
function_call_trampolines,
dynamic_function_trampolines,
None,
dwarf,
))
}
}

View File

@@ -8,9 +8,7 @@ use itertools::Itertools;
use std::sync::Arc;
use target_lexicon::Architecture;
use wasm_common::{FunctionType, LocalFunctionIndex};
use wasmer_compiler::{
Compiler, CompilerConfig, Features, FunctionMiddlewareGenerator, Target, Triple,
};
use wasmer_compiler::{Compiler, CompilerConfig, FunctionMiddlewareGenerator, Target, Triple};
/// The InkWell ModuleInfo type
pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
@@ -41,50 +39,60 @@ pub trait LLVMCallbacks: Send + Sync {
}
#[derive(Clone)]
pub struct LLVMConfig {
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub enable_nan_canonicalization: bool,
/// Should the LLVM IR verifier be enabled.
///
/// The verifier assures that the generated LLVM IR is valid.
pub enable_verifier: bool,
/// The optimization levels when optimizing the IR.
pub opt_level: OptimizationLevel,
/// Whether to emit PIC.
pub is_pic: bool,
/// Callbacks that will triggered in the different compilation
/// phases in LLVM.
pub callbacks: Option<Arc<dyn LLVMCallbacks>>,
pub struct LLVM {
pub(crate) enable_nan_canonicalization: bool,
pub(crate) enable_verifier: bool,
pub(crate) opt_level: OptimizationLevel,
is_pic: bool,
pub(crate) callbacks: Option<Arc<dyn LLVMCallbacks>>,
/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
features: Features,
target: Target,
}
impl LLVMConfig {
impl LLVM {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new(features: Features, target: Target) -> Self {
pub fn new() -> Self {
Self {
enable_nan_canonicalization: true,
enable_nan_canonicalization: false,
enable_verifier: false,
opt_level: OptimizationLevel::Aggressive,
is_pic: false,
features,
target,
callbacks: None,
middlewares: vec![],
}
}
/// Should the LLVM verifier be enabled.
///
/// The verifier assures that the generated LLVM IR is valid.
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
self.enable_verifier = enable;
self
}
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
self.enable_nan_canonicalization = enable;
self
}
/// The optimization levels when optimizing the IR.
pub fn opt_level(&mut self, opt_level: OptimizationLevel) -> &mut Self {
self.opt_level = opt_level;
self
}
/// Callbacks that will triggered in the different compilation
/// phases in LLVM.
pub fn callbacks(&mut self, callbacks: Option<Arc<dyn LLVMCallbacks>>) -> &mut Self {
self.callbacks = callbacks;
self
}
fn reloc_mode(&self) -> RelocMode {
if self.is_pic {
RelocMode::PIC
@@ -97,8 +105,7 @@ impl LLVMConfig {
CodeModel::Large
}
pub fn target_triple(&self) -> TargetTriple {
let target = self.target();
fn target_triple(&self, target: &Target) -> TargetTriple {
let operating_system =
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
// LLVM detects static relocation + darwin + 64-bit and
@@ -122,8 +129,7 @@ impl LLVMConfig {
}
/// Generates the target machine for the current target
pub fn target_machine(&self) -> TargetMachine {
let target = self.target();
pub fn target_machine(&self, target: &Target) -> TargetMachine {
let triple = target.triple();
let cpu_features = &target.cpu_features();
@@ -155,7 +161,7 @@ impl LLVMConfig {
.map(|feature| format!("+{}", feature.to_string()))
.join(",");
let target_triple = self.target_triple();
let target_triple = self.target_triple(&target);
let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
llvm_target
.create_target_machine(
@@ -170,12 +176,7 @@ impl LLVMConfig {
}
}
impl CompilerConfig for LLVMConfig {
/// Gets the WebAssembly features.
fn features(&self) -> &Features {
&self.features
}
impl CompilerConfig for LLVM {
/// Emit code suitable for dlopen.
fn enable_pic(&mut self) {
// TODO: although we can emit PIC, the object file parser does not yet
@@ -183,12 +184,6 @@ impl CompilerConfig for LLVMConfig {
self.is_pic = true;
}
/// Gets the target that we will use for compiling.
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Transform it into the compiler.
fn compiler(&self) -> Box<dyn Compiler + Send> {
Box::new(LLVMCompiler::new(&self))
@@ -200,8 +195,8 @@ impl CompilerConfig for LLVMConfig {
}
}
impl Default for LLVMConfig {
fn default() -> LLVMConfig {
Self::new(Default::default(), Default::default())
impl Default for LLVM {
fn default() -> LLVM {
Self::new()
}
}

View File

@@ -22,5 +22,5 @@ mod translator;
pub use crate::compiler::LLVMCompiler;
pub use crate::config::{
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVMConfig,
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
};

View File

@@ -3,10 +3,9 @@ use std::convert::TryFrom;
use wasm_common::entity::{PrimaryMap, SecondaryMap};
use wasmer_compiler::{
CompileError, CompiledFunction, CompiledFunctionFrameInfo, CustomSection,
CustomSectionProtection, CustomSections, FunctionAddressMap, FunctionBody,
InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SectionBody, SectionIndex,
SourceLoc,
CompileError, CompiledFunctionFrameInfo, CustomSection, CustomSectionProtection,
CustomSections, FunctionAddressMap, FunctionBody, InstructionAddressMap, Relocation,
RelocationKind, RelocationTarget, SectionBody, SectionIndex, SourceLoc,
};
use wasmer_runtime::libcalls::LibCall;
@@ -38,12 +37,18 @@ fn map_goblin_err(error: goblin::error::Error) -> CompileError {
CompileError::Codegen(format!("error parsing ELF file: {}", error))
}
pub struct CompiledFunction {
pub compiled_function: wasmer_compiler::CompiledFunction,
pub custom_sections: CustomSections,
pub eh_frame_section_indices: Vec<SectionIndex>,
}
pub fn load_object_file<F>(
contents: &[u8],
root_section: &str,
self_referential_relocation_target: Option<RelocationTarget>,
root_section_reloc_target: RelocationTarget,
mut symbol_name_to_relocation_target: F,
) -> Result<(CompiledFunction, CustomSections), CompileError>
) -> Result<CompiledFunction, CompileError>
where
F: FnMut(&String) -> Result<Option<RelocationTarget>, CompileError>,
{
@@ -102,9 +107,7 @@ where
let mut section_to_custom_section = HashMap::new();
if let Some(reloc_target) = self_referential_relocation_target {
section_targets.insert(root_section_index, reloc_target);
};
section_targets.insert(root_section_index, root_section_reloc_target);
let mut next_custom_section: u32 = 0;
let mut elf_section_to_target = |elf_section_index: ElfSectionIndex| {
@@ -142,6 +145,22 @@ where
// the sections we want to include.
worklist.push(root_section_index);
visited.insert(root_section_index);
// Also add any .eh_frame sections.
let mut eh_frame_section_indices = vec![];
// TODO: this constant has been added to goblin, now waiting for release
const SHT_X86_64_UNWIND: u32 = 0x7000_0001;
for (index, shdr) in elf.section_headers.iter().enumerate() {
if shdr.sh_type == SHT_X86_64_UNWIND {
let index = ElfSectionIndex::from_usize(index)?;
worklist.push(index);
visited.insert(index);
eh_frame_section_indices.push(index);
// This allocates a custom section index for the ELF section.
elf_section_to_target(index);
}
}
while let Some(section_index) = worklist.pop() {
for reloc in reloc_sections
.get(&section_index)
@@ -152,6 +171,7 @@ where
// TODO: these constants are not per-arch, we'll need to
// make the whole match per-arch.
goblin::elf::reloc::R_X86_64_64 => RelocationKind::Abs8,
goblin::elf::reloc::R_X86_64_PC64 => RelocationKind::X86PCRel8,
goblin::elf::reloc::R_X86_64_GOT64 => {
return Err(CompileError::Codegen(
"unimplemented PIC relocation R_X86_64_GOT64".into(),
@@ -180,17 +200,13 @@ where
))
})?;
let elf_target_section = ElfSectionIndex::from_usize(elf_target.st_shndx)?;
let reloc_target = if elf_target.st_type() == goblin::elf::sym::STT_SECTION {
let reloc_target = if elf_target_section == root_section_index {
root_section_reloc_target
} else if elf_target.st_type() == goblin::elf::sym::STT_SECTION {
if visited.insert(elf_target_section) {
worklist.push(elf_target_section);
}
elf_section_to_target(elf_target_section)
} else if elf_target.st_type() == goblin::elf::sym::STT_FUNC
&& elf_target_section == root_section_index
&& self_referential_relocation_target.is_some()
{
// This is a function referencing its own byte stream.
self_referential_relocation_target.unwrap()
} else if elf_target.st_type() == goblin::elf::sym::STT_NOTYPE
&& elf_target_section.is_undef()
{
@@ -220,6 +236,21 @@ where
}
}
let eh_frame_section_indices = eh_frame_section_indices
.iter()
.map(|index| {
section_to_custom_section.get(index).map_or_else(
|| {
Err(CompileError::Codegen(format!(
".eh_frame section with index={:?} was never loaded",
index
)))
},
|idx| Ok(*idx),
)
})
.collect::<Result<Vec<SectionIndex>, _>>()?;
let mut custom_sections = section_to_custom_section
.iter()
.map(|(elf_section_index, custom_section_index)| {
@@ -258,8 +289,8 @@ where
body_len: function_body.body.len(),
};
Ok((
CompiledFunction {
Ok(CompiledFunction {
compiled_function: wasmer_compiler::CompiledFunction {
body: function_body,
jt_offsets: SecondaryMap::new(),
relocations: relocations
@@ -271,5 +302,6 @@ where
},
},
custom_sections,
))
eh_frame_section_indices,
})
}

View File

@@ -1,5 +1,5 @@
use crate::config::{CompiledFunctionKind, LLVMConfig};
use crate::object_file::load_object_file;
use crate::config::{CompiledFunctionKind, LLVM};
use crate::object_file::{load_object_file, CompiledFunction};
use crate::translator::abi::{
func_type_to_llvm, get_vmctx_ptr_param, is_sret, pack_values_for_register_return,
rets_from_call,
@@ -10,39 +10,41 @@ use inkwell::{
context::Context,
module::Linkage,
passes::PassManager,
targets::FileType,
targets::{FileType, TargetMachine},
types::BasicType,
values::{BasicValue, FunctionValue},
AddressSpace,
};
use std::cmp;
use std::convert::TryInto;
use wasm_common::{FunctionType, Type};
use wasmer_compiler::{CompileError, FunctionBody};
use wasm_common::{FunctionType, LocalFunctionIndex, Type};
use wasmer_compiler::{CompileError, FunctionBody, RelocationTarget};
pub struct FuncTrampoline {
ctx: Context,
target_machine: TargetMachine,
}
const FUNCTION_SECTION: &str = ".wasmer_trampoline";
impl FuncTrampoline {
pub fn new() -> Self {
pub fn new(target_machine: TargetMachine) -> Self {
Self {
ctx: Context::create(),
target_machine,
}
}
pub fn trampoline(
&mut self,
ty: &FunctionType,
config: &LLVMConfig,
config: &LLVM,
) -> Result<FunctionBody, CompileError> {
// The function type, used for the callbacks.
let function = CompiledFunctionKind::FunctionCallTrampoline(ty.clone());
let module = self.ctx.create_module("");
let target_triple = config.target_triple();
let target_machine = config.target_machine();
let target_machine = &self.target_machine;
let target_triple = target_machine.get_triple();
module.set_triple(&target_triple);
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
let intrinsics = Intrinsics::declare(&module, &self.ctx);
@@ -92,24 +94,45 @@ impl FuncTrampoline {
}
let mem_buf_slice = memory_buffer.as_slice();
let (function, sections) =
load_object_file(mem_buf_slice, FUNCTION_SECTION, None, |name: &String| {
let CompiledFunction {
compiled_function,
custom_sections,
eh_frame_section_indices,
} = load_object_file(
mem_buf_slice,
FUNCTION_SECTION,
RelocationTarget::LocalFunc(LocalFunctionIndex::from_u32(0)),
|name: &String| {
Err(CompileError::Codegen(format!(
"trampoline generation produced reference to unknown function {}",
name
)))
})?;
if !sections.is_empty() {
},
)?;
let mut all_sections_are_eh_sections = true;
if eh_frame_section_indices.len() != custom_sections.len() {
all_sections_are_eh_sections = false;
} else {
let mut eh_frame_section_indices = eh_frame_section_indices;
eh_frame_section_indices.sort_unstable();
for (idx, section_idx) in eh_frame_section_indices.iter().enumerate() {
if idx as u32 != section_idx.as_u32() {
all_sections_are_eh_sections = false;
break;
}
}
}
if !all_sections_are_eh_sections {
return Err(CompileError::Codegen(
"trampoline generation produced custom sections".into(),
"trampoline generation produced non-eh custom sections".into(),
));
}
if !function.relocations.is_empty() {
if !compiled_function.relocations.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced relocations".into(),
));
}
if !function.jt_offsets.is_empty() {
if !compiled_function.jt_offsets.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced jump tables".into(),
));
@@ -117,21 +140,21 @@ impl FuncTrampoline {
// Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
Ok(FunctionBody {
body: function.body.body,
unwind_info: function.body.unwind_info,
body: compiled_function.body.body,
unwind_info: compiled_function.body.unwind_info,
})
}
pub fn dynamic_trampoline(
&mut self,
ty: &FunctionType,
config: &LLVMConfig,
config: &LLVM,
) -> Result<FunctionBody, CompileError> {
// The function type, used for the callbacks
let function = CompiledFunctionKind::DynamicFunctionTrampoline(ty.clone());
let module = self.ctx.create_module("");
let target_triple = config.target_triple();
let target_machine = config.target_machine();
let target_machine = &self.target_machine;
let target_triple = target_machine.get_triple();
module.set_triple(&target_triple);
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
let intrinsics = Intrinsics::declare(&module, &self.ctx);
@@ -173,24 +196,45 @@ impl FuncTrampoline {
}
let mem_buf_slice = memory_buffer.as_slice();
let (function, sections) =
load_object_file(mem_buf_slice, FUNCTION_SECTION, None, |name: &String| {
let CompiledFunction {
compiled_function,
custom_sections,
eh_frame_section_indices,
} = load_object_file(
mem_buf_slice,
FUNCTION_SECTION,
RelocationTarget::LocalFunc(LocalFunctionIndex::from_u32(0)),
|name: &String| {
Err(CompileError::Codegen(format!(
"trampoline generation produced reference to unknown function {}",
name
)))
})?;
if !sections.is_empty() {
},
)?;
let mut all_sections_are_eh_sections = true;
if eh_frame_section_indices.len() != custom_sections.len() {
all_sections_are_eh_sections = false;
} else {
let mut eh_frame_section_indices = eh_frame_section_indices;
eh_frame_section_indices.sort_unstable();
for (idx, section_idx) in eh_frame_section_indices.iter().enumerate() {
if idx as u32 != section_idx.as_u32() {
all_sections_are_eh_sections = false;
break;
}
}
}
if !all_sections_are_eh_sections {
return Err(CompileError::Codegen(
"trampoline generation produced custom sections".into(),
"trampoline generation produced non-eh custom sections".into(),
));
}
if !function.relocations.is_empty() {
if !compiled_function.relocations.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced relocations".into(),
));
}
if !function.jt_offsets.is_empty() {
if !compiled_function.jt_offsets.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced jump tables".into(),
));
@@ -198,8 +242,8 @@ impl FuncTrampoline {
// Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
Ok(FunctionBody {
body: function.body.body,
unwind_info: function.body.unwind_info,
body: compiled_function.body.body,
unwind_info: compiled_function.body.unwind_info,
})
}
}

View File

@@ -12,7 +12,7 @@ use inkwell::{
context::Context,
module::{Linkage, Module},
passes::PassManager,
targets::FileType,
targets::{FileType, TargetMachine},
types::{BasicType, BasicTypeEnum, FloatMathType, IntType, PointerType, VectorType},
values::{
BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionOpcode, InstructionValue,
@@ -22,8 +22,8 @@ use inkwell::{
};
use smallvec::SmallVec;
use crate::config::{CompiledFunctionKind, LLVMConfig};
use crate::object_file::load_object_file;
use crate::config::{CompiledFunctionKind, LLVM};
use crate::object_file::{load_object_file, CompiledFunction};
use wasm_common::entity::{PrimaryMap, SecondaryMap};
use wasm_common::{
FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex,
@@ -31,9 +31,8 @@ use wasm_common::{
};
use wasmer_compiler::wasmparser::{MemoryImmediate, Operator};
use wasmer_compiler::{
to_wasm_error, wptype_to_type, CompileError, CompiledFunction, CustomSections,
FunctionBodyData, GenerateMiddlewareChain, MiddlewareBinaryReader, ModuleTranslationState,
RelocationTarget,
to_wasm_error, wptype_to_type, CompileError, FunctionBodyData, GenerateMiddlewareChain,
MiddlewareBinaryReader, ModuleTranslationState, RelocationTarget,
};
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
@@ -55,12 +54,14 @@ fn const_zero(ty: BasicTypeEnum) -> BasicValueEnum {
pub struct FuncTranslator {
ctx: Context,
target_machine: TargetMachine,
}
impl FuncTranslator {
pub fn new() -> Self {
pub fn new(target_machine: TargetMachine) -> Self {
Self {
ctx: Context::create(),
target_machine,
}
}
@@ -70,11 +71,11 @@ impl FuncTranslator {
module_translation: &ModuleTranslationState,
local_func_index: &LocalFunctionIndex,
function_body: &FunctionBodyData,
config: &LLVMConfig,
config: &LLVM,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: &PrimaryMap<TableIndex, TablePlan>,
func_names: &SecondaryMap<FunctionIndex, String>,
) -> Result<(CompiledFunction, CustomSections), CompileError> {
) -> Result<CompiledFunction, CompileError> {
// The function type, used for the callbacks.
let function = CompiledFunctionKind::Local(*local_func_index);
let func_index = wasm_module.func_index(*local_func_index);
@@ -85,8 +86,8 @@ impl FuncTranslator {
};
let module = self.ctx.create_module(module_name.as_str());
let target_triple = config.target_triple();
let target_machine = config.target_machine();
let target_machine = &self.target_machine;
let target_triple = target_machine.get_triple();
module.set_triple(&target_triple);
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
let wasm_fn_type = wasm_module
@@ -272,7 +273,7 @@ impl FuncTranslator {
load_object_file(
mem_buf_slice,
".wasmer_function",
Some(RelocationTarget::LocalFunc(*local_func_index)),
RelocationTarget::LocalFunc(*local_func_index),
|name: &String| {
if let Some((index, _)) = func_names
.iter()

View File

@@ -3,6 +3,27 @@
This is the `wasmer-compiler-singlepass` crate, which contains a
compiler implementation based on Singlepass.
## Usage
Add this crate into your `Cargo.toml` dependencies:
```toml
wasmer-compiler-singlepass = "1.0.0-alpha.1"
```
And then:
```rust
use wasmer::{Store, JIT};
use wasmer_compiler_singlepass::Singlepass;
let compiler = Singlepass::new();
// Put it into an engine and add it to the store
let store = Store::new(&JIT::new(&compiler).engine());
```
## When to use Singlepass
Singlepass is designed to emit compiled code at linear time, as such
is not prone to JIT bombs and also offers great compilation performance
orders of magnitude faster than `wasmer-compiler-cranelift` and

View File

@@ -1,6 +1,4 @@
use crate::{
common_decl::*, config::SinglepassConfig, emitter_x64::*, machine::Machine, x64_decl::*,
};
use crate::{common_decl::*, config::Singlepass, emitter_x64::*, machine::Machine, x64_decl::*};
use dynasmrt::{x64::Assembler, DynamicLabel};
use smallvec::{smallvec, SmallVec};
use std::collections::BTreeMap;
@@ -32,7 +30,7 @@ pub struct FuncGen<'a> {
module: &'a ModuleInfo,
/// ModuleInfo compilation config.
config: &'a SinglepassConfig,
config: &'a Singlepass,
/// Offsets of vmctx fields.
vmoffsets: &'a VMOffsets,
@@ -410,7 +408,7 @@ impl<'a> FuncGen<'a> {
let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst {
Location::Imm32(_) | Location::Imm64(_) => {
return Err(CodegenError {
message: format!("emit_relaxed_zx_sx dst Imm: unreachable code"),
message: "emit_relaxed_zx_sx dst Imm: unreachable code".to_string(),
})
}
Location::Memory(_, _) => {
@@ -427,7 +425,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_relaxed_zx_sx dst: unreachable code"),
message: "emit_relaxed_zx_sx dst: unreachable code".to_string(),
})
}
};
@@ -448,7 +446,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_relaxed_zx_sx src: unreachable code"),
message: "emit_relaxed_zx_sx src: unreachable code".to_string(),
})
}
}
@@ -584,7 +582,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_relaxed_avx_base src1: unreachable code"),
message: "emit_relaxed_avx_base src1: unreachable code".to_string(),
})
}
};
@@ -613,7 +611,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_relaxed_avx_base src2: unreachable code"),
message: "emit_relaxed_avx_base src2: unreachable code".to_string(),
})
}
};
@@ -628,7 +626,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_relaxed_avx_base dst: unreachable code"),
message: "emit_relaxed_avx_base dst: unreachable code".to_string(),
})
}
}
@@ -703,7 +701,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_cmpop_i32_dynamic_b ret: unreachable code"),
message: "emit_cmpop_i32_dynamic_b ret: unreachable code".to_string(),
})
}
}
@@ -750,7 +748,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_cmpop_i64_dynamic_b ret: unreachable code"),
message: "emit_cmpop_i64_dynamic_b ret: unreachable code".to_string(),
})
}
}
@@ -810,7 +808,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_xcnt_i32 loc: unreachable code"),
message: "emit_xcnt_i32 loc: unreachable code".to_string(),
})
}
}
@@ -863,7 +861,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_xcnt_i64 loc: unreachable code"),
message: "emit_xcnt_i64 loc: unreachable code".to_string(),
})
}
}
@@ -967,7 +965,7 @@ impl<'a> FuncGen<'a> {
self.machine.state.register_values[X64Register::GPR(*r).to_index().0].clone();
if content == MachineValue::Undefined {
return Err(CodegenError {
message: format!("emit_call_sysv: Undefined used_gprs content"),
message: "emit_call_sysv: Undefined used_gprs content".to_string(),
});
}
self.machine.state.stack_values.push(content);
@@ -994,7 +992,7 @@ impl<'a> FuncGen<'a> {
self.machine.state.register_values[X64Register::XMM(*r).to_index().0].clone();
if content == MachineValue::Undefined {
return Err(CodegenError {
message: format!("emit_call_sysv: Undefined used_xmms content"),
message: "emit_call_sysv: Undefined used_xmms content".to_string(),
});
}
self.machine.state.stack_values.push(content);
@@ -1005,13 +1003,9 @@ impl<'a> FuncGen<'a> {
// Calculate stack offset.
for (i, _param) in params.iter().enumerate() {
let loc = Machine::get_param_location(1 + i);
match loc {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
stack_offset += 8;
}
_ => {}
}
}
// Align stack to 16 bytes.
@@ -1062,7 +1056,8 @@ impl<'a> FuncGen<'a> {
Location::Memory(reg, offset) => {
if reg != GPR::RBP {
return Err(CodegenError {
message: format!("emit_call_sysv loc param: unreachable code"),
message: "emit_call_sysv loc param: unreachable code"
.to_string(),
});
}
self.machine
@@ -1112,7 +1107,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("emit_call_sysv loc: unreachable code"),
message: "emit_call_sysv loc: unreachable code".to_string(),
})
}
}
@@ -1137,7 +1132,7 @@ impl<'a> FuncGen<'a> {
if (self.machine.state.stack_values.len() % 2) != 1 {
return Err(CodegenError {
message: format!("emit_call_sysv: explicit shadow takes one slot"),
message: "emit_call_sysv: explicit shadow takes one slot".to_string(),
});
}
@@ -1171,7 +1166,7 @@ impl<'a> FuncGen<'a> {
);
if (stack_offset % 8) != 0 {
return Err(CodegenError {
message: format!("emit_call_sysv: Bad restoring stack alignement"),
message: "emit_call_sysv: Bad restoring stack alignement".to_string(),
});
}
for _ in 0..stack_offset / 8 {
@@ -1180,7 +1175,7 @@ impl<'a> FuncGen<'a> {
}
// Restore XMMs.
if used_xmms.len() > 0 {
if !used_xmms.is_empty() {
for (i, r) in used_xmms.iter().enumerate() {
self.assembler.emit_mov(
Size::S64,
@@ -1206,7 +1201,7 @@ impl<'a> FuncGen<'a> {
if self.machine.state.stack_values.pop().unwrap() != MachineValue::ExplicitShadow {
return Err(CodegenError {
message: format!("emit_call_sysv: Popped value is not ExplicitShadow"),
message: "emit_call_sysv: Popped value is not ExplicitShadow".to_string(),
});
}
Ok(())
@@ -1330,7 +1325,7 @@ impl<'a> FuncGen<'a> {
3 => 8,
_ => {
return Err(CodegenError {
message: format!("emit_memory_op align: unreachable value"),
message: "emit_memory_op align: unreachable value".to_string(),
})
}
};
@@ -1371,7 +1366,7 @@ impl<'a> FuncGen<'a> {
) -> Result<(), CodegenError> {
if memory_sz > stack_sz {
return Err(CodegenError {
message: format!("emit_compare_and_swap: memory size > stac size"),
message: "emit_compare_and_swap: memory size > stack size".to_string(),
});
}
@@ -1773,7 +1768,7 @@ impl<'a> FuncGen<'a> {
if self.machine.state.wasm_inst_offset != std::usize::MAX {
return Err(CodegenError {
message: format!("emit_head: wasm_inst_offset not std::usize::MAX"),
message: "emit_head: wasm_inst_offset not std::usize::MAX".to_string(),
});
}
Ok(())
@@ -1781,7 +1776,7 @@ impl<'a> FuncGen<'a> {
pub fn new(
module: &'a ModuleInfo,
config: &'a SinglepassConfig,
config: &'a Singlepass,
vmoffsets: &'a VMOffsets,
memory_plans: &'a PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: &'a PrimaryMap<TableIndex, TablePlan>,
@@ -1842,7 +1837,7 @@ impl<'a> FuncGen<'a> {
}
pub fn has_control_frames(&self) -> bool {
self.control_stack.len() > 0
!self.control_stack.is_empty()
}
pub fn feed_operator(&mut self, op: Operator) -> Result<(), CodegenError> {
@@ -2160,7 +2155,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I32Clz src: unreachable code"),
message: "I32Clz src: unreachable code".to_string(),
})
}
};
@@ -2177,7 +2172,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I32Clz dst: unreachable code"),
message: "I32Clz dst: unreachable code".to_string(),
})
}
};
@@ -2211,12 +2206,9 @@ impl<'a> FuncGen<'a> {
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = ret {
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I32Ctz => {
@@ -2230,7 +2222,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I32Ctz src: unreachable code"),
message: "I32Ctz src: unreachable code".to_string(),
})
}
};
@@ -2247,7 +2239,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I32Ctz dst: unreachable code"),
message: "I32Ctz dst: unreachable code".to_string(),
})
}
};
@@ -2279,12 +2271,9 @@ impl<'a> FuncGen<'a> {
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = ret {
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I32Popcnt => self.emit_xcnt_i32(Assembler::emit_popcnt)?,
@@ -2405,7 +2394,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I64Clz src: unreachable code"),
message: "I64Clz src: unreachable code".to_string(),
})
}
};
@@ -2422,7 +2411,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I64Clz dst: unreachable code"),
message: "I64Clz dst: unreachable code".to_string(),
})
}
};
@@ -2456,12 +2445,9 @@ impl<'a> FuncGen<'a> {
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = ret {
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I64Ctz => {
@@ -2475,7 +2461,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I64Ctz src: unreachable code"),
message: "I64Ctz src: unreachable code".to_string(),
})
}
};
@@ -2492,7 +2478,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg,
_ => {
return Err(CodegenError {
message: format!("I64Ctz dst: unreachable code"),
message: "I64Ctz dst: unreachable code".to_string(),
})
}
};
@@ -2524,12 +2510,9 @@ impl<'a> FuncGen<'a> {
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = ret {
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I64Popcnt => self.emit_xcnt_i64(Assembler::emit_popcnt)?,
@@ -2721,7 +2704,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Max src1: unreachable code"),
message: "F32Max src1: unreachable code".to_string(),
})
}
};
@@ -2754,7 +2737,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Max src2: unreachable code"),
message: "F32Max src2: unreachable code".to_string(),
})
}
};
@@ -2811,7 +2794,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Max ret: unreachable code"),
message: "F32Max ret: unreachable code".to_string(),
})
}
}
@@ -2865,7 +2848,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Min src1: unreachable code"),
message: "F32Min src1: unreachable code".to_string(),
})
}
};
@@ -2898,7 +2881,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Min src2: unreachable code"),
message: "F32Min src2: unreachable code".to_string(),
})
}
};
@@ -2964,7 +2947,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F32Min ret: unreachable code"),
message: "F32Min ret: unreachable code".to_string(),
})
}
}
@@ -3211,7 +3194,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Max src1: unreachable code"),
message: "F64Max src1: unreachable code".to_string(),
})
}
};
@@ -3244,7 +3227,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Max src2: unreachable code"),
message: "F64Max src2: unreachable code".to_string(),
})
}
};
@@ -3301,7 +3284,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Max ret: unreachable code"),
message: "F64Max ret: unreachable code".to_string(),
})
}
}
@@ -3356,7 +3339,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Min src1: unreachable code"),
message: "F64Min src1: unreachable code".to_string(),
})
}
};
@@ -3389,7 +3372,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Min src2: unreachable code"),
message: "F64Min src2: unreachable code".to_string(),
})
}
};
@@ -3455,7 +3438,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("F64Min ret: unreachable code"),
message: "F64Min ret: unreachable code".to_string(),
})
}
}
@@ -5187,13 +5170,13 @@ impl<'a> FuncGen<'a> {
|this| {
this.assembler.emit_call_location(Location::GPR(GPR::RAX));
},
params.iter().map(|x| *x),
params.iter().copied(),
)?;
self.machine
.release_locations_only_stack(&mut self.assembler, &params);
if return_types.len() > 0 {
if !return_types.is_empty() {
let ret = self.machine.acquire_locations(
&mut self.assembler,
&[(
@@ -5217,7 +5200,7 @@ impl<'a> FuncGen<'a> {
Operator::CallIndirect { index, table_index } => {
if table_index != 0 {
return Err(CodegenError {
message: format!("CallIndirect: table_index is not 0"),
message: "CallIndirect: table_index is not 0".to_string(),
});
}
let table_index = TableIndex::new(table_index as _);
@@ -5387,13 +5370,13 @@ impl<'a> FuncGen<'a> {
));
}
},
params.iter().map(|x| *x),
params.iter().copied(),
)?;
self.machine
.release_locations_only_stack(&mut self.assembler, &params);
if return_types.len() > 0 {
if !return_types.is_empty() {
let ret = self.machine.acquire_locations(
&mut self.assembler,
&[(
@@ -5429,7 +5412,7 @@ impl<'a> FuncGen<'a> {
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => {
return Err(CodegenError {
message: format!("If: multi-value returns not yet implemented"),
message: "If: multi-value returns not yet implemented".to_string(),
})
}
},
@@ -5445,7 +5428,7 @@ impl<'a> FuncGen<'a> {
Operator::Else => {
let frame = self.control_stack.last_mut().unwrap();
if !was_unreachable && frame.returns.len() > 0 {
if !was_unreachable && !frame.returns.is_empty() {
let first_return = frame.returns[0];
let loc = *self.value_stack.last().unwrap();
if first_return.is_float() {
@@ -5497,7 +5480,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("Else: frame.if_else unreachable code"),
message: "Else: frame.if_else unreachable code".to_string(),
})
}
}
@@ -5570,7 +5553,8 @@ impl<'a> FuncGen<'a> {
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => {
return Err(CodegenError {
message: format!("Block: multi-value returns not yet implemented"),
message: "Block: multi-value returns not yet implemented"
.to_string(),
})
}
},
@@ -5598,7 +5582,7 @@ impl<'a> FuncGen<'a> {
let _activate_offset = self.assembler.get_offset().0;
self.control_stack.push(ControlFrame {
label: label,
label,
loop_like: true,
if_else: IfElseState::None,
returns: match ty {
@@ -5606,7 +5590,8 @@ impl<'a> FuncGen<'a> {
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => {
return Err(CodegenError {
message: format!("Loop: multi-value returns not yet implemented"),
message: "Loop: multi-value returns not yet implemented"
.to_string(),
})
}
},
@@ -5627,7 +5612,7 @@ impl<'a> FuncGen<'a> {
Location::Memory(
Machine::get_vmctx_reg(),
self.vmoffsets.vmctx_builtin_function(
if let Some(_) = self.module.local_memory_index(memory_index) {
if self.module.local_memory_index(memory_index).is_some() {
VMBuiltinFunctionIndex::get_memory32_size_index()
} else {
VMBuiltinFunctionIndex::get_imported_memory32_size_index()
@@ -5669,7 +5654,7 @@ impl<'a> FuncGen<'a> {
Location::Memory(
Machine::get_vmctx_reg(),
self.vmoffsets.vmctx_builtin_function(
if let Some(_) = self.module.local_memory_index(memory_index) {
if self.module.local_memory_index(memory_index).is_some() {
VMBuiltinFunctionIndex::get_memory32_grow_index()
} else {
VMBuiltinFunctionIndex::get_imported_memory32_grow_index()
@@ -6035,7 +6020,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("I64Load32U ret: unreachable code"),
message: "I64Load32U ret: unreachable code".to_string(),
})
}
}
@@ -6158,10 +6143,10 @@ impl<'a> FuncGen<'a> {
}
Operator::Return => {
let frame = &self.control_stack[0];
if frame.returns.len() > 0 {
if !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!("Return: incorrect frame.returns"),
message: "Return: incorrect frame.returns".to_string(),
});
}
let first_return = frame.returns[0];
@@ -6208,10 +6193,10 @@ impl<'a> FuncGen<'a> {
Operator::Br { relative_depth } => {
let frame =
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
if !frame.loop_like && frame.returns.len() > 0 {
if !frame.loop_like && !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!("Br: incorrect frame.returns"),
message: "Br: incorrect frame.returns".to_string(),
});
}
let first_return = frame.returns[0];
@@ -6262,10 +6247,10 @@ impl<'a> FuncGen<'a> {
let frame =
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
if !frame.loop_like && frame.returns.len() > 0 {
if !frame.loop_like && !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!("BrIf: incorrect frame.returns"),
message: "BrIf: incorrect frame.returns".to_string(),
});
}
@@ -6345,7 +6330,7 @@ impl<'a> FuncGen<'a> {
table.push(label);
let frame =
&self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
if !frame.loop_like && frame.returns.len() > 0 {
if !frame.loop_like && !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!(
@@ -6397,10 +6382,10 @@ impl<'a> FuncGen<'a> {
{
let frame = &self.control_stack
[self.control_stack.len() - 1 - (default_target as usize)];
if !frame.loop_like && frame.returns.len() > 0 {
if !frame.loop_like && !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!("BrTable: incorrect frame.returns"),
message: "BrTable: incorrect frame.returns".to_string(),
});
}
@@ -6459,7 +6444,7 @@ impl<'a> FuncGen<'a> {
Operator::End => {
let frame = self.control_stack.pop().unwrap();
if !was_unreachable && frame.returns.len() > 0 {
if !was_unreachable && !frame.returns.is_empty() {
let loc = *self.value_stack.last().unwrap();
if frame.returns[0].is_float() {
let fp = self.fp_stack.peek1()?;
@@ -6494,7 +6479,7 @@ impl<'a> FuncGen<'a> {
}
}
if self.control_stack.len() == 0 {
if self.control_stack.is_empty() {
self.assembler.emit_label(frame.label);
self.machine
.finalize_locals(&mut self.assembler, &self.locals);
@@ -6532,10 +6517,10 @@ impl<'a> FuncGen<'a> {
self.assembler.emit_label(label);
}
if frame.returns.len() > 0 {
if !frame.returns.is_empty() {
if frame.returns.len() != 1 {
return Err(CodegenError {
message: format!("End: incorrect frame.returns"),
message: "End: incorrect frame.returns".to_string(),
});
}
let loc = self.machine.acquire_locations(
@@ -6747,7 +6732,7 @@ impl<'a> FuncGen<'a> {
}
_ => {
return Err(CodegenError {
message: format!("I64AtomicLoad32U ret: unreachable code"),
message: "I64AtomicLoad32U ret: unreachable code".to_string(),
})
}
}
@@ -8262,13 +8247,9 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
// Calculate stack offset.
let mut stack_offset: u32 = 0;
for (i, _param) in sig.params().iter().enumerate() {
let loc = Machine::get_param_location(1 + i);
match loc {
Location::Memory(_, _) => {
if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
stack_offset += 8;
}
_ => {}
}
}
// Align to 16 bytes. We push two 8-byte registers below, so here we need to ensure stack_offset % 16 == 8.
@@ -8338,7 +8319,7 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
);
// Write return value.
if sig.results().len() > 0 {
if !sig.results().is_empty() {
a.emit_mov(
Size::S64,
Location::GPR(GPR::RAX),
@@ -8374,7 +8355,7 @@ pub fn gen_std_dynamic_import_trampoline(
);
// Copy arguments.
if sig.params().len() > 0 {
if !sig.params().is_empty() {
let mut argalloc = ArgumentRegisterAllocator::default();
argalloc.next(Type::I64).unwrap(); // skip VMContext
@@ -8426,7 +8407,7 @@ pub fn gen_std_dynamic_import_trampoline(
a.emit_call_location(Location::GPR(GPR::RAX));
// Fetch return value.
if sig.results().len() > 0 {
if !sig.results().is_empty() {
assert_eq!(sig.results().len(), 1);
a.emit_mov(
Size::S64,
@@ -8470,8 +8451,7 @@ pub fn gen_import_call_trampoline(
if sig
.params()
.iter()
.find(|&&x| x == Type::F32 || x == Type::F64)
.is_some()
.any(|&x| x == Type::F32 || x == Type::F64)
{
let mut param_locations: Vec<Location> = vec![];
@@ -8493,8 +8473,7 @@ pub fn gen_import_call_trampoline(
for i in 0..sig.params().len() {
let loc = match i {
0..=4 => {
static PARAM_REGS: &'static [GPR] =
&[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
static PARAM_REGS: &[GPR] = &[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
let loc = Location::Memory(GPR::RSP, (i * 8) as i32);
a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc);
loc

View File

@@ -161,7 +161,7 @@ impl MachineState {
.enumerate()
.find(|&(_, (a, b))| a != b)
.map(|x| x.0)
.unwrap_or(old.stack_values.len().min(self.stack_values.len()));
.unwrap_or_else(|| old.stack_values.len().min(self.stack_values.len()));
assert_eq!(self.register_values.len(), old.register_values.len());
let reg_diff: Vec<_> = self
.register_values
@@ -196,7 +196,7 @@ impl MachineState {
.enumerate()
.find(|&(_, (a, b))| a != b)
.map(|x| x.0)
.unwrap_or(old.wasm_stack.len().min(self.wasm_stack.len()));
.unwrap_or_else(|| old.wasm_stack.len().min(self.wasm_stack.len()));
MachineStateDiff {
last: None,
stack_push: self.stack_values[first_diff_stack_depth..].to_vec(),

View File

@@ -6,65 +6,58 @@ use crate::codegen_x64::{
gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline,
CodegenError, FuncGen,
};
use crate::config::SinglepassConfig;
use crate::config::Singlepass;
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::sync::Arc;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::Features;
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_compiler::wasmparser::BinaryReaderError;
use wasmer_compiler::TrapInformation;
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler, SectionIndex};
use wasmer_compiler::{
CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader, ModuleTranslationState, Target,
CompileModuleInfo, CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader,
ModuleTranslationState, Target,
};
use wasmer_compiler::{FunctionBody, FunctionBodyData};
use wasmer_runtime::ModuleInfo;
use wasmer_runtime::TrapCode;
use wasmer_runtime::{MemoryPlan, TablePlan, VMOffsets};
use wasmer_runtime::{ModuleInfo, TrapCode, VMOffsets};
/// A compiler that compiles a WebAssembly module with Singlepass.
/// It does the compilation in one pass
pub struct SinglepassCompiler {
config: SinglepassConfig,
config: Singlepass,
}
impl SinglepassCompiler {
/// Creates a new Singlepass compiler
pub fn new(config: &SinglepassConfig) -> Self {
pub fn new(config: &Singlepass) -> Self {
Self {
config: config.clone(),
}
}
/// Gets the WebAssembly features for this Compiler
fn config(&self) -> &SinglepassConfig {
/// Gets the config for this Compiler
fn config(&self) -> &Singlepass {
&self.config
}
}
impl Compiler for SinglepassCompiler {
/// Gets the WebAssembly features for this Compiler
fn features(&self) -> &Features {
self.config.features()
}
/// Gets the target associated to this Compiler.
fn target(&self) -> &Target {
self.config.target()
}
/// Compile the module using Singlepass, producing a compilation result with
/// associated relocations.
fn compile_module(
&self,
module: &ModuleInfo,
_target: &Target,
compile_info: &CompileModuleInfo,
_module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
let vmoffsets = VMOffsets::new(8, module);
if compile_info.features.multi_value {
return Err(CompileError::UnsupportedFeature("multivalue".to_string()));
}
let vmoffsets = VMOffsets::new(8, &compile_info.module);
let memory_plans = &compile_info.memory_plans;
let table_plans = &compile_info.table_plans;
let module = &compile_info.module;
let import_trampolines: PrimaryMap<SectionIndex, _> = (0..module.num_imported_funcs)
.map(FunctionIndex::new)
.collect::<Vec<_>>()

View File

@@ -3,17 +3,27 @@
use crate::compiler::SinglepassCompiler;
use std::sync::Arc;
use wasmer_compiler::{
Compiler, CompilerConfig, CpuFeature, Features, FunctionMiddlewareGenerator, Target,
};
use wasm_common::Features;
use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, FunctionMiddlewareGenerator, Target};
#[derive(Clone)]
pub struct SinglepassConfig {
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub enable_nan_canonicalization: bool,
pub struct Singlepass {
pub(crate) enable_nan_canonicalization: bool,
pub(crate) enable_stack_check: bool,
/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
}
impl Singlepass {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
Self {
enable_nan_canonicalization: true,
enable_stack_check: false,
middlewares: vec![],
}
}
/// Enable stack check.
///
@@ -22,62 +32,47 @@ pub struct SinglepassConfig {
///
/// Note that this doesn't guarantee deterministic execution across
/// different platforms.
pub enable_stack_check: bool,
features: Features,
target: Target,
/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
}
impl SinglepassConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new(mut features: Features, target: Target) -> Self {
// Override the default multi-value switch
features.multi_value = false;
Self {
enable_nan_canonicalization: true,
enable_stack_check: false,
features,
target,
middlewares: vec![],
pub fn enable_stack_check(&mut self, enable: bool) -> &mut Self {
self.enable_stack_check = enable;
self
}
/// Enable NaN canonicalization.
///
/// NaN canonicalization is useful when trying to run WebAssembly
/// deterministically across different architectures.
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
self.enable_nan_canonicalization = enable;
self
}
}
impl CompilerConfig for SinglepassConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features {
&self.features
}
impl CompilerConfig for Singlepass {
fn enable_pic(&mut self) {
// Do nothing, since singlepass already emits
// PIC code.
}
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Transform it into the compiler
fn compiler(&self) -> Box<dyn Compiler + Send> {
Box::new(SinglepassCompiler::new(&self))
}
/// Gets the default features for this compiler in the given target
fn default_features_for_target(&self, _target: &Target) -> Features {
let mut features = Features::default();
features.multi_value(false);
features
}
/// Pushes a middleware onto the back of the middleware chain.
fn push_middleware(&mut self, middleware: Arc<dyn FunctionMiddlewareGenerator>) {
self.middlewares.push(middleware);
}
}
impl Default for SinglepassConfig {
fn default() -> SinglepassConfig {
Self::new(Default::default(), Default::default())
impl Default for Singlepass {
fn default() -> Singlepass {
Self::new()
}
}

View File

@@ -702,13 +702,10 @@ impl Emitter for Assembler {
fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) {
// fast path
match (src, dst) {
(Location::Imm32(0), Location::GPR(x)) => {
if let (Location::Imm32(0), Location::GPR(x)) = (src, dst) {
dynasm!(self ; xor Rd(x as u8), Rd(x as u8));
return;
}
_ => {}
}
binop_all_nofp!(mov, self, sz, src, dst, {
binop_imm64_gpr!(mov, self, sz, src, dst, {

View File

@@ -21,4 +21,4 @@ mod machine;
mod x64_decl;
pub use crate::compiler::SinglepassCompiler;
pub use crate::config::SinglepassConfig;
pub use crate::config::Singlepass;

View File

@@ -50,7 +50,7 @@ impl Machine {
/// This method does not mark the register as used.
pub fn pick_gpr(&self) -> Option<GPR> {
use GPR::*;
static REGS: &'static [GPR] = &[RSI, RDI, R8, R9, R10, R11];
static REGS: &[GPR] = &[RSI, RDI, R8, R9, R10, R11];
for r in REGS {
if !self.used_gprs.contains(r) {
return Some(*r);
@@ -64,7 +64,7 @@ impl Machine {
/// This method does not mark the register as used.
pub fn pick_temp_gpr(&self) -> Option<GPR> {
use GPR::*;
static REGS: &'static [GPR] = &[RAX, RCX, RDX];
static REGS: &[GPR] = &[RAX, RCX, RDX];
for r in REGS {
if !self.used_gprs.contains(r) {
return Some(*r);
@@ -99,7 +99,7 @@ impl Machine {
/// This method does not mark the register as used.
pub fn pick_xmm(&self) -> Option<XMM> {
use XMM::*;
static REGS: &'static [XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7];
static REGS: &[XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7];
for r in REGS {
if !self.used_xmms.contains(r) {
return Some(*r);
@@ -113,7 +113,7 @@ impl Machine {
/// This method does not mark the register as used.
pub fn pick_temp_xmm(&self) -> Option<XMM> {
use XMM::*;
static REGS: &'static [XMM] = &[XMM0, XMM1, XMM2];
static REGS: &[XMM] = &[XMM0, XMM1, XMM2];
for r in REGS {
if !self.used_xmms.contains(r) {
return Some(*r);
@@ -260,8 +260,7 @@ impl Machine {
let mut delta_stack_offset: usize = 0;
for loc in locs.iter().rev() {
match *loc {
Location::Memory(GPR::RBP, x) => {
if let Location::Memory(GPR::RBP, x) = *loc {
if x >= 0 {
unreachable!();
}
@@ -273,8 +272,6 @@ impl Machine {
delta_stack_offset += 8;
self.state.stack_values.pop().unwrap();
}
_ => {}
}
// Wasm state popping is deferred to `release_locations_only_osr_state`.
}
@@ -302,8 +299,7 @@ impl Machine {
let mut stack_offset = self.stack_offset.0;
for loc in locs.iter().rev() {
match *loc {
Location::Memory(GPR::RBP, x) => {
if let Location::Memory(GPR::RBP, x) = *loc {
if x >= 0 {
unreachable!();
}
@@ -314,8 +310,6 @@ impl Machine {
stack_offset -= 8;
delta_stack_offset += 8;
}
_ => {}
}
}
if delta_stack_offset != 0 {

View File

@@ -5,8 +5,41 @@ This crate is the base for Compiler implementations.
It performs the translation from a Wasm module into a basic ModuleInfo,
but leaves the Wasm function bytecode translation to the compiler implementor.
Here are some of the Compilers provided by Wasmer:
* [Singlepass](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-singlepass)
* [Cranelift](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-cranelift)
* [LLVM](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-llvm)
## How to create a compiler
Creating a new compiler is quite easy, you just need to impement two traits: `CompilerConfig` and `Compiler`:
```rust
/// The compiler configuration options.
pub trait CompilerConfig {
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler + Send>;
}
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
pub trait Compiler {
/// Compiles a parsed module.
///
/// It returns the [`Compilation`] or a [`CompileError`].
fn compile_module<'data, 'module>(
&self,
target: &Target,
compile_info: &'module CompileModuleInfo,
module_translation: &ModuleTranslationState,
// The list of function bodies
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
) -> Result<Compilation, CompileError>;
}
```
### Acknowledgments
This project borrowed some of the code strucutre from the [cranelift-wasm](https://crates.io/crates/cranelift-wasm), however it's been adapted to not depend on any specific IR and be abstract of any compiler.
This project borrowed some of the code strucutre from the [cranelift-wasm](https://crates.io/crates/cranelift-wasm) crate, however it's been adapted to not depend on any specific IR and be abstract of any compiler.
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

View File

@@ -5,55 +5,49 @@ use crate::error::CompileError;
use crate::function::Compilation;
use crate::lib::std::boxed::Box;
use crate::lib::std::sync::Arc;
use crate::module::CompileModuleInfo;
use crate::target::Target;
use crate::translator::FunctionMiddlewareGenerator;
use crate::FunctionBodyData;
use crate::ModuleTranslationState;
use wasm_common::entity::PrimaryMap;
use wasm_common::{Features, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_runtime::ModuleInfo;
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasm_common::{Features, LocalFunctionIndex};
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
/// The compiler configuration options.
///
/// This options must have WebAssembly `Features` and a specific
/// `Target` to compile to.
pub trait CompilerConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features;
/// Should Position Independent Code (PIC) be enabled.
/// Enable Position Independent Code (PIC).
///
/// This is required for shared object generation (Native Engine),
/// but will make the JIT Engine to fail, since PIC is not yet
/// supported in the JIT linking phase.
fn enable_pic(&mut self);
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target;
fn enable_pic(&mut self) {
// By default we do nothing, each backend will need to customize this
// in case they do something special for emitting PIC code.
}
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler + Send>;
/// Gets the default features for this compiler in the given target
fn default_features_for_target(&self, _target: &Target) -> Features {
Features::default()
}
/// Pushes a middleware onto the back of the middleware chain.
fn push_middleware(&mut self, middleware: Arc<dyn FunctionMiddlewareGenerator>);
}
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
pub trait Compiler {
/// Gets the target associated with this compiler
fn target(&self) -> &Target;
/// Gets the WebAssembly features for this Compiler
fn features(&self) -> &Features;
/// Validates a module.
///
/// It returns the a succesful Result in case is valid, `CompileError` in case is not.
fn validate_module<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
let features = self.features();
fn validate_module<'data>(
&self,
features: &Features,
data: &'data [u8],
) -> Result<(), CompileError> {
let config = ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: features.threads,
@@ -72,13 +66,10 @@ pub trait Compiler {
/// It returns the [`Compilation`] or a [`CompileError`].
fn compile_module<'data, 'module>(
&self,
module: &'module ModuleInfo,
target: &Target,
module: &'module CompileModuleInfo,
module_translation: &ModuleTranslationState,
// The list of function bodies
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
// The plans for the module memories (imported and local)
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
// The plans for the module tables (imported and local)
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError>;
}

View File

@@ -23,6 +23,10 @@ pub enum CompileError {
#[error("Validation error: {0}")]
Validate(String),
/// The compiler doesn't support a Wasm feature
#[error("Feature {0} is not yet supported")]
UnsupportedFeature(String),
/// Insufficient resources available for execution.
#[error("Insufficient resources: {0}")]
Resource(String),

View File

@@ -57,6 +57,7 @@ mod compiler;
mod error;
mod function;
mod jump_table;
mod module;
mod relocation;
mod target;
mod trap;
@@ -76,6 +77,7 @@ pub use crate::function::{
Functions,
};
pub use crate::jump_table::{JumpTable, JumpTableOffsets};
pub use crate::module::CompileModuleInfo;
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex};
pub use crate::sourceloc::SourceLoc;

View File

@@ -0,0 +1,27 @@
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{Features, MemoryIndex, TableIndex};
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
/// The required info for compiling a module.
///
/// This differs from [`ModuleInfo`] because it have extra info only
/// possible after translation (such as the features used for compiling,
/// or the `MemoryPlan` and `TablePlan`).
#[derive(Debug)]
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
pub struct CompileModuleInfo {
/// The features used for compiling the module
pub features: Features,
/// The module information
pub module: Arc<ModuleInfo>,
/// The memory plans used for compiling.
///
/// The compiler will emit the most optimal code based
/// on the memory style (static or dynamic) chosen.
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
/// The table plans used for compiling.
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
}

View File

@@ -29,6 +29,8 @@ pub enum RelocationKind {
Abs8,
/// x86 PC-relative 4-byte
X86PCRel4,
/// x86 PC-relative 8-byte
X86PCRel8,
/// x86 PC-relative 4-byte offset to trailing rodata
X86PCRelRodata4,
/// x86 call to PC-relative 4-byte
@@ -62,6 +64,7 @@ impl fmt::Display for RelocationKind {
Self::Abs4 => write!(f, "Abs4"),
Self::Abs8 => write!(f, "Abs8"),
Self::X86PCRel4 => write!(f, "PCRel4"),
Self::X86PCRel8 => write!(f, "PCRel8"),
Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
@@ -112,7 +115,7 @@ impl Relocation {
RelocationKind::Abs8 => {
let reloc_address = start + self.offset as usize;
let reloc_addend = self.addend as isize;
let reloc_abs = (target_func_address)
let reloc_abs = target_func_address
.checked_add(reloc_addend as u64)
.unwrap();
(reloc_address, reloc_abs)
@@ -126,6 +129,15 @@ impl Relocation {
.unwrap();
(reloc_address, reloc_delta_u32 as u64)
}
RelocationKind::X86PCRel8 => {
let reloc_address = start + self.offset as usize;
let reloc_addend = self.addend as isize;
let reloc_delta = target_func_address
.wrapping_sub(reloc_address as u64)
.checked_add(reloc_addend as u64)
.unwrap();
(reloc_address, reloc_delta)
}
RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
let reloc_address = start + self.offset as usize;
let reloc_addend = self.addend as isize;

View File

@@ -11,8 +11,7 @@ use wasmparser::{BinaryReader, Operator, Result as WpResult, Type};
/// A shared builder for function middlewares.
pub trait FunctionMiddlewareGenerator: Debug + Send + Sync {
/// Generates a `FunctionMiddleware` for a given function.
fn generate<'a>(&self, local_function_index: LocalFunctionIndex)
-> Box<dyn FunctionMiddleware>;
fn generate(&self, local_function_index: LocalFunctionIndex) -> Box<dyn FunctionMiddleware>;
}
/// A function middleware specialized for a single function.

View File

@@ -61,8 +61,7 @@ pub fn parse_type_section(
environ.reserve_signatures(count)?;
for entry in types {
match entry.map_err(to_wasm_error)? {
WPFunctionType { params, returns } => {
let WPFunctionType { params, returns } = entry.map_err(to_wasm_error)?;
let sig_params: Vec<Type> = params
.iter()
.map(|ty| {
@@ -81,8 +80,6 @@ pub fn parse_type_section(
environ.declare_signature(sig)?;
module_translation_state.wasm_types.push((params, returns));
}
}
}
Ok(())
}

View File

@@ -263,7 +263,7 @@ pub fn _getaddrinfo(
.get_mut();
guest_sockaddr.sa_family = (*host_sockaddr_ptr).sa_family as i16;
guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data.clone();
guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data;
guest_sockaddr_ptr
};
@@ -308,7 +308,7 @@ pub fn _getaddrinfo(
}
// this frees all connected nodes on the linked list
freeaddrinfo(out_ptr);
head_of_list.unwrap_or(WasmPtr::new(0))
head_of_list.unwrap_or_else(|| WasmPtr::new(0))
};
res_val_ptr.deref(ctx.memory(0)).unwrap().set(head_of_list);

View File

@@ -31,7 +31,7 @@ pub fn sigdelset(ctx: &mut EmEnv, set: i32, signum: i32) -> i32 {
#[allow(clippy::cast_ptr_alignment)]
let ptr = emscripten_memory_pointer!(memory, set) as *mut i32;
unsafe { *ptr = *ptr & !(1 << (signum - 1)) }
unsafe { *ptr &= !(1 << (signum - 1)) }
0
}

View File

@@ -105,7 +105,7 @@ const STATIC_BUMP: u32 = 215_536;
lazy_static! {
static ref OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG: FunctionType =
{ FunctionType::new(vec![], vec![ValType::I32]) };
FunctionType::new(vec![], vec![ValType::I32]);
}
// The address globals begin at. Very low in memory, for code size and optimization opportunities.
@@ -465,7 +465,7 @@ pub fn emscripten_call_main(
}
/// Top level function to execute emscripten
pub fn run_emscripten_instance<'a>(
pub fn run_emscripten_instance(
instance: &mut Instance,
env: &mut EmEnv,
globals: &mut EmscriptenGlobals,
@@ -1105,7 +1105,7 @@ pub fn generate_emscripten_env(
// Compatibility with newer versions of Emscripten
let mut to_insert: Vec<(String, _)> = vec![];
for (k, v) in env_ns.iter() {
if k.starts_with("_") {
if k.starts_with('_') {
let k = &k[1..];
if !env_ns.contains(k) {
to_insert.push((k.to_string(), v.clone()));

View File

@@ -93,7 +93,7 @@ pub fn sbrk(ctx: &mut EmEnv, increment: i32) -> i32 {
}
}
ctx.memory(0).view::<u32>()[dynamictop_ptr].set(new_dynamic_top as u32);
return old_dynamic_top as _;
old_dynamic_top as _
}
/// emscripten: getTotalMemory

View File

@@ -33,11 +33,11 @@ pub fn _pthread_attr_setstacksize(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
0
}
pub fn _pthread_cleanup_pop(_ctx: &mut EmEnv, _a: i32) -> () {
pub fn _pthread_cleanup_pop(_ctx: &mut EmEnv, _a: i32) {
trace!("emscripten::_pthread_cleanup_pop");
}
pub fn _pthread_cleanup_push(_ctx: &mut EmEnv, _a: i32, _b: i32) -> () {
pub fn _pthread_cleanup_push(_ctx: &mut EmEnv, _a: i32, _b: i32) {
trace!("emscripten::_pthread_cleanup_push");
}
@@ -97,7 +97,7 @@ pub fn _pthread_equal(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
0
}
pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) -> () {
pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) {
trace!("emscripten::_pthread_exit");
}

View File

@@ -646,7 +646,7 @@ pub fn ___syscall102(ctx: &mut EmEnv, _which: c_int, mut varargs: VarArgs) -> c_
let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_addr.sa_family = host_address.sa_family as _;
address_addr.sa_data = host_address.sa_data.clone();
address_addr.sa_data = host_address.sa_data;
// why is this here?
// set_cloexec
@@ -686,7 +686,7 @@ pub fn ___syscall102(ctx: &mut EmEnv, _which: c_int, mut varargs: VarArgs) -> c_
// translate from host data into emscripten data
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_mut.sa_family = sock_addr_host.sa_family as _;
address_mut.sa_data = sock_addr_host.sa_data.clone();
address_mut.sa_data = sock_addr_host.sa_data;
debug!(
"=> socket: {}, address, {:?}, address_len: {}, result = {}",

View File

@@ -241,7 +241,7 @@ pub fn _localtime(ctx: &mut EmEnv, time_p: u32) -> c_int {
let timespec = unsafe {
let time_p_addr = emscripten_memory_pointer!(ctx.memory(0), time_p) as *mut i64;
let seconds = *time_p_addr.clone();
let seconds = *time_p_addr;
time::Timespec::new(seconds, 0)
};
let result_tm = time::at(timespec);

View File

@@ -31,7 +31,7 @@ pub fn get_emscripten_table_size(module: &Module) -> Result<(u32, Option<u32>),
let ty = import.ty();
Ok((ty.minimum, ty.maximum))
} else {
return Err("Emscripten requires at least one imported table".to_string());
Err("Emscripten requires at least one imported table".to_string())
}
}
@@ -40,7 +40,7 @@ pub fn get_emscripten_memory_size(module: &Module) -> Result<(Pages, Option<Page
let ty = import.ty();
Ok((ty.minimum, ty.maximum, ty.shared))
} else {
return Err("Emscripten requires at least one imported memory".to_string());
Err("Emscripten requires at least one imported memory".to_string())
}
}

View File

@@ -13,11 +13,12 @@ use wasm_common::{
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{CompileError, Features, Triple};
#[cfg(feature = "compiler")]
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment};
use wasmer_engine::{
register_frame_info, Artifact, DeserializeError, GlobalFrameInfoRegistration, SerializeError,
Tunables,
};
#[cfg(feature = "compiler")]
use wasmer_engine::{Engine, SerializableFunctionFrameInfo};
@@ -43,10 +44,14 @@ impl JITArtifact {
/// Compile a data buffer into a `JITArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
pub fn new(
jit: &JITEngine,
data: &[u8],
tunables: &dyn Tunables,
) -> Result<Self, CompileError> {
let environ = ModuleEnvironment::new();
let mut inner_jit = jit.inner_mut();
let tunables = jit.tunables();
let features = inner_jit.features();
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
@@ -63,15 +68,21 @@ impl JITArtifact {
.map(|table_type| tunables.table_plan(*table_type))
.collect();
let compile_info = CompileModuleInfo {
module: Arc::new(translation.module),
features: features.clone(),
memory_plans,
table_plans,
};
let compiler = inner_jit.compiler()?;
// Compile the Module
let compilation = compiler.compile_module(
&translation.module,
&jit.target(),
&compile_info,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?;
let function_call_trampolines = compilation.get_function_call_trampolines();
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
@@ -102,11 +113,8 @@ impl JITArtifact {
};
let serializable = SerializableModule {
compilation: serializable_compilation,
module: Arc::new(translation.module),
features: inner_jit.compiler()?.features().clone(),
compile_info,
data_initializers,
memory_plans,
table_plans,
};
Self::from_parts(&mut inner_jit, serializable)
}
@@ -150,7 +158,7 @@ impl JITArtifact {
finished_dynamic_function_trampolines,
) = inner_jit.allocate(
&mut unwind_registry,
&serializable.module,
&serializable.compile_info.module,
&serializable.compilation.function_bodies,
&serializable.compilation.function_call_trampolines,
&serializable.compilation.dynamic_function_trampolines,
@@ -159,7 +167,7 @@ impl JITArtifact {
inner_jit.allocate_custom_sections(&serializable.compilation.custom_sections)?;
link_module(
&serializable.module,
&serializable.compile_info.module,
&finished_functions,
&serializable.compilation.function_jt_offsets,
serializable.compilation.function_relocations.clone(),
@@ -171,6 +179,7 @@ impl JITArtifact {
let signatures = {
let signature_registry = inner_jit.signatures();
serializable
.compile_info
.module
.signatures
.values()
@@ -227,15 +236,15 @@ impl JITArtifact {
impl Artifact for JITArtifact {
fn module(&self) -> Arc<ModuleInfo> {
self.serializable.module.clone()
self.serializable.compile_info.module.clone()
}
fn module_ref(&self) -> &ModuleInfo {
&self.serializable.module
&self.serializable.compile_info.module
}
fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
Arc::get_mut(&mut self.serializable.module)
Arc::get_mut(&mut self.serializable.compile_info.module)
}
fn register_frame_info(&self) {
@@ -248,14 +257,14 @@ impl Artifact for JITArtifact {
let frame_infos = &self.serializable.compilation.function_frame_info;
let finished_functions = &self.finished_functions;
*info = register_frame_info(
self.serializable.module.clone(),
self.serializable.compile_info.module.clone(),
finished_functions,
frame_infos.clone(),
);
}
fn features(&self) -> &Features {
&self.serializable.features
&self.serializable.compile_info.features
}
fn data_initializers(&self) -> &[OwnedDataInitializer] {
@@ -263,11 +272,11 @@ impl Artifact for JITArtifact {
}
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.serializable.memory_plans
&self.serializable.compile_info.memory_plans
}
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
&self.serializable.table_plans
&self.serializable.compile_info.table_plans
}
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]> {

View File

@@ -0,0 +1,55 @@
use crate::JITEngine;
use wasmer_compiler::{CompilerConfig, Features, Target};
/// The JIT builder
pub struct JIT<'a> {
compiler_config: Option<&'a dyn CompilerConfig>,
target: Option<Target>,
features: Option<Features>,
}
impl<'a> JIT<'a> {
/// Create a new JIT
pub fn new(compiler_config: &'a dyn CompilerConfig) -> Self {
Self {
compiler_config: Some(compiler_config),
target: None,
features: None,
}
}
/// Create a new headless JIT
pub fn headless() -> Self {
Self {
compiler_config: None,
target: None,
features: None,
}
}
/// Set the target
pub fn target(mut self, target: Target) -> Self {
self.target = Some(target);
self
}
/// Set the features
pub fn features(mut self, features: Features) -> Self {
self.features = Some(features);
self
}
/// Build the `JITEngine` for this configuration
pub fn engine(self) -> JITEngine {
let target = self.target.unwrap_or_default();
if let Some(compiler_config) = self.compiler_config {
let features = self
.features
.unwrap_or_else(|| compiler_config.default_features_for_target(&target));
let compiler = compiler_config.compiler();
JITEngine::new(compiler, target, features)
} else {
JITEngine::headless()
}
}
}

View File

@@ -3,7 +3,6 @@
//! Memory management for executable code.
use crate::unwind::UnwindRegistry;
use region;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use std::{cmp, mem};
@@ -150,7 +149,7 @@ impl CodeMemory {
Ok(self
.read_sections
.last_mut()
.ok_or("Can't get last section".to_string())?)
.ok_or_else(|| "Can't get last section".to_string())?)
}
/// Make all allocated memory executable.

View File

@@ -5,12 +5,13 @@ use crate::{CodeMemory, JITArtifact};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use wasm_common::entity::PrimaryMap;
use wasm_common::Features;
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, SignatureIndex};
use wasmer_compiler::{
CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_compiler::Compiler;
use wasmer_compiler::{
CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target,
};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{
ModuleInfo, SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
@@ -20,26 +21,24 @@ use wasmer_runtime::{
#[derive(Clone)]
pub struct JITEngine {
inner: Arc<Mutex<JITEngineInner>>,
tunables: Arc<dyn Tunables + Send + Sync>,
/// The target for the compiler
target: Arc<Target>,
engine_id: EngineId,
}
impl JITEngine {
/// Create a new `JITEngine` with the given config
#[cfg(feature = "compiler")]
pub fn new(
config: Box<dyn CompilerConfig>,
tunables: impl Tunables + 'static + Send + Sync,
) -> Self {
let compiler = config.compiler();
pub fn new(compiler: Box<dyn Compiler + Send>, target: Target, features: Features) -> Self {
Self {
inner: Arc::new(Mutex::new(JITEngineInner {
compiler: Some(compiler),
function_call_trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
features,
})),
tunables: Arc::new(tunables),
target: Arc::new(target),
engine_id: EngineId::default(),
}
}
@@ -57,7 +56,7 @@ impl JITEngine {
///
/// Headless engines can't compile or validate any modules,
/// they just take already processed Modules (via `Module::serialize`).
pub fn headless(tunables: impl Tunables + 'static + Send + Sync) -> Self {
pub fn headless() -> Self {
Self {
inner: Arc::new(Mutex::new(JITEngineInner {
#[cfg(feature = "compiler")]
@@ -65,8 +64,9 @@ impl JITEngine {
function_call_trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
features: Features::default(),
})),
tunables: Arc::new(tunables),
target: Arc::new(Target::default()),
engine_id: EngineId::default(),
}
}
@@ -81,9 +81,9 @@ impl JITEngine {
}
impl Engine for JITEngine {
/// Get the tunables
fn tunables(&self) -> &dyn Tunables {
&*self.tunables
/// The target
fn target(&self) -> &Target {
&self.target
}
/// Register a signature
@@ -109,8 +109,12 @@ impl Engine for JITEngine {
}
/// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError> {
Ok(Arc::new(JITArtifact::new(&self, binary)?))
fn compile(
&self,
binary: &[u8],
tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError> {
Ok(Arc::new(JITArtifact::new(&self, binary, tunables)?))
}
/// Deserializes a WebAssembly module
@@ -121,6 +125,10 @@ impl Engine for JITEngine {
fn id(&self) -> &EngineId {
&self.engine_id
}
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
Arc::new(self.clone())
}
}
/// The inner contents of `JITEngine`
@@ -130,6 +138,8 @@ pub struct JITEngineInner {
compiler: Option<Box<dyn Compiler + Send>>,
/// Pointers to trampoline functions used to enter particular signatures
function_call_trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The features to compile the Wasm module with
features: Features,
/// The code memory is responsible of publishing the compiled
/// functions to memory.
code_memory: CodeMemory,
@@ -151,7 +161,7 @@ impl JITEngineInner {
/// Validate the module
#[cfg(feature = "compiler")]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.compiler()?.validate_module(data)
self.compiler()?.validate_module(self.features(), data)
}
/// Validate the module
@@ -163,6 +173,11 @@ impl JITEngineInner {
))
}
/// The Wasm features
pub fn features(&self) -> &Features {
&self.features
}
/// Allocate custom sections into memory
pub(crate) fn allocate_custom_sections(
&mut self,

View File

@@ -26,6 +26,7 @@
)]
mod artifact;
mod builder;
mod code_memory;
mod engine;
mod link;
@@ -33,6 +34,7 @@ mod serialize;
mod unwind;
pub use crate::artifact::JITArtifact;
pub use crate::builder::JIT;
pub use crate::code_memory::CodeMemory;
pub use crate::engine::JITEngine;
pub use crate::link::link_module;

View File

@@ -47,6 +47,11 @@ fn apply_relocation(
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
write_unaligned(reloc_address as *mut u32, reloc_delta as _);
},
#[cfg(target_pointer_width = "64")]
RelocationKind::X86PCRel8 => unsafe {
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
write_unaligned(reloc_address as *mut u64, reloc_delta);
},
#[cfg(target_pointer_width = "32")]
RelocationKind::X86CallPCRel4 => unsafe {
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);

View File

@@ -1,16 +1,11 @@
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
use wasm_common::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
use wasmer_compiler::{
CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation, SectionIndex,
CompileModuleInfo, CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation,
SectionIndex,
};
use wasmer_engine::SerializableFunctionFrameInfo;
use wasmer_runtime::ModuleInfo;
use wasmer_runtime::{MemoryPlan, TablePlan};
// /// The serializable function data
// #[derive(Serialize, Deserialize)]
@@ -45,10 +40,6 @@ pub struct SerializableCompilation {
#[derive(Serialize, Deserialize)]
pub struct SerializableModule {
pub compilation: SerializableCompilation,
pub features: Features,
pub module: Arc<ModuleInfo>,
pub compile_info: CompileModuleInfo,
pub data_initializers: Box<[OwnedDataInitializer]>,
// Plans for that module
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
}

View File

@@ -15,7 +15,7 @@ wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
object = { version = "0.19", default-features = false, features = ["write"] }
wasmer-object = { path = "../object", version = "1.0.0-alpha.1" }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" }
cfg-if = "0.1"

View File

@@ -4,10 +4,6 @@
use crate::engine::{NativeEngine, NativeEngineInner};
use crate::serialize::ModuleMetadata;
use libloading::{Library, Symbol as LibrarySymbol};
#[cfg(feature = "compiler")]
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
#[cfg(feature = "compiler")]
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
use std::error::Error;
use std::fs::File;
use std::io::{Read, Write};
@@ -20,20 +16,22 @@ use tempfile::NamedTempFile;
use tracing::trace;
use wasm_common::entity::{BoxedSlice, PrimaryMap};
use wasm_common::{
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer,
SignatureIndex, TableIndex,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::{
Architecture, BinaryFormat, CustomSectionProtection, Endianness, ModuleEnvironment,
OperatingSystem, RelocationTarget, Triple,
Compilation, CompileModuleInfo, Compiler, ModuleEnvironment, OperatingSystem, Target, Triple,
};
use wasmer_compiler::{CompileError, Features};
#[cfg(feature = "compiler")]
use wasmer_engine::Engine;
use wasmer_engine::{
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
Tunables,
};
#[cfg(feature = "compiler")]
use wasmer_object::{emit_compilation, emit_data, get_object_for_target, CompilationNamer};
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasmer_runtime::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline};
@@ -93,15 +91,16 @@ impl NativeArtifact {
}
}
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")]
pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> {
/// Generate a compilation
pub fn generate_compilation<'data>(
data: &'data [u8],
compiler: &dyn Compiler,
target: &Target,
features: &Features,
tunables: &dyn Tunables,
) -> Result<(CompileModuleInfo, Compilation, Vec<DataInitializer<'data>>), CompileError> {
let environ = ModuleEnvironment::new();
let mut engine_inner = engine.inner_mut();
let tunables = engine.tunables();
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
.module
.memories
@@ -115,272 +114,73 @@ impl NativeArtifact {
.map(|table_type| tunables.table_plan(*table_type))
.collect();
let compiler = engine_inner.compiler()?;
let compile_info = CompileModuleInfo {
module: Arc::new(translation.module),
features: features.clone(),
memory_plans,
table_plans,
};
// Compile the Module
let compilation = compiler.compile_module(
&translation.module,
&target,
&compile_info,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?;
let function_call_trampolines = compilation.get_function_call_trampolines();
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
Ok((compile_info, compilation, translation.data_initializers))
}
let data_initializers = translation
.data_initializers
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")]
pub fn new(
engine: &NativeEngine,
data: &[u8],
tunables: &dyn Tunables,
) -> Result<Self, CompileError> {
let mut engine_inner = engine.inner_mut();
let target = engine.target();
let (compile_info, compilation, data_initializers) = Self::generate_compilation(
data,
engine_inner.compiler()?,
target,
engine_inner.features(),
tunables,
)?;
let data_initializers = data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
let target = compiler.target();
let target_triple = target.triple().clone();
let obj_binary_format = match target_triple.binary_format {
BinaryFormat::Elf => object::BinaryFormat::Elf,
BinaryFormat::Macho => object::BinaryFormat::MachO,
BinaryFormat::Coff => object::BinaryFormat::Coff,
format => {
return Err(CompileError::Codegen(format!(
"Binary format {} not supported",
format
)))
}
};
let obj_architecture = match target_triple.architecture {
Architecture::X86_64 => object::Architecture::X86_64,
Architecture::Aarch64(_) => object::Architecture::Aarch64,
architecture => {
return Err(CompileError::Codegen(format!(
"Architecture {} not supported",
architecture
)))
}
};
let obj_endianness = match target_triple.endianness() {
Ok(Endianness::Little) => object::Endianness::Little,
Ok(Endianness::Big) => object::Endianness::Big,
Err(e) => {
return Err(CompileError::Codegen(format!(
"Can't detect the endianness for the target: {:?}",
e
)))
}
};
let mut obj = Object::new(obj_binary_format, obj_architecture, obj_endianness);
let function_bodies = compilation.get_function_bodies();
let custom_sections = compilation.get_custom_sections();
let custom_section_relocations = compilation.get_custom_section_relocations();
// We construct the function body lengths
let function_body_lengths = function_bodies
let function_body_lengths = compilation
.get_function_bodies()
.values()
.map(|function_body| function_body.body.len() as u64)
.collect::<PrimaryMap<LocalFunctionIndex, u64>>();
let metadata = ModuleMetadata {
features: compiler.features().clone(),
module: Arc::new(translation.module),
compile_info,
prefix: engine_inner.get_prefix(&data),
data_initializers,
function_body_lengths,
memory_plans,
table_plans,
};
let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?;
let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
let mut metadata_length = vec![0; 10];
let mut writable = &mut metadata_length[..];
let mut metadata_binary = vec![0; 10];
let mut writable = &mut metadata_binary[..];
leb128::write::unsigned(&mut writable, serialized_data.len() as u64)
.expect("Should write number");
metadata_length.extend(serialized_data);
metadata_binary.extend(serialized_data);
let symbol_id = obj.add_symbol(Symbol {
name: b"WASMER_METADATA".to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Data);
obj.add_symbol_data(symbol_id, section_id, &metadata_length, 1);
let function_relocations = compilation.get_relocations();
// Add sections
for (section_index, custom_section) in custom_sections.iter() {
// TODO: We need to rename the sections corresponding to the DWARF information
// to the proper names (like `.eh_frame`)
let section_name = metadata.get_section_name(section_index);
let (section_kind, standard_section) = match custom_section.protection {
CustomSectionProtection::ReadExecute => (SymbolKind::Text, StandardSection::Text),
// TODO: Fix this to be StandardSection::Data
CustomSectionProtection::Read => (SymbolKind::Data, StandardSection::Text),
};
let symbol_id = obj.add_symbol(Symbol {
name: section_name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: section_kind,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(standard_section);
obj.add_symbol_data(symbol_id, section_id, custom_section.bytes.as_slice(), 1);
}
// Add functions
for (function_local_index, function) in function_bodies.into_iter() {
let function_name = metadata.get_function_name(function_local_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add function call trampolines
for (signature_index, function) in function_call_trampolines.into_iter() {
let function_name = metadata.get_function_call_trampoline_name(signature_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add dynamic function trampolines
for (func_index, function) in dynamic_function_trampolines.into_iter() {
let function_name = metadata.get_dynamic_function_trampoline_name(func_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add relocations (function and sections)
let mut all_relocations = Vec::new();
for (function_local_index, relocations) in function_relocations.into_iter() {
let function_name = metadata.get_function_name(function_local_index);
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
all_relocations.push((symbol_id, relocations))
}
for (section_index, relocations) in custom_section_relocations.into_iter() {
let function_name = metadata.get_section_name(section_index);
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
all_relocations.push((symbol_id, relocations))
}
for (symbol_id, relocations) in all_relocations.into_iter() {
let (_symbol_id, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap();
let section_id = obj.section_id(StandardSection::Text);
for r in relocations {
let relocation_address = section_offset + r.offset as u64;
match r.reloc_target {
RelocationTarget::LocalFunc(index) => {
let target_name = metadata.get_function_name(index);
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(to_compile_error)?;
}
RelocationTarget::LibCall(libcall) => {
let libcall_fn_name = libcall.to_function_name().as_bytes();
// We add the symols lazily as we see them
let target_symbol = obj.symbol_id(libcall_fn_name).unwrap_or_else(|| {
obj.add_symbol(Symbol {
name: libcall_fn_name.to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Unknown,
scope: SymbolScope::Unknown,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
})
});
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(to_compile_error)?;
}
RelocationTarget::CustomSection(section_index) => {
let target_name = metadata.get_section_name(section_index);
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(to_compile_error)?;
}
RelocationTarget::JumpTable(_func_index, _jt) => {
// do nothing
}
};
}
}
emit_data(&mut obj, b"WASMER_METADATA", &metadata_binary).map_err(to_compile_error)?;
emit_compilation(&mut obj, compilation, &metadata).map_err(to_compile_error)?;
let filepath = {
let file = tempfile::Builder::new()
@@ -511,7 +311,7 @@ impl NativeArtifact {
let mut finished_functions: PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]> =
PrimaryMap::new();
for (function_local_index, function_len) in metadata.function_body_lengths.iter() {
let function_name = metadata.get_function_name(function_local_index);
let function_name = metadata.get_function_name(&function_local_index);
unsafe {
// We use a fake function signature `fn()` because we just
// want to get the function address.
@@ -530,8 +330,8 @@ impl NativeArtifact {
}
// Retrieve function call trampolines (for all signatures in the module)
for (sig_index, func_type) in metadata.module.signatures.iter() {
let function_name = metadata.get_function_call_trampoline_name(sig_index);
for (sig_index, func_type) in metadata.compile_info.module.signatures.iter() {
let function_name = metadata.get_function_call_trampoline_name(&sig_index);
unsafe {
let trampoline: LibrarySymbol<VMTrampoline> = lib
.get(function_name.as_bytes())
@@ -544,14 +344,15 @@ impl NativeArtifact {
let mut finished_dynamic_function_trampolines: PrimaryMap<
FunctionIndex,
*mut [VMFunctionBody],
> = PrimaryMap::with_capacity(metadata.module.num_imported_funcs);
> = PrimaryMap::with_capacity(metadata.compile_info.module.num_imported_funcs);
for func_index in metadata
.compile_info
.module
.functions
.keys()
.take(metadata.module.num_imported_funcs)
.take(metadata.compile_info.module.num_imported_funcs)
{
let function_name = metadata.get_dynamic_function_trampoline_name(func_index);
let function_name = metadata.get_dynamic_function_trampoline_name(&func_index);
unsafe {
let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib
.get(function_name.as_bytes())
@@ -582,6 +383,7 @@ impl NativeArtifact {
let signatures = {
let signature_registry = engine_inner.signatures();
metadata
.compile_info
.module
.signatures
.values()
@@ -697,15 +499,15 @@ impl NativeArtifact {
impl Artifact for NativeArtifact {
fn module(&self) -> Arc<ModuleInfo> {
self.metadata.module.clone()
self.metadata.compile_info.module.clone()
}
fn module_ref(&self) -> &ModuleInfo {
&self.metadata.module
&self.metadata.compile_info.module
}
fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
Arc::get_mut(&mut self.metadata.module)
Arc::get_mut(&mut self.metadata.compile_info.module)
}
fn register_frame_info(&self) {
@@ -713,7 +515,7 @@ impl Artifact for NativeArtifact {
}
fn features(&self) -> &Features {
&self.metadata.features
&self.metadata.compile_info.features
}
fn data_initializers(&self) -> &[OwnedDataInitializer] {
@@ -721,11 +523,11 @@ impl Artifact for NativeArtifact {
}
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.metadata.memory_plans
&self.metadata.compile_info.memory_plans
}
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
&self.metadata.table_plans
&self.metadata.compile_info.table_plans
}
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]> {

View File

@@ -0,0 +1,56 @@
use crate::NativeEngine;
use wasmer_compiler::{CompilerConfig, Features, Target};
/// The Native builder
pub struct Native<'a> {
compiler_config: Option<&'a dyn CompilerConfig>,
target: Option<Target>,
features: Option<Features>,
}
impl<'a> Native<'a> {
/// Create a new Native
pub fn new(compiler_config: &'a mut dyn CompilerConfig) -> Self {
compiler_config.enable_pic();
Self {
compiler_config: Some(compiler_config),
target: None,
features: None,
}
}
/// Create a new headless Native
pub fn headless() -> Self {
Self {
compiler_config: None,
target: None,
features: None,
}
}
/// Set the target
pub fn target(mut self, target: Target) -> Self {
self.target = Some(target);
self
}
/// Set the features
pub fn features(mut self, features: Features) -> Self {
self.features = Some(features);
self
}
/// Build the `NativeEngine` for this configuration
pub fn engine(self) -> NativeEngine {
let target = self.target.unwrap_or_default();
if let Some(compiler_config) = self.compiler_config {
let features = self
.features
.unwrap_or_else(|| compiler_config.default_features_for_target(&target));
let compiler = compiler_config.compiler();
NativeEngine::new(compiler, target, features)
} else {
NativeEngine::headless()
}
}
}

View File

@@ -5,10 +5,10 @@ use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use wasm_common::FunctionType;
use wasmer_compiler::CompileError;
use wasm_common::{Features, FunctionType};
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_compiler::Compiler;
use wasmer_compiler::{CompileError, Target};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
@@ -16,27 +16,24 @@ use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
#[derive(Clone)]
pub struct NativeEngine {
inner: Arc<Mutex<NativeEngineInner>>,
tunables: Arc<dyn Tunables + Send + Sync>,
/// The target for the compiler
target: Arc<Target>,
engine_id: EngineId,
}
impl NativeEngine {
/// Create a new `NativeEngine` with the given config
#[cfg(feature = "compiler")]
pub fn new(
mut config: Box<dyn CompilerConfig>,
tunables: impl Tunables + 'static + Send + Sync,
) -> Self {
config.enable_pic();
let compiler = config.compiler();
pub fn new(compiler: Box<dyn Compiler + Send>, target: Target, features: Features) -> Self {
Self {
inner: Arc::new(Mutex::new(NativeEngineInner {
compiler: Some(compiler),
trampolines: HashMap::new(),
signatures: SignatureRegistry::new(),
prefixer: None,
features,
})),
tunables: Arc::new(tunables),
target: Arc::new(target),
engine_id: EngineId::default(),
}
}
@@ -54,7 +51,7 @@ impl NativeEngine {
///
/// Headless engines can't compile or validate any modules,
/// they just take already processed Modules (via `Module::serialize`).
pub fn headless(tunables: impl Tunables + 'static + Send + Sync) -> Self {
pub fn headless() -> Self {
Self {
inner: Arc::new(Mutex::new(NativeEngineInner {
#[cfg(feature = "compiler")]
@@ -62,8 +59,9 @@ impl NativeEngine {
trampolines: HashMap::new(),
signatures: SignatureRegistry::new(),
prefixer: None,
features: Features::default(),
})),
tunables: Arc::new(tunables),
target: Arc::new(Target::default()),
engine_id: EngineId::default(),
}
}
@@ -96,9 +94,9 @@ impl NativeEngine {
}
impl Engine for NativeEngine {
/// Get the tunables
fn tunables(&self) -> &dyn Tunables {
&*self.tunables
/// The target
fn target(&self) -> &Target {
&self.target
}
/// Register a signature
@@ -124,8 +122,12 @@ impl Engine for NativeEngine {
}
/// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError> {
Ok(Arc::new(NativeArtifact::new(&self, binary)?))
fn compile(
&self,
binary: &[u8],
tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError> {
Ok(Arc::new(NativeArtifact::new(&self, binary, tunables)?))
}
/// Deserializes a WebAssembly module (binary content of a Shared Object file)
@@ -147,6 +149,10 @@ impl Engine for NativeEngine {
fn id(&self) -> &EngineId {
&self.engine_id
}
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
Arc::new(self.clone())
}
}
/// The inner contents of `NativeEngine`
@@ -154,6 +160,8 @@ pub struct NativeEngineInner {
/// The compiler
#[cfg(feature = "compiler")]
compiler: Option<Box<dyn Compiler + Send>>,
/// The WebAssembly features to use
features: Features,
/// Pointers to trampoline functions used to enter particular signatures
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The signature registry is used mainly to operate with trampolines
@@ -186,10 +194,14 @@ impl NativeEngineInner {
}
}
pub(crate) fn features(&self) -> &Features {
&self.features
}
/// Validate the module
#[cfg(feature = "compiler")]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.compiler()?.validate_module(data)
self.compiler()?.validate_module(self.features(), data)
}
/// Validate the module

View File

@@ -26,10 +26,12 @@
)]
mod artifact;
mod builder;
mod engine;
mod serialize;
pub use crate::artifact::NativeArtifact;
pub use crate::builder::Native;
pub use crate::engine::NativeEngine;
/// Version number of this crate.

View File

@@ -1,41 +1,32 @@
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
use wasmer_compiler::SectionIndex;
use wasmer_runtime::ModuleInfo;
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasm_common::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
use wasmer_compiler::{CompileModuleInfo, SectionIndex};
use wasmer_object::CompilationNamer;
/// Serializable struct that represents the compiled metadata.
#[derive(Serialize, Deserialize, Debug)]
pub struct ModuleMetadata {
pub features: Features,
pub module: Arc<ModuleInfo>,
pub compile_info: CompileModuleInfo,
pub prefix: String,
pub data_initializers: Box<[OwnedDataInitializer]>,
// The function body lengths (used for reverse-locate traps in the function)
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
// Plans for that module
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
}
impl ModuleMetadata {
impl CompilationNamer for ModuleMetadata {
/// Gets the function name given a local function index
pub fn get_function_name(&self, index: LocalFunctionIndex) -> String {
fn get_function_name(&self, index: &LocalFunctionIndex) -> String {
format!("wasmer_function_{}_{}", self.prefix, index.index())
}
/// Gets the section name given a section index
pub fn get_section_name(&self, index: SectionIndex) -> String {
fn get_section_name(&self, index: &SectionIndex) -> String {
format!("wasmer_section_{}_{}", self.prefix, index.index())
}
/// Gets the function call trampoline name given a signature index
pub fn get_function_call_trampoline_name(&self, index: SignatureIndex) -> String {
fn get_function_call_trampoline_name(&self, index: &SignatureIndex) -> String {
format!(
"wasmer_trampoline_function_call_{}_{}",
self.prefix,
@@ -44,7 +35,7 @@ impl ModuleMetadata {
}
/// Gets the dynamic function trampoline name given a function index
pub fn get_dynamic_function_trampoline_name(&self, index: FunctionIndex) -> String {
fn get_dynamic_function_trampoline_name(&self, index: &FunctionIndex) -> String {
format!(
"wasmer_trampoline_dynamic_function_{}_{}",
self.prefix,

View File

@@ -6,7 +6,7 @@ use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasm_common::FunctionType;
use wasmer_compiler::CompileError;
use wasmer_compiler::{CompileError, Target};
use wasmer_runtime::{VMSharedSignatureIndex, VMTrampoline};
/// A unimplemented Wasmer `Engine`.
@@ -16,8 +16,8 @@ use wasmer_runtime::{VMSharedSignatureIndex, VMTrampoline};
///
/// The product that an `Engine` produces and consumes is the [`Artifact`].
pub trait Engine {
/// Get the tunables
fn tunables(&self) -> &dyn Tunables;
/// Gets the target
fn target(&self) -> &Target;
/// Register a signature
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex;
@@ -32,7 +32,11 @@ pub trait Engine {
fn validate(&self, binary: &[u8]) -> Result<(), CompileError>;
/// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError>;
fn compile(
&self,
binary: &[u8],
tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError>;
/// Deserializes a WebAssembly module
///
@@ -60,6 +64,9 @@ pub trait Engine {
/// comparing two trait objects unsafely relies on implementation details
/// of trait representation.
fn id(&self) -> &EngineId;
/// Clone the engine
fn cloned(&self) -> Arc<dyn Engine + Send + Sync>;
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]

View File

@@ -11,10 +11,10 @@ use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunables for an engine
pub trait Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType`
/// Construct a `MemoryPlan` for the provided `MemoryType`
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan;
/// Get a `TablePlan` for the provided `TableType`
/// Construct a `TablePlan` for the provided `TableType`
fn table_plan(&self, table: TableType) -> TablePlan;
/// Create a memory given a memory type

20
lib/object/Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[package]
name = "wasmer-object"
version = "1.0.0-alpha.1"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer Native Object generator"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/wasmerio/wasmer"
readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false, features = [
"std",
"translator"
] }
object = { version = "0.19", default-features = false, features = ["write"] }
thiserror = "1.0"

0
lib/object/README.md Normal file
View File

20
lib/object/src/error.rs Normal file
View File

@@ -0,0 +1,20 @@
use object::write::Error as ObjectWriteError;
use thiserror::Error;
/// The Object error can occur when creating an object file
/// from a `Compilation`.
#[derive(Error, Debug)]
pub enum ObjectError {
/// The object was provided a not-supported binary format
#[error("Binary format {0} not supported")]
UnsupportedBinaryFormat(String),
/// The object was provided a not-supported architecture
#[error("Architecture {0} not supported")]
UnsupportedArchitecture(String),
/// The object was provided an unknown endianness
#[error("Unknown Endianness")]
UnknownEndianness,
/// The object was provided a not-supported architecture
#[error("Error when writing the object: {0}")]
Write(#[from] ObjectWriteError),
}

28
lib/object/src/lib.rs Normal file
View File

@@ -0,0 +1,28 @@
//! Object creator for Wasm Compilations.
//!
//! Given a compilation result (this is, the result when calling `Compiler::compile_module`)
//! this exposes functions to create an Object file for a given target.
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
mod error;
mod module;
pub use crate::error::ObjectError;
pub use crate::module::{emit_compilation, emit_data, get_object_for_target, CompilationNamer};

262
lib/object/src/module.rs Normal file
View File

@@ -0,0 +1,262 @@
use crate::error::ObjectError;
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
use wasm_common::{FunctionIndex, LocalFunctionIndex, SignatureIndex};
use wasmer_compiler::{
Architecture, BinaryFormat, Compilation, CustomSectionProtection, Endianness, RelocationTarget,
SectionIndex, Triple,
};
/// Returns names for the compilation types (functions, sections and trampolines)
pub trait CompilationNamer {
/// Gets the function name given a local function index
fn get_function_name(&self, index: &LocalFunctionIndex) -> String;
/// Gets the section name given a section index
fn get_section_name(&self, index: &SectionIndex) -> String;
/// Gets the function call trampoline name given a signature index
fn get_function_call_trampoline_name(&self, index: &SignatureIndex) -> String;
/// Gets the dynamic function trampoline name given a function index
fn get_dynamic_function_trampoline_name(&self, index: &FunctionIndex) -> String;
}
/// Create an object for a given target [`Triple`]
pub fn get_object_for_target(triple: &Triple) -> Result<Object, ObjectError> {
let obj_binary_format = match triple.binary_format {
BinaryFormat::Elf => object::BinaryFormat::Elf,
BinaryFormat::Macho => object::BinaryFormat::MachO,
BinaryFormat::Coff => object::BinaryFormat::Coff,
binary_format => {
return Err(ObjectError::UnsupportedBinaryFormat(format!(
"{}",
binary_format
)));
}
};
let obj_architecture = match triple.architecture {
Architecture::X86_64 => object::Architecture::X86_64,
Architecture::Aarch64(_) => object::Architecture::Aarch64,
architecture => {
return Err(ObjectError::UnsupportedArchitecture(format!(
"{}",
architecture
)));
}
};
let obj_endianness = match triple.endianness() {
Ok(Endianness::Little) => object::Endianness::Little,
Ok(Endianness::Big) => object::Endianness::Big,
Err(_) => {
return Err(ObjectError::UnknownEndianness);
}
};
Ok(Object::new(
obj_binary_format,
obj_architecture,
obj_endianness,
))
}
/// Emit data in into an object
pub fn emit_data(obj: &mut Object, name: &[u8], data: &[u8]) -> Result<(), ObjectError> {
let symbol_id = obj.add_symbol(Symbol {
name: name.to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Data);
obj.add_symbol_data(symbol_id, section_id, &data, 1);
Ok(())
}
/// Emit the compilation into an object
pub fn emit_compilation(
obj: &mut Object,
compilation: Compilation,
namer: &impl CompilationNamer,
) -> Result<(), ObjectError> {
let function_bodies = compilation.get_function_bodies();
let function_relocations = compilation.get_relocations();
let custom_sections = compilation.get_custom_sections();
let custom_section_relocations = compilation.get_custom_section_relocations();
let function_call_trampolines = compilation.get_function_call_trampolines();
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
// Add sections
for (section_index, custom_section) in custom_sections.iter() {
// TODO: We need to rename the sections corresponding to the DWARF information
// to the proper names (like `.eh_frame`)
let section_name = namer.get_section_name(&section_index);
let (section_kind, standard_section) = match custom_section.protection {
CustomSectionProtection::ReadExecute => (SymbolKind::Text, StandardSection::Text),
// TODO: Fix this to be StandardSection::Data
CustomSectionProtection::Read => (SymbolKind::Data, StandardSection::Text),
};
let symbol_id = obj.add_symbol(Symbol {
name: section_name.into_bytes(),
value: 0,
size: 0,
kind: section_kind,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(standard_section);
obj.add_symbol_data(symbol_id, section_id, custom_section.bytes.as_slice(), 1);
}
// Add functions
for (function_local_index, function) in function_bodies.into_iter() {
let function_name = namer.get_function_name(&function_local_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.into_bytes(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add function call trampolines
for (signature_index, function) in function_call_trampolines.into_iter() {
let function_name = namer.get_function_call_trampoline_name(&signature_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.into_bytes(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add dynamic function trampolines
for (func_index, function) in dynamic_function_trampolines.into_iter() {
let function_name = namer.get_dynamic_function_trampoline_name(&func_index);
let symbol_id = obj.add_symbol(Symbol {
name: function_name.into_bytes(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let section_id = obj.section_id(StandardSection::Text);
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
}
// Add relocations (function and sections)
let mut all_relocations = Vec::new();
for (function_local_index, relocations) in function_relocations.into_iter() {
let function_name = namer.get_function_name(&function_local_index);
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
all_relocations.push((symbol_id, relocations))
}
for (section_index, relocations) in custom_section_relocations.into_iter() {
let section_name = namer.get_section_name(&section_index);
let symbol_id = obj.symbol_id(section_name.as_bytes()).unwrap();
all_relocations.push((symbol_id, relocations))
}
for (symbol_id, relocations) in all_relocations.into_iter() {
let (_symbol_id, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap();
let section_id = obj.section_id(StandardSection::Text);
for r in relocations {
let relocation_address = section_offset + r.offset as u64;
match r.reloc_target {
RelocationTarget::LocalFunc(index) => {
let target_name = namer.get_function_name(&index);
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(ObjectError::Write)?;
}
RelocationTarget::LibCall(libcall) => {
let libcall_fn_name = libcall.to_function_name().as_bytes();
// We add the symols lazily as we see them
let target_symbol = obj.symbol_id(libcall_fn_name).unwrap_or_else(|| {
obj.add_symbol(Symbol {
name: libcall_fn_name.to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Unknown,
scope: SymbolScope::Unknown,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
})
});
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(ObjectError::Write)?;
}
RelocationTarget::CustomSection(section_index) => {
let target_name = namer.get_section_name(&section_index);
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
obj.add_relocation(
section_id,
Relocation {
offset: relocation_address,
size: 32, // FIXME for all targets
kind: RelocationKind::PltRelative,
encoding: RelocationEncoding::X86Branch,
// size: 64, // FIXME for all targets
// kind: RelocationKind::Absolute,
// encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: r.addend,
},
)
.map_err(ObjectError::Write)?;
}
RelocationTarget::JumpTable(_func_index, _jt) => {
// do nothing
}
};
}
}
Ok(())
}

View File

@@ -155,7 +155,7 @@ impl ModuleInfo {
.iter()
.filter_map(|(_name, export_index)| match export_index {
ExportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap();
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(signature.clone()).unwrap();
Some(func_type.clone())
}
@@ -169,20 +169,20 @@ impl ModuleInfo {
let iter = self.exports.iter().map(move |(name, export_index)| {
let extern_type = match export_index {
ExportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap();
let func_type = self.signatures.get(signature.clone()).unwrap();
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone())
}
ExportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap();
let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type)
}
ExportIndex::Memory(i) => {
let memory_type = self.memories.get(i.clone()).unwrap();
let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type)
}
ExportIndex::Global(i) => {
let global_type = self.globals.get(i.clone()).unwrap();
let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type)
}
};
@@ -202,20 +202,20 @@ impl ModuleInfo {
.map(move |((module, field, _), import_index)| {
let extern_type = match import_index {
ImportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap();
let func_type = self.signatures.get(signature.clone()).unwrap();
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone())
}
ImportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap();
let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type)
}
ImportIndex::Memory(i) => {
let memory_type = self.memories.get(i.clone()).unwrap();
let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type)
}
ImportIndex::Global(i) => {
let global_type = self.globals.get(i.clone()).unwrap();
let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type)
}
};

View File

@@ -118,7 +118,7 @@ mod test_vmfunction_body {
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
pub enum VMFunctionKind {
/// A function is static when it's address matches the signature:
/// A function is static when its address matches the signature:
/// (vmctx, vmctx, arg1, arg2...) -> (result1, result2, ...)
///
/// This is the default for functions that are defined:
@@ -126,7 +126,7 @@ pub enum VMFunctionKind {
/// 2. In the WebAssembly file
Static,
/// A function is dynamic when it's address matches the signature:
/// A function is dynamic when its address matches the signature:
/// (ctx, &[Type]) -> Vec<Type>
///
/// This is the default for functions that are defined:

View File

@@ -21,13 +21,19 @@ mod utils;
use crate::syscalls::*;
pub use crate::state::{Fd, WasiFile, WasiFs, WasiFsError, WasiState, ALL_RIGHTS, VIRTUAL_ROOT_FD};
pub use crate::state::{
Fd, WasiFile, WasiFs, WasiFsError, WasiState, WasiStateCreationError, ALL_RIGHTS,
VIRTUAL_ROOT_FD,
};
pub use crate::syscalls::types;
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
use thiserror::Error;
use wasmer::{imports, Function, ImportObject, Memory, Module, Store};
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
/// This is returned in `RuntimeError`.
@@ -45,14 +51,88 @@ pub enum WasiError {
#[derive(Clone)]
pub struct WasiEnv {
state: Arc<Mutex<WasiState>>,
memory: Option<Arc<Memory>>,
memory: Arc<WasiMemory>,
}
/// Wrapper type around `Memory` used to delay initialization of the memory.
///
/// The `initialized` field is used to indicate if it's safe to read `memory` as `Memory`.
///
/// The `mutate_lock` is used to prevent access from multiple threads during initialization.
struct WasiMemory {
initialized: AtomicBool,
memory: UnsafeCell<MaybeUninit<Memory>>,
mutate_lock: Mutex<()>,
}
impl WasiMemory {
fn new() -> Self {
Self {
initialized: AtomicBool::new(false),
memory: UnsafeCell::new(MaybeUninit::zeroed()),
mutate_lock: Mutex::new(()),
}
}
/// Initialize the memory, making it safe to read from.
///
/// Returns whether or not the set was successful. If the set failed then
/// the memory has already been initialized.
fn set_memory(&self, memory: Memory) -> bool {
// synchronize it
let _guard = self.mutate_lock.lock();
if self.initialized.load(Ordering::Acquire) {
return false;
}
unsafe {
let ptr = self.memory.get();
let mem_inner: &mut MaybeUninit<Memory> = &mut *ptr;
mem_inner.as_mut_ptr().write(memory);
}
self.initialized.store(true, Ordering::Release);
true
}
/// Returns `None` if the memory has not been initialized yet.
/// Otherwise returns the memory that was used to initialize it.
fn get_memory(&self) -> Option<&Memory> {
// Based on normal usage, `Relaxed` is fine...
// TODO: investigate if it's possible to use the API in a way where `Relaxed`
// is not fine
if self.initialized.load(Ordering::Relaxed) {
unsafe {
let maybe_mem = self.memory.get();
Some(&*(*maybe_mem).as_ptr())
}
} else {
None
}
}
}
impl Drop for WasiMemory {
fn drop(&mut self) {
if self.initialized.load(Ordering::Acquire) {
unsafe {
// We want to get the internal value in memory, so we need to consume
// the `UnsafeCell` and assume the `MapbeInit` is initialized, but because
// we only have a `&mut self` we can't do this directly, so we swap the data
// out so we can drop it (via `assume_init`).
let mut maybe_uninit = UnsafeCell::new(MaybeUninit::zeroed());
std::mem::swap(&mut self.memory, &mut maybe_uninit);
maybe_uninit.into_inner().assume_init();
}
}
}
}
impl WasiEnv {
pub fn new(state: WasiState) -> Self {
Self {
state: Arc::new(Mutex::new(state)),
memory: None,
memory: Arc::new(WasiMemory::new()),
}
}
@@ -66,8 +146,8 @@ impl WasiEnv {
}
/// Set the memory
pub fn set_memory(&mut self, memory: Arc<Memory>) {
self.memory = Some(memory);
pub fn set_memory(&mut self, memory: Memory) -> bool {
self.memory.set_memory(memory)
}
/// Get the WASI state
@@ -82,14 +162,14 @@ impl WasiEnv {
/// Get a reference to the memory
pub fn memory(&self) -> &Memory {
self.memory.as_ref().expect("The expected Memory is not attached to the `WasiEnv`. Did you forgot to call wasi_env.set_memory(...)?")
self.memory.get_memory().expect("The expected Memory is not attached to the `WasiEnv`. Did you forgot to call wasi_env.set_memory(...)?")
}
pub(crate) fn get_memory_and_wasi_state(
&mut self,
_mem_index: u32,
) -> (&Memory, MutexGuard<WasiState>) {
let memory = self.memory.as_ref().unwrap();
let memory = self.memory.get_memory().unwrap();
let state = self.state.lock().unwrap();
(memory, state)
}

View File

@@ -20,7 +20,7 @@ pub(crate) fn create_wasi_state(program_name: &str) -> WasiStateBuilder {
///
/// Usage:
/// ```no_run
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
/// # fn main() -> Result<(), WasiStateCreationError> {
/// let mut state_builder = WasiState::new("wasi-prog-name");
/// state_builder
@@ -196,7 +196,7 @@ impl WasiStateBuilder {
/// Usage:
///
/// ```no_run
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
/// # fn main() -> Result<(), WasiStateCreationError> {
/// WasiState::new("program_name")
/// .preopen(|p| p.directory("src").read(true).write(true).create(true))?

View File

@@ -1452,7 +1452,7 @@ impl WasiFs {
/// Usage:
///
/// ```no_run
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
/// # fn main() -> Result<(), WasiStateCreationError> {
/// WasiState::new("program_name")
/// .env(b"HOME", "/home/home".to_string())

View File

@@ -1,8 +1,10 @@
/// types for use in the WASI filesystem
use crate::syscalls::types::*;
use serde::{de, Deserialize, Serialize};
use std::any::Any;
#[cfg(unix)]
use std::convert::TryInto;
use std::fmt;
use std::{
fs,
io::{self, Read, Seek, Write},
@@ -148,7 +150,7 @@ impl WasiFsError {
/// This trait relies on your file closing when it goes out of scope via `Drop`
#[typetag::serde(tag = "type")]
pub trait WasiFile: std::fmt::Debug + Send + Write + Read + Seek {
pub trait WasiFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable {
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
fn last_accessed(&self) -> __wasi_timestamp_t;
@@ -210,6 +212,40 @@ pub trait WasiFile: std::fmt::Debug + Send + Write + Read + Seek {
}
}
// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 .
/// Trait needed to get downcasting from `WasiFile` to work.
pub trait Upcastable {
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any;
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any;
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + fmt::Debug + 'static> Upcastable for T {
#[inline]
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any {
self
}
#[inline]
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any {
self
}
#[inline]
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl dyn WasiFile + 'static {
#[inline]
pub fn downcast_ref<T: 'static>(self: &'_ Self) -> Option<&'_ T> {
self.upcast_any_ref().downcast_ref::<T>()
}
#[inline]
pub fn downcast_mut<T: 'static>(self: &'_ mut Self) -> Option<&'_ mut T> {
self.upcast_any_mut().downcast_mut::<T>()
}
}
#[derive(Debug, Clone)]
pub enum PollEvent {
/// Data available to read

View File

@@ -43,10 +43,7 @@ pub use crate::indexes::{
LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
SignatureIndex, TableIndex,
};
pub use crate::native::{
Func, HostFunction, NativeWasmType, ValueType, WasmExternType, WasmTypeList, WithEnv,
WithoutEnv,
};
pub use crate::native::{NativeWasmType, ValueType};
pub use crate::r#ref::{ExternRef, HostInfo, HostRef};
pub use crate::units::{Bytes, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE};
pub use crate::values::Value;

View File

@@ -1,10 +1,8 @@
//! This module permits to create native functions
//! easily in Rust, thanks to its advanced typing system.
use crate::types::{FunctionType, Type};
use crate::types::Type;
use crate::values::Value;
use std::convert::Infallible;
use std::marker::PhantomData;
/// `NativeWasmType` represents a native Wasm type.
/// It uses the Rust Type system to automatically detect the
@@ -154,53 +152,6 @@ mod test_native_type {
}
}
/// A trait to represent a wasm extern type.
pub unsafe trait WasmExternType: Copy
where
Self: Sized,
{
/// 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;
}
macro_rules! wasm_extern_type {
($type:ty => $native_type:ty) => {
#[allow(clippy::use_self)]
unsafe impl WasmExternType for $type {
type Native = $native_type;
#[inline]
fn from_native(native: Self::Native) -> Self {
native as _
}
#[inline]
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);
// wasm_extern_type!(u128 => i128);
// wasm_extern_type!(i128 => i128);
// pub trait IntegerAtomic
// where
// Self: Sized
@@ -243,514 +194,3 @@ macro_rules! impl_value_type_for {
}
impl_value_type_for!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
/// Represents a list of WebAssembly values.
pub trait WasmTypeList {
/// CStruct type.
type CStruct;
/// Array of return values.
type Array: AsMut<[i128]>;
/// Construct `Self` based on an array of returned values.
fn from_array(array: Self::Array) -> Self;
/// Transforms Rust values into an Array
fn into_array(self) -> Self::Array;
/// Generates an empty array that will hold the returned values of
/// the WebAssembly function.
fn empty_array() -> Self::Array;
/// Transforms C values into Rust values.
fn from_c_struct(c_struct: Self::CStruct) -> Self;
/// Transforms Rust values into C values.
fn into_c_struct(self) -> Self::CStruct;
/// Get types of the current values.
fn wasm_types() -> &'static [Type];
}
/// Represents a TrapEarly type.
pub trait TrapEarly<Rets>
where
Rets: WasmTypeList,
{
/// The error type for this trait.
type Error: Send + 'static;
/// Get returns or error result.
fn report(self) -> Result<Rets, Self::Error>;
}
impl<Rets> TrapEarly<Rets> for Rets
where
Rets: WasmTypeList,
{
type Error = Infallible;
fn report(self) -> Result<Self, Infallible> {
Ok(self)
}
}
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
where
Rets: WasmTypeList,
E: Send + 'static,
{
type Error = E;
fn report(self) -> Self {
self
}
}
/// Empty trait to specify the kind of `HostFunction`: With or
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
/// `ImplicitVmCtx` structures.
///
/// 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.
#[doc(hidden)]
pub trait HostFunctionKind {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` doesn't take an Environment
pub struct WithEnv {}
impl HostFunctionKind for WithEnv {}
/// An empty struct to help Rust typing to determine
/// when a `HostFunction` takes an Environment
pub struct WithoutEnv {}
impl HostFunctionKind for WithoutEnv {}
/// Represents a function that can be converted to a `vm::Func`
/// (function pointer) that can be called within WebAssembly.
pub trait HostFunction<Args, Rets, Kind, T>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
T: Sized,
Self: Sized,
{
/// Convert to function pointer.
fn to_raw(self) -> *const FunctionBody;
}
#[repr(transparent)]
pub struct FunctionBody(*mut u8);
/// Represents a function that can be used by WebAssembly.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Func<Args = (), Rets = ()> {
address: *const FunctionBody,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for Func<Args, Rets> {}
impl<Args, Rets> Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
/// Creates a new `Func`.
pub fn new<F, T, E>(func: F) -> Self
where
F: HostFunction<Args, Rets, T, E>,
T: HostFunctionKind,
E: Sized,
{
Self {
address: func.to_raw(),
_phantom: PhantomData,
}
}
/// Get the type of the Func
pub fn ty(&self) -> FunctionType {
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
}
/// Get the address of the Func
pub fn address(&self) -> *const FunctionBody {
self.address
}
}
impl WasmTypeList for Infallible {
type CStruct = Self;
type Array = [i128; 0];
fn from_array(_: Self::Array) -> Self {
unreachable!()
}
fn into_array(self) -> Self::Array {
[]
}
fn empty_array() -> Self::Array {
unreachable!()
}
fn from_c_struct(_: Self::CStruct) -> Self {
unreachable!()
}
fn into_c_struct(self) -> Self::CStruct {
unreachable!()
}
fn wasm_types() -> &'static [Type] {
&[]
}
// #[allow(non_snake_case)]
// unsafe fn call<Rets>(
// self,
// ) -> Result<Rets>
// where
// Rets: WasmTypeList,
// {
// unreachable!()
// }
}
macro_rules! impl_traits {
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
/// Struct for typed funcs.
#[repr($repr)]
pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
where
$( $x: WasmExternType ),*;
#[allow(unused_parens, dead_code)]
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
where
$( $x: WasmExternType ),*
{
type CStruct = $struct_name<$( $x ),*>;
type Array = [i128; count_idents!( $( $x ),* )];
fn from_array(array: Self::Array) -> Self {
#[allow(non_snake_case)]
let [ $( $x ),* ] = array;
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
}
fn into_array(self) -> Self::Array {
#[allow(non_snake_case)]
let ( $( $x ),* ) = self;
[ $( WasmExternType::to_native($x).to_binary() ),* ]
}
fn empty_array() -> Self::Array {
[0; count_idents!( $( $x ),* )]
}
fn from_c_struct(c_struct: Self::CStruct) -> Self {
#[allow(non_snake_case)]
let $struct_name ( $( $x ),* ) = c_struct;
( $( WasmExternType::from_native($x) ),* )
}
#[allow(unused_parens, non_snake_case)]
fn into_c_struct(self) -> Self::CStruct {
let ( $( $x ),* ) = self;
$struct_name ( $( WasmExternType::to_native($x) ),* )
}
fn wasm_types() -> &'static [Type] {
&[$( $x::Native::WASM_TYPE ),*]
}
}
#[allow(unused_parens)]
impl< $( $x, )* Rets, FN > HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
FN: Fn($( $x , )*) -> Rets + 'static + Send
{
#[allow(non_snake_case)]
fn to_raw(self) -> *const FunctionBody {
// unimplemented!("");
extern fn wrap<$( $x, )* Rets, FN>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct
where
Rets: WasmTypeList,
$($x: WasmExternType,)*
FN: Fn( $( $x ),* ) -> Rets + 'static
{
// println!("WRAP");
// println!("Struct {:?}", (($( $x ),*) as WasmTypeList).into_c_struct());
// $( println!("X: {:?}", $x); )*
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
f( $( WasmExternType::from_native($x) ),* ).into_c_struct()
}
wrap::<$( $x, )* Rets, Self> as *const FunctionBody
// extern fn wrap<$( $x: WasmExternType, )* Rets>(a: &dyn Any, b: &dyn Any, $($x: $x, )* ) -> Rets::CStruct
// where
// Rets: WasmTypeList
// {
// println!("WRAP");
// let f: &fn( &dyn Any, &dyn Any, $( $x ),* ) -> Rets = unsafe { std::mem::transmute(&()) };
// f( a, b, $( $x ),* ).into_c_struct()
// }
// wrap::<$( $x, )* Rets> as *const FunctionBody
// extern fn wrap<$( $x, )* Rets, FN>(
// $($x: <$x as WasmExternType>::Native , )*
// ) -> Rets::CStruct
// where
// $( $x: WasmExternType, )*
// Rets: WasmTypeList,
// FN: Fn($( $x, )*) -> Rets::CStruct,
// {
// // let self_pointer = wrap::<$( $x, )* Rets, FN> as *const FunctionBody;
// let f: &FN = unsafe {
// std::mem::transmute(&())
// };
// f($( $x, )*)
// }
// unimplemented!("");
// extern fn wrap<Args, Rets>(
// env: &FuncEnv,
// args: Args::Array,
// returns: Rets::Array
// )
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// {
// let self_pointer = wrap::<Args, Rets> as *const FunctionBody;
// self_pointer($( $x , )*);
// }
// unimplemented!("");
// $( $x: WasmExternType, )*
// Rets: WasmTypeList,
// Trap: TrapEarly<Rets>,
// FN: Fn($( $x, )*) -> Trap,
// {
// let x = |args: <(i32, i32) as WasmTypeList>::Array, rets: &mut <(i32, i32) as WasmTypeList>::Array| {
// let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _);
// rets[0] = result.0 as _;
// rets[1] = result.1 as _;
// };
// &self as *const _ as *const FunctionBody
// let x: *const FunctionBody = unsafe { std::mem::transmute(self) };
// unimplemented!("");
}
}
#[allow(unused_parens)]
impl< $( $x, )* Rets, FN, T > HostFunction<( $( $x ),* ), Rets, WithEnv, T> for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
T: Sized,
FN: Fn(&mut T, $( $x , )*) -> Rets + 'static + Send
{
#[allow(non_snake_case)]
fn to_raw(self) -> *const FunctionBody {
extern fn wrap<$( $x, )* Rets, FN, T>( ctx: &mut T, $($x: $x::Native, )* ) -> Rets::CStruct
where
Rets: WasmTypeList,
$($x: WasmExternType,)*
T: Sized,
FN: Fn(&mut T, $( $x ),* ) -> Rets + 'static
{
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
f(ctx, $( WasmExternType::from_native($x) ),* ).into_c_struct()
}
wrap::<$( $x, )* Rets, Self, T> as *const FunctionBody
}
}
};
}
macro_rules! count_idents {
( $($idents:ident),* ) => {{
#[allow(dead_code, non_camel_case_types)]
enum Idents { $($idents,)* __CountIdentsLast }
const COUNT: usize = Idents::__CountIdentsLast as usize;
COUNT
}};
}
impl_traits!([C] S0,);
impl_traits!([transparent] S1, A1);
impl_traits!([C] S2, A1, A2);
impl_traits!([C] S3, A1, A2, A3);
impl_traits!([C] S4, A1, A2, A3, A4);
impl_traits!([C] S5, A1, A2, A3, A4, A5);
impl_traits!([C] S6, A1, A2, A3, A4, A5, A6);
impl_traits!([C] S7, A1, A2, A3, A4, A5, A6, A7);
impl_traits!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
impl_traits!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_traits!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_traits!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_traits!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_traits!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_traits!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_traits!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
// impl_traits!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
// impl_traits!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
// impl_traits!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
// impl_traits!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
// impl_traits!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
// impl_traits!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);
#[cfg(test)]
mod test_wasm_type_list {
use super::*;
use crate::types::Type;
// WasmTypeList
#[test]
fn test_simple_values() {
// Simple values
assert_eq!(<i32>::wasm_types(), [Type::I32]);
assert_eq!(<i64>::wasm_types(), [Type::I64]);
assert_eq!(<f32>::wasm_types(), [Type::F32]);
assert_eq!(<f64>::wasm_types(), [Type::F64]);
// Multi values
assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]);
assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]);
assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]);
assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]);
// Mixed values
// assert_eq!(<(i32, i64, f32, f64)>::wasm_types(), [Type::I32, Type::I64, Type::F32, Type::F64]);
}
#[test]
fn test_empty_array() {
assert_eq!(<()>::empty_array().len(), 0);
assert_eq!(<i32>::empty_array().len(), 1);
assert_eq!(<(i32, i64)>::empty_array().len(), 2);
}
// #[test]
// fn test_from_array() {
// assert_eq!(<()>::from_array([]), ());
// assert_eq!(<(i32)>::from_array([1]), (1));
// assert_eq!(<(i32, i32)>::from_array([1, 1]), (1, 1));
// // This doesn't work
// // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64));
// }
// #[test]
// fn test_into_array() {
// assert_eq!(().into_array(), []);
// assert_eq!((1).into_array(), [1]);
// assert_eq!((1, 2).into_array(), [1, 2]);
// assert_eq!((1, 2, 3).into_array(), [1, 2, 3]);
// // This doesn't work
// // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64));
// }
#[test]
fn test_into_c_struct() {
// assert_eq!(<()>::into_c_struct(), &[]);
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod test_func {
use super::*;
use crate::types::Type;
use std::ptr;
// WasmTypeList
fn func() {}
fn func__i32() -> i32 {
0
}
fn func_i32(_a: i32) {}
fn func_i32__i32(a: i32) -> i32 {
a * 2
}
fn func_i32_i32__i32(a: i32, b: i32) -> i32 {
a + b
}
fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) {
(a, b)
}
fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) {
(b, a)
}
#[test]
fn test_function_types() {
assert_eq!(Func::new(func).ty(), FunctionType::new(vec![], vec![]));
assert_eq!(
Func::new(func__i32).ty(),
FunctionType::new(vec![], vec![Type::I32])
);
assert_eq!(
Func::new(func_i32).ty(),
FunctionType::new(vec![Type::I32], vec![])
);
assert_eq!(
Func::new(func_i32__i32).ty(),
FunctionType::new(vec![Type::I32], vec![Type::I32])
);
assert_eq!(
Func::new(func_i32_i32__i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
);
assert_eq!(
Func::new(func_i32_i32__i32_i32).ty(),
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
);
assert_eq!(
Func::new(func_f32_i32__i32_f32).ty(),
FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32])
);
}
#[test]
fn test_function_pointer() {
let f = Func::new(func_i32__i32);
let function =
unsafe { std::mem::transmute::<*const FunctionBody, fn(usize, i32) -> i32>(f.address) };
assert_eq!(function(0, 3), 6);
}
#[test]
fn test_function_call() {
let f = Func::new(func_i32__i32);
let x = |args: <(i32, i32) as WasmTypeList>::Array,
rets: &mut <(i32, i32) as WasmTypeList>::Array| {
let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _);
rets[0] = result.0 as _;
rets[1] = result.1 as _;
};
let mut rets = <(i64, i64)>::empty_array();
x([20, 10], &mut rets);
// panic!("Rets: {:?}",rets);
let mut rets = <(i64)>::empty_array();
// let result = f.call([1], &mut rets);
// assert_eq!(result.is_err(), true);
}
}

View File

@@ -45,7 +45,7 @@ impl Compile {
.fold(CpuFeature::set(), |a, b| a | b);
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
// usage
features = features | CpuFeature::SSE2;
features |= CpuFeature::SSE2;
Target::new(target_triple.clone(), features)
})
.unwrap_or_default();

View File

@@ -41,7 +41,7 @@ impl Config {
/// Runs logic for the `config` subcommand
pub fn execute(&self) -> Result<()> {
self.inner_execute()
.context(format!("failed to retrieve the wasmer config"))
.context("failed to retrieve the wasmer config".to_string())
}
fn inner_execute(&self) -> Result<()> {
let key = "WASMER_DIR";
@@ -64,7 +64,7 @@ impl Config {
println!("exec_prefix={}", bindir);
println!("includedir={}", includedir);
println!("libdir={}", libdir);
println!("");
println!();
println!("Name: wasmer");
println!("Description: The Wasmer library for running WebAssembly");
println!("Version: {}", VERSION);

View File

@@ -5,7 +5,6 @@ use crate::warning;
use anyhow::{anyhow, Context, Result};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use wasmer::*;
#[cfg(feature = "cache")]
use wasmer_cache::{Cache, FileSystemCache, WasmHash};
@@ -79,10 +78,10 @@ impl Run {
format!(
"failed to run `{}`{}",
self.path.display(),
if compilers.len() > 0 {
""
} else {
if compilers.is_empty() {
" (no compilers enabled)"
} else {
""
}
)
})
@@ -173,9 +172,8 @@ impl Run {
#[cfg(feature = "native")]
{
if wasmer_engine_native::NativeArtifact::is_deserializable(&contents) {
let tunables = Tunables::default();
let engine = wasmer_engine_native::NativeEngine::headless(tunables);
let store = Store::new(Arc::new(engine));
let engine = wasmer_engine_native::Native::headless().engine();
let store = Store::new(&engine);
let module = unsafe { Module::deserialize_from_file(&store, &self.path)? };
return Ok(module);
}
@@ -183,9 +181,8 @@ impl Run {
#[cfg(feature = "jit")]
{
if wasmer_engine_jit::JITArtifact::is_deserializable(&contents) {
let tunables = Tunables::default();
let engine = wasmer_engine_jit::JITEngine::headless(tunables);
let store = Store::new(Arc::new(engine));
let engine = wasmer_engine_jit::JIT::headless().engine();
let store = Store::new(&engine);
let module = unsafe { Module::deserialize_from_file(&store, &self.path)? };
return Ok(module);
}

View File

@@ -1,7 +1,6 @@
use crate::utils::{parse_envvar, parse_mapdir};
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::sync::Arc;
use wasmer::{Instance, Module};
use wasmer_wasi::{get_wasi_version, WasiState, WasiVersion};
@@ -59,7 +58,7 @@ impl Wasi {
let import_object = wasi_env.import_object(&module)?;
let instance = Instance::new(&module, &import_object)?;
wasi_env.set_memory(Arc::new(instance.exports.get_memory("memory")?.clone()));
wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());
let start = instance.exports.get_function("_start")?;
start

View File

@@ -6,6 +6,7 @@ use anyhow::{Error, Result};
use std::path::PathBuf;
use std::str::FromStr;
use std::string::ToString;
#[allow(unused_imports)]
use std::sync::Arc;
use structopt::StructOpt;
use wasmer::*;
@@ -174,22 +175,18 @@ impl StoreOptions {
/// Get the Compiler Config for the current options
#[allow(unused_variables)]
fn get_compiler_config(
&self,
target: Target,
) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
let compiler = self.get_compiler()?;
let features = self.get_features()?;
let compiler_config: Box<dyn CompilerConfig> = match compiler {
CompilerType::Headless => bail!("The headless engine can't be chosen"),
#[cfg(feature = "singlepass")]
CompilerType::Singlepass => {
let config = wasmer_compiler_singlepass::SinglepassConfig::new(features, target);
let config = wasmer_compiler_singlepass::Singlepass::new();
Box::new(config)
}
#[cfg(feature = "cranelift")]
CompilerType::Cranelift => {
let config = wasmer_compiler_cranelift::CraneliftConfig::new(features, target);
let config = wasmer_compiler_cranelift::Cranelift::new();
Box::new(config)
}
#[cfg(feature = "llvm")]
@@ -198,10 +195,9 @@ impl StoreOptions {
use std::io::Write;
use wasm_common::entity::EntityRef;
use wasmer_compiler_llvm::{
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks,
LLVMConfig,
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
};
let mut config = LLVMConfig::new(features, target);
let mut config = LLVM::new();
struct Callbacks {
debug_dir: PathBuf,
}
@@ -278,7 +274,7 @@ impl StoreOptions {
}
}
if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
config.callbacks = Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?));
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
}
Box::new(config)
}
@@ -291,11 +287,6 @@ impl StoreOptions {
Ok((compiler_config, compiler))
}
/// Gets the tunables for the compiler target
pub fn get_tunables(&self, compiler_config: &dyn CompilerConfig) -> Tunables {
Tunables::for_target(compiler_config.target().triple())
}
/// Gets the store for the host target, with the engine name and compiler name selected
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
let target = Target::default();
@@ -307,29 +298,34 @@ impl StoreOptions {
&self,
target: Target,
) -> Result<(Store, EngineType, CompilerType)> {
let (compiler_config, compiler_type) = self.get_compiler_config(target)?;
let tunables = self.get_tunables(&*compiler_config);
let (engine, engine_type) = self.get_engine_with_compiler(tunables, compiler_config)?;
let store = Store::new(engine);
let (compiler_config, compiler_type) = self.get_compiler_config()?;
let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?;
let store = Store::new(&*engine);
Ok((store, engine_type, compiler_type))
}
fn get_engine_with_compiler(
&self,
tunables: Tunables,
compiler_config: Box<dyn CompilerConfig>,
) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
target: Target,
mut compiler_config: Box<dyn CompilerConfig>,
) -> Result<(Box<dyn Engine + Send + Sync>, EngineType)> {
let engine_type = self.get_engine()?;
let engine: Arc<dyn Engine + Send + Sync> = match engine_type {
let features = self.get_features()?;
let engine: Box<dyn Engine + Send + Sync> = match engine_type {
#[cfg(feature = "jit")]
EngineType::JIT => {
Arc::new(wasmer_engine_jit::JITEngine::new(compiler_config, tunables))
}
EngineType::JIT => Box::new(
wasmer_engine_jit::JIT::new(&*compiler_config)
.features(features)
.target(target)
.engine(),
),
#[cfg(feature = "native")]
EngineType::Native => Arc::new(wasmer_engine_native::NativeEngine::new(
compiler_config,
tunables,
)),
EngineType::Native => Box::new(
wasmer_engine_native::Native::new(&mut *compiler_config)
.target(target)
.features(features)
.engine(),
),
#[cfg(not(all(feature = "jit", feature = "native",)))]
engine => bail!(
"The `{}` engine is not included in this binary.",
@@ -363,16 +359,13 @@ impl StoreOptions {
// If we don't have a compiler, but we have an engine
#[cfg(all(not(feature = "compiler"), feature = "engine"))]
impl StoreOptions {
fn get_engine_headless(
&self,
tunables: Tunables,
) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
fn get_engine_headless(&self) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
let engine_type = self.get_engine()?;
let engine: Arc<dyn Engine + Send + Sync> = match engine_type {
#[cfg(feature = "jit")]
EngineType::JIT => Arc::new(wasmer_engine_jit::JITEngine::headless(tunables)),
EngineType::JIT => Arc::new(wasmer_engine_jit::JIT::headless().engine()),
#[cfg(feature = "native")]
EngineType::Native => Arc::new(wasmer_engine_native::NativeEngine::headless(tunables)),
EngineType::Native => Arc::new(wasmer_engine_native::Native::headless().engine()),
#[cfg(not(all(feature = "jit", feature = "native",)))]
engine => bail!(
"The `{}` engine is not included in this binary.",
@@ -384,10 +377,8 @@ impl StoreOptions {
/// Get the store (headless engine)
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
// Get the tunables for the current host
let tunables = Tunables::default();
let (engine, engine_type) = self.get_engine_headless(tunables)?;
let store = Store::new(engine);
let (engine, engine_type) = self.get_engine_headless()?;
let store = Store::new(&engine);
Ok((store, engine_type, CompilerType::Headless))
}

View File

@@ -33,7 +33,7 @@ pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
/// Parses a mapdir from an env var
pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
return Ok((env_var.to_string(), value.to_string()));
Ok((env_var.to_string(), value.to_string()))
} else {
bail!(
"Env vars must be of the form <var_name>=<value>. Found {}",

View File

@@ -11,3 +11,9 @@ mod multi_value_imports;
mod serialize;
mod traps;
mod utils;
mod wasi;
mod wast;
pub use crate::utils::get_compiler;
pub use crate::wasi::run_wasi;
pub use crate::wast::run_wast;

View File

@@ -39,7 +39,7 @@ fn test_trap_return() -> Result<()> {
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
#[cfg_attr(feature = "test-singlepass", ignore)]
fn test_trap_trace() -> Result<()> {
let store = get_store();
let wat = r#"
@@ -119,7 +119,7 @@ fn test_trap_trace_cb() -> Result<()> {
}
#[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
#[cfg_attr(feature = "test-singlepass", ignore)]
fn test_trap_stack_overflow() -> Result<()> {
let store = get_store();
let wat = r#"
@@ -419,7 +419,7 @@ RuntimeError: indirect call type mismatch
}
#[ignore]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
#[cfg_attr(feature = "test-singlepass", ignore)]
fn start_trap_pretty() -> Result<()> {
let store = get_store();
let wat = r#"

View File

@@ -1,9 +1,9 @@
use std::sync::Arc;
use test_utils::get_compiler_config_from_str;
use wasmer::{Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
use wasmer_engine_jit::JITEngine;
use wasmer::{CompilerConfig, Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
use wasmer_engine::Engine;
use wasmer_engine_jit::JIT;
fn get_compiler_str() -> &'static str {
pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
cfg_if::cfg_if! {
if #[cfg(any(
all(feature = "test-llvm", any(feature = "test-cranelift", feature = "test-singlepass")),
@@ -11,44 +11,43 @@ fn get_compiler_str() -> &'static str {
))] {
compile_error!("Only one compiler can be selected")
} else if #[cfg(feature = "test-cranelift")] {
"cranelift"
let mut compiler = wasmer_compiler_cranelift::Cranelift::new();
compiler.canonicalize_nans(canonicalize_nans);
compiler
} else if #[cfg(feature = "test-llvm")] {
"llvm"
let mut compiler = wasmer_compiler_llvm::LLVM::new();
compiler.canonicalize_nans(canonicalize_nans);
compiler
} else if #[cfg(feature = "test-singlepass")] {
"singlepass"
let mut compiler = wasmer_compiler_singlepass::Singlepass::new();
compiler.canonicalize_nans(canonicalize_nans);
compiler
} else {
compile_error!("No compiler chosen for the tests")
}
}
}
pub fn get_engine() -> impl Engine {
let compiler_config = get_compiler(false);
JIT::new(&compiler_config).engine()
}
pub fn get_store() -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let compiler_config =
get_compiler_config_from_str(get_compiler_str(), try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
Store::new(&get_engine())
}
pub fn get_store_with_middlewares<I: Iterator<Item = Arc<dyn FunctionMiddlewareGenerator>>>(
middlewares: I,
) -> Store {
let features = Features::default();
let try_nan_canonicalization = false;
let mut compiler_config =
get_compiler_config_from_str(get_compiler_str(), try_nan_canonicalization, features);
let mut compiler_config = get_compiler(false);
for x in middlewares {
compiler_config.push_middleware(x);
}
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
store
let engine = JIT::new(&compiler_config).engine();
Store::new(&engine)
}
pub fn get_headless_store() -> Store {
let tunables = Tunables::for_target(&Triple::host());
let store = Store::new(Arc::new(JITEngine::headless(tunables)));
store
Store::new(&JIT::headless().engine())
}

44
tests/compilers/wasi.rs Normal file
View File

@@ -0,0 +1,44 @@
#![cfg(all(feature = "compiler", feature = "engine"))]
use crate::utils::get_compiler;
use std::fs::File;
use std::io::Read;
use wasmer::Store;
#[cfg(feature = "jit")]
use wasmer_engine_jit::JIT;
use wasmer_wast::WasiTest;
// The generated tests (from build.rs) look like:
// #[cfg(test)]
// mod singlepass {
// mod spec {
// #[test]
// fn address() -> anyhow::Result<()> {
// crate::run_wast("tests/spectests/address.wast", "singlepass")
// }
// }
// }
include!(concat!(env!("OUT_DIR"), "/generated_wasitests.rs"));
pub fn run_wasi(wast_path: &str, base_dir: &str, compiler: &str) -> anyhow::Result<()> {
println!(
"Running wasi wast `{}` with the {} compiler",
wast_path, compiler
);
let compiler_config = get_compiler(true);
let store = Store::new(&JIT::new(&compiler_config).engine());
let source = {
let mut out = String::new();
let mut f = File::open(wast_path)?;
f.read_to_string(&mut out)?;
out
};
let tokens = WasiTest::lex_string(&source)?;
let wasi_test = WasiTest::parse_tokens(&tokens)?;
let succeeded = wasi_test.run(&store, base_dir)?;
assert!(succeeded);
Ok(())
}

View File

@@ -1,11 +1,10 @@
#![cfg(all(feature = "compiler", feature = "engine"))]
use crate::utils::get_compiler;
use std::path::Path;
use std::sync::Arc;
use test_utils::get_compiler_config_from_str;
use wasmer::{Features, Store, Tunables};
use wasmer::{Features, Store};
#[cfg(feature = "jit")]
use wasmer_engine_jit::JITEngine;
use wasmer_engine_jit::JIT;
#[cfg(feature = "native")]
use wasmer_engine_native::NativeEngine;
use wasmer_wast::Wast;
@@ -29,7 +28,7 @@ fn native_prefixer(bytes: &[u8]) -> String {
format!("{}", hash.to_hex())
}
fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
println!(
"Running wast `{}` with the {} compiler",
wast_path, compiler
@@ -39,13 +38,13 @@ fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
if wast_path.contains("bulk-memory") {
features.bulk_memory(true);
}
let compiler_config =
get_compiler_config_from_str(compiler, try_nan_canonicalization, features);
let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
#[cfg(feature = "test-singlepass")]
features.multi_value(false);
let compiler_config = get_compiler(true);
let store = Store::new(&JIT::new(&compiler_config).features(features).engine());
// let mut native = NativeEngine::new(compiler_config, tunables);
// native.set_deterministic_prefixer(native_prefixer);
// let store = Store::new(Arc::new(native));
// let store = Store::new(&native);
let mut wast = Wast::new_with_spectest(store);
if compiler == "singlepass" {
// We don't support multivalue yet in singlepass

View File

@@ -7,10 +7,44 @@ singlepass on windows # Singlepass is not yet supported on Windows
## WASI
wasi::snapshot1::fd_read
wasi::snapshot1::poll_oneoff
wasi::snapshot1::fd_pread
wasi::snapshot1::fd_close
wasi::snapshot1::fd_allocate
wasi::snapshot1::close_preopen_fd
wasi::snapshot1::envvar
### due to hard-coded direct calls into WASI for wasi unstable
wasitests::snapshot1::fd_read
wasitests::snapshot1::poll_oneoff
wasitests::snapshot1::fd_pread
wasitests::snapshot1::fd_close
wasitests::snapshot1::fd_allocate
wasitests::snapshot1::close_preopen_fd
wasitests::snapshot1::envvar
### TODO: resolve the disabled tests below. These are newly disabled tests from the migration:
### due to git clone not preserving symlinks:
wasitests::snapshot1::readlink
wasitests::unstable::readlink
### failing due to `remove_dir_all`. this test is also bad for parallelism
wasitests::snapshot1::create_dir
wasitests::unstable::create_dir
### failing because it closes `stdout` which breaks our testing system
wasitests::unstable::fd_close
### failing because we're operating on stdout which is now overridden.
### TODO: check WasiFile implementation
### Alterative: split test into 2 parts, one printing to stderr, the other printing to stdout to test the real versions
wasitests::unstable::poll_oneoff
## Failing due to different line endings on Windows
## we need a better solution to this problem:
wasitests::snapshot1::file_metadata on windows
wasitests::snapshot1::fseek on windows
wasitests::snapshot1::path_link on windows
wasitests::snapshot1::path_symlink on windows
wasitests::unstable::fd_pread on windows
wasitests::unstable::fd_read on windows
wasitests::unstable::file_metadata on windows
wasitests::unstable::fseek on windows
wasitests::unstable::path_link on windows
wasitests::unstable::path_symlink on windows

View File

@@ -13,7 +13,7 @@ use wasm_common::{
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError};
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables};
use wasmer_runtime::{
MemoryPlan, ModuleInfo, TablePlan, VMContext, VMFunctionBody, VMSharedSignatureIndex,
};
@@ -55,9 +55,12 @@ impl DummyArtifact {
#[cfg(feature = "compiler")]
/// Compile a data buffer into a `DummyArtifact`, which may then be instantiated.
pub fn new(engine: &DummyEngine, data: &[u8]) -> Result<Self, CompileError> {
pub fn new(
engine: &DummyEngine,
data: &[u8],
tunables: &dyn Tunables,
) -> Result<Self, CompileError> {
let environ = ModuleEnvironment::new();
let tunables = engine.tunables();
let translation = environ.translate(data).map_err(CompileError::Wasm)?;

Some files were not shown because too many files have changed in this diff Show More