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

View File

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

View File

@@ -111,6 +111,7 @@ test-packages:
cargo test -p wasmer --release cargo test -p wasmer --release
cargo test -p wasmer-runtime --release cargo test -p wasmer-runtime --release
cargo test -p wasm-common --release cargo test -p wasm-common --release
cargo test -p wasmer-wasi --release
test-capi-singlepass: build-capi-singlepass test-capi-singlepass: build-capi-singlepass
cargo test --manifest-path lib/c-api/Cargo.toml --release \ 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-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 # # Packaging #
############# #############

View File

@@ -1,8 +1,7 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use test_utils::get_compiler_config_from_str;
use wasmer::*; use wasmer::*;
use wasmer_engine_jit::JITEngine; use wasmer_engine_jit::JIT;
static BASIC_WAT: &str = r#"(module static BASIC_WAT: &str = r#"(module
(func $multiply (import "env" "multiply") (param i32 i32) (result i32)) (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) { fn run_static_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")] #[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); run_basic_static_function(&store, "llvm", c);
} }
#[cfg(feature = "cranelift")] #[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); run_basic_static_function(&store, "cranelift", c);
} }
#[cfg(feature = "singlepass")] #[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); 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) { fn run_dynamic_benchmarks(c: &mut Criterion) {
#[cfg(feature = "llvm")] #[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); run_basic_dynamic_function(&store, "llvm", c);
} }
#[cfg(feature = "cranelift")] #[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); run_basic_dynamic_function(&store, "cranelift", c);
} }
#[cfg(feature = "singlepass")] #[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); run_basic_dynamic_function(&store, "singlepass", c);
} }
} }

View File

@@ -9,53 +9,91 @@ use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use test_generator::{ use test_generator::{
build_ignores_from_textfile, test_directory, test_directory_module, wast_processor, build_ignores_from_textfile, test_directory, test_directory_module, wasi_processor,
with_features, with_test_module, Testsuite, wast_processor, with_features, with_test_module, Testsuite,
}; };
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=tests/ignores.txt"); 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( let out_dir = PathBuf::from(
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"), 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 ignores = build_ignores_from_textfile("tests/ignores.txt".into())?;
let compilers = ["singlepass", "cranelift", "llvm"];
// Spectests test generation // Spectests test generation
let mut spectests = Testsuite { {
buffer: String::new(), let mut spectests = Testsuite {
path: vec![], buffer: String::new(),
ignores, path: vec![],
}; ignores: ignores.clone(),
};
let backends = vec!["singlepass", "cranelift", "llvm"]; with_features(&mut spectests, &compilers, |mut spectests| {
with_features(&mut spectests, &backends, |mut spectests| { with_test_module(&mut spectests, "spec", |spectests| {
with_test_module(&mut spectests, "spec", |spectests| { let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?; test_directory_module(
test_directory_module( spectests,
spectests, "tests/wast/spec/proposals/multi-value",
"tests/wast/spec/proposals/multi-value", wast_processor,
wast_processor, )?;
)?; // test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?; Ok(())
})?;
with_test_module(&mut spectests, "wasmer", |spectests| {
let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?;
Ok(())
})?;
Ok(()) Ok(())
})?; })?;
with_test_module(&mut spectests, "wasmer", |spectests| {
let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?; let spectests_output = out_dir.join("generated_spectests.rs");
fs::write(&spectests_output, spectests.buffer)?;
// Write out our auto-generated tests and opportunistically format them with
// `rustfmt` if it's installed.
// 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(()) Ok(())
})?; })?;
Ok(())
})?;
let spectests_output = out_dir.join("generated_spectests.rs"); let wasitests_output = out_dir.join("generated_wasitests.rs");
fs::write(&spectests_output, spectests.buffer)?; fs::write(&wasitests_output, wasitests.buffer)?;
// Write out our auto-generated tests and opportunistically format them with drop(Command::new("rustfmt").arg(&wasitests_output).status());
// `rustfmt` if it's installed. }
// 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());
Ok(()) Ok(())
} }

View File

@@ -40,7 +40,7 @@ anyhow = "1.0"
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[features] [features]
default = ["wat", "cranelift", "jit"] default = ["wat", "default-cranelift", "default-jit"]
compiler = [ compiler = [
"wasmer-engine-jit/compiler", "wasmer-engine-jit/compiler",
"wasmer-engine-native/compiler", "wasmer-engine-native/compiler",
@@ -66,3 +66,26 @@ llvm = [
"wasmer-compiler-llvm", "wasmer-compiler-llvm",
"compiler", "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::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace; use crate::import_object::LikeNamespace;
use crate::native::NativeFunc; use crate::native::NativeFunc;
use crate::WasmTypeList;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::{ use std::{
iter::{ExactSizeIterator, FromIterator}, iter::{ExactSizeIterator, FromIterator},
sync::Arc, sync::Arc,
}; };
use thiserror::Error; use thiserror::Error;
use wasm_common::WasmTypeList;
use wasmer_runtime::Export; use wasmer_runtime::Export;
/// The `ExportError` can happen when trying to get a specific /// The `ExportError` can happen when trying to get a specific
@@ -127,7 +127,7 @@ impl Exports {
{ {
self.get_function(name)? self.get_function(name)?
.native() .native()
.ok_or(ExportError::IncompatibleType) .map_err(|_| ExportError::IncompatibleType)
} }
/// Get an export as an `Extern`. /// Get an export as an `Extern`.

View File

@@ -5,12 +5,14 @@ use crate::types::Val;
use crate::FunctionType; use crate::FunctionType;
use crate::NativeFunc; use crate::NativeFunc;
use crate::RuntimeError; use crate::RuntimeError;
pub use inner::{HostFunction, WasmExternType, WasmTypeList};
use inner::{WithEnv, WithoutEnv};
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::max; use std::cmp::max;
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use wasmer_runtime::{ use wasmer_runtime::{
wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext, raise_user_trap, resume_panic, wasmer_call_trampoline, Export, ExportFunction,
VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
VMTrampoline,
}; };
/// A function defined in the Wasm module /// A function defined in the Wasm module
@@ -47,10 +49,13 @@ pub struct Function {
} }
impl 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 /// 1. Static/Monomorphic, i.e. all inputs and outputs have a
/// * `func` - the function. /// 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 pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Self
where where
F: HostFunction<Args, Rets, WithoutEnv, Env>, F: HostFunction<Args, Rets, WithoutEnv, Env>,
@@ -58,10 +63,11 @@ impl Function {
Rets: WasmTypeList, Rets: WasmTypeList,
Env: Sized + 'static, Env: Sized + 'static,
{ {
let func: wasm_common::Func<Args, Rets> = wasm_common::Func::new(func); let function = inner::Function::<Args, Rets>::new(func);
let address = func.address() as *const VMFunctionBody; let address = function.address() as *const VMFunctionBody;
let vmctx = std::ptr::null_mut() as *mut _ as *mut VMContext; let vmctx = std::ptr::null_mut() as *mut _ as *mut VMContext;
let signature = func.ty(); let signature = function.ty();
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: true, 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)] #[allow(clippy::cast_ptr_alignment)]
pub fn new_dynamic<F>(store: &Store, ty: &FunctionType, func: F) -> Self pub fn new_dynamic<F>(store: &Store, ty: &FunctionType, func: F) -> Self
where where
@@ -89,6 +142,7 @@ impl Function {
// generated dynamic trampoline. // generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody; let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: true, 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)] #[allow(clippy::cast_ptr_alignment)]
pub fn new_dynamic_env<F, Env>(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self pub fn new_dynamic_env<F, Env>(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self
where where
@@ -118,6 +180,7 @@ impl Function {
// generated dynamic trampoline. // generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody; let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: true, 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. /// Returns the underlying type of this function.
pub fn ty(&self) -> &FunctionType { pub fn ty(&self) -> &FunctionType {
&self.exported.signature &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 where
Args: WasmTypeList, Args: WasmTypeList,
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
// type check // type check
if self.exported.signature.params() != Args::wasm_types() { {
// todo: error param types don't match let expected = self.exported.signature.params();
return None; let given = Args::wasm_types();
}
if self.exported.signature.results() != Rets::wasm_types() { if expected != given {
// todo: error result types don't match return Err(RuntimeError::new(format!(
return None; "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
given,
expected,
)));
}
} }
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.store.clone(),
self.exported.address, self.exported.address,
self.exported.vmctx, self.exported.vmctx,
@@ -441,8 +489,607 @@ impl<T: VMDynamicFunction> VMDynamicFunctionCall<T> for VMDynamicFunctionContext
match result { match result {
Ok(Ok(())) => {} Ok(Ok(())) => {}
Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)), Ok(Err(trap)) => raise_user_trap(Box::new(trap)),
Err(panic) => wasmer_runtime::resume_panic(panic), 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 { impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Result<Memory, MemoryError> { 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_plan = tunables.memory_plan(ty);
let memory = tunables.create_memory(memory_plan)?; let memory = tunables.create_memory(memory_plan)?;

View File

@@ -3,7 +3,7 @@ mod global;
mod memory; mod memory;
mod table; mod table;
pub use self::function::Function; pub use self::function::{Function, HostFunction, WasmExternType, WasmTypeList};
pub use self::global::Global; pub use self::global::Global;
pub use self::memory::Memory; pub use self::memory::Memory;
pub use self::table::Table; 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. /// 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> { pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
let item = init.into_checked_anyfunc(store)?; 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_plan = tunables.table_plan(ty);
let table = tunables let table = tunables
.create_table(table_plan) .create_table(table_plan)

View File

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

View File

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

View File

@@ -156,7 +156,7 @@ impl Module {
} }
fn compile(store: &Store, binary: &[u8]) -> Result<Self, CompileError> { 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)) Ok(Self::from_artifact(store, artifact))
} }
@@ -261,11 +261,9 @@ impl Module {
resolver: &dyn Resolver, resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> { ) -> Result<InstanceHandle, InstantiationError> {
unsafe { unsafe {
let instance_handle = self.artifact.instantiate( let instance_handle =
self.store.engine().tunables(), self.artifact
resolver, .instantiate(self.store.tunables(), resolver, Box::new(()))?;
Box::new(()),
)?;
// After the instance handle is created, we need to initialize // After the instance handle is created, we need to initialize
// the data, call the start function and so. However, if any // 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, FunctionDefinition, HostFunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithEnv,
VMDynamicFunctionWithoutEnv, WasmFunctionDefinition, VMDynamicFunctionWithoutEnv, WasmFunctionDefinition,
}; };
use crate::{Function, FunctionType, RuntimeError, Store}; use crate::{Function, FunctionType, RuntimeError, Store, WasmExternType, WasmTypeList};
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, AssertUnwindSafe};
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList}; use wasm_common::NativeWasmType;
use wasmer_runtime::{ use wasmer_runtime::{
ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
}; };

View File

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

View File

@@ -1,7 +1,7 @@
#[cfg(all(feature = "compiler", feature = "engine"))]
use crate::tunables::Tunables; use crate::tunables::Tunables;
#[cfg(all(feature = "compiler", feature = "engine"))] #[cfg(all(feature = "compiler", feature = "engine"))]
use wasmer_compiler::CompilerConfig; use wasmer_compiler::CompilerConfig;
use wasmer_engine::Tunables as BaseTunables;
use std::sync::Arc; use std::sync::Arc;
use wasmer_engine::Engine; use wasmer_engine::Engine;
@@ -9,11 +9,35 @@ use wasmer_engine::Engine;
#[derive(Clone)] #[derive(Clone)]
pub struct Store { pub struct Store {
engine: Arc<dyn Engine + Send + Sync>, engine: Arc<dyn Engine + Send + Sync>,
tunables: Arc<dyn BaseTunables + Send + Sync>,
} }
impl Store { impl Store {
pub fn new(engine: Arc<dyn Engine + Send + Sync>) -> Store { pub fn new<E>(engine: &E) -> Store
Store { engine } 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> { 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 // 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 { impl Default for Store {
fn default() -> Store { fn default() -> Store {
// We store them on a function that returns to make // We store them on a function that returns to make
// sure this function doesn't emit a compile error even if // sure this function doesn't emit a compile error even if
// more than one compiler is enabled. // more than one compiler is enabled.
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn get_config() -> Box<dyn CompilerConfig + Send + Sync> { fn get_config() -> impl CompilerConfig + Send + Sync {
#[cfg(feature = "cranelift")] cfg_if::cfg_if! {
return Box::new(wasmer_compiler_cranelift::CraneliftConfig::default()); if #[cfg(feature = "default-cranelift")] {
wasmer_compiler_cranelift::Cranelift::default()
#[cfg(feature = "llvm")] } else if #[cfg(feature = "default-llvm")] {
return Box::new(wasmer_compiler_llvm::LLVMConfig::default()); wasmer_compiler_llvm::LLVM::default()
} else if #[cfg(feature = "default-singlepass")] {
#[cfg(feature = "singlepass")] wasmer_compiler_singlepass::Singlepass::default()
return Box::new(wasmer_compiler_singlepass::SinglepassConfig::default()); } else {
compile_error!("No default compiler chosen")
}
}
} }
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn get_engine( fn get_engine(config: impl CompilerConfig + Send + Sync) -> impl Engine + Send + Sync {
config: Box<dyn CompilerConfig + Send + Sync>, cfg_if::cfg_if! {
) -> Arc<dyn Engine + Send + Sync> { if #[cfg(feature = "default-jit")] {
let tunables = Tunables::for_target(config.target().triple()); wasmer_engine_jit::JIT::new(&config)
.engine()
#[cfg(feature = "jit")] } else if #[cfg(feature = "default-native")] {
return Arc::new(wasmer_engine_jit::JITEngine::new(config, tunables)); wasmer_engine_native::Native::new(&config)
.engine()
#[cfg(feature = "native")] } else {
return Arc::new(wasmer_engine_native::NativeEngine::new(config, tunables)); compile_error!("No default engine chosen")
}
}
} }
let config = get_config(); let config = get_config();
let engine = get_engine(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 more_asserts::assert_ge;
use std::cmp::min; use std::cmp::min;
use std::sync::Arc; 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_engine::Tunables as BaseTunables;
use wasmer_runtime::MemoryError; use wasmer_runtime::MemoryError;
use wasmer_runtime::{Memory, MemoryPlan, MemoryStyle, Table, TablePlan, TableStyle}; use wasmer_runtime::{Memory, MemoryPlan, MemoryStyle, Table, TablePlan, TableStyle};
@@ -24,7 +25,8 @@ pub struct Tunables {
impl Tunables { impl Tunables {
/// Get the `Tunables` for a specific Target /// 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 pointer_width: PointerWidth = triple.pointer_width().unwrap();
let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) = let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) =
match pointer_width { match pointer_width {
@@ -102,9 +104,3 @@ impl BaseTunables for Tunables {
Ok(Arc::new(LinearTable::new(&plan)?)) 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::path::PathBuf;
use std::ptr; use std::ptr;
use std::str; use std::str;
use std::sync::Arc;
use wasmer::{Memory, MemoryType, NamedResolver}; use wasmer::{Memory, MemoryType, NamedResolver};
use wasmer_wasi as wasi; 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` // this API will now leak a `Memory`
let memory_type = MemoryType::new(0, None, false); let memory_type = MemoryType::new(0, None, false);
let memory = Memory::new(store, memory_type).expect("create memory"); 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 // TODO(mark): review lifetime of `Memory` here
let import_object_inner: Box<dyn NamedResolver> = Box::new( let import_object_inner: Box<dyn NamedResolver> = Box::new(
wasi::generate_import_object_from_env(store, wasi_env, wasi::WasiVersion::Latest), 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! { lazy_static! {
pub(crate) static ref GLOBAL_STORE: wasmer::Store = 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 { 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::slice;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "engine")]
use wasmer::Tunables;
use wasmer::{ use wasmer::{
Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, Instance, Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, Instance,
Memory, MemoryType, Module, Mutability, OrderedResolver, Pages, RuntimeError, Store, Table, Memory, MemoryType, Module, Mutability, OrderedResolver, Pages, RuntimeError, Store, Table,
TableType, Val, ValType, TableType, Val, ValType,
}; };
#[cfg(feature = "jit")] #[cfg(feature = "jit")]
use wasmer_engine_jit::JITEngine; use wasmer_engine_jit::JIT;
use crate::error::update_last_error; use crate::error::update_last_error;
@@ -58,11 +56,11 @@ cfg_if! {
fn get_default_compiler_config() -> Box<dyn CompilerConfig> { fn get_default_compiler_config() -> Box<dyn CompilerConfig> {
cfg_if! { cfg_if! {
if #[cfg(feature = "cranelift")] { if #[cfg(feature = "cranelift")] {
Box::new(wasmer_compiler_cranelift::CraneliftConfig::default()) Box::new(wasmer_compiler_cranelift::Cranelift::default())
} else if #[cfg(feature = "llvm")] { } else if #[cfg(feature = "llvm")] {
Box::new(wasmer_compiler_llvm::LLVMConfig::default()) Box::new(wasmer_compiler_llvm::LLVM::default())
} else if #[cfg(feature = "singlepass")] { } else if #[cfg(feature = "singlepass")] {
Box::new(wasmer_compiler_singlepass::SinglepassConfig::default()) Box::new(wasmer_compiler_singlepass::Singlepass::default())
} else { } else {
compile_error!("Please enable one of the compiler backends") compile_error!("Please enable one of the compiler backends")
} }
@@ -72,8 +70,7 @@ cfg_if! {
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> { pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
let compiler_config: Box<dyn CompilerConfig> = get_default_compiler_config(); let compiler_config: Box<dyn CompilerConfig> = get_default_compiler_config();
let tunables = Tunables::default(); let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::new(&*compiler_config).engine());
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::new(compiler_config, tunables));
Box::new(wasm_engine_t { inner: engine }) Box::new(wasm_engine_t { inner: engine })
} }
} }
@@ -81,8 +78,7 @@ cfg_if! {
// Headless JIT // Headless JIT
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> { pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
let tunables = Tunables::default(); let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::headless().engine());
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::headless(tunables));
Box::new(wasm_engine_t { inner: 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>> { ) -> Option<NonNull<wasm_store_t>> {
let wasm_engine_ptr = wasm_engine_ptr?; let wasm_engine_ptr = wasm_engine_ptr?;
let wasm_engine = wasm_engine_ptr.as_ref(); 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( Some(NonNull::new_unchecked(
Box::into_raw(Box::new(store)) as *mut wasm_store_t 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 This is the `wasmer-compiler-cranelift` crate, which contains a
compiler implementation based on Cranelift. 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 For production we recommend using `wasmer-compiler-llvm` as it offers
a much better runtime speed (50% faster on average). a much better runtime speed (50% faster on average).

View File

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

View File

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

View File

@@ -57,7 +57,7 @@ mod trampoline;
mod translator; mod translator;
pub use crate::compiler::CraneliftCompiler; 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::debug::{ModuleInfoMemoryOffset, ModuleInfoVmctxInfo, ValueLabelsRanges};
pub use crate::trampoline::make_trampoline_function_call; 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 } => { Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder); 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)) state.push1(builder.ins().sextend(I32, extracted))
} }
Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => { Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder); 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)); state.push1(builder.ins().uextend(I32, extracted));
// On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so // 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 // 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::F32x4ExtractLane { lane }
| Operator::F64x2ExtractLane { lane } => { | Operator::F64x2ExtractLane { lane } => {
let vector = pop1_with_bitcast(state, type_of(op), builder); 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 } => { Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
let (vector, replacement) = state.pop2(); 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_environ::{FuncEnvironment, ReturnMode};
use super::func_state::FuncTranslationState; use super::func_state::FuncTranslationState;
use super::translation_utils::get_vmctx_value_label; use super::translation_utils::get_vmctx_value_label;
use crate::config::CraneliftConfig; use crate::config::Cranelift;
use cranelift_codegen::entity::EntityRef; use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing; use cranelift_codegen::timing;
@@ -69,7 +69,7 @@ impl FuncTranslator {
func: &mut ir::Function, func: &mut ir::Function,
environ: &mut FE, environ: &mut FE,
local_function_index: LocalFunctionIndex, local_function_index: LocalFunctionIndex,
config: &CraneliftConfig, config: &Cranelift,
) -> WasmResult<()> { ) -> WasmResult<()> {
let mut reader = MiddlewareBinaryReader::new_with_offset(code, code_offset); let mut reader = MiddlewareBinaryReader::new_with_offset(code, code_offset);
reader.set_middleware_chain( reader.set_middleware_chain(

View File

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

View File

@@ -3,6 +3,27 @@
This is the `wasmer-compiler-llvm` crate, which contains a This is the `wasmer-compiler-llvm` crate, which contains a
compiler implementation based on LLVM. 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 We recommend using LLVM as the default compiler when running WebAssembly
files on any **production** system, as it offers maximum peformance near files on any **production** system, as it offers maximum peformance near
to native speeds. to native speeds.

View File

@@ -1,61 +1,52 @@
use crate::config::LLVMConfig; use crate::config::LLVM;
use crate::trampoline::FuncTrampoline; use crate::trampoline::FuncTrampoline;
use crate::translator::FuncTranslator; use crate::translator::FuncTranslator;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap}; use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
use wasm_common::Features; use wasm_common::LocalFunctionIndex;
use wasm_common::{LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_compiler::{ use wasmer_compiler::{
Compilation, CompileError, Compiler, CompilerConfig, FunctionBodyData, ModuleTranslationState, Compilation, CompileError, CompileModuleInfo, Compiler, CustomSection, CustomSectionProtection,
RelocationTarget, SectionIndex, Target, Dwarf, FunctionBodyData, ModuleTranslationState, RelocationTarget, SectionBody, SectionIndex,
Target,
}; };
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
//use std::sync::{Arc, Mutex}; //use std::sync::{Arc, Mutex};
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR, /// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
/// optimizing it and then translating to assembly. /// optimizing it and then translating to assembly.
pub struct LLVMCompiler { pub struct LLVMCompiler {
config: LLVMConfig, config: LLVM,
} }
impl LLVMCompiler { impl LLVMCompiler {
/// Creates a new LLVM compiler /// Creates a new LLVM compiler
pub fn new(config: &LLVMConfig) -> LLVMCompiler { pub fn new(config: &LLVM) -> LLVMCompiler {
LLVMCompiler { LLVMCompiler {
config: config.clone(), config: config.clone(),
} }
} }
/// Gets the WebAssembly features for this Compiler /// Gets the config for this Compiler
fn config(&self) -> &LLVMConfig { fn config(&self) -> &LLVM {
&self.config &self.config
} }
} }
impl Compiler for LLVMCompiler { 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 /// Compile the module using LLVM, producing a compilation result with
/// associated relocations. /// associated relocations.
fn compile_module<'data, 'module>( fn compile_module<'data, 'module>(
&self, &self,
module: &'module ModuleInfo, target: &Target,
compile_info: &'module CompileModuleInfo,
module_translation: &ModuleTranslationState, module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>, function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> { ) -> Result<Compilation, CompileError> {
//let data = Arc::new(Mutex::new(0)); //let data = Arc::new(Mutex::new(0));
let mut func_names = SecondaryMap::new(); 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. // TODO: merge constants in sections.
@@ -67,29 +58,37 @@ impl Compiler for LLVMCompiler {
.unwrap_or_else(|| format!("fn{}", func_index.index())); .unwrap_or_else(|| format!("fn{}", func_index.index()));
} }
let mut module_custom_sections = PrimaryMap::new(); let mut module_custom_sections = PrimaryMap::new();
let mut frame_section_bytes = vec![];
let mut frame_section_relocations = vec![];
let functions = function_body_inputs let functions = function_body_inputs
.into_iter() .into_iter()
.collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>() .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
.par_iter() .par_iter()
.map_init(FuncTranslator::new, |func_translator, (i, input)| { .map_init(
// TODO: remove (to serialize) || {
//let mut data = data.lock().unwrap(); let target_machine = self.config().target_machine(target);
func_translator.translate( FuncTranslator::new(target_machine)
module, },
module_translation, |func_translator, (i, input)| {
i, // TODO: remove (to serialize)
input, //let mut data = data.lock().unwrap();
self.config(), func_translator.translate(
&memory_plans, &module,
&table_plans, module_translation,
&func_names, i,
) input,
}) self.config(),
&memory_plans,
&table_plans,
&func_names,
)
},
)
.collect::<Result<Vec<_>, CompileError>>()? .collect::<Result<Vec<_>, CompileError>>()?
.into_iter() .into_iter()
.map(|(mut compiled_function, function_custom_sections)| { .map(|mut compiled_function| {
let first_section = module_custom_sections.len() as u32; 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() // TODO: remove this call to clone()
let mut custom_section = custom_section.clone(); let mut custom_section = custom_section.clone();
for mut reloc in &mut custom_section.relocations { for mut reloc in &mut custom_section.relocations {
@@ -99,27 +98,74 @@ impl Compiler for LLVMCompiler {
) )
} }
} }
module_custom_sections.push(custom_section); 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 { if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
reloc.reloc_target = RelocationTarget::CustomSection( reloc.reloc_target = RelocationTarget::CustomSection(
SectionIndex::from_u32(first_section + index.as_u32()), SectionIndex::from_u32(first_section + index.as_u32()),
) )
} }
} }
compiled_function compiled_function.compiled_function
}) })
.collect::<PrimaryMap<LocalFunctionIndex, _>>(); .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 let function_call_trampolines = module
.signatures .signatures
.values() .values()
.collect::<Vec<_>>() .collect::<Vec<_>>()
.par_iter() .par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, sig| { .map_init(
func_trampoline.trampoline(sig, self.config()) || {
}) let target_machine = self.config().target_machine(target);
FuncTrampoline::new(target_machine)
},
|func_trampoline, sig| func_trampoline.trampoline(sig, self.config()),
)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
.collect::<Result<PrimaryMap<_, _>, CompileError>>()?; .collect::<Result<PrimaryMap<_, _>, CompileError>>()?;
@@ -128,9 +174,15 @@ impl Compiler for LLVMCompiler {
.imported_function_types() .imported_function_types()
.collect::<Vec<_>>() .collect::<Vec<_>>()
.par_iter() .par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, func_type| { .map_init(
func_trampoline.dynamic_trampoline(&func_type, self.config()) || {
}) 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>>()? .collect::<Result<Vec<_>, CompileError>>()?
.into_iter() .into_iter()
.collect::<PrimaryMap<_, _>>(); .collect::<PrimaryMap<_, _>>();
@@ -140,7 +192,7 @@ impl Compiler for LLVMCompiler {
module_custom_sections, module_custom_sections,
function_call_trampolines, function_call_trampolines,
dynamic_function_trampolines, dynamic_function_trampolines,
None, dwarf,
)) ))
} }
} }

View File

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

View File

@@ -22,5 +22,5 @@ mod translator;
pub use crate::compiler::LLVMCompiler; pub use crate::compiler::LLVMCompiler;
pub use crate::config::{ 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 wasm_common::entity::{PrimaryMap, SecondaryMap};
use wasmer_compiler::{ use wasmer_compiler::{
CompileError, CompiledFunction, CompiledFunctionFrameInfo, CustomSection, CompileError, CompiledFunctionFrameInfo, CustomSection, CustomSectionProtection,
CustomSectionProtection, CustomSections, FunctionAddressMap, FunctionBody, CustomSections, FunctionAddressMap, FunctionBody, InstructionAddressMap, Relocation,
InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SectionBody, SectionIndex, RelocationKind, RelocationTarget, SectionBody, SectionIndex, SourceLoc,
SourceLoc,
}; };
use wasmer_runtime::libcalls::LibCall; 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)) 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>( pub fn load_object_file<F>(
contents: &[u8], contents: &[u8],
root_section: &str, root_section: &str,
self_referential_relocation_target: Option<RelocationTarget>, root_section_reloc_target: RelocationTarget,
mut symbol_name_to_relocation_target: F, mut symbol_name_to_relocation_target: F,
) -> Result<(CompiledFunction, CustomSections), CompileError> ) -> Result<CompiledFunction, CompileError>
where where
F: FnMut(&String) -> Result<Option<RelocationTarget>, CompileError>, F: FnMut(&String) -> Result<Option<RelocationTarget>, CompileError>,
{ {
@@ -102,9 +107,7 @@ where
let mut section_to_custom_section = HashMap::new(); let mut section_to_custom_section = HashMap::new();
if let Some(reloc_target) = self_referential_relocation_target { section_targets.insert(root_section_index, root_section_reloc_target);
section_targets.insert(root_section_index, reloc_target);
};
let mut next_custom_section: u32 = 0; let mut next_custom_section: u32 = 0;
let mut elf_section_to_target = |elf_section_index: ElfSectionIndex| { let mut elf_section_to_target = |elf_section_index: ElfSectionIndex| {
@@ -142,6 +145,22 @@ where
// the sections we want to include. // the sections we want to include.
worklist.push(root_section_index); worklist.push(root_section_index);
visited.insert(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() { while let Some(section_index) = worklist.pop() {
for reloc in reloc_sections for reloc in reloc_sections
.get(&section_index) .get(&section_index)
@@ -152,6 +171,7 @@ where
// TODO: these constants are not per-arch, we'll need to // TODO: these constants are not per-arch, we'll need to
// make the whole match per-arch. // make the whole match per-arch.
goblin::elf::reloc::R_X86_64_64 => RelocationKind::Abs8, 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 => { goblin::elf::reloc::R_X86_64_GOT64 => {
return Err(CompileError::Codegen( return Err(CompileError::Codegen(
"unimplemented PIC relocation R_X86_64_GOT64".into(), "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 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) { if visited.insert(elf_target_section) {
worklist.push(elf_target_section); worklist.push(elf_target_section);
} }
elf_section_to_target(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 } else if elf_target.st_type() == goblin::elf::sym::STT_NOTYPE
&& elf_target_section.is_undef() && 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 let mut custom_sections = section_to_custom_section
.iter() .iter()
.map(|(elf_section_index, custom_section_index)| { .map(|(elf_section_index, custom_section_index)| {
@@ -258,8 +289,8 @@ where
body_len: function_body.body.len(), body_len: function_body.body.len(),
}; };
Ok(( Ok(CompiledFunction {
CompiledFunction { compiled_function: wasmer_compiler::CompiledFunction {
body: function_body, body: function_body,
jt_offsets: SecondaryMap::new(), jt_offsets: SecondaryMap::new(),
relocations: relocations relocations: relocations
@@ -271,5 +302,6 @@ where
}, },
}, },
custom_sections, custom_sections,
)) eh_frame_section_indices,
})
} }

View File

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

View File

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

View File

@@ -3,6 +3,27 @@
This is the `wasmer-compiler-singlepass` crate, which contains a This is the `wasmer-compiler-singlepass` crate, which contains a
compiler implementation based on Singlepass. 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 Singlepass is designed to emit compiled code at linear time, as such
is not prone to JIT bombs and also offers great compilation performance is not prone to JIT bombs and also offers great compilation performance
orders of magnitude faster than `wasmer-compiler-cranelift` and orders of magnitude faster than `wasmer-compiler-cranelift` and

View File

@@ -1,6 +1,4 @@
use crate::{ use crate::{common_decl::*, config::Singlepass, emitter_x64::*, machine::Machine, x64_decl::*};
common_decl::*, config::SinglepassConfig, emitter_x64::*, machine::Machine, x64_decl::*,
};
use dynasmrt::{x64::Assembler, DynamicLabel}; use dynasmrt::{x64::Assembler, DynamicLabel};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@@ -32,7 +30,7 @@ pub struct FuncGen<'a> {
module: &'a ModuleInfo, module: &'a ModuleInfo,
/// ModuleInfo compilation config. /// ModuleInfo compilation config.
config: &'a SinglepassConfig, config: &'a Singlepass,
/// Offsets of vmctx fields. /// Offsets of vmctx fields.
vmoffsets: &'a VMOffsets, vmoffsets: &'a VMOffsets,
@@ -410,7 +408,7 @@ impl<'a> FuncGen<'a> {
let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst { let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst {
Location::Imm32(_) | Location::Imm64(_) => { Location::Imm32(_) | Location::Imm64(_) => {
return Err(CodegenError { 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(_, _) => { Location::Memory(_, _) => {
@@ -427,7 +425,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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(); self.machine.state.register_values[X64Register::GPR(*r).to_index().0].clone();
if content == MachineValue::Undefined { if content == MachineValue::Undefined {
return Err(CodegenError { 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); 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(); self.machine.state.register_values[X64Register::XMM(*r).to_index().0].clone();
if content == MachineValue::Undefined { if content == MachineValue::Undefined {
return Err(CodegenError { 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); self.machine.state.stack_values.push(content);
@@ -1005,12 +1003,8 @@ impl<'a> FuncGen<'a> {
// Calculate stack offset. // Calculate stack offset.
for (i, _param) in params.iter().enumerate() { for (i, _param) in params.iter().enumerate() {
let loc = Machine::get_param_location(1 + i); if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
match loc { stack_offset += 8;
Location::Memory(_, _) => {
stack_offset += 8;
}
_ => {}
} }
} }
@@ -1062,7 +1056,8 @@ impl<'a> FuncGen<'a> {
Location::Memory(reg, offset) => { Location::Memory(reg, offset) => {
if reg != GPR::RBP { if reg != GPR::RBP {
return Err(CodegenError { return Err(CodegenError {
message: format!("emit_call_sysv loc param: unreachable code"), message: "emit_call_sysv loc param: unreachable code"
.to_string(),
}); });
} }
self.machine self.machine
@@ -1112,7 +1107,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { 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 { if (self.machine.state.stack_values.len() % 2) != 1 {
return Err(CodegenError { 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 { if (stack_offset % 8) != 0 {
return Err(CodegenError { 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 { for _ in 0..stack_offset / 8 {
@@ -1180,7 +1175,7 @@ impl<'a> FuncGen<'a> {
} }
// Restore XMMs. // Restore XMMs.
if used_xmms.len() > 0 { if !used_xmms.is_empty() {
for (i, r) in used_xmms.iter().enumerate() { for (i, r) in used_xmms.iter().enumerate() {
self.assembler.emit_mov( self.assembler.emit_mov(
Size::S64, Size::S64,
@@ -1206,7 +1201,7 @@ impl<'a> FuncGen<'a> {
if self.machine.state.stack_values.pop().unwrap() != MachineValue::ExplicitShadow { if self.machine.state.stack_values.pop().unwrap() != MachineValue::ExplicitShadow {
return Err(CodegenError { 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(()) Ok(())
@@ -1330,7 +1325,7 @@ impl<'a> FuncGen<'a> {
3 => 8, 3 => 8,
_ => { _ => {
return Err(CodegenError { 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> { ) -> Result<(), CodegenError> {
if memory_sz > stack_sz { if memory_sz > stack_sz {
return Err(CodegenError { 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 { if self.machine.state.wasm_inst_offset != std::usize::MAX {
return Err(CodegenError { 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(()) Ok(())
@@ -1781,7 +1776,7 @@ impl<'a> FuncGen<'a> {
pub fn new( pub fn new(
module: &'a ModuleInfo, module: &'a ModuleInfo,
config: &'a SinglepassConfig, config: &'a Singlepass,
vmoffsets: &'a VMOffsets, vmoffsets: &'a VMOffsets,
memory_plans: &'a PrimaryMap<MemoryIndex, MemoryPlan>, memory_plans: &'a PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: &'a PrimaryMap<TableIndex, TablePlan>, _table_plans: &'a PrimaryMap<TableIndex, TablePlan>,
@@ -1842,7 +1837,7 @@ impl<'a> FuncGen<'a> {
} }
pub fn has_control_frames(&self) -> bool { 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> { pub fn feed_operator(&mut self, op: Operator) -> Result<(), CodegenError> {
@@ -2160,7 +2155,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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 { if let Location::Memory(_, _) = ret {
Location::Memory(_, _) => { self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret); self.machine.release_temp_gpr(dst);
self.machine.release_temp_gpr(dst);
}
_ => {}
}; };
} }
Operator::I32Ctz => { Operator::I32Ctz => {
@@ -2230,7 +2222,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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 { if let Location::Memory(_, _) = ret {
Location::Memory(_, _) => { self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret); self.machine.release_temp_gpr(dst);
self.machine.release_temp_gpr(dst);
}
_ => {}
}; };
} }
Operator::I32Popcnt => self.emit_xcnt_i32(Assembler::emit_popcnt)?, Operator::I32Popcnt => self.emit_xcnt_i32(Assembler::emit_popcnt)?,
@@ -2405,7 +2394,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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 { if let Location::Memory(_, _) = ret {
Location::Memory(_, _) => { self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret); self.machine.release_temp_gpr(dst);
self.machine.release_temp_gpr(dst);
}
_ => {}
}; };
} }
Operator::I64Ctz => { Operator::I64Ctz => {
@@ -2475,7 +2461,7 @@ impl<'a> FuncGen<'a> {
Location::GPR(reg) => reg, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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, Location::GPR(reg) => reg,
_ => { _ => {
return Err(CodegenError { 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 { if let Location::Memory(_, _) = ret {
Location::Memory(_, _) => { self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret); self.machine.release_temp_gpr(dst);
self.machine.release_temp_gpr(dst);
}
_ => {}
}; };
} }
Operator::I64Popcnt => self.emit_xcnt_i64(Assembler::emit_popcnt)?, Operator::I64Popcnt => self.emit_xcnt_i64(Assembler::emit_popcnt)?,
@@ -2721,7 +2704,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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| {
this.assembler.emit_call_location(Location::GPR(GPR::RAX)); this.assembler.emit_call_location(Location::GPR(GPR::RAX));
}, },
params.iter().map(|x| *x), params.iter().copied(),
)?; )?;
self.machine self.machine
.release_locations_only_stack(&mut self.assembler, &params); .release_locations_only_stack(&mut self.assembler, &params);
if return_types.len() > 0 { if !return_types.is_empty() {
let ret = self.machine.acquire_locations( let ret = self.machine.acquire_locations(
&mut self.assembler, &mut self.assembler,
&[( &[(
@@ -5217,7 +5200,7 @@ impl<'a> FuncGen<'a> {
Operator::CallIndirect { index, table_index } => { Operator::CallIndirect { index, table_index } => {
if table_index != 0 { if table_index != 0 {
return Err(CodegenError { 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 _); 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 self.machine
.release_locations_only_stack(&mut self.assembler, &params); .release_locations_only_stack(&mut self.assembler, &params);
if return_types.len() > 0 { if !return_types.is_empty() {
let ret = self.machine.acquire_locations( let ret = self.machine.acquire_locations(
&mut self.assembler, &mut self.assembler,
&[( &[(
@@ -5429,7 +5412,7 @@ impl<'a> FuncGen<'a> {
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => { _ => {
return Err(CodegenError { 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 => { Operator::Else => {
let frame = self.control_stack.last_mut().unwrap(); 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 first_return = frame.returns[0];
let loc = *self.value_stack.last().unwrap(); let loc = *self.value_stack.last().unwrap();
if first_return.is_float() { if first_return.is_float() {
@@ -5497,7 +5480,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { 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], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => { _ => {
return Err(CodegenError { 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; let _activate_offset = self.assembler.get_offset().0;
self.control_stack.push(ControlFrame { self.control_stack.push(ControlFrame {
label: label, label,
loop_like: true, loop_like: true,
if_else: IfElseState::None, if_else: IfElseState::None,
returns: match ty { returns: match ty {
@@ -5606,7 +5590,8 @@ impl<'a> FuncGen<'a> {
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
_ => { _ => {
return Err(CodegenError { 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( Location::Memory(
Machine::get_vmctx_reg(), Machine::get_vmctx_reg(),
self.vmoffsets.vmctx_builtin_function( 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() VMBuiltinFunctionIndex::get_memory32_size_index()
} else { } else {
VMBuiltinFunctionIndex::get_imported_memory32_size_index() VMBuiltinFunctionIndex::get_imported_memory32_size_index()
@@ -5669,7 +5654,7 @@ impl<'a> FuncGen<'a> {
Location::Memory( Location::Memory(
Machine::get_vmctx_reg(), Machine::get_vmctx_reg(),
self.vmoffsets.vmctx_builtin_function( 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() VMBuiltinFunctionIndex::get_memory32_grow_index()
} else { } else {
VMBuiltinFunctionIndex::get_imported_memory32_grow_index() VMBuiltinFunctionIndex::get_imported_memory32_grow_index()
@@ -6035,7 +6020,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { 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 => { Operator::Return => {
let frame = &self.control_stack[0]; let frame = &self.control_stack[0];
if frame.returns.len() > 0 { if !frame.returns.is_empty() {
if frame.returns.len() != 1 { if frame.returns.len() != 1 {
return Err(CodegenError { return Err(CodegenError {
message: format!("Return: incorrect frame.returns"), message: "Return: incorrect frame.returns".to_string(),
}); });
} }
let first_return = frame.returns[0]; let first_return = frame.returns[0];
@@ -6208,10 +6193,10 @@ impl<'a> FuncGen<'a> {
Operator::Br { relative_depth } => { Operator::Br { relative_depth } => {
let frame = let frame =
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; &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 { if frame.returns.len() != 1 {
return Err(CodegenError { return Err(CodegenError {
message: format!("Br: incorrect frame.returns"), message: "Br: incorrect frame.returns".to_string(),
}); });
} }
let first_return = frame.returns[0]; let first_return = frame.returns[0];
@@ -6262,10 +6247,10 @@ impl<'a> FuncGen<'a> {
let frame = let frame =
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; &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 { if frame.returns.len() != 1 {
return Err(CodegenError { 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); table.push(label);
let frame = let frame =
&self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; &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 { if frame.returns.len() != 1 {
return Err(CodegenError { return Err(CodegenError {
message: format!( message: format!(
@@ -6397,10 +6382,10 @@ impl<'a> FuncGen<'a> {
{ {
let frame = &self.control_stack let frame = &self.control_stack
[self.control_stack.len() - 1 - (default_target as usize)]; [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 { if frame.returns.len() != 1 {
return Err(CodegenError { 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 => { Operator::End => {
let frame = self.control_stack.pop().unwrap(); 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(); let loc = *self.value_stack.last().unwrap();
if frame.returns[0].is_float() { if frame.returns[0].is_float() {
let fp = self.fp_stack.peek1()?; 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.assembler.emit_label(frame.label);
self.machine self.machine
.finalize_locals(&mut self.assembler, &self.locals); .finalize_locals(&mut self.assembler, &self.locals);
@@ -6532,10 +6517,10 @@ impl<'a> FuncGen<'a> {
self.assembler.emit_label(label); self.assembler.emit_label(label);
} }
if frame.returns.len() > 0 { if !frame.returns.is_empty() {
if frame.returns.len() != 1 { if frame.returns.len() != 1 {
return Err(CodegenError { return Err(CodegenError {
message: format!("End: incorrect frame.returns"), message: "End: incorrect frame.returns".to_string(),
}); });
} }
let loc = self.machine.acquire_locations( let loc = self.machine.acquire_locations(
@@ -6747,7 +6732,7 @@ impl<'a> FuncGen<'a> {
} }
_ => { _ => {
return Err(CodegenError { return Err(CodegenError {
message: format!("I64AtomicLoad32U ret: unreachable code"), message: "I64AtomicLoad32U ret: unreachable code".to_string(),
}) })
} }
} }
@@ -8262,12 +8247,8 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
// Calculate stack offset. // Calculate stack offset.
let mut stack_offset: u32 = 0; let mut stack_offset: u32 = 0;
for (i, _param) in sig.params().iter().enumerate() { for (i, _param) in sig.params().iter().enumerate() {
let loc = Machine::get_param_location(1 + i); if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
match loc { stack_offset += 8;
Location::Memory(_, _) => {
stack_offset += 8;
}
_ => {}
} }
} }
@@ -8338,7 +8319,7 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
); );
// Write return value. // Write return value.
if sig.results().len() > 0 { if !sig.results().is_empty() {
a.emit_mov( a.emit_mov(
Size::S64, Size::S64,
Location::GPR(GPR::RAX), Location::GPR(GPR::RAX),
@@ -8374,7 +8355,7 @@ pub fn gen_std_dynamic_import_trampoline(
); );
// Copy arguments. // Copy arguments.
if sig.params().len() > 0 { if !sig.params().is_empty() {
let mut argalloc = ArgumentRegisterAllocator::default(); let mut argalloc = ArgumentRegisterAllocator::default();
argalloc.next(Type::I64).unwrap(); // skip VMContext 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)); a.emit_call_location(Location::GPR(GPR::RAX));
// Fetch return value. // Fetch return value.
if sig.results().len() > 0 { if !sig.results().is_empty() {
assert_eq!(sig.results().len(), 1); assert_eq!(sig.results().len(), 1);
a.emit_mov( a.emit_mov(
Size::S64, Size::S64,
@@ -8470,8 +8451,7 @@ pub fn gen_import_call_trampoline(
if sig if sig
.params() .params()
.iter() .iter()
.find(|&&x| x == Type::F32 || x == Type::F64) .any(|&x| x == Type::F32 || x == Type::F64)
.is_some()
{ {
let mut param_locations: Vec<Location> = vec![]; let mut param_locations: Vec<Location> = vec![];
@@ -8493,8 +8473,7 @@ pub fn gen_import_call_trampoline(
for i in 0..sig.params().len() { for i in 0..sig.params().len() {
let loc = match i { let loc = match i {
0..=4 => { 0..=4 => {
static PARAM_REGS: &'static [GPR] = static PARAM_REGS: &[GPR] = &[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
&[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
let loc = Location::Memory(GPR::RSP, (i * 8) as i32); let loc = Location::Memory(GPR::RSP, (i * 8) as i32);
a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc); a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc);
loc loc

View File

@@ -161,7 +161,7 @@ impl MachineState {
.enumerate() .enumerate()
.find(|&(_, (a, b))| a != b) .find(|&(_, (a, b))| a != b)
.map(|x| x.0) .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()); assert_eq!(self.register_values.len(), old.register_values.len());
let reg_diff: Vec<_> = self let reg_diff: Vec<_> = self
.register_values .register_values
@@ -196,7 +196,7 @@ impl MachineState {
.enumerate() .enumerate()
.find(|&(_, (a, b))| a != b) .find(|&(_, (a, b))| a != b)
.map(|x| x.0) .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 { MachineStateDiff {
last: None, last: None,
stack_push: self.stack_values[first_diff_stack_depth..].to_vec(), 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, gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline,
CodegenError, FuncGen, CodegenError, FuncGen,
}; };
use crate::config::SinglepassConfig; use crate::config::Singlepass;
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::sync::Arc; use std::sync::Arc;
use wasm_common::entity::{EntityRef, PrimaryMap}; use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::Features;
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex}; use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_compiler::wasmparser::BinaryReaderError; use wasmer_compiler::wasmparser::BinaryReaderError;
use wasmer_compiler::TrapInformation; use wasmer_compiler::TrapInformation;
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler, SectionIndex}; use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler, SectionIndex};
use wasmer_compiler::{ use wasmer_compiler::{
CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader, ModuleTranslationState, Target, CompileModuleInfo, CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader,
ModuleTranslationState, Target,
}; };
use wasmer_compiler::{FunctionBody, FunctionBodyData}; use wasmer_compiler::{FunctionBody, FunctionBodyData};
use wasmer_runtime::ModuleInfo; use wasmer_runtime::{ModuleInfo, TrapCode, VMOffsets};
use wasmer_runtime::TrapCode;
use wasmer_runtime::{MemoryPlan, TablePlan, VMOffsets};
/// A compiler that compiles a WebAssembly module with Singlepass. /// A compiler that compiles a WebAssembly module with Singlepass.
/// It does the compilation in one pass /// It does the compilation in one pass
pub struct SinglepassCompiler { pub struct SinglepassCompiler {
config: SinglepassConfig, config: Singlepass,
} }
impl SinglepassCompiler { impl SinglepassCompiler {
/// Creates a new Singlepass compiler /// Creates a new Singlepass compiler
pub fn new(config: &SinglepassConfig) -> Self { pub fn new(config: &Singlepass) -> Self {
Self { Self {
config: config.clone(), config: config.clone(),
} }
} }
/// Gets the WebAssembly features for this Compiler /// Gets the config for this Compiler
fn config(&self) -> &SinglepassConfig { fn config(&self) -> &Singlepass {
&self.config &self.config
} }
} }
impl Compiler for SinglepassCompiler { 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 /// Compile the module using Singlepass, producing a compilation result with
/// associated relocations. /// associated relocations.
fn compile_module( fn compile_module(
&self, &self,
module: &ModuleInfo, _target: &Target,
compile_info: &CompileModuleInfo,
_module_translation: &ModuleTranslationState, _module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>, function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> { ) -> 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) let import_trampolines: PrimaryMap<SectionIndex, _> = (0..module.num_imported_funcs)
.map(FunctionIndex::new) .map(FunctionIndex::new)
.collect::<Vec<_>>() .collect::<Vec<_>>()

View File

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

View File

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

View File

@@ -21,4 +21,4 @@ mod machine;
mod x64_decl; mod x64_decl;
pub use crate::compiler::SinglepassCompiler; 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. /// This method does not mark the register as used.
pub fn pick_gpr(&self) -> Option<GPR> { pub fn pick_gpr(&self) -> Option<GPR> {
use 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 { for r in REGS {
if !self.used_gprs.contains(r) { if !self.used_gprs.contains(r) {
return Some(*r); return Some(*r);
@@ -64,7 +64,7 @@ impl Machine {
/// This method does not mark the register as used. /// This method does not mark the register as used.
pub fn pick_temp_gpr(&self) -> Option<GPR> { pub fn pick_temp_gpr(&self) -> Option<GPR> {
use GPR::*; use GPR::*;
static REGS: &'static [GPR] = &[RAX, RCX, RDX]; static REGS: &[GPR] = &[RAX, RCX, RDX];
for r in REGS { for r in REGS {
if !self.used_gprs.contains(r) { if !self.used_gprs.contains(r) {
return Some(*r); return Some(*r);
@@ -99,7 +99,7 @@ impl Machine {
/// This method does not mark the register as used. /// This method does not mark the register as used.
pub fn pick_xmm(&self) -> Option<XMM> { pub fn pick_xmm(&self) -> Option<XMM> {
use XMM::*; use XMM::*;
static REGS: &'static [XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7]; static REGS: &[XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7];
for r in REGS { for r in REGS {
if !self.used_xmms.contains(r) { if !self.used_xmms.contains(r) {
return Some(*r); return Some(*r);
@@ -113,7 +113,7 @@ impl Machine {
/// This method does not mark the register as used. /// This method does not mark the register as used.
pub fn pick_temp_xmm(&self) -> Option<XMM> { pub fn pick_temp_xmm(&self) -> Option<XMM> {
use XMM::*; use XMM::*;
static REGS: &'static [XMM] = &[XMM0, XMM1, XMM2]; static REGS: &[XMM] = &[XMM0, XMM1, XMM2];
for r in REGS { for r in REGS {
if !self.used_xmms.contains(r) { if !self.used_xmms.contains(r) {
return Some(*r); return Some(*r);
@@ -260,20 +260,17 @@ impl Machine {
let mut delta_stack_offset: usize = 0; let mut delta_stack_offset: usize = 0;
for loc in locs.iter().rev() { for loc in locs.iter().rev() {
match *loc { if let Location::Memory(GPR::RBP, x) = *loc {
Location::Memory(GPR::RBP, x) => { if x >= 0 {
if x >= 0 { unreachable!();
unreachable!();
}
let offset = (-x) as usize;
if offset != self.stack_offset.0 {
unreachable!();
}
self.stack_offset.0 -= 8;
delta_stack_offset += 8;
self.state.stack_values.pop().unwrap();
} }
_ => {} let offset = (-x) as usize;
if offset != self.stack_offset.0 {
unreachable!();
}
self.stack_offset.0 -= 8;
delta_stack_offset += 8;
self.state.stack_values.pop().unwrap();
} }
// Wasm state popping is deferred to `release_locations_only_osr_state`. // Wasm state popping is deferred to `release_locations_only_osr_state`.
} }
@@ -302,19 +299,16 @@ impl Machine {
let mut stack_offset = self.stack_offset.0; let mut stack_offset = self.stack_offset.0;
for loc in locs.iter().rev() { for loc in locs.iter().rev() {
match *loc { if let Location::Memory(GPR::RBP, x) = *loc {
Location::Memory(GPR::RBP, x) => { if x >= 0 {
if x >= 0 { unreachable!();
unreachable!();
}
let offset = (-x) as usize;
if offset != stack_offset {
unreachable!();
}
stack_offset -= 8;
delta_stack_offset += 8;
} }
_ => {} let offset = (-x) as usize;
if offset != stack_offset {
unreachable!();
}
stack_offset -= 8;
delta_stack_offset += 8;
} }
} }

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

View File

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

View File

@@ -57,6 +57,7 @@ mod compiler;
mod error; mod error;
mod function; mod function;
mod jump_table; mod jump_table;
mod module;
mod relocation; mod relocation;
mod target; mod target;
mod trap; mod trap;
@@ -76,6 +77,7 @@ pub use crate::function::{
Functions, Functions,
}; };
pub use crate::jump_table::{JumpTable, JumpTableOffsets}; pub use crate::jump_table::{JumpTable, JumpTableOffsets};
pub use crate::module::CompileModuleInfo;
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations}; pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex}; pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex};
pub use crate::sourceloc::SourceLoc; 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, Abs8,
/// x86 PC-relative 4-byte /// x86 PC-relative 4-byte
X86PCRel4, X86PCRel4,
/// x86 PC-relative 8-byte
X86PCRel8,
/// x86 PC-relative 4-byte offset to trailing rodata /// x86 PC-relative 4-byte offset to trailing rodata
X86PCRelRodata4, X86PCRelRodata4,
/// x86 call to PC-relative 4-byte /// x86 call to PC-relative 4-byte
@@ -62,6 +64,7 @@ impl fmt::Display for RelocationKind {
Self::Abs4 => write!(f, "Abs4"), Self::Abs4 => write!(f, "Abs4"),
Self::Abs8 => write!(f, "Abs8"), Self::Abs8 => write!(f, "Abs8"),
Self::X86PCRel4 => write!(f, "PCRel4"), Self::X86PCRel4 => write!(f, "PCRel4"),
Self::X86PCRel8 => write!(f, "PCRel8"),
Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"), Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
Self::X86CallPCRel4 => write!(f, "CallPCRel4"), Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"), Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
@@ -112,7 +115,7 @@ impl Relocation {
RelocationKind::Abs8 => { RelocationKind::Abs8 => {
let reloc_address = start + self.offset as usize; let reloc_address = start + self.offset as usize;
let reloc_addend = self.addend as isize; 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) .checked_add(reloc_addend as u64)
.unwrap(); .unwrap();
(reloc_address, reloc_abs) (reloc_address, reloc_abs)
@@ -126,6 +129,15 @@ impl Relocation {
.unwrap(); .unwrap();
(reloc_address, reloc_delta_u32 as u64) (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 => { RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
let reloc_address = start + self.offset as usize; let reloc_address = start + self.offset as usize;
let reloc_addend = self.addend as isize; 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. /// A shared builder for function middlewares.
pub trait FunctionMiddlewareGenerator: Debug + Send + Sync { pub trait FunctionMiddlewareGenerator: Debug + Send + Sync {
/// Generates a `FunctionMiddleware` for a given function. /// Generates a `FunctionMiddleware` for a given function.
fn generate<'a>(&self, local_function_index: LocalFunctionIndex) fn generate(&self, local_function_index: LocalFunctionIndex) -> Box<dyn FunctionMiddleware>;
-> Box<dyn FunctionMiddleware>;
} }
/// A function middleware specialized for a single function. /// A function middleware specialized for a single function.

View File

@@ -61,27 +61,24 @@ pub fn parse_type_section(
environ.reserve_signatures(count)?; environ.reserve_signatures(count)?;
for entry in types { for entry in types {
match entry.map_err(to_wasm_error)? { let WPFunctionType { params, returns } = entry.map_err(to_wasm_error)?;
WPFunctionType { params, returns } => { let sig_params: Vec<Type> = params
let sig_params: Vec<Type> = params .iter()
.iter() .map(|ty| {
.map(|ty| { wptype_to_type(*ty)
wptype_to_type(*ty) .expect("only numeric types are supported in function signatures")
.expect("only numeric types are supported in function signatures") })
}) .collect();
.collect(); let sig_returns: Vec<Type> = returns
let sig_returns: Vec<Type> = returns .iter()
.iter() .map(|ty| {
.map(|ty| { wptype_to_type(*ty)
wptype_to_type(*ty) .expect("only numeric types are supported in function signatures")
.expect("only numeric types are supported in function signatures") })
}) .collect();
.collect(); let sig = FunctionType::new(sig_params, sig_returns);
let sig = FunctionType::new(sig_params, sig_returns); environ.declare_signature(sig)?;
environ.declare_signature(sig)?; module_translation_state.wasm_types.push((params, returns));
module_translation_state.wasm_types.push((params, returns));
}
}
} }
Ok(()) Ok(())
} }

View File

@@ -263,7 +263,7 @@ pub fn _getaddrinfo(
.get_mut(); .get_mut();
guest_sockaddr.sa_family = (*host_sockaddr_ptr).sa_family as i16; 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 guest_sockaddr_ptr
}; };
@@ -308,7 +308,7 @@ pub fn _getaddrinfo(
} }
// this frees all connected nodes on the linked list // this frees all connected nodes on the linked list
freeaddrinfo(out_ptr); 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); 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)] #[allow(clippy::cast_ptr_alignment)]
let ptr = emscripten_memory_pointer!(memory, set) as *mut i32; let ptr = emscripten_memory_pointer!(memory, set) as *mut i32;
unsafe { *ptr = *ptr & !(1 << (signum - 1)) } unsafe { *ptr &= !(1 << (signum - 1)) }
0 0
} }

View File

@@ -105,7 +105,7 @@ const STATIC_BUMP: u32 = 215_536;
lazy_static! { lazy_static! {
static ref OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG: FunctionType = 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. // 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 /// Top level function to execute emscripten
pub fn run_emscripten_instance<'a>( pub fn run_emscripten_instance(
instance: &mut Instance, instance: &mut Instance,
env: &mut EmEnv, env: &mut EmEnv,
globals: &mut EmscriptenGlobals, globals: &mut EmscriptenGlobals,
@@ -1105,7 +1105,7 @@ pub fn generate_emscripten_env(
// Compatibility with newer versions of Emscripten // Compatibility with newer versions of Emscripten
let mut to_insert: Vec<(String, _)> = vec![]; let mut to_insert: Vec<(String, _)> = vec![];
for (k, v) in env_ns.iter() { for (k, v) in env_ns.iter() {
if k.starts_with("_") { if k.starts_with('_') {
let k = &k[1..]; let k = &k[1..];
if !env_ns.contains(k) { if !env_ns.contains(k) {
to_insert.push((k.to_string(), v.clone())); 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); ctx.memory(0).view::<u32>()[dynamictop_ptr].set(new_dynamic_top as u32);
return old_dynamic_top as _; old_dynamic_top as _
} }
/// emscripten: getTotalMemory /// emscripten: getTotalMemory

View File

@@ -33,11 +33,11 @@ pub fn _pthread_attr_setstacksize(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
0 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"); 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"); trace!("emscripten::_pthread_cleanup_push");
} }
@@ -97,7 +97,7 @@ pub fn _pthread_equal(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
0 0
} }
pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) -> () { pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) {
trace!("emscripten::_pthread_exit"); 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() }; 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_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? // why is this here?
// set_cloexec // 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 // translate from host data into emscripten data
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() }; 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_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!( debug!(
"=> socket: {}, address, {:?}, address_len: {}, result = {}", "=> 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 timespec = unsafe {
let time_p_addr = emscripten_memory_pointer!(ctx.memory(0), time_p) as *mut i64; 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) time::Timespec::new(seconds, 0)
}; };
let result_tm = time::at(timespec); 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(); let ty = import.ty();
Ok((ty.minimum, ty.maximum)) Ok((ty.minimum, ty.maximum))
} else { } 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(); let ty = import.ty();
Ok((ty.minimum, ty.maximum, ty.shared)) Ok((ty.minimum, ty.maximum, ty.shared))
} else { } 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, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex, TableIndex,
}; };
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{CompileError, Features, Triple}; use wasmer_compiler::{CompileError, Features, Triple};
#[cfg(feature = "compiler")]
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment};
use wasmer_engine::{ use wasmer_engine::{
register_frame_info, Artifact, DeserializeError, GlobalFrameInfoRegistration, SerializeError, register_frame_info, Artifact, DeserializeError, GlobalFrameInfoRegistration, SerializeError,
Tunables,
}; };
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_engine::{Engine, SerializableFunctionFrameInfo}; use wasmer_engine::{Engine, SerializableFunctionFrameInfo};
@@ -43,10 +44,14 @@ impl JITArtifact {
/// Compile a data buffer into a `JITArtifact`, which may then be instantiated. /// Compile a data buffer into a `JITArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")] #[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 environ = ModuleEnvironment::new();
let mut inner_jit = jit.inner_mut(); 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)?; let translation = environ.translate(data).map_err(CompileError::Wasm)?;
@@ -63,15 +68,21 @@ impl JITArtifact {
.map(|table_type| tunables.table_plan(*table_type)) .map(|table_type| tunables.table_plan(*table_type))
.collect(); .collect();
let compile_info = CompileModuleInfo {
module: Arc::new(translation.module),
features: features.clone(),
memory_plans,
table_plans,
};
let compiler = inner_jit.compiler()?; let compiler = inner_jit.compiler()?;
// Compile the Module // Compile the Module
let compilation = compiler.compile_module( let compilation = compiler.compile_module(
&translation.module, &jit.target(),
&compile_info,
translation.module_translation.as_ref().unwrap(), translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs, translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?; )?;
let function_call_trampolines = compilation.get_function_call_trampolines(); let function_call_trampolines = compilation.get_function_call_trampolines();
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
@@ -102,11 +113,8 @@ impl JITArtifact {
}; };
let serializable = SerializableModule { let serializable = SerializableModule {
compilation: serializable_compilation, compilation: serializable_compilation,
module: Arc::new(translation.module), compile_info,
features: inner_jit.compiler()?.features().clone(),
data_initializers, data_initializers,
memory_plans,
table_plans,
}; };
Self::from_parts(&mut inner_jit, serializable) Self::from_parts(&mut inner_jit, serializable)
} }
@@ -150,7 +158,7 @@ impl JITArtifact {
finished_dynamic_function_trampolines, finished_dynamic_function_trampolines,
) = inner_jit.allocate( ) = inner_jit.allocate(
&mut unwind_registry, &mut unwind_registry,
&serializable.module, &serializable.compile_info.module,
&serializable.compilation.function_bodies, &serializable.compilation.function_bodies,
&serializable.compilation.function_call_trampolines, &serializable.compilation.function_call_trampolines,
&serializable.compilation.dynamic_function_trampolines, &serializable.compilation.dynamic_function_trampolines,
@@ -159,7 +167,7 @@ impl JITArtifact {
inner_jit.allocate_custom_sections(&serializable.compilation.custom_sections)?; inner_jit.allocate_custom_sections(&serializable.compilation.custom_sections)?;
link_module( link_module(
&serializable.module, &serializable.compile_info.module,
&finished_functions, &finished_functions,
&serializable.compilation.function_jt_offsets, &serializable.compilation.function_jt_offsets,
serializable.compilation.function_relocations.clone(), serializable.compilation.function_relocations.clone(),
@@ -171,6 +179,7 @@ impl JITArtifact {
let signatures = { let signatures = {
let signature_registry = inner_jit.signatures(); let signature_registry = inner_jit.signatures();
serializable serializable
.compile_info
.module .module
.signatures .signatures
.values() .values()
@@ -227,15 +236,15 @@ impl JITArtifact {
impl Artifact for JITArtifact { impl Artifact for JITArtifact {
fn module(&self) -> Arc<ModuleInfo> { fn module(&self) -> Arc<ModuleInfo> {
self.serializable.module.clone() self.serializable.compile_info.module.clone()
} }
fn module_ref(&self) -> &ModuleInfo { fn module_ref(&self) -> &ModuleInfo {
&self.serializable.module &self.serializable.compile_info.module
} }
fn module_mut(&mut self) -> Option<&mut ModuleInfo> { 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) { fn register_frame_info(&self) {
@@ -248,14 +257,14 @@ impl Artifact for JITArtifact {
let frame_infos = &self.serializable.compilation.function_frame_info; let frame_infos = &self.serializable.compilation.function_frame_info;
let finished_functions = &self.finished_functions; let finished_functions = &self.finished_functions;
*info = register_frame_info( *info = register_frame_info(
self.serializable.module.clone(), self.serializable.compile_info.module.clone(),
finished_functions, finished_functions,
frame_infos.clone(), frame_infos.clone(),
); );
} }
fn features(&self) -> &Features { fn features(&self) -> &Features {
&self.serializable.features &self.serializable.compile_info.features
} }
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
@@ -263,11 +272,11 @@ impl Artifact for JITArtifact {
} }
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> { fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.serializable.memory_plans &self.serializable.compile_info.memory_plans
} }
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> { 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]> { 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. //! Memory management for executable code.
use crate::unwind::UnwindRegistry; use crate::unwind::UnwindRegistry;
use region;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::sync::Arc; use std::sync::Arc;
use std::{cmp, mem}; use std::{cmp, mem};
@@ -150,7 +149,7 @@ impl CodeMemory {
Ok(self Ok(self
.read_sections .read_sections
.last_mut() .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. /// Make all allocated memory executable.

View File

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

View File

@@ -26,6 +26,7 @@
)] )]
mod artifact; mod artifact;
mod builder;
mod code_memory; mod code_memory;
mod engine; mod engine;
mod link; mod link;
@@ -33,6 +34,7 @@ mod serialize;
mod unwind; mod unwind;
pub use crate::artifact::JITArtifact; pub use crate::artifact::JITArtifact;
pub use crate::builder::JIT;
pub use crate::code_memory::CodeMemory; pub use crate::code_memory::CodeMemory;
pub use crate::engine::JITEngine; pub use crate::engine::JITEngine;
pub use crate::link::link_module; 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); let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
write_unaligned(reloc_address as *mut u32, reloc_delta as _); 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")] #[cfg(target_pointer_width = "32")]
RelocationKind::X86CallPCRel4 => unsafe { RelocationKind::X86CallPCRel4 => unsafe {
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); 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 serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasm_common::entity::PrimaryMap; use wasm_common::entity::PrimaryMap;
use wasm_common::{ use wasm_common::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
use wasmer_compiler::{ use wasmer_compiler::{
CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation, SectionIndex, CompileModuleInfo, CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation,
SectionIndex,
}; };
use wasmer_engine::SerializableFunctionFrameInfo; use wasmer_engine::SerializableFunctionFrameInfo;
use wasmer_runtime::ModuleInfo;
use wasmer_runtime::{MemoryPlan, TablePlan};
// /// The serializable function data // /// The serializable function data
// #[derive(Serialize, Deserialize)] // #[derive(Serialize, Deserialize)]
@@ -45,10 +40,6 @@ pub struct SerializableCompilation {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct SerializableModule { pub struct SerializableModule {
pub compilation: SerializableCompilation, pub compilation: SerializableCompilation,
pub features: Features, pub compile_info: CompileModuleInfo,
pub module: Arc<ModuleInfo>,
pub data_initializers: Box<[OwnedDataInitializer]>, 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-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" } wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
wasmer-engine = { path = "../engine", 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 = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" } serde_bytes = { version = "0.11" }
cfg-if = "0.1" cfg-if = "0.1"

View File

@@ -4,10 +4,6 @@
use crate::engine::{NativeEngine, NativeEngineInner}; use crate::engine::{NativeEngine, NativeEngineInner};
use crate::serialize::ModuleMetadata; use crate::serialize::ModuleMetadata;
use libloading::{Library, Symbol as LibrarySymbol}; 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::error::Error;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
@@ -20,20 +16,22 @@ use tempfile::NamedTempFile;
use tracing::trace; use tracing::trace;
use wasm_common::entity::{BoxedSlice, PrimaryMap}; use wasm_common::entity::{BoxedSlice, PrimaryMap};
use wasm_common::{ use wasm_common::{
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex, DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer,
TableIndex, SignatureIndex, TableIndex,
}; };
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::{ use wasmer_compiler::{
Architecture, BinaryFormat, CustomSectionProtection, Endianness, ModuleEnvironment, Compilation, CompileModuleInfo, Compiler, ModuleEnvironment, OperatingSystem, Target, Triple,
OperatingSystem, RelocationTarget, Triple,
}; };
use wasmer_compiler::{CompileError, Features}; use wasmer_compiler::{CompileError, Features};
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_engine::Engine; use wasmer_engine::Engine;
use wasmer_engine::{ use wasmer_engine::{
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError, 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::{MemoryPlan, TablePlan};
use wasmer_runtime::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline}; 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. /// Generate a compilation
#[cfg(feature = "compiler")] pub fn generate_compilation<'data>(
pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> { 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 environ = ModuleEnvironment::new();
let mut engine_inner = engine.inner_mut();
let tunables = engine.tunables();
let translation = environ.translate(data).map_err(CompileError::Wasm)?; let translation = environ.translate(data).map_err(CompileError::Wasm)?;
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
.module .module
.memories .memories
@@ -115,272 +114,73 @@ impl NativeArtifact {
.map(|table_type| tunables.table_plan(*table_type)) .map(|table_type| tunables.table_plan(*table_type))
.collect(); .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 // Compile the Module
let compilation = compiler.compile_module( let compilation = compiler.compile_module(
&translation.module, &target,
&compile_info,
translation.module_translation.as_ref().unwrap(), translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs, translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?; )?;
let function_call_trampolines = compilation.get_function_call_trampolines(); Ok((compile_info, compilation, translation.data_initializers))
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); }
let data_initializers = translation /// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
.data_initializers #[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() .iter()
.map(OwnedDataInitializer::new) .map(OwnedDataInitializer::new)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_boxed_slice(); .into_boxed_slice();
let target = compiler.target();
let target_triple = target.triple().clone(); 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 // We construct the function body lengths
let function_body_lengths = function_bodies let function_body_lengths = compilation
.get_function_bodies()
.values() .values()
.map(|function_body| function_body.body.len() as u64) .map(|function_body| function_body.body.len() as u64)
.collect::<PrimaryMap<LocalFunctionIndex, u64>>(); .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
let metadata = ModuleMetadata { let metadata = ModuleMetadata {
features: compiler.features().clone(), compile_info,
module: Arc::new(translation.module),
prefix: engine_inner.get_prefix(&data), prefix: engine_inner.get_prefix(&data),
data_initializers, data_initializers,
function_body_lengths, 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 serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
let mut metadata_length = vec![0; 10]; let mut metadata_binary = vec![0; 10];
let mut writable = &mut metadata_length[..]; let mut writable = &mut metadata_binary[..];
leb128::write::unsigned(&mut writable, serialized_data.len() as u64) leb128::write::unsigned(&mut writable, serialized_data.len() as u64)
.expect("Should write number"); .expect("Should write number");
metadata_length.extend(serialized_data); metadata_binary.extend(serialized_data);
let symbol_id = obj.add_symbol(Symbol { emit_data(&mut obj, b"WASMER_METADATA", &metadata_binary).map_err(to_compile_error)?;
name: b"WASMER_METADATA".to_vec(), emit_compilation(&mut obj, compilation, &metadata).map_err(to_compile_error)?;
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
}
};
}
}
let filepath = { let filepath = {
let file = tempfile::Builder::new() let file = tempfile::Builder::new()
@@ -511,7 +311,7 @@ impl NativeArtifact {
let mut finished_functions: PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]> = let mut finished_functions: PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]> =
PrimaryMap::new(); PrimaryMap::new();
for (function_local_index, function_len) in metadata.function_body_lengths.iter() { 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 { unsafe {
// We use a fake function signature `fn()` because we just // We use a fake function signature `fn()` because we just
// want to get the function address. // want to get the function address.
@@ -530,8 +330,8 @@ impl NativeArtifact {
} }
// Retrieve function call trampolines (for all signatures in the module) // Retrieve function call trampolines (for all signatures in the module)
for (sig_index, func_type) in metadata.module.signatures.iter() { for (sig_index, func_type) in metadata.compile_info.module.signatures.iter() {
let function_name = metadata.get_function_call_trampoline_name(sig_index); let function_name = metadata.get_function_call_trampoline_name(&sig_index);
unsafe { unsafe {
let trampoline: LibrarySymbol<VMTrampoline> = lib let trampoline: LibrarySymbol<VMTrampoline> = lib
.get(function_name.as_bytes()) .get(function_name.as_bytes())
@@ -544,14 +344,15 @@ impl NativeArtifact {
let mut finished_dynamic_function_trampolines: PrimaryMap< let mut finished_dynamic_function_trampolines: PrimaryMap<
FunctionIndex, FunctionIndex,
*mut [VMFunctionBody], *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 for func_index in metadata
.compile_info
.module .module
.functions .functions
.keys() .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 { unsafe {
let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib
.get(function_name.as_bytes()) .get(function_name.as_bytes())
@@ -582,6 +383,7 @@ impl NativeArtifact {
let signatures = { let signatures = {
let signature_registry = engine_inner.signatures(); let signature_registry = engine_inner.signatures();
metadata metadata
.compile_info
.module .module
.signatures .signatures
.values() .values()
@@ -697,15 +499,15 @@ impl NativeArtifact {
impl Artifact for NativeArtifact { impl Artifact for NativeArtifact {
fn module(&self) -> Arc<ModuleInfo> { fn module(&self) -> Arc<ModuleInfo> {
self.metadata.module.clone() self.metadata.compile_info.module.clone()
} }
fn module_ref(&self) -> &ModuleInfo { fn module_ref(&self) -> &ModuleInfo {
&self.metadata.module &self.metadata.compile_info.module
} }
fn module_mut(&mut self) -> Option<&mut ModuleInfo> { 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) { fn register_frame_info(&self) {
@@ -713,7 +515,7 @@ impl Artifact for NativeArtifact {
} }
fn features(&self) -> &Features { fn features(&self) -> &Features {
&self.metadata.features &self.metadata.compile_info.features
} }
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
@@ -721,11 +523,11 @@ impl Artifact for NativeArtifact {
} }
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> { fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.metadata.memory_plans &self.metadata.compile_info.memory_plans
} }
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> { 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]> { 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::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use wasm_common::FunctionType; use wasm_common::{Features, FunctionType};
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")] #[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_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline}; use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
@@ -16,27 +16,24 @@ use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
#[derive(Clone)] #[derive(Clone)]
pub struct NativeEngine { pub struct NativeEngine {
inner: Arc<Mutex<NativeEngineInner>>, inner: Arc<Mutex<NativeEngineInner>>,
tunables: Arc<dyn Tunables + Send + Sync>, /// The target for the compiler
target: Arc<Target>,
engine_id: EngineId, engine_id: EngineId,
} }
impl NativeEngine { impl NativeEngine {
/// Create a new `NativeEngine` with the given config /// Create a new `NativeEngine` with the given config
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn new( pub fn new(compiler: Box<dyn Compiler + Send>, target: Target, features: Features) -> Self {
mut config: Box<dyn CompilerConfig>,
tunables: impl Tunables + 'static + Send + Sync,
) -> Self {
config.enable_pic();
let compiler = config.compiler();
Self { Self {
inner: Arc::new(Mutex::new(NativeEngineInner { inner: Arc::new(Mutex::new(NativeEngineInner {
compiler: Some(compiler), compiler: Some(compiler),
trampolines: HashMap::new(), trampolines: HashMap::new(),
signatures: SignatureRegistry::new(), signatures: SignatureRegistry::new(),
prefixer: None, prefixer: None,
features,
})), })),
tunables: Arc::new(tunables), target: Arc::new(target),
engine_id: EngineId::default(), engine_id: EngineId::default(),
} }
} }
@@ -54,7 +51,7 @@ impl NativeEngine {
/// ///
/// Headless engines can't compile or validate any modules, /// Headless engines can't compile or validate any modules,
/// they just take already processed Modules (via `Module::serialize`). /// they just take already processed Modules (via `Module::serialize`).
pub fn headless(tunables: impl Tunables + 'static + Send + Sync) -> Self { pub fn headless() -> Self {
Self { Self {
inner: Arc::new(Mutex::new(NativeEngineInner { inner: Arc::new(Mutex::new(NativeEngineInner {
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
@@ -62,8 +59,9 @@ impl NativeEngine {
trampolines: HashMap::new(), trampolines: HashMap::new(),
signatures: SignatureRegistry::new(), signatures: SignatureRegistry::new(),
prefixer: None, prefixer: None,
features: Features::default(),
})), })),
tunables: Arc::new(tunables), target: Arc::new(Target::default()),
engine_id: EngineId::default(), engine_id: EngineId::default(),
} }
} }
@@ -96,9 +94,9 @@ impl NativeEngine {
} }
impl Engine for NativeEngine { impl Engine for NativeEngine {
/// Get the tunables /// The target
fn tunables(&self) -> &dyn Tunables { fn target(&self) -> &Target {
&*self.tunables &self.target
} }
/// Register a signature /// Register a signature
@@ -124,8 +122,12 @@ impl Engine for NativeEngine {
} }
/// Compile a WebAssembly binary /// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError> { fn compile(
Ok(Arc::new(NativeArtifact::new(&self, binary)?)) &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) /// Deserializes a WebAssembly module (binary content of a Shared Object file)
@@ -147,6 +149,10 @@ impl Engine for NativeEngine {
fn id(&self) -> &EngineId { fn id(&self) -> &EngineId {
&self.engine_id &self.engine_id
} }
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
Arc::new(self.clone())
}
} }
/// The inner contents of `NativeEngine` /// The inner contents of `NativeEngine`
@@ -154,6 +160,8 @@ pub struct NativeEngineInner {
/// The compiler /// The compiler
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
compiler: Option<Box<dyn Compiler + Send>>, compiler: Option<Box<dyn Compiler + Send>>,
/// The WebAssembly features to use
features: Features,
/// Pointers to trampoline functions used to enter particular signatures /// Pointers to trampoline functions used to enter particular signatures
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>, trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The signature registry is used mainly to operate with trampolines /// 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 /// Validate the module
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> { 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 /// Validate the module

View File

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

View File

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

View File

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

View File

@@ -11,10 +11,10 @@ use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunables for an engine /// Tunables for an engine
pub trait Tunables { pub trait Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType` /// Construct a `MemoryPlan` for the provided `MemoryType`
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan; 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; fn table_plan(&self, table: TableType) -> TablePlan;
/// Create a memory given a memory type /// 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() .iter()
.filter_map(|(_name, export_index)| match export_index { .filter_map(|(_name, export_index)| match export_index {
ExportIndex::Function(i) => { 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(); let func_type = self.signatures.get(signature.clone()).unwrap();
Some(func_type.clone()) Some(func_type.clone())
} }
@@ -169,20 +169,20 @@ impl ModuleInfo {
let iter = self.exports.iter().map(move |(name, export_index)| { let iter = self.exports.iter().map(move |(name, export_index)| {
let extern_type = match export_index { let extern_type = match export_index {
ExportIndex::Function(i) => { 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(); let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone()) ExternType::Function(func_type.clone())
} }
ExportIndex::Table(i) => { ExportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap(); let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type) ExternType::Table(*table_type)
} }
ExportIndex::Memory(i) => { ExportIndex::Memory(i) => {
let memory_type = self.memories.get(i.clone()).unwrap(); let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type) ExternType::Memory(*memory_type)
} }
ExportIndex::Global(i) => { ExportIndex::Global(i) => {
let global_type = self.globals.get(i.clone()).unwrap(); let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type) ExternType::Global(*global_type)
} }
}; };
@@ -202,20 +202,20 @@ impl ModuleInfo {
.map(move |((module, field, _), import_index)| { .map(move |((module, field, _), import_index)| {
let extern_type = match import_index { let extern_type = match import_index {
ImportIndex::Function(i) => { ImportIndex::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(); let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone()) ExternType::Function(func_type.clone())
} }
ImportIndex::Table(i) => { ImportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap(); let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type) ExternType::Table(*table_type)
} }
ImportIndex::Memory(i) => { ImportIndex::Memory(i) => {
let memory_type = self.memories.get(i.clone()).unwrap(); let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type) ExternType::Memory(*memory_type)
} }
ImportIndex::Global(i) => { ImportIndex::Global(i) => {
let global_type = self.globals.get(i.clone()).unwrap(); let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type) ExternType::Global(*global_type)
} }
}; };

View File

@@ -118,7 +118,7 @@ mod test_vmfunction_body {
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)] #[repr(C)]
pub enum VMFunctionKind { 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, ...) /// (vmctx, vmctx, arg1, arg2...) -> (result1, result2, ...)
/// ///
/// This is the default for functions that are defined: /// This is the default for functions that are defined:
@@ -126,7 +126,7 @@ pub enum VMFunctionKind {
/// 2. In the WebAssembly file /// 2. In the WebAssembly file
Static, 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> /// (ctx, &[Type]) -> Vec<Type>
/// ///
/// This is the default for functions that are defined: /// This is the default for functions that are defined:

View File

@@ -21,13 +21,19 @@ mod utils;
use crate::syscalls::*; 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::syscalls::types;
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion}; pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
use thiserror::Error; use thiserror::Error;
use wasmer::{imports, Function, ImportObject, Memory, Module, Store}; 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}; use std::sync::{Arc, Mutex, MutexGuard};
/// This is returned in `RuntimeError`. /// This is returned in `RuntimeError`.
@@ -45,14 +51,88 @@ pub enum WasiError {
#[derive(Clone)] #[derive(Clone)]
pub struct WasiEnv { pub struct WasiEnv {
state: Arc<Mutex<WasiState>>, 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 { impl WasiEnv {
pub fn new(state: WasiState) -> Self { pub fn new(state: WasiState) -> Self {
Self { Self {
state: Arc::new(Mutex::new(state)), state: Arc::new(Mutex::new(state)),
memory: None, memory: Arc::new(WasiMemory::new()),
} }
} }
@@ -66,8 +146,8 @@ impl WasiEnv {
} }
/// Set the memory /// Set the memory
pub fn set_memory(&mut self, memory: Arc<Memory>) { pub fn set_memory(&mut self, memory: Memory) -> bool {
self.memory = Some(memory); self.memory.set_memory(memory)
} }
/// Get the WASI state /// Get the WASI state
@@ -82,14 +162,14 @@ impl WasiEnv {
/// Get a reference to the memory /// Get a reference to the memory
pub fn memory(&self) -> &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( pub(crate) fn get_memory_and_wasi_state(
&mut self, &mut self,
_mem_index: u32, _mem_index: u32,
) -> (&Memory, MutexGuard<WasiState>) { ) -> (&Memory, MutexGuard<WasiState>) {
let memory = self.memory.as_ref().unwrap(); let memory = self.memory.get_memory().unwrap();
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
(memory, state) (memory, state)
} }

View File

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

View File

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

View File

@@ -1,8 +1,10 @@
/// types for use in the WASI filesystem /// types for use in the WASI filesystem
use crate::syscalls::types::*; use crate::syscalls::types::*;
use serde::{de, Deserialize, Serialize}; use serde::{de, Deserialize, Serialize};
use std::any::Any;
#[cfg(unix)] #[cfg(unix)]
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt;
use std::{ use std::{
fs, fs,
io::{self, Read, Seek, Write}, 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` /// This trait relies on your file closing when it goes out of scope via `Drop`
#[typetag::serde(tag = "type")] #[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 /// the last time the file was accessed in nanoseconds as a UNIX timestamp
fn last_accessed(&self) -> __wasi_timestamp_t; 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)] #[derive(Debug, Clone)]
pub enum PollEvent { pub enum PollEvent {
/// Data available to read /// Data available to read

View File

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

View File

@@ -1,10 +1,8 @@
//! This module permits to create native functions //! This module permits to create native functions
//! easily in Rust, thanks to its advanced typing system. //! easily in Rust, thanks to its advanced typing system.
use crate::types::{FunctionType, Type}; use crate::types::Type;
use crate::values::Value; use crate::values::Value;
use std::convert::Infallible;
use std::marker::PhantomData;
/// `NativeWasmType` represents a native Wasm type. /// `NativeWasmType` represents a native Wasm type.
/// It uses the Rust Type system to automatically detect the /// 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 // pub trait IntegerAtomic
// where // where
// Self: Sized // 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); 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); .fold(CpuFeature::set(), |a, b| a | b);
// Cranelift requires SSE2, so we have this "hack" for now to facilitate // Cranelift requires SSE2, so we have this "hack" for now to facilitate
// usage // usage
features = features | CpuFeature::SSE2; features |= CpuFeature::SSE2;
Target::new(target_triple.clone(), features) Target::new(target_triple.clone(), features)
}) })
.unwrap_or_default(); .unwrap_or_default();

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,3 +11,9 @@ mod multi_value_imports;
mod serialize; mod serialize;
mod traps; mod traps;
mod utils; 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] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(feature = "test-singlepass", ignore)]
fn test_trap_trace() -> Result<()> { fn test_trap_trace() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -119,7 +119,7 @@ fn test_trap_trace_cb() -> Result<()> {
} }
#[test] #[test]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(feature = "test-singlepass", ignore)]
fn test_trap_stack_overflow() -> Result<()> { fn test_trap_stack_overflow() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"
@@ -419,7 +419,7 @@ RuntimeError: indirect call type mismatch
} }
#[ignore] #[ignore]
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)] #[cfg_attr(feature = "test-singlepass", ignore)]
fn start_trap_pretty() -> Result<()> { fn start_trap_pretty() -> Result<()> {
let store = get_store(); let store = get_store();
let wat = r#" let wat = r#"

View File

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

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"))] #![cfg(all(feature = "compiler", feature = "engine"))]
use crate::utils::get_compiler;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use wasmer::{Features, Store};
use test_utils::get_compiler_config_from_str;
use wasmer::{Features, Store, Tunables};
#[cfg(feature = "jit")] #[cfg(feature = "jit")]
use wasmer_engine_jit::JITEngine; use wasmer_engine_jit::JIT;
#[cfg(feature = "native")] #[cfg(feature = "native")]
use wasmer_engine_native::NativeEngine; use wasmer_engine_native::NativeEngine;
use wasmer_wast::Wast; use wasmer_wast::Wast;
@@ -29,7 +28,7 @@ fn native_prefixer(bytes: &[u8]) -> String {
format!("{}", hash.to_hex()) 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!( println!(
"Running wast `{}` with the {} compiler", "Running wast `{}` with the {} compiler",
wast_path, compiler wast_path, compiler
@@ -39,13 +38,13 @@ fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
if wast_path.contains("bulk-memory") { if wast_path.contains("bulk-memory") {
features.bulk_memory(true); features.bulk_memory(true);
} }
let compiler_config = #[cfg(feature = "test-singlepass")]
get_compiler_config_from_str(compiler, try_nan_canonicalization, features); features.multi_value(false);
let tunables = Tunables::for_target(compiler_config.target().triple()); let compiler_config = get_compiler(true);
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables))); let store = Store::new(&JIT::new(&compiler_config).features(features).engine());
// let mut native = NativeEngine::new(compiler_config, tunables); // let mut native = NativeEngine::new(compiler_config, tunables);
// native.set_deterministic_prefixer(native_prefixer); // 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); let mut wast = Wast::new_with_spectest(store);
if compiler == "singlepass" { if compiler == "singlepass" {
// We don't support multivalue yet in 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
wasi::snapshot1::fd_read ### due to hard-coded direct calls into WASI for wasi unstable
wasi::snapshot1::poll_oneoff
wasi::snapshot1::fd_pread wasitests::snapshot1::fd_read
wasi::snapshot1::fd_close wasitests::snapshot1::poll_oneoff
wasi::snapshot1::fd_allocate wasitests::snapshot1::fd_pread
wasi::snapshot1::close_preopen_fd wasitests::snapshot1::fd_close
wasi::snapshot1::envvar 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; use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment; use wasmer_compiler::ModuleEnvironment;
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError}; use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables};
use wasmer_runtime::{ use wasmer_runtime::{
MemoryPlan, ModuleInfo, TablePlan, VMContext, VMFunctionBody, VMSharedSignatureIndex, MemoryPlan, ModuleInfo, TablePlan, VMContext, VMFunctionBody, VMSharedSignatureIndex,
}; };
@@ -55,9 +55,12 @@ impl DummyArtifact {
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
/// Compile a data buffer into a `DummyArtifact`, which may then be instantiated. /// 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 environ = ModuleEnvironment::new();
let tunables = engine.tunables();
let translation = environ.translate(data).map_err(CompileError::Wasm)?; 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