mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-07 05:08:19 +00:00
Merge branch 'master' into feat-deprecated-runtime-core
This commit is contained in:
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -160,6 +160,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build-deps"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64f14468960818ce4f3e3553c32d524446687884f8e7af5d3e252331d8a87e43"
|
||||
dependencies = [
|
||||
"glob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
@@ -1794,18 +1803,6 @@ dependencies = [
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-utils"
|
||||
version = "1.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"wasmer",
|
||||
"wasmer-compiler",
|
||||
"wasmer-compiler-cranelift",
|
||||
"wasmer-compiler-llvm",
|
||||
"wasmer-compiler-singlepass",
|
||||
"wasmer-engine-jit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
@@ -2088,6 +2085,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"blake3",
|
||||
"build-deps",
|
||||
"bytesize",
|
||||
"cfg-if",
|
||||
"colored",
|
||||
@@ -2098,7 +2096,6 @@ dependencies = [
|
||||
"rustc_version",
|
||||
"structopt",
|
||||
"test-generator",
|
||||
"test-utils",
|
||||
"wasm-common",
|
||||
"wasmer",
|
||||
"wasmer-cache",
|
||||
@@ -2297,7 +2294,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"leb128",
|
||||
"libloading 0.6.2",
|
||||
"object",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"tempfile",
|
||||
@@ -2305,9 +2301,20 @@ dependencies = [
|
||||
"wasm-common",
|
||||
"wasmer-compiler",
|
||||
"wasmer-engine",
|
||||
"wasmer-object",
|
||||
"wasmer-runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-object"
|
||||
version = "1.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"object",
|
||||
"thiserror",
|
||||
"wasm-common",
|
||||
"wasmer-compiler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-runtime"
|
||||
version = "1.0.0-alpha.1"
|
||||
@@ -2361,8 +2368,12 @@ name = "wasmer-wast"
|
||||
version = "1.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"typetag",
|
||||
"wasmer",
|
||||
"wasmer-wasi",
|
||||
"wast",
|
||||
]
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ exclude = [
|
||||
|
||||
[build-dependencies]
|
||||
test-generator = { path = "tests/lib/test-generator" }
|
||||
build-deps = "0.1.4"
|
||||
anyhow = "1.0"
|
||||
glob = "0.3"
|
||||
rustc_version = "0.2"
|
||||
@@ -68,7 +69,6 @@ anyhow = "1.0"
|
||||
blake3 = "0.3"
|
||||
criterion = "0.3"
|
||||
lazy_static = "1.4"
|
||||
test-utils = { path = "tests/lib/test-utils" }
|
||||
wasmer-engine-dummy = { path = "tests/lib/engine-dummy" }
|
||||
|
||||
[features]
|
||||
@@ -107,17 +107,14 @@ experimental-io-devices = [
|
||||
]
|
||||
singlepass = [
|
||||
"wasmer-compiler-singlepass",
|
||||
"test-utils/singlepass",
|
||||
"compiler",
|
||||
]
|
||||
cranelift = [
|
||||
"wasmer-compiler-cranelift",
|
||||
"test-utils/cranelift",
|
||||
"compiler",
|
||||
]
|
||||
llvm = [
|
||||
"wasmer-compiler-llvm",
|
||||
"test-utils/llvm",
|
||||
"compiler",
|
||||
]
|
||||
|
||||
|
||||
4
Makefile
4
Makefile
@@ -111,6 +111,7 @@ test-packages:
|
||||
cargo test -p wasmer --release
|
||||
cargo test -p wasmer-runtime --release
|
||||
cargo test -p wasm-common --release
|
||||
cargo test -p wasmer-wasi --release
|
||||
|
||||
test-capi-singlepass: build-capi-singlepass
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
@@ -126,6 +127,9 @@ test-capi-llvm: build-capi-llvm
|
||||
|
||||
test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-emscripten
|
||||
|
||||
test-wasi-unit:
|
||||
cargo test --manifest-path lib/wasi/Cargo.toml --release
|
||||
|
||||
#############
|
||||
# Packaging #
|
||||
#############
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use test_utils::get_compiler_config_from_str;
|
||||
|
||||
use wasmer::*;
|
||||
use wasmer_engine_jit::JITEngine;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
static BASIC_WAT: &str = r#"(module
|
||||
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
|
||||
@@ -151,19 +150,19 @@ pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Cr
|
||||
fn run_static_benchmarks(c: &mut Criterion) {
|
||||
#[cfg(feature = "llvm")]
|
||||
{
|
||||
let store = test_utils::get_default_llvm_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_llvm::LLVM::new()).engine());
|
||||
run_basic_static_function(&store, "llvm", c);
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift")]
|
||||
{
|
||||
let store = test_utils::get_default_cranelift_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_cranelift::Cranelift::new()).engine());
|
||||
run_basic_static_function(&store, "cranelift", c);
|
||||
}
|
||||
|
||||
#[cfg(feature = "singlepass")]
|
||||
{
|
||||
let store = test_utils::get_default_singlepass_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_singlepass::Singlepass::new()).engine());
|
||||
run_basic_static_function(&store, "singlepass", c);
|
||||
}
|
||||
}
|
||||
@@ -171,19 +170,19 @@ fn run_static_benchmarks(c: &mut Criterion) {
|
||||
fn run_dynamic_benchmarks(c: &mut Criterion) {
|
||||
#[cfg(feature = "llvm")]
|
||||
{
|
||||
let store = test_utils::get_default_llvm_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_llvm::LLVM::new()).engine());
|
||||
run_basic_dynamic_function(&store, "llvm", c);
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift")]
|
||||
{
|
||||
let store = test_utils::get_default_cranelift_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_cranelift::Cranelift::new()).engine());
|
||||
run_basic_dynamic_function(&store, "cranelift", c);
|
||||
}
|
||||
|
||||
#[cfg(feature = "singlepass")]
|
||||
{
|
||||
let store = test_utils::get_default_singlepass_store();
|
||||
let store = Store::new(&JIT::new(&wasmer_compiler_singlepass::Singlepass::new()).engine());
|
||||
run_basic_dynamic_function(&store, "singlepass", c);
|
||||
}
|
||||
}
|
||||
|
||||
96
build.rs
96
build.rs
@@ -9,53 +9,91 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use test_generator::{
|
||||
build_ignores_from_textfile, test_directory, test_directory_module, wast_processor,
|
||||
with_features, with_test_module, Testsuite,
|
||||
build_ignores_from_textfile, test_directory, test_directory_module, wasi_processor,
|
||||
wast_processor, with_features, with_test_module, Testsuite,
|
||||
};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=tests/ignores.txt");
|
||||
// As rerun-if-changed doesn't support globs, we use another crate
|
||||
// to check changes in directories.
|
||||
build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/unstable/*")
|
||||
.expect("Can't get directory");
|
||||
build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/snapshot1/*")
|
||||
.expect("Can't get directory");
|
||||
|
||||
let out_dir = PathBuf::from(
|
||||
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
|
||||
);
|
||||
let ignores = build_ignores_from_textfile("tests/ignores.txt".into())?;
|
||||
let compilers = ["singlepass", "cranelift", "llvm"];
|
||||
|
||||
// Spectests test generation
|
||||
let mut spectests = Testsuite {
|
||||
buffer: String::new(),
|
||||
path: vec![],
|
||||
ignores,
|
||||
};
|
||||
{
|
||||
let mut spectests = Testsuite {
|
||||
buffer: String::new(),
|
||||
path: vec![],
|
||||
ignores: ignores.clone(),
|
||||
};
|
||||
|
||||
let backends = vec!["singlepass", "cranelift", "llvm"];
|
||||
with_features(&mut spectests, &backends, |mut spectests| {
|
||||
with_test_module(&mut spectests, "spec", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
|
||||
test_directory_module(
|
||||
spectests,
|
||||
"tests/wast/spec/proposals/multi-value",
|
||||
wast_processor,
|
||||
)?;
|
||||
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
|
||||
with_features(&mut spectests, &compilers, |mut spectests| {
|
||||
with_test_module(&mut spectests, "spec", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
|
||||
test_directory_module(
|
||||
spectests,
|
||||
"tests/wast/spec/proposals/multi-value",
|
||||
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(())
|
||||
})?;
|
||||
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(())
|
||||
})?;
|
||||
|
||||
let spectests_output = out_dir.join("generated_spectests.rs");
|
||||
fs::write(&spectests_output, spectests.buffer)?;
|
||||
let wasitests_output = out_dir.join("generated_wasitests.rs");
|
||||
fs::write(&wasitests_output, wasitests.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());
|
||||
drop(Command::new("rustfmt").arg(&wasitests_output).status());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ anyhow = "1.0"
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
default = ["wat", "cranelift", "jit"]
|
||||
default = ["wat", "default-cranelift", "default-jit"]
|
||||
compiler = [
|
||||
"wasmer-engine-jit/compiler",
|
||||
"wasmer-engine-native/compiler",
|
||||
@@ -66,3 +66,26 @@ llvm = [
|
||||
"wasmer-compiler-llvm",
|
||||
"compiler",
|
||||
]
|
||||
default-compiler = []
|
||||
default-engine = []
|
||||
|
||||
default-singlepass = [
|
||||
"singlepass",
|
||||
"default-compiler"
|
||||
]
|
||||
default-cranelift = [
|
||||
"cranelift",
|
||||
"default-compiler"
|
||||
]
|
||||
default-llvm = [
|
||||
"llvm",
|
||||
"default-compiler"
|
||||
]
|
||||
default-jit = [
|
||||
"jit",
|
||||
"default-engine"
|
||||
]
|
||||
default-native = [
|
||||
"native",
|
||||
"default-engine"
|
||||
]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::externals::{Extern, Function, Global, Memory, Table};
|
||||
use crate::import_object::LikeNamespace;
|
||||
use crate::native::NativeFunc;
|
||||
use crate::WasmTypeList;
|
||||
use indexmap::IndexMap;
|
||||
use std::{
|
||||
iter::{ExactSizeIterator, FromIterator},
|
||||
sync::Arc,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use wasm_common::WasmTypeList;
|
||||
use wasmer_runtime::Export;
|
||||
|
||||
/// The `ExportError` can happen when trying to get a specific
|
||||
@@ -127,7 +127,7 @@ impl Exports {
|
||||
{
|
||||
self.get_function(name)?
|
||||
.native()
|
||||
.ok_or(ExportError::IncompatibleType)
|
||||
.map_err(|_| ExportError::IncompatibleType)
|
||||
}
|
||||
|
||||
/// Get an export as an `Extern`.
|
||||
|
||||
757
lib/api/src/externals/function.rs
vendored
757
lib/api/src/externals/function.rs
vendored
@@ -5,12 +5,14 @@ use crate::types::Val;
|
||||
use crate::FunctionType;
|
||||
use crate::NativeFunc;
|
||||
use crate::RuntimeError;
|
||||
pub use inner::{HostFunction, WasmExternType, WasmTypeList};
|
||||
use inner::{WithEnv, WithoutEnv};
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::max;
|
||||
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
|
||||
use wasmer_runtime::{
|
||||
wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext,
|
||||
VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
|
||||
raise_user_trap, resume_panic, wasmer_call_trampoline, Export, ExportFunction,
|
||||
VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
|
||||
VMTrampoline,
|
||||
};
|
||||
|
||||
/// A function defined in the Wasm module
|
||||
@@ -47,10 +49,13 @@ pub struct Function {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Creates a new `Func` with the given parameters.
|
||||
/// Creates a new `Function` that is:
|
||||
///
|
||||
/// * `store` - a global cache to store information in
|
||||
/// * `func` - the function.
|
||||
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
|
||||
/// unique _statically declared type_. The outputs can be
|
||||
/// wrapped in a `Result`.
|
||||
/// 2. Independent, i.e. the function _does not_ receive an
|
||||
/// environment argument.
|
||||
pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Self
|
||||
where
|
||||
F: HostFunction<Args, Rets, WithoutEnv, Env>,
|
||||
@@ -58,10 +63,11 @@ impl Function {
|
||||
Rets: WasmTypeList,
|
||||
Env: Sized + 'static,
|
||||
{
|
||||
let func: wasm_common::Func<Args, Rets> = wasm_common::Func::new(func);
|
||||
let address = func.address() as *const VMFunctionBody;
|
||||
let function = inner::Function::<Args, Rets>::new(func);
|
||||
let address = function.address() as *const VMFunctionBody;
|
||||
let vmctx = std::ptr::null_mut() as *mut _ as *mut VMContext;
|
||||
let signature = func.ty();
|
||||
let signature = function.ty();
|
||||
|
||||
Self {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
@@ -75,6 +81,53 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
///
|
||||
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
|
||||
/// unique statically declared type. The outputs can be wrapped
|
||||
/// in a `Result`.
|
||||
/// 2. Dependent, i.e. the function _does_ receive an environment
|
||||
/// argument (given by `env`).
|
||||
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: Env, func: F) -> Self
|
||||
where
|
||||
F: HostFunction<Args, Rets, WithEnv, Env>,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Env: Sized + 'static,
|
||||
{
|
||||
let function = inner::Function::<Args, Rets>::new(func);
|
||||
let address = function.address();
|
||||
|
||||
// TODO: We need to refactor the Function context.
|
||||
// Right now is structured as it's always a `VMContext`. However, only
|
||||
// Wasm-defined functions have a `VMContext`.
|
||||
// In the case of Host-defined functions `VMContext` is whatever environment
|
||||
// the user want to attach to the function.
|
||||
let box_env = Box::new(env);
|
||||
let vmctx = Box::into_raw(box_env) as *mut _ as *mut VMContext;
|
||||
let signature = function.ty();
|
||||
|
||||
Self {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
|
||||
exported: ExportFunction {
|
||||
address,
|
||||
kind: VMFunctionKind::Static,
|
||||
vmctx,
|
||||
signature,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
///
|
||||
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
|
||||
/// slice of `Val` (the set of all Wasm values), and all
|
||||
/// outputs are stored in a vector of `Val`, wrapped in a
|
||||
/// `Result`.
|
||||
/// 2. Independent, i.e. the function _does not_ receive an
|
||||
/// environment argument.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub fn new_dynamic<F>(store: &Store, ty: &FunctionType, func: F) -> Self
|
||||
where
|
||||
@@ -89,6 +142,7 @@ impl Function {
|
||||
// generated dynamic trampoline.
|
||||
let address = std::ptr::null() as *const VMFunctionBody;
|
||||
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
|
||||
|
||||
Self {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
@@ -102,6 +156,14 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
///
|
||||
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
|
||||
/// slice of `Val` (the set of all Wasm values), and all
|
||||
/// outputs are stored in a vector of `Val`, wrapped in a
|
||||
/// `Result`.
|
||||
/// 2. Dependent, i.e. the function _does_ receive an environment
|
||||
/// argument (given by `env`).
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub fn new_dynamic_env<F, Env>(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self
|
||||
where
|
||||
@@ -118,6 +180,7 @@ impl Function {
|
||||
// generated dynamic trampoline.
|
||||
let address = std::ptr::null() as *const VMFunctionBody;
|
||||
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
|
||||
|
||||
Self {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
@@ -131,41 +194,6 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Func` with the given parameters.
|
||||
///
|
||||
/// * `store` - a global cache to store information in.
|
||||
/// * `env` - the function environment.
|
||||
/// * `func` - the function.
|
||||
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: Env, func: F) -> Self
|
||||
where
|
||||
F: HostFunction<Args, Rets, WithEnv, Env>,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Env: Sized + 'static,
|
||||
{
|
||||
let func: wasm_common::Func<Args, Rets> = wasm_common::Func::new(func);
|
||||
let address = func.address() as *const VMFunctionBody;
|
||||
// TODO: We need to refactor the Function context.
|
||||
// Right now is structured as it's always a `VMContext`. However, only
|
||||
// Wasm-defined functions have a `VMContext`.
|
||||
// In the case of Host-defined functions `VMContext` is whatever environment
|
||||
// the user want to attach to the function.
|
||||
let box_env = Box::new(env);
|
||||
let vmctx = Box::into_raw(box_env) as *mut _ as *mut VMContext;
|
||||
let signature = func.ty();
|
||||
Self {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
definition: FunctionDefinition::Host(HostFunctionDefinition { has_env: true }),
|
||||
exported: ExportFunction {
|
||||
address,
|
||||
kind: VMFunctionKind::Static,
|
||||
vmctx,
|
||||
signature,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying type of this function.
|
||||
pub fn ty(&self) -> &FunctionType {
|
||||
&self.exported.signature
|
||||
@@ -300,22 +328,42 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native<'a, Args, Rets>(&self) -> Option<NativeFunc<'a, Args, Rets>>
|
||||
/// Transform this WebAssembly function into a function with the
|
||||
/// native ABI. See `NativeFunc` to learn more.
|
||||
pub fn native<'a, Args, Rets>(&self) -> Result<NativeFunc<'a, Args, Rets>, RuntimeError>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
// type check
|
||||
if self.exported.signature.params() != Args::wasm_types() {
|
||||
// todo: error param types don't match
|
||||
return None;
|
||||
}
|
||||
if self.exported.signature.results() != Rets::wasm_types() {
|
||||
// todo: error result types don't match
|
||||
return None;
|
||||
{
|
||||
let expected = self.exported.signature.params();
|
||||
let given = Args::wasm_types();
|
||||
|
||||
if expected != given {
|
||||
return Err(RuntimeError::new(format!(
|
||||
"given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
|
||||
given,
|
||||
expected,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Some(NativeFunc::new(
|
||||
{
|
||||
let expected = self.exported.signature.results();
|
||||
let given = Rets::wasm_types();
|
||||
|
||||
if expected != given {
|
||||
// todo: error result types don't match
|
||||
return Err(RuntimeError::new(format!(
|
||||
"given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
|
||||
given,
|
||||
expected,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NativeFunc::new(
|
||||
self.store.clone(),
|
||||
self.exported.address,
|
||||
self.exported.vmctx,
|
||||
@@ -441,8 +489,607 @@ impl<T: VMDynamicFunction> VMDynamicFunctionCall<T> for VMDynamicFunctionContext
|
||||
|
||||
match result {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(trap)) => wasmer_runtime::raise_user_trap(Box::new(trap)),
|
||||
Err(panic) => wasmer_runtime::resume_panic(panic),
|
||||
Ok(Err(trap)) => raise_user_trap(Box::new(trap)),
|
||||
Err(panic) => resume_panic(panic),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This private inner module contains the low-level implementation
|
||||
/// for `Function` and its siblings.
|
||||
mod inner {
|
||||
use std::convert::Infallible;
|
||||
use std::error::Error;
|
||||
use std::marker::PhantomData;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use wasm_common::{FunctionType, NativeWasmType, Type};
|
||||
use wasmer_runtime::{raise_user_trap, resume_panic, VMFunctionBody};
|
||||
|
||||
/// A trait to represent a wasm extern type.
|
||||
pub unsafe trait WasmExternType: Copy
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Native wasm type for this `WasmExternType`.
|
||||
type Native: NativeWasmType;
|
||||
|
||||
/// Convert from given `Native` type to self.
|
||||
fn from_native(native: Self::Native) -> Self;
|
||||
|
||||
/// Convert self to `Native` type.
|
||||
fn to_native(self) -> Self::Native;
|
||||
}
|
||||
|
||||
macro_rules! wasm_extern_type {
|
||||
( $( $type:ty => $native_type:ty ),* ) => {
|
||||
$(
|
||||
#[allow(clippy::use_self)]
|
||||
unsafe impl WasmExternType for $type {
|
||||
type Native = $native_type;
|
||||
|
||||
#[inline]
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
wasm_extern_type!(
|
||||
i8 => i32,
|
||||
u8 => i32,
|
||||
i16 => i32,
|
||||
u16 => i32,
|
||||
i32 => i32,
|
||||
u32 => i32,
|
||||
i64 => i64,
|
||||
u64 => i64,
|
||||
f32 => f32,
|
||||
f64 => f64
|
||||
);
|
||||
|
||||
/// The `WasmTypeList` trait represents a tuple (list) of Wasm
|
||||
/// typed values. It is used to get low-level representation of
|
||||
/// such a tuple.
|
||||
pub trait WasmTypeList {
|
||||
/// The C type (a struct) that can hold/represent all the
|
||||
/// represented values.
|
||||
type CStruct;
|
||||
|
||||
/// The array type that can hold all the represented values.
|
||||
///
|
||||
/// Note that all values are stored in their binary form.
|
||||
type Array: AsMut<[i128]>;
|
||||
|
||||
/// Constructs `Self` based on an array of values.
|
||||
fn from_array(array: Self::Array) -> Self;
|
||||
|
||||
/// Builds and returns an array of type `Array` from a tuple
|
||||
/// (list) of values.
|
||||
fn into_array(self) -> Self::Array;
|
||||
|
||||
/// Allocates and return an empty array of type `Array` that
|
||||
/// will hold a tuple (list) of values, usually to hold the
|
||||
/// returned values of a WebAssembly function call.
|
||||
fn empty_array() -> Self::Array;
|
||||
|
||||
/// Builds a tuple (list) of values from a C struct of type
|
||||
/// `CStruct`.
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self;
|
||||
|
||||
/// Builds and returns a C struct of type `CStruct` from a
|
||||
/// tuple (list) of values.
|
||||
fn into_c_struct(self) -> Self::CStruct;
|
||||
|
||||
/// Get the Wasm types for the tuple (list) of currently
|
||||
/// represented values.
|
||||
fn wasm_types() -> &'static [Type];
|
||||
}
|
||||
|
||||
/// The `IntoResult` trait turns a `WasmTypeList` into a
|
||||
/// `Result<WasmTypeList, Self::Error>`.
|
||||
///
|
||||
/// It is mostly used to turn result values of a Wasm function
|
||||
/// call into a `Result`.
|
||||
pub trait IntoResult<T>
|
||||
where
|
||||
T: WasmTypeList,
|
||||
{
|
||||
/// The error type for this trait.
|
||||
type Error: Error + Sync + Send + 'static;
|
||||
|
||||
/// Transforms `Self` into a `Result`.
|
||||
fn into_result(self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> IntoResult<T> for T
|
||||
where
|
||||
T: WasmTypeList,
|
||||
{
|
||||
// `T` is not a `Result`, it's already a value, so no error
|
||||
// can be built.
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_result(self) -> Result<Self, Infallible> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> IntoResult<T> for Result<T, E>
|
||||
where
|
||||
T: WasmTypeList,
|
||||
E: Error + Sync + Send + 'static,
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The `HostFunction` trait represents the set of functions that
|
||||
/// can be used as host function. To uphold this statement, it is
|
||||
/// necessary for a function to be transformed into a pointer to
|
||||
/// `VMFunctionBody`.
|
||||
pub trait HostFunction<Args, Rets, Kind, T>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Kind: HostFunctionKind,
|
||||
T: Sized,
|
||||
Self: Sized,
|
||||
{
|
||||
/// Get the pointer to the function body.
|
||||
fn function_body_ptr(self) -> *const VMFunctionBody;
|
||||
}
|
||||
|
||||
/// Empty trait to specify the kind of `HostFunction`: With or
|
||||
/// without an environment.
|
||||
///
|
||||
/// This trait is never aimed to be used by a user. It is used by
|
||||
/// the trait system to automatically generate the appropriate
|
||||
/// host functions.
|
||||
#[doc(hidden)]
|
||||
pub trait HostFunctionKind {}
|
||||
|
||||
/// An empty struct to help Rust typing to determine
|
||||
/// when a `HostFunction` does have an environment.
|
||||
pub struct WithEnv;
|
||||
|
||||
impl HostFunctionKind for WithEnv {}
|
||||
|
||||
/// An empty struct to help Rust typing to determine
|
||||
/// when a `HostFunction` does not have an environment.
|
||||
pub struct WithoutEnv;
|
||||
|
||||
impl HostFunctionKind for WithoutEnv {}
|
||||
|
||||
/// Represents a low-level Wasm static host function. See
|
||||
/// `super::Function::new` and `super::Function::new_env` to learn
|
||||
/// more.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Function<Args = (), Rets = ()> {
|
||||
address: *const VMFunctionBody,
|
||||
_phantom: PhantomData<(Args, Rets)>,
|
||||
}
|
||||
|
||||
unsafe impl<Args, Rets> Send for Function<Args, Rets> {}
|
||||
|
||||
impl<Args, Rets> Function<Args, Rets>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Creates a new `Function`.
|
||||
pub fn new<F, T, E>(function: F) -> Self
|
||||
where
|
||||
F: HostFunction<Args, Rets, T, E>,
|
||||
T: HostFunctionKind,
|
||||
E: Sized,
|
||||
{
|
||||
Self {
|
||||
address: function.function_body_ptr(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the function type of this `Function`.
|
||||
pub fn ty(&self) -> FunctionType {
|
||||
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
|
||||
}
|
||||
|
||||
/// Get the address of this `Function`.
|
||||
pub fn address(&self) -> *const VMFunctionBody {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_host_function {
|
||||
( [$c_struct_representation:ident]
|
||||
$c_struct_name:ident,
|
||||
$( $x:ident ),* ) => {
|
||||
|
||||
/// A structure with a C-compatible representation that can hold a set of Wasm values.
|
||||
/// This type is used by `WasmTypeList::CStruct`.
|
||||
#[repr($c_struct_representation)]
|
||||
pub struct $c_struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*;
|
||||
|
||||
// Implement `WasmTypeList` for a specific tuple.
|
||||
#[allow(unused_parens, dead_code)]
|
||||
impl< $( $x ),* >
|
||||
WasmTypeList
|
||||
for
|
||||
( $( $x ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*
|
||||
{
|
||||
type CStruct = $c_struct_name< $( $x ),* >;
|
||||
|
||||
type Array = [i128; count_idents!( $( $x ),* )];
|
||||
|
||||
fn from_array(array: Self::Array) -> Self {
|
||||
// Unpack items of the array.
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
|
||||
// Build the tuple.
|
||||
(
|
||||
$(
|
||||
WasmExternType::from_native(NativeWasmType::from_binary($x))
|
||||
),*
|
||||
)
|
||||
}
|
||||
|
||||
fn into_array(self) -> Self::Array {
|
||||
// Unpack items of the tuple.
|
||||
#[allow(non_snake_case)]
|
||||
let ( $( $x ),* ) = self;
|
||||
|
||||
// Build the array.
|
||||
[
|
||||
$(
|
||||
WasmExternType::to_native($x).to_binary()
|
||||
),*
|
||||
]
|
||||
}
|
||||
|
||||
fn empty_array() -> Self::Array {
|
||||
// Build an array initialized with `0`.
|
||||
[0; count_idents!( $( $x ),* )]
|
||||
}
|
||||
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||
// Unpack items of the C structure.
|
||||
#[allow(non_snake_case)]
|
||||
let $c_struct_name( $( $x ),* ) = c_struct;
|
||||
|
||||
(
|
||||
$(
|
||||
WasmExternType::from_native($x)
|
||||
),*
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
// Unpack items of the tuple.
|
||||
let ( $( $x ),* ) = self;
|
||||
|
||||
// Build the C structure.
|
||||
$c_struct_name(
|
||||
$(
|
||||
WasmExternType::to_native($x)
|
||||
),*
|
||||
)
|
||||
}
|
||||
|
||||
fn wasm_types() -> &'static [Type] {
|
||||
&[
|
||||
$(
|
||||
$x::Native::WASM_TYPE
|
||||
),*
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Implement `HostFunction` for a function that has the same arity than the tuple.
|
||||
// This specific function has no environment.
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, RetsAsResult, Func >
|
||||
HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()>
|
||||
for
|
||||
Func
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
RetsAsResult: IntoResult<Rets>,
|
||||
Func: Fn($( $x , )*) -> RetsAsResult + 'static + Send,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn function_body_ptr(self) -> *const VMFunctionBody {
|
||||
/// This is a function that wraps the real host
|
||||
/// function. Its address will be used inside the
|
||||
/// runtime.
|
||||
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
RetsAsResult: IntoResult<Rets>,
|
||||
Func: Fn( $( $x ),* ) -> RetsAsResult + 'static
|
||||
{
|
||||
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
func( $( WasmExternType::from_native($x) ),* ).into_result()
|
||||
}));
|
||||
|
||||
match result {
|
||||
Ok(Ok(result)) => return result.into_c_struct(),
|
||||
Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) },
|
||||
Err(panic) => unsafe { resume_panic(panic) },
|
||||
}
|
||||
}
|
||||
|
||||
func_wrapper::<$( $x, )* Rets, RetsAsResult, Self> as *const VMFunctionBody
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, RetsAsResult, Env, Func >
|
||||
HostFunction<( $( $x ),* ), Rets, WithEnv, Env>
|
||||
for
|
||||
Func
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
RetsAsResult: IntoResult<Rets>,
|
||||
Env: Sized,
|
||||
Func: Fn(&mut Env, $( $x , )*) -> RetsAsResult + Send + 'static,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn function_body_ptr(self) -> *const VMFunctionBody {
|
||||
/// This is a function that wraps the real host
|
||||
/// function. Its address will be used inside the
|
||||
/// runtime.
|
||||
extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( env: &mut Env, $( $x: $x::Native, )* ) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
RetsAsResult: IntoResult<Rets>,
|
||||
Env: Sized,
|
||||
Func: Fn(&mut Env, $( $x ),* ) -> RetsAsResult + 'static
|
||||
{
|
||||
let func: &Func = unsafe { &*(&() as *const () as *const Func) };
|
||||
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
func(env, $( WasmExternType::from_native($x) ),* ).into_result()
|
||||
}));
|
||||
|
||||
match result {
|
||||
Ok(Ok(result)) => return result.into_c_struct(),
|
||||
Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) },
|
||||
Err(panic) => unsafe { resume_panic(panic) },
|
||||
}
|
||||
}
|
||||
|
||||
func_wrapper::<$( $x, )* Rets, RetsAsResult, Env, Self> as *const VMFunctionBody
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// Black-magic to count the number of identifiers at compile-time.
|
||||
macro_rules! count_idents {
|
||||
( $($idents:ident),* ) => {
|
||||
{
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
enum Idents { $( $idents, )* __CountIdentsLast }
|
||||
const COUNT: usize = Idents::__CountIdentsLast as usize;
|
||||
COUNT
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Here we go! Let's generate all the C struct, `WasmTypeList`
|
||||
// implementations and `HostFunction` implementations.
|
||||
impl_host_function!([C] S0,);
|
||||
impl_host_function!([transparent] S1, A1);
|
||||
impl_host_function!([C] S2, A1, A2);
|
||||
impl_host_function!([C] S3, A1, A2, A3);
|
||||
impl_host_function!([C] S4, A1, A2, A3, A4);
|
||||
impl_host_function!([C] S5, A1, A2, A3, A4, A5);
|
||||
impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
|
||||
impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
|
||||
impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
|
||||
impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
|
||||
impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
|
||||
impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
|
||||
impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
|
||||
impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
|
||||
impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
|
||||
impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
|
||||
impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
|
||||
impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
|
||||
impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
|
||||
impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
|
||||
impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
|
||||
impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
|
||||
impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
|
||||
impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
|
||||
impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
|
||||
impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
|
||||
impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);
|
||||
|
||||
// Implement `WasmTypeList` on `Infallible`, which means that
|
||||
// `Infallible` can be used as a returned type of a host function
|
||||
// to express that it doesn't return.
|
||||
impl WasmTypeList for Infallible {
|
||||
type CStruct = Self;
|
||||
type Array = [i128; 0];
|
||||
|
||||
fn from_array(_: Self::Array) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn into_array(self) -> Self::Array {
|
||||
[]
|
||||
}
|
||||
|
||||
fn empty_array() -> Self::Array {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn from_c_struct(_: Self::CStruct) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn wasm_types() -> &'static [Type] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_wasm_type_list {
|
||||
use super::*;
|
||||
use wasm_common::Type;
|
||||
|
||||
#[test]
|
||||
fn test_from_array() {
|
||||
assert_eq!(<()>::from_array([]), ());
|
||||
assert_eq!(<i32>::from_array([1]), (1i32));
|
||||
assert_eq!(<(i32, i64)>::from_array([1, 2]), (1i32, 2i64));
|
||||
assert_eq!(
|
||||
<(i32, i64, f32, f64)>::from_array([
|
||||
1,
|
||||
2,
|
||||
(3.1f32).to_bits().into(),
|
||||
(4.2f64).to_bits().into()
|
||||
]),
|
||||
(1, 2, 3.1f32, 4.2f64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_array() {
|
||||
assert_eq!(().into_array(), []);
|
||||
assert_eq!((1).into_array(), [1]);
|
||||
assert_eq!((1i32, 2i64).into_array(), [1, 2]);
|
||||
assert_eq!(
|
||||
(1i32, 2i32, 3.1f32, 4.2f64).into_array(),
|
||||
[1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_array() {
|
||||
assert_eq!(<()>::empty_array().len(), 0);
|
||||
assert_eq!(<i32>::empty_array().len(), 1);
|
||||
assert_eq!(<(i32, i64)>::empty_array().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_c_struct() {
|
||||
assert_eq!(<()>::from_c_struct(S0()), ());
|
||||
assert_eq!(<i32>::from_c_struct(S1(1)), (1i32));
|
||||
assert_eq!(<(i32, i64)>::from_c_struct(S2(1, 2)), (1i32, 2i64));
|
||||
assert_eq!(
|
||||
<(i32, i64, f32, f64)>::from_c_struct(S4(1, 2, 3.1, 4.2)),
|
||||
(1i32, 2i64, 3.1f32, 4.2f64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasm_types_for_uni_values() {
|
||||
assert_eq!(<i32>::wasm_types(), [Type::I32]);
|
||||
assert_eq!(<i64>::wasm_types(), [Type::I64]);
|
||||
assert_eq!(<f32>::wasm_types(), [Type::F32]);
|
||||
assert_eq!(<f64>::wasm_types(), [Type::F64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasm_types_for_multi_values() {
|
||||
assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]);
|
||||
assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]);
|
||||
assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]);
|
||||
assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]);
|
||||
|
||||
assert_eq!(
|
||||
<(i32, i64, f32, f64)>::wasm_types(),
|
||||
[Type::I32, Type::I64, Type::F32, Type::F64]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(test)]
|
||||
mod test_function {
|
||||
use super::*;
|
||||
use wasm_common::Type;
|
||||
|
||||
fn func() {}
|
||||
fn func__i32() -> i32 {
|
||||
0
|
||||
}
|
||||
fn func_i32(_a: i32) {}
|
||||
fn func_i32__i32(a: i32) -> i32 {
|
||||
a * 2
|
||||
}
|
||||
fn func_i32_i32__i32(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) {
|
||||
(a, b)
|
||||
}
|
||||
fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) {
|
||||
(b, a)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_types() {
|
||||
assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![]));
|
||||
assert_eq!(
|
||||
Function::new(func__i32).ty(),
|
||||
FunctionType::new(vec![], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Function::new(func_i32).ty(),
|
||||
FunctionType::new(vec![Type::I32], vec![])
|
||||
);
|
||||
assert_eq!(
|
||||
Function::new(func_i32__i32).ty(),
|
||||
FunctionType::new(vec![Type::I32], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Function::new(func_i32_i32__i32).ty(),
|
||||
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Function::new(func_i32_i32__i32_i32).ty(),
|
||||
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Function::new(func_f32_i32__i32_f32).ty(),
|
||||
FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_pointer() {
|
||||
let f = Function::new(func_i32__i32);
|
||||
let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) };
|
||||
assert_eq!(function(0, 3), 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
lib/api/src/externals/memory.rs
vendored
2
lib/api/src/externals/memory.rs
vendored
@@ -19,7 +19,7 @@ pub struct Memory {
|
||||
|
||||
impl Memory {
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Result<Memory, MemoryError> {
|
||||
let tunables = store.engine().tunables();
|
||||
let tunables = store.tunables();
|
||||
let memory_plan = tunables.memory_plan(ty);
|
||||
let memory = tunables.create_memory(memory_plan)?;
|
||||
|
||||
|
||||
2
lib/api/src/externals/mod.rs
vendored
2
lib/api/src/externals/mod.rs
vendored
@@ -3,7 +3,7 @@ mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
|
||||
pub use self::function::Function;
|
||||
pub use self::function::{Function, HostFunction, WasmExternType, WasmTypeList};
|
||||
pub use self::global::Global;
|
||||
pub use self::memory::Memory;
|
||||
pub use self::table::Table;
|
||||
|
||||
2
lib/api/src/externals/table.rs
vendored
2
lib/api/src/externals/table.rs
vendored
@@ -33,7 +33,7 @@ impl Table {
|
||||
/// All the elements in the table will be set to the `init` value.
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
|
||||
let item = init.into_checked_anyfunc(store)?;
|
||||
let tunables = store.engine().tunables();
|
||||
let tunables = store.tunables();
|
||||
let table_plan = tunables.table_plan(ty);
|
||||
let table = tunables
|
||||
.create_table(table_plan)
|
||||
|
||||
@@ -18,7 +18,9 @@ mod types;
|
||||
mod utils;
|
||||
|
||||
pub use crate::exports::{ExportError, Exportable, Exports};
|
||||
pub use crate::externals::{Extern, Function, Global, Memory, Table};
|
||||
pub use crate::externals::{
|
||||
Extern, Function, Global, HostFunction, Memory, Table, WasmExternType, WasmTypeList,
|
||||
};
|
||||
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::memory_view::{Atomically, MemoryView};
|
||||
@@ -34,11 +36,10 @@ pub use crate::types::{
|
||||
};
|
||||
pub use crate::types::{Val as Value, ValType as Type};
|
||||
pub use crate::utils::is_wasm;
|
||||
|
||||
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
|
||||
pub use wasm_common::{
|
||||
Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WasmExternType, WasmTypeList,
|
||||
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
|
||||
Bytes, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES,
|
||||
WASM_PAGE_SIZE,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
pub use wasmer_compiler::CompilerConfig;
|
||||
@@ -56,37 +57,38 @@ pub use wat::parse_bytes as wat2wasm;
|
||||
|
||||
// The compilers are mutually exclusive
|
||||
#[cfg(any(
|
||||
all(feature = "llvm", any(feature = "cranelift", feature = "singlepass")),
|
||||
all(feature = "cranelift", feature = "singlepass")
|
||||
all(
|
||||
feature = "default-llvm",
|
||||
any(feature = "default-cranelift", feature = "default-singlepass")
|
||||
),
|
||||
all(feature = "default-cranelift", feature = "default-singlepass")
|
||||
))]
|
||||
compile_error!(
|
||||
r#"The `singlepass`, `cranelift` and `llvm` features are mutually exclusive.
|
||||
If you wish to use more than one compiler, you can simply import it from it's own crate. Eg.:
|
||||
r#"The `default-singlepass`, `default-cranelift` and `default-llvm` features are mutually exclusive.
|
||||
If you wish to use more than one compiler, you can simply create the own store. Eg.:
|
||||
|
||||
```
|
||||
use wasmer::{Store, Engine};
|
||||
use wasmer_compiler_singlepass::SinglepassConfig;
|
||||
use wasmer::{Store, JIT, Singlepass};
|
||||
|
||||
// TODO: update this, this is now out of date:
|
||||
let engine = Engine::new(SinglepassConfig::default());
|
||||
let store = Store::new_config(&engine);
|
||||
let engine = JIT::new(&Singlepass::default()).engine();
|
||||
let store = Store::new(&engine);
|
||||
```"#
|
||||
);
|
||||
|
||||
#[cfg(feature = "singlepass")]
|
||||
pub use wasmer_compiler_singlepass::SinglepassConfig;
|
||||
pub use wasmer_compiler_singlepass::Singlepass;
|
||||
|
||||
#[cfg(feature = "cranelift")]
|
||||
pub use wasmer_compiler_cranelift::CraneliftConfig;
|
||||
pub use wasmer_compiler_cranelift::Cranelift;
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
pub use wasmer_compiler_llvm::LLVMConfig;
|
||||
pub use wasmer_compiler_llvm::LLVM;
|
||||
|
||||
#[cfg(feature = "jit")]
|
||||
pub use wasmer_engine_jit::JITEngine;
|
||||
pub use wasmer_engine_jit::{JITArtifact, JITEngine, JIT};
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
pub use wasmer_engine_native::NativeEngine;
|
||||
pub use wasmer_engine_native::{Native, NativeArtifact, NativeEngine};
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -107,7 +107,6 @@ impl Memory for LinearMemory {
|
||||
/// of wasm pages.
|
||||
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError> {
|
||||
// Optimization of memory.grow 0 calls.
|
||||
let delta: Pages = delta.into();
|
||||
let mut mmap = self.mmap.borrow_mut();
|
||||
if delta.0 == 0 {
|
||||
return Ok(mmap.size);
|
||||
|
||||
@@ -156,7 +156,7 @@ impl Module {
|
||||
}
|
||||
|
||||
fn compile(store: &Store, binary: &[u8]) -> Result<Self, CompileError> {
|
||||
let artifact = store.engine().compile(binary)?;
|
||||
let artifact = store.engine().compile(binary, store.tunables())?;
|
||||
Ok(Self::from_artifact(store, artifact))
|
||||
}
|
||||
|
||||
@@ -261,11 +261,9 @@ impl Module {
|
||||
resolver: &dyn Resolver,
|
||||
) -> Result<InstanceHandle, InstantiationError> {
|
||||
unsafe {
|
||||
let instance_handle = self.artifact.instantiate(
|
||||
self.store.engine().tunables(),
|
||||
resolver,
|
||||
Box::new(()),
|
||||
)?;
|
||||
let instance_handle =
|
||||
self.artifact
|
||||
.instantiate(self.store.tunables(), resolver, Box::new(()))?;
|
||||
|
||||
// After the instance handle is created, we need to initialize
|
||||
// the data, call the start function and so. However, if any
|
||||
|
||||
@@ -13,9 +13,9 @@ use crate::externals::function::{
|
||||
FunctionDefinition, HostFunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithEnv,
|
||||
VMDynamicFunctionWithoutEnv, WasmFunctionDefinition,
|
||||
};
|
||||
use crate::{Function, FunctionType, RuntimeError, Store};
|
||||
use crate::{Function, FunctionType, RuntimeError, Store, WasmExternType, WasmTypeList};
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
|
||||
use wasm_common::NativeWasmType;
|
||||
use wasmer_runtime::{
|
||||
ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
|
||||
};
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
//! Therefore, you should use this abstraction whenever possible to avoid memory
|
||||
//! related bugs when implementing an ABI.
|
||||
|
||||
use crate::externals::Memory;
|
||||
use wasm_common::{ValueType, WasmExternType};
|
||||
|
||||
use crate::{externals::Memory, WasmExternType};
|
||||
use std::{cell::Cell, fmt, marker::PhantomData, mem};
|
||||
use wasm_common::ValueType;
|
||||
|
||||
/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
|
||||
/// to get access to methods
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#[cfg(all(feature = "compiler", feature = "engine"))]
|
||||
use crate::tunables::Tunables;
|
||||
#[cfg(all(feature = "compiler", feature = "engine"))]
|
||||
use wasmer_compiler::CompilerConfig;
|
||||
use wasmer_engine::Tunables as BaseTunables;
|
||||
|
||||
use std::sync::Arc;
|
||||
use wasmer_engine::Engine;
|
||||
@@ -9,11 +9,35 @@ use wasmer_engine::Engine;
|
||||
#[derive(Clone)]
|
||||
pub struct Store {
|
||||
engine: Arc<dyn Engine + Send + Sync>,
|
||||
tunables: Arc<dyn BaseTunables + Send + Sync>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new(engine: Arc<dyn Engine + Send + Sync>) -> Store {
|
||||
Store { engine }
|
||||
pub fn new<E>(engine: &E) -> Store
|
||||
where
|
||||
E: Engine + ?Sized,
|
||||
{
|
||||
Store {
|
||||
engine: engine.cloned(),
|
||||
tunables: Arc::new(Tunables::for_target(engine.target())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_tunables<E>(
|
||||
engine: &E,
|
||||
tunables: impl BaseTunables + Send + Sync + 'static,
|
||||
) -> Store
|
||||
where
|
||||
E: Engine + ?Sized,
|
||||
{
|
||||
Store {
|
||||
engine: engine.cloned(),
|
||||
tunables: Arc::new(tunables),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tunables(&self) -> &dyn BaseTunables {
|
||||
self.tunables.as_ref()
|
||||
}
|
||||
|
||||
pub fn engine(&self) -> &Arc<dyn Engine + Send + Sync> {
|
||||
@@ -32,40 +56,49 @@ impl PartialEq for Store {
|
||||
}
|
||||
|
||||
// We only implement default if we have assigned a default compiler and engine
|
||||
#[cfg(all(feature = "compiler", feature = "engine"))]
|
||||
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
|
||||
impl Default for Store {
|
||||
fn default() -> Store {
|
||||
// We store them on a function that returns to make
|
||||
// sure this function doesn't emit a compile error even if
|
||||
// more than one compiler is enabled.
|
||||
#[allow(unreachable_code)]
|
||||
fn get_config() -> Box<dyn CompilerConfig + Send + Sync> {
|
||||
#[cfg(feature = "cranelift")]
|
||||
return Box::new(wasmer_compiler_cranelift::CraneliftConfig::default());
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
return Box::new(wasmer_compiler_llvm::LLVMConfig::default());
|
||||
|
||||
#[cfg(feature = "singlepass")]
|
||||
return Box::new(wasmer_compiler_singlepass::SinglepassConfig::default());
|
||||
fn get_config() -> impl CompilerConfig + Send + Sync {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "default-cranelift")] {
|
||||
wasmer_compiler_cranelift::Cranelift::default()
|
||||
} else if #[cfg(feature = "default-llvm")] {
|
||||
wasmer_compiler_llvm::LLVM::default()
|
||||
} else if #[cfg(feature = "default-singlepass")] {
|
||||
wasmer_compiler_singlepass::Singlepass::default()
|
||||
} else {
|
||||
compile_error!("No default compiler chosen")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn get_engine(
|
||||
config: Box<dyn CompilerConfig + Send + Sync>,
|
||||
) -> Arc<dyn Engine + Send + Sync> {
|
||||
let tunables = Tunables::for_target(config.target().triple());
|
||||
|
||||
#[cfg(feature = "jit")]
|
||||
return Arc::new(wasmer_engine_jit::JITEngine::new(config, tunables));
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
return Arc::new(wasmer_engine_native::NativeEngine::new(config, tunables));
|
||||
fn get_engine(config: impl CompilerConfig + Send + Sync) -> impl Engine + Send + Sync {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "default-jit")] {
|
||||
wasmer_engine_jit::JIT::new(&config)
|
||||
.engine()
|
||||
} else if #[cfg(feature = "default-native")] {
|
||||
wasmer_engine_native::Native::new(&config)
|
||||
.engine()
|
||||
} else {
|
||||
compile_error!("No default engine chosen")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = get_config();
|
||||
let engine = get_engine(config);
|
||||
Store::new(engine)
|
||||
let tunables = Tunables::for_target(engine.target());
|
||||
Store {
|
||||
engine: Arc::new(engine),
|
||||
tunables: Arc::new(tunables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::{MemoryType, Pages, TableType};
|
||||
use more_asserts::assert_ge;
|
||||
use std::cmp::min;
|
||||
use std::sync::Arc;
|
||||
use target_lexicon::{OperatingSystem, PointerWidth, Triple, HOST};
|
||||
use target_lexicon::{OperatingSystem, PointerWidth};
|
||||
use wasmer_compiler::Target;
|
||||
use wasmer_engine::Tunables as BaseTunables;
|
||||
use wasmer_runtime::MemoryError;
|
||||
use wasmer_runtime::{Memory, MemoryPlan, MemoryStyle, Table, TablePlan, TableStyle};
|
||||
@@ -24,7 +25,8 @@ pub struct Tunables {
|
||||
|
||||
impl Tunables {
|
||||
/// Get the `Tunables` for a specific Target
|
||||
pub fn for_target(triple: &Triple) -> Self {
|
||||
pub fn for_target(target: &Target) -> Self {
|
||||
let triple = target.triple();
|
||||
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
|
||||
let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) =
|
||||
match pointer_width {
|
||||
@@ -102,9 +104,3 @@ impl BaseTunables for Tunables {
|
||||
Ok(Arc::new(LinearTable::new(&plan)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Tunables {
|
||||
fn default() -> Self {
|
||||
Self::for_target(&HOST)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use libc::c_uchar;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use wasmer::{Memory, MemoryType, NamedResolver};
|
||||
use wasmer_wasi as wasi;
|
||||
|
||||
@@ -217,7 +216,7 @@ pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wa
|
||||
// this API will now leak a `Memory`
|
||||
let memory_type = MemoryType::new(0, None, false);
|
||||
let memory = Memory::new(store, memory_type).expect("create memory");
|
||||
wasi_env.set_memory(Arc::new(memory));
|
||||
wasi_env.set_memory(memory);
|
||||
// TODO(mark): review lifetime of `Memory` here
|
||||
let import_object_inner: Box<dyn NamedResolver> = Box::new(
|
||||
wasi::generate_import_object_from_env(store, wasi_env, wasi::WasiVersion::Latest),
|
||||
|
||||
@@ -195,7 +195,7 @@ pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a
|
||||
|
||||
lazy_static! {
|
||||
pub(crate) static ref GLOBAL_STORE: wasmer::Store =
|
||||
wasmer::Store::new(crate::wasm_c_api::wasm_engine_new().inner);
|
||||
wasmer::Store::new(&*crate::wasm_c_api::wasm_engine_new().inner);
|
||||
}
|
||||
|
||||
pub(crate) fn get_global_store() -> &'static wasmer::Store {
|
||||
|
||||
@@ -7,15 +7,13 @@ use std::ptr::{self, NonNull};
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "engine")]
|
||||
use wasmer::Tunables;
|
||||
use wasmer::{
|
||||
Engine, ExportType, Extern, ExternType, Function, FunctionType, Global, GlobalType, Instance,
|
||||
Memory, MemoryType, Module, Mutability, OrderedResolver, Pages, RuntimeError, Store, Table,
|
||||
TableType, Val, ValType,
|
||||
};
|
||||
#[cfg(feature = "jit")]
|
||||
use wasmer_engine_jit::JITEngine;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
use crate::error::update_last_error;
|
||||
|
||||
@@ -58,11 +56,11 @@ cfg_if! {
|
||||
fn get_default_compiler_config() -> Box<dyn CompilerConfig> {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "cranelift")] {
|
||||
Box::new(wasmer_compiler_cranelift::CraneliftConfig::default())
|
||||
Box::new(wasmer_compiler_cranelift::Cranelift::default())
|
||||
} else if #[cfg(feature = "llvm")] {
|
||||
Box::new(wasmer_compiler_llvm::LLVMConfig::default())
|
||||
Box::new(wasmer_compiler_llvm::LLVM::default())
|
||||
} else if #[cfg(feature = "singlepass")] {
|
||||
Box::new(wasmer_compiler_singlepass::SinglepassConfig::default())
|
||||
Box::new(wasmer_compiler_singlepass::Singlepass::default())
|
||||
} else {
|
||||
compile_error!("Please enable one of the compiler backends")
|
||||
}
|
||||
@@ -72,8 +70,7 @@ cfg_if! {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
|
||||
let compiler_config: Box<dyn CompilerConfig> = get_default_compiler_config();
|
||||
let tunables = Tunables::default();
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::new(compiler_config, tunables));
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::new(&*compiler_config).engine());
|
||||
Box::new(wasm_engine_t { inner: engine })
|
||||
}
|
||||
}
|
||||
@@ -81,8 +78,7 @@ cfg_if! {
|
||||
// Headless JIT
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
|
||||
let tunables = Tunables::default();
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JITEngine::headless(tunables));
|
||||
let engine: Arc<dyn Engine + Send + Sync> = Arc::new(JIT::headless().engine());
|
||||
Box::new(wasm_engine_t { inner: engine })
|
||||
}
|
||||
}
|
||||
@@ -299,7 +295,7 @@ pub unsafe extern "C" fn wasm_store_new(
|
||||
) -> Option<NonNull<wasm_store_t>> {
|
||||
let wasm_engine_ptr = wasm_engine_ptr?;
|
||||
let wasm_engine = wasm_engine_ptr.as_ref();
|
||||
let store = Store::new(wasm_engine.inner.clone());
|
||||
let store = Store::new(&*wasm_engine.inner);
|
||||
Some(NonNull::new_unchecked(
|
||||
Box::into_raw(Box::new(store)) as *mut wasm_store_t
|
||||
))
|
||||
|
||||
@@ -3,7 +3,28 @@
|
||||
This is the `wasmer-compiler-cranelift` crate, which contains a
|
||||
compiler implementation based on Cranelift.
|
||||
|
||||
We recommend using this compiler only for development proposes.
|
||||
## Usage
|
||||
|
||||
First, add this crate into your `Cargo.toml` dependencies:
|
||||
|
||||
```toml
|
||||
wasmer-compiler-cranelift = "1.0.0-alpha.1"
|
||||
```
|
||||
|
||||
And then:
|
||||
|
||||
```rust
|
||||
use wasmer::{Store, JIT};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
|
||||
let compiler = Cranelift::new();
|
||||
// Put it into an engine and add it to the store
|
||||
let store = Store::new(&JIT::new(&compiler).engine());
|
||||
```
|
||||
|
||||
## When to use Cranelift
|
||||
|
||||
We recommend using this compiler crate **only for development proposes**.
|
||||
For production we recommend using `wasmer-compiler-llvm` as it offers
|
||||
a much better runtime speed (50% faster on average).
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Support for compiling with Cranelift.
|
||||
|
||||
use crate::address_map::get_function_address_map;
|
||||
use crate::config::CraneliftConfig;
|
||||
use crate::config::Cranelift;
|
||||
#[cfg(feature = "unwind")]
|
||||
use crate::dwarf::WriterRelocate;
|
||||
use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||
@@ -15,73 +15,54 @@ use crate::translator::{
|
||||
};
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::{binemit, isa, Context};
|
||||
use cranelift_codegen::{binemit, Context};
|
||||
#[cfg(feature = "unwind")]
|
||||
use gimli::write::{Address, EhFrame, FrameTable};
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap};
|
||||
use wasm_common::{
|
||||
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex, TableIndex,
|
||||
};
|
||||
use wasm_common::{FunctionIndex, LocalFunctionIndex, SignatureIndex};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_compiler::{CallingConvention, CompilerConfig, ModuleTranslationState, Target};
|
||||
use wasmer_compiler::{CallingConvention, ModuleTranslationState, Target};
|
||||
use wasmer_compiler::{
|
||||
Compilation, CompiledFunction, CompiledFunctionFrameInfo, CompiledFunctionUnwindInfo, Compiler,
|
||||
Dwarf, FunctionBody, FunctionBodyData, SectionIndex,
|
||||
Compilation, CompileModuleInfo, CompiledFunction, CompiledFunctionFrameInfo,
|
||||
CompiledFunctionUnwindInfo, Compiler, Dwarf, FunctionBody, FunctionBodyData, SectionIndex,
|
||||
};
|
||||
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
|
||||
/// optimizing it and then translating to assembly.
|
||||
pub struct CraneliftCompiler {
|
||||
isa: Box<dyn isa::TargetIsa>,
|
||||
config: CraneliftConfig,
|
||||
config: Cranelift,
|
||||
}
|
||||
|
||||
impl CraneliftCompiler {
|
||||
/// Creates a new Cranelift compiler
|
||||
pub fn new(config: &CraneliftConfig) -> Self {
|
||||
let isa = config.isa();
|
||||
pub fn new(config: &Cranelift) -> Self {
|
||||
Self {
|
||||
isa,
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the starget ISA
|
||||
fn isa(&self) -> &dyn isa::TargetIsa {
|
||||
&*self.isa
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
pub fn config(&self) -> &CraneliftConfig {
|
||||
pub fn config(&self) -> &Cranelift {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for CraneliftCompiler {
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> &Features {
|
||||
self.config.features()
|
||||
}
|
||||
|
||||
/// Gets the target associated to the Cranelift ISA.
|
||||
fn target(&self) -> &Target {
|
||||
self.config.target()
|
||||
}
|
||||
|
||||
/// Compile the module using Cranelift, producing a compilation result with
|
||||
/// associated relocations.
|
||||
fn compile_module(
|
||||
&self,
|
||||
module: &ModuleInfo,
|
||||
target: &Target,
|
||||
compile_info: &CompileModuleInfo,
|
||||
module_translation: &ModuleTranslationState,
|
||||
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError> {
|
||||
let isa = self.isa();
|
||||
let isa = self.config().isa(target);
|
||||
let frontend_config = isa.frontend_config();
|
||||
let memory_plans = &compile_info.memory_plans;
|
||||
let table_plans = &compile_info.table_plans;
|
||||
let module = &compile_info.module;
|
||||
let signatures = module
|
||||
.signatures
|
||||
.iter()
|
||||
@@ -92,7 +73,7 @@ impl Compiler for CraneliftCompiler {
|
||||
#[cfg(feature = "unwind")]
|
||||
let dwarf_frametable = {
|
||||
use std::sync::{Arc, Mutex};
|
||||
match self.target().triple().default_calling_convention() {
|
||||
match target.triple().default_calling_convention() {
|
||||
Ok(CallingConvention::SystemV) => {
|
||||
match isa.create_systemv_cie() {
|
||||
Some(cie) => {
|
||||
@@ -144,17 +125,17 @@ impl Compiler for CraneliftCompiler {
|
||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&*isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(&*isa), error))
|
||||
})?;
|
||||
|
||||
let unwind_info = match compiled_function_unwind_info(isa, &context)? {
|
||||
let unwind_info = match compiled_function_unwind_info(&*isa, &context)? {
|
||||
#[cfg(feature = "unwind")]
|
||||
CraneliftUnwindInfo::FDE(fde) => {
|
||||
if let Some((dwarf_frametable, cie_id)) = &dwarf_frametable {
|
||||
@@ -181,7 +162,7 @@ impl Compiler for CraneliftCompiler {
|
||||
other => other.maybe_into_to_windows_unwind(),
|
||||
};
|
||||
|
||||
let address_map = get_function_address_map(&context, input, code_buf.len(), isa);
|
||||
let address_map = get_function_address_map(&context, input, code_buf.len(), &*isa);
|
||||
|
||||
// We transform the Cranelift JumpTable's into compiler JumpTables
|
||||
let func_jt_offsets = transform_jump_table(context.func.jt_offsets);
|
||||
@@ -207,9 +188,7 @@ impl Compiler for CraneliftCompiler {
|
||||
let (custom_sections, dwarf) = {
|
||||
let mut custom_sections = PrimaryMap::new();
|
||||
let dwarf = if let Some((dwarf_frametable, _cie_id)) = dwarf_frametable {
|
||||
let mut eh_frame = EhFrame(WriterRelocate::new(
|
||||
self.target().triple().endianness().ok(),
|
||||
));
|
||||
let mut eh_frame = EhFrame(WriterRelocate::new(target.triple().endianness().ok()));
|
||||
dwarf_frametable
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -234,7 +213,7 @@ impl Compiler for CraneliftCompiler {
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.map_init(FunctionBuilderContext::new, |mut cx, sig| {
|
||||
make_trampoline_function_call(&*self.isa, &mut cx, sig)
|
||||
make_trampoline_function_call(&*isa, &mut cx, sig)
|
||||
})
|
||||
.collect::<Result<Vec<FunctionBody>, CompileError>>()?
|
||||
.into_iter()
|
||||
@@ -248,7 +227,7 @@ impl Compiler for CraneliftCompiler {
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.map_init(FunctionBuilderContext::new, |mut cx, func_type| {
|
||||
make_trampoline_dynamic_function(&*self.isa, &offsets, &mut cx, &func_type)
|
||||
make_trampoline_dynamic_function(&*isa, &offsets, &mut cx, &func_type)
|
||||
})
|
||||
.collect::<Result<Vec<_>, CompileError>>()?
|
||||
.into_iter()
|
||||
|
||||
@@ -3,8 +3,7 @@ use cranelift_codegen::isa::{lookup, TargetIsa};
|
||||
use cranelift_codegen::settings::{self, Configurable};
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler::{
|
||||
Architecture, Compiler, CompilerConfig, CpuFeature, Features, FunctionMiddlewareGenerator,
|
||||
Target,
|
||||
Architecture, Compiler, CompilerConfig, CpuFeature, FunctionMiddlewareGenerator, Target,
|
||||
};
|
||||
|
||||
// Runtime Environment
|
||||
@@ -29,48 +28,61 @@ pub enum OptLevel {
|
||||
/// This structure exposed a builder-like interface and is primarily consumed by
|
||||
/// [`Engine::new()`]
|
||||
#[derive(Clone)]
|
||||
pub struct CraneliftConfig {
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub enable_nan_canonicalization: bool,
|
||||
|
||||
/// Should the Cranelift verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated Cranelift IR is valid.
|
||||
pub enable_verifier: bool,
|
||||
|
||||
pub struct Cranelift {
|
||||
enable_nan_canonicalization: bool,
|
||||
enable_verifier: bool,
|
||||
enable_simd: bool,
|
||||
enable_pic: bool,
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub opt_level: OptLevel,
|
||||
|
||||
features: Features,
|
||||
target: Target,
|
||||
|
||||
opt_level: OptLevel,
|
||||
/// The middleware chain.
|
||||
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
|
||||
}
|
||||
|
||||
impl CraneliftConfig {
|
||||
impl Cranelift {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new(features: Features, target: Target) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enable_nan_canonicalization: false,
|
||||
enable_verifier: false,
|
||||
opt_level: OptLevel::Speed,
|
||||
enable_pic: false,
|
||||
features,
|
||||
target,
|
||||
enable_simd: false,
|
||||
middlewares: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the ISA for the current target
|
||||
pub fn isa(&self) -> Box<dyn TargetIsa> {
|
||||
let target = self.target();
|
||||
/// Should the Cranelift verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated Cranelift IR is valid.
|
||||
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_verifier = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_nan_canonicalization = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable SIMD support.
|
||||
pub fn enable_simd(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_simd = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub fn opt_level(&mut self, opt_level: OptLevel) -> &mut Self {
|
||||
self.opt_level = opt_level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Generates the ISA for the provided target
|
||||
pub fn isa(&self, target: &Target) -> Box<dyn TargetIsa> {
|
||||
let mut builder =
|
||||
lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
|
||||
// Cpu Features
|
||||
@@ -124,7 +136,7 @@ impl CraneliftConfig {
|
||||
builder.finish(self.flags())
|
||||
}
|
||||
|
||||
/// Generates the flags for the current target
|
||||
/// Generates the flags for the compiler
|
||||
pub fn flags(&self) -> settings::Flags {
|
||||
let mut flags = settings::builder();
|
||||
|
||||
@@ -148,7 +160,7 @@ impl CraneliftConfig {
|
||||
.set("enable_verifier", enable_verifier)
|
||||
.expect("should be valid flag");
|
||||
|
||||
let opt_level = if self.features.simd {
|
||||
let opt_level = if self.enable_simd {
|
||||
"none"
|
||||
} else {
|
||||
match self.opt_level {
|
||||
@@ -162,7 +174,7 @@ impl CraneliftConfig {
|
||||
.set("opt_level", opt_level)
|
||||
.expect("should be valid flag");
|
||||
|
||||
let enable_simd = if self.features.simd { "true" } else { "false" };
|
||||
let enable_simd = if self.enable_simd { "true" } else { "false" };
|
||||
flags
|
||||
.set("enable_simd", enable_simd)
|
||||
.expect("should be valid flag");
|
||||
@@ -180,22 +192,11 @@ impl CraneliftConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig for CraneliftConfig {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
impl CompilerConfig for Cranelift {
|
||||
fn enable_pic(&mut self) {
|
||||
self.enable_pic = true;
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Transform it into the compiler
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
Box::new(CraneliftCompiler::new(&self))
|
||||
@@ -207,8 +208,8 @@ impl CompilerConfig for CraneliftConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CraneliftConfig {
|
||||
impl Default for Cranelift {
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default(), Default::default())
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ mod trampoline;
|
||||
mod translator;
|
||||
|
||||
pub use crate::compiler::CraneliftCompiler;
|
||||
pub use crate::config::CraneliftConfig;
|
||||
pub use crate::config::Cranelift;
|
||||
pub use crate::debug::{ModuleInfoMemoryOffset, ModuleInfoVmctxInfo, ValueLabelsRanges};
|
||||
pub use crate::trampoline::make_trampoline_function_call;
|
||||
|
||||
|
||||
@@ -1290,12 +1290,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
|
||||
let vector = pop1_with_bitcast(state, type_of(op), builder);
|
||||
let extracted = builder.ins().extractlane(vector, lane.clone());
|
||||
let extracted = builder.ins().extractlane(vector, *lane);
|
||||
state.push1(builder.ins().sextend(I32, extracted))
|
||||
}
|
||||
Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
|
||||
let vector = pop1_with_bitcast(state, type_of(op), builder);
|
||||
let extracted = builder.ins().extractlane(vector, lane.clone());
|
||||
let extracted = builder.ins().extractlane(vector, *lane);
|
||||
state.push1(builder.ins().uextend(I32, extracted));
|
||||
// On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so
|
||||
// uextend could be elided; for now, uextend is needed for Cranelift's type checks to
|
||||
@@ -1306,7 +1306,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
| Operator::F32x4ExtractLane { lane }
|
||||
| Operator::F64x2ExtractLane { lane } => {
|
||||
let vector = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().extractlane(vector, lane.clone()))
|
||||
state.push1(builder.ins().extractlane(vector, *lane))
|
||||
}
|
||||
Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
|
||||
let (vector, replacement) = state.pop2();
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::code_translator::{bitcast_arguments, translate_operator, wasm_param_t
|
||||
use super::func_environ::{FuncEnvironment, ReturnMode};
|
||||
use super::func_state::FuncTranslationState;
|
||||
use super::translation_utils::get_vmctx_value_label;
|
||||
use crate::config::CraneliftConfig;
|
||||
use crate::config::Cranelift;
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
|
||||
use cranelift_codegen::timing;
|
||||
@@ -69,7 +69,7 @@ impl FuncTranslator {
|
||||
func: &mut ir::Function,
|
||||
environ: &mut FE,
|
||||
local_function_index: LocalFunctionIndex,
|
||||
config: &CraneliftConfig,
|
||||
config: &Cranelift,
|
||||
) -> WasmResult<()> {
|
||||
let mut reader = MiddlewareBinaryReader::new_with_offset(code, code_offset);
|
||||
reader.set_middleware_chain(
|
||||
|
||||
@@ -24,7 +24,7 @@ impl CraneliftUnwindInfo {
|
||||
/// main users of this function)
|
||||
pub fn maybe_into_to_windows_unwind(self) -> Option<CompiledFunctionUnwindInfo> {
|
||||
match self {
|
||||
CraneliftUnwindInfo::WindowsX64(unwind_info) => {
|
||||
Self::WindowsX64(unwind_info) => {
|
||||
Some(CompiledFunctionUnwindInfo::WindowsX64(unwind_info))
|
||||
}
|
||||
_ => None,
|
||||
|
||||
@@ -3,6 +3,27 @@
|
||||
This is the `wasmer-compiler-llvm` crate, which contains a
|
||||
compiler implementation based on LLVM.
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this crate into your `Cargo.toml` dependencies:
|
||||
|
||||
```toml
|
||||
wasmer-compiler-llvm = "1.0.0-alpha.1"
|
||||
```
|
||||
|
||||
And then:
|
||||
|
||||
```rust
|
||||
use wasmer::{Store, JIT};
|
||||
use wasmer_compiler_llvm::LLVM;
|
||||
|
||||
let compiler = LLVM::new();
|
||||
// Put it into an engine and add it to the store
|
||||
let store = Store::new(&JIT::new(&compiler).engine());
|
||||
```
|
||||
|
||||
## When to use LLVM
|
||||
|
||||
We recommend using LLVM as the default compiler when running WebAssembly
|
||||
files on any **production** system, as it offers maximum peformance near
|
||||
to native speeds.
|
||||
|
||||
@@ -1,61 +1,52 @@
|
||||
use crate::config::LLVMConfig;
|
||||
use crate::config::LLVM;
|
||||
use crate::trampoline::FuncTrampoline;
|
||||
use crate::translator::FuncTranslator;
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
|
||||
use wasm_common::Features;
|
||||
use wasm_common::{LocalFunctionIndex, MemoryIndex, TableIndex};
|
||||
use wasm_common::LocalFunctionIndex;
|
||||
use wasmer_compiler::{
|
||||
Compilation, CompileError, Compiler, CompilerConfig, FunctionBodyData, ModuleTranslationState,
|
||||
RelocationTarget, SectionIndex, Target,
|
||||
Compilation, CompileError, CompileModuleInfo, Compiler, CustomSection, CustomSectionProtection,
|
||||
Dwarf, FunctionBodyData, ModuleTranslationState, RelocationTarget, SectionBody, SectionIndex,
|
||||
Target,
|
||||
};
|
||||
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
|
||||
|
||||
//use std::sync::{Arc, Mutex};
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
|
||||
/// optimizing it and then translating to assembly.
|
||||
pub struct LLVMCompiler {
|
||||
config: LLVMConfig,
|
||||
config: LLVM,
|
||||
}
|
||||
|
||||
impl LLVMCompiler {
|
||||
/// Creates a new LLVM compiler
|
||||
pub fn new(config: &LLVMConfig) -> LLVMCompiler {
|
||||
pub fn new(config: &LLVM) -> LLVMCompiler {
|
||||
LLVMCompiler {
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn config(&self) -> &LLVMConfig {
|
||||
/// Gets the config for this Compiler
|
||||
fn config(&self) -> &LLVM {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for LLVMCompiler {
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> &Features {
|
||||
self.config.features()
|
||||
}
|
||||
|
||||
/// Gets the target associated to this Compiler.
|
||||
fn target(&self) -> &Target {
|
||||
self.config.target()
|
||||
}
|
||||
|
||||
/// Compile the module using LLVM, producing a compilation result with
|
||||
/// associated relocations.
|
||||
fn compile_module<'data, 'module>(
|
||||
&self,
|
||||
module: &'module ModuleInfo,
|
||||
target: &Target,
|
||||
compile_info: &'module CompileModuleInfo,
|
||||
module_translation: &ModuleTranslationState,
|
||||
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError> {
|
||||
//let data = Arc::new(Mutex::new(0));
|
||||
let mut func_names = SecondaryMap::new();
|
||||
let memory_plans = &compile_info.memory_plans;
|
||||
let table_plans = &compile_info.table_plans;
|
||||
let module = &compile_info.module;
|
||||
|
||||
// TODO: merge constants in sections.
|
||||
|
||||
@@ -67,29 +58,37 @@ impl Compiler for LLVMCompiler {
|
||||
.unwrap_or_else(|| format!("fn{}", func_index.index()));
|
||||
}
|
||||
let mut module_custom_sections = PrimaryMap::new();
|
||||
let mut frame_section_bytes = vec![];
|
||||
let mut frame_section_relocations = vec![];
|
||||
let functions = function_body_inputs
|
||||
.into_iter()
|
||||
.collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
|
||||
.par_iter()
|
||||
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
|
||||
// TODO: remove (to serialize)
|
||||
//let mut data = data.lock().unwrap();
|
||||
func_translator.translate(
|
||||
module,
|
||||
module_translation,
|
||||
i,
|
||||
input,
|
||||
self.config(),
|
||||
&memory_plans,
|
||||
&table_plans,
|
||||
&func_names,
|
||||
)
|
||||
})
|
||||
.map_init(
|
||||
|| {
|
||||
let target_machine = self.config().target_machine(target);
|
||||
FuncTranslator::new(target_machine)
|
||||
},
|
||||
|func_translator, (i, input)| {
|
||||
// TODO: remove (to serialize)
|
||||
//let mut data = data.lock().unwrap();
|
||||
func_translator.translate(
|
||||
&module,
|
||||
module_translation,
|
||||
i,
|
||||
input,
|
||||
self.config(),
|
||||
&memory_plans,
|
||||
&table_plans,
|
||||
&func_names,
|
||||
)
|
||||
},
|
||||
)
|
||||
.collect::<Result<Vec<_>, CompileError>>()?
|
||||
.into_iter()
|
||||
.map(|(mut compiled_function, function_custom_sections)| {
|
||||
.map(|mut compiled_function| {
|
||||
let first_section = module_custom_sections.len() as u32;
|
||||
for (_, custom_section) in function_custom_sections.iter() {
|
||||
for (section_index, custom_section) in compiled_function.custom_sections.iter() {
|
||||
// TODO: remove this call to clone()
|
||||
let mut custom_section = custom_section.clone();
|
||||
for mut reloc in &mut custom_section.relocations {
|
||||
@@ -99,27 +98,74 @@ impl Compiler for LLVMCompiler {
|
||||
)
|
||||
}
|
||||
}
|
||||
module_custom_sections.push(custom_section);
|
||||
if compiled_function
|
||||
.eh_frame_section_indices
|
||||
.contains(§ion_index)
|
||||
{
|
||||
let offset = frame_section_bytes.len() as u32;
|
||||
for mut reloc in &mut custom_section.relocations {
|
||||
reloc.offset += offset;
|
||||
}
|
||||
frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
|
||||
frame_section_relocations.extend(custom_section.relocations);
|
||||
// TODO: we do this to keep the count right, remove it.
|
||||
module_custom_sections.push(CustomSection {
|
||||
protection: CustomSectionProtection::Read,
|
||||
bytes: SectionBody::new_with_vec(vec![]),
|
||||
relocations: vec![],
|
||||
});
|
||||
} else {
|
||||
module_custom_sections.push(custom_section);
|
||||
}
|
||||
}
|
||||
for mut reloc in &mut compiled_function.relocations {
|
||||
for mut reloc in &mut compiled_function.compiled_function.relocations {
|
||||
if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
|
||||
reloc.reloc_target = RelocationTarget::CustomSection(
|
||||
SectionIndex::from_u32(first_section + index.as_u32()),
|
||||
)
|
||||
}
|
||||
}
|
||||
compiled_function
|
||||
compiled_function.compiled_function
|
||||
})
|
||||
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
|
||||
|
||||
let dwarf = if !frame_section_bytes.is_empty() {
|
||||
let dwarf = Some(Dwarf::new(SectionIndex::from_u32(
|
||||
module_custom_sections.len() as u32,
|
||||
)));
|
||||
// Terminating zero-length CIE.
|
||||
frame_section_bytes.extend(vec![
|
||||
0x00, 0x00, 0x00, 0x00, // Length
|
||||
0x00, 0x00, 0x00, 0x00, // CIE ID
|
||||
0x10, // Version (must be 1)
|
||||
0x00, // Augmentation data
|
||||
0x00, // Code alignment factor
|
||||
0x00, // Data alignment factor
|
||||
0x00, // Return address register
|
||||
0x00, 0x00, 0x00, // Padding to a multiple of 4 bytes
|
||||
]);
|
||||
module_custom_sections.push(CustomSection {
|
||||
protection: CustomSectionProtection::Read,
|
||||
bytes: SectionBody::new_with_vec(frame_section_bytes),
|
||||
relocations: frame_section_relocations,
|
||||
});
|
||||
dwarf
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let function_call_trampolines = module
|
||||
.signatures
|
||||
.values()
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.map_init(FuncTrampoline::new, |func_trampoline, sig| {
|
||||
func_trampoline.trampoline(sig, self.config())
|
||||
})
|
||||
.map_init(
|
||||
|| {
|
||||
let target_machine = self.config().target_machine(target);
|
||||
FuncTrampoline::new(target_machine)
|
||||
},
|
||||
|func_trampoline, sig| func_trampoline.trampoline(sig, self.config()),
|
||||
)
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.collect::<Result<PrimaryMap<_, _>, CompileError>>()?;
|
||||
@@ -128,9 +174,15 @@ impl Compiler for LLVMCompiler {
|
||||
.imported_function_types()
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.map_init(FuncTrampoline::new, |func_trampoline, func_type| {
|
||||
func_trampoline.dynamic_trampoline(&func_type, self.config())
|
||||
})
|
||||
.map_init(
|
||||
|| {
|
||||
let target_machine = self.config().target_machine(target);
|
||||
FuncTrampoline::new(target_machine)
|
||||
},
|
||||
|func_trampoline, func_type| {
|
||||
func_trampoline.dynamic_trampoline(&func_type, self.config())
|
||||
},
|
||||
)
|
||||
.collect::<Result<Vec<_>, CompileError>>()?
|
||||
.into_iter()
|
||||
.collect::<PrimaryMap<_, _>>();
|
||||
@@ -140,7 +192,7 @@ impl Compiler for LLVMCompiler {
|
||||
module_custom_sections,
|
||||
function_call_trampolines,
|
||||
dynamic_function_trampolines,
|
||||
None,
|
||||
dwarf,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ use itertools::Itertools;
|
||||
use std::sync::Arc;
|
||||
use target_lexicon::Architecture;
|
||||
use wasm_common::{FunctionType, LocalFunctionIndex};
|
||||
use wasmer_compiler::{
|
||||
Compiler, CompilerConfig, Features, FunctionMiddlewareGenerator, Target, Triple,
|
||||
};
|
||||
use wasmer_compiler::{Compiler, CompilerConfig, FunctionMiddlewareGenerator, Target, Triple};
|
||||
|
||||
/// The InkWell ModuleInfo type
|
||||
pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
|
||||
@@ -41,50 +39,60 @@ pub trait LLVMCallbacks: Send + Sync {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LLVMConfig {
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub enable_nan_canonicalization: bool,
|
||||
|
||||
/// Should the LLVM IR verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated LLVM IR is valid.
|
||||
pub enable_verifier: bool,
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub opt_level: OptimizationLevel,
|
||||
|
||||
/// Whether to emit PIC.
|
||||
pub is_pic: bool,
|
||||
|
||||
/// Callbacks that will triggered in the different compilation
|
||||
/// phases in LLVM.
|
||||
pub callbacks: Option<Arc<dyn LLVMCallbacks>>,
|
||||
|
||||
pub struct LLVM {
|
||||
pub(crate) enable_nan_canonicalization: bool,
|
||||
pub(crate) enable_verifier: bool,
|
||||
pub(crate) opt_level: OptimizationLevel,
|
||||
is_pic: bool,
|
||||
pub(crate) callbacks: Option<Arc<dyn LLVMCallbacks>>,
|
||||
/// The middleware chain.
|
||||
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
|
||||
|
||||
features: Features,
|
||||
target: Target,
|
||||
}
|
||||
|
||||
impl LLVMConfig {
|
||||
impl LLVM {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new(features: Features, target: Target) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enable_nan_canonicalization: true,
|
||||
enable_nan_canonicalization: false,
|
||||
enable_verifier: false,
|
||||
opt_level: OptimizationLevel::Aggressive,
|
||||
is_pic: false,
|
||||
features,
|
||||
target,
|
||||
callbacks: None,
|
||||
middlewares: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Should the LLVM verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated LLVM IR is valid.
|
||||
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_verifier = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_nan_canonicalization = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub fn opt_level(&mut self, opt_level: OptimizationLevel) -> &mut Self {
|
||||
self.opt_level = opt_level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Callbacks that will triggered in the different compilation
|
||||
/// phases in LLVM.
|
||||
pub fn callbacks(&mut self, callbacks: Option<Arc<dyn LLVMCallbacks>>) -> &mut Self {
|
||||
self.callbacks = callbacks;
|
||||
self
|
||||
}
|
||||
|
||||
fn reloc_mode(&self) -> RelocMode {
|
||||
if self.is_pic {
|
||||
RelocMode::PIC
|
||||
@@ -97,8 +105,7 @@ impl LLVMConfig {
|
||||
CodeModel::Large
|
||||
}
|
||||
|
||||
pub fn target_triple(&self) -> TargetTriple {
|
||||
let target = self.target();
|
||||
fn target_triple(&self, target: &Target) -> TargetTriple {
|
||||
let operating_system =
|
||||
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
|
||||
// LLVM detects static relocation + darwin + 64-bit and
|
||||
@@ -122,8 +129,7 @@ impl LLVMConfig {
|
||||
}
|
||||
|
||||
/// Generates the target machine for the current target
|
||||
pub fn target_machine(&self) -> TargetMachine {
|
||||
let target = self.target();
|
||||
pub fn target_machine(&self, target: &Target) -> TargetMachine {
|
||||
let triple = target.triple();
|
||||
let cpu_features = &target.cpu_features();
|
||||
|
||||
@@ -155,7 +161,7 @@ impl LLVMConfig {
|
||||
.map(|feature| format!("+{}", feature.to_string()))
|
||||
.join(",");
|
||||
|
||||
let target_triple = self.target_triple();
|
||||
let target_triple = self.target_triple(&target);
|
||||
let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
|
||||
llvm_target
|
||||
.create_target_machine(
|
||||
@@ -170,12 +176,7 @@ impl LLVMConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig for LLVMConfig {
|
||||
/// Gets the WebAssembly features.
|
||||
fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
impl CompilerConfig for LLVM {
|
||||
/// Emit code suitable for dlopen.
|
||||
fn enable_pic(&mut self) {
|
||||
// TODO: although we can emit PIC, the object file parser does not yet
|
||||
@@ -183,12 +184,6 @@ impl CompilerConfig for LLVMConfig {
|
||||
self.is_pic = true;
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling.
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Transform it into the compiler.
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
Box::new(LLVMCompiler::new(&self))
|
||||
@@ -200,8 +195,8 @@ impl CompilerConfig for LLVMConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LLVMConfig {
|
||||
fn default() -> LLVMConfig {
|
||||
Self::new(Default::default(), Default::default())
|
||||
impl Default for LLVM {
|
||||
fn default() -> LLVM {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,5 +22,5 @@ mod translator;
|
||||
|
||||
pub use crate::compiler::LLVMCompiler;
|
||||
pub use crate::config::{
|
||||
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVMConfig,
|
||||
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
|
||||
};
|
||||
|
||||
@@ -3,10 +3,9 @@ use std::convert::TryFrom;
|
||||
|
||||
use wasm_common::entity::{PrimaryMap, SecondaryMap};
|
||||
use wasmer_compiler::{
|
||||
CompileError, CompiledFunction, CompiledFunctionFrameInfo, CustomSection,
|
||||
CustomSectionProtection, CustomSections, FunctionAddressMap, FunctionBody,
|
||||
InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SectionBody, SectionIndex,
|
||||
SourceLoc,
|
||||
CompileError, CompiledFunctionFrameInfo, CustomSection, CustomSectionProtection,
|
||||
CustomSections, FunctionAddressMap, FunctionBody, InstructionAddressMap, Relocation,
|
||||
RelocationKind, RelocationTarget, SectionBody, SectionIndex, SourceLoc,
|
||||
};
|
||||
use wasmer_runtime::libcalls::LibCall;
|
||||
|
||||
@@ -38,12 +37,18 @@ fn map_goblin_err(error: goblin::error::Error) -> CompileError {
|
||||
CompileError::Codegen(format!("error parsing ELF file: {}", error))
|
||||
}
|
||||
|
||||
pub struct CompiledFunction {
|
||||
pub compiled_function: wasmer_compiler::CompiledFunction,
|
||||
pub custom_sections: CustomSections,
|
||||
pub eh_frame_section_indices: Vec<SectionIndex>,
|
||||
}
|
||||
|
||||
pub fn load_object_file<F>(
|
||||
contents: &[u8],
|
||||
root_section: &str,
|
||||
self_referential_relocation_target: Option<RelocationTarget>,
|
||||
root_section_reloc_target: RelocationTarget,
|
||||
mut symbol_name_to_relocation_target: F,
|
||||
) -> Result<(CompiledFunction, CustomSections), CompileError>
|
||||
) -> Result<CompiledFunction, CompileError>
|
||||
where
|
||||
F: FnMut(&String) -> Result<Option<RelocationTarget>, CompileError>,
|
||||
{
|
||||
@@ -102,9 +107,7 @@ where
|
||||
|
||||
let mut section_to_custom_section = HashMap::new();
|
||||
|
||||
if let Some(reloc_target) = self_referential_relocation_target {
|
||||
section_targets.insert(root_section_index, reloc_target);
|
||||
};
|
||||
section_targets.insert(root_section_index, root_section_reloc_target);
|
||||
|
||||
let mut next_custom_section: u32 = 0;
|
||||
let mut elf_section_to_target = |elf_section_index: ElfSectionIndex| {
|
||||
@@ -142,6 +145,22 @@ where
|
||||
// the sections we want to include.
|
||||
worklist.push(root_section_index);
|
||||
visited.insert(root_section_index);
|
||||
|
||||
// Also add any .eh_frame sections.
|
||||
let mut eh_frame_section_indices = vec![];
|
||||
// TODO: this constant has been added to goblin, now waiting for release
|
||||
const SHT_X86_64_UNWIND: u32 = 0x7000_0001;
|
||||
for (index, shdr) in elf.section_headers.iter().enumerate() {
|
||||
if shdr.sh_type == SHT_X86_64_UNWIND {
|
||||
let index = ElfSectionIndex::from_usize(index)?;
|
||||
worklist.push(index);
|
||||
visited.insert(index);
|
||||
eh_frame_section_indices.push(index);
|
||||
// This allocates a custom section index for the ELF section.
|
||||
elf_section_to_target(index);
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(section_index) = worklist.pop() {
|
||||
for reloc in reloc_sections
|
||||
.get(§ion_index)
|
||||
@@ -152,6 +171,7 @@ where
|
||||
// TODO: these constants are not per-arch, we'll need to
|
||||
// make the whole match per-arch.
|
||||
goblin::elf::reloc::R_X86_64_64 => RelocationKind::Abs8,
|
||||
goblin::elf::reloc::R_X86_64_PC64 => RelocationKind::X86PCRel8,
|
||||
goblin::elf::reloc::R_X86_64_GOT64 => {
|
||||
return Err(CompileError::Codegen(
|
||||
"unimplemented PIC relocation R_X86_64_GOT64".into(),
|
||||
@@ -180,17 +200,13 @@ where
|
||||
))
|
||||
})?;
|
||||
let elf_target_section = ElfSectionIndex::from_usize(elf_target.st_shndx)?;
|
||||
let reloc_target = if elf_target.st_type() == goblin::elf::sym::STT_SECTION {
|
||||
let reloc_target = if elf_target_section == root_section_index {
|
||||
root_section_reloc_target
|
||||
} else if elf_target.st_type() == goblin::elf::sym::STT_SECTION {
|
||||
if visited.insert(elf_target_section) {
|
||||
worklist.push(elf_target_section);
|
||||
}
|
||||
elf_section_to_target(elf_target_section)
|
||||
} else if elf_target.st_type() == goblin::elf::sym::STT_FUNC
|
||||
&& elf_target_section == root_section_index
|
||||
&& self_referential_relocation_target.is_some()
|
||||
{
|
||||
// This is a function referencing its own byte stream.
|
||||
self_referential_relocation_target.unwrap()
|
||||
} else if elf_target.st_type() == goblin::elf::sym::STT_NOTYPE
|
||||
&& elf_target_section.is_undef()
|
||||
{
|
||||
@@ -220,6 +236,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let eh_frame_section_indices = eh_frame_section_indices
|
||||
.iter()
|
||||
.map(|index| {
|
||||
section_to_custom_section.get(index).map_or_else(
|
||||
|| {
|
||||
Err(CompileError::Codegen(format!(
|
||||
".eh_frame section with index={:?} was never loaded",
|
||||
index
|
||||
)))
|
||||
},
|
||||
|idx| Ok(*idx),
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<SectionIndex>, _>>()?;
|
||||
|
||||
let mut custom_sections = section_to_custom_section
|
||||
.iter()
|
||||
.map(|(elf_section_index, custom_section_index)| {
|
||||
@@ -258,8 +289,8 @@ where
|
||||
body_len: function_body.body.len(),
|
||||
};
|
||||
|
||||
Ok((
|
||||
CompiledFunction {
|
||||
Ok(CompiledFunction {
|
||||
compiled_function: wasmer_compiler::CompiledFunction {
|
||||
body: function_body,
|
||||
jt_offsets: SecondaryMap::new(),
|
||||
relocations: relocations
|
||||
@@ -271,5 +302,6 @@ where
|
||||
},
|
||||
},
|
||||
custom_sections,
|
||||
))
|
||||
eh_frame_section_indices,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::config::{CompiledFunctionKind, LLVMConfig};
|
||||
use crate::object_file::load_object_file;
|
||||
use crate::config::{CompiledFunctionKind, LLVM};
|
||||
use crate::object_file::{load_object_file, CompiledFunction};
|
||||
use crate::translator::abi::{
|
||||
func_type_to_llvm, get_vmctx_ptr_param, is_sret, pack_values_for_register_return,
|
||||
rets_from_call,
|
||||
@@ -10,39 +10,41 @@ use inkwell::{
|
||||
context::Context,
|
||||
module::Linkage,
|
||||
passes::PassManager,
|
||||
targets::FileType,
|
||||
targets::{FileType, TargetMachine},
|
||||
types::BasicType,
|
||||
values::{BasicValue, FunctionValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use std::cmp;
|
||||
use std::convert::TryInto;
|
||||
use wasm_common::{FunctionType, Type};
|
||||
use wasmer_compiler::{CompileError, FunctionBody};
|
||||
use wasm_common::{FunctionType, LocalFunctionIndex, Type};
|
||||
use wasmer_compiler::{CompileError, FunctionBody, RelocationTarget};
|
||||
|
||||
pub struct FuncTrampoline {
|
||||
ctx: Context,
|
||||
target_machine: TargetMachine,
|
||||
}
|
||||
|
||||
const FUNCTION_SECTION: &str = ".wasmer_trampoline";
|
||||
|
||||
impl FuncTrampoline {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(target_machine: TargetMachine) -> Self {
|
||||
Self {
|
||||
ctx: Context::create(),
|
||||
target_machine,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trampoline(
|
||||
&mut self,
|
||||
ty: &FunctionType,
|
||||
config: &LLVMConfig,
|
||||
config: &LLVM,
|
||||
) -> Result<FunctionBody, CompileError> {
|
||||
// The function type, used for the callbacks.
|
||||
let function = CompiledFunctionKind::FunctionCallTrampoline(ty.clone());
|
||||
let module = self.ctx.create_module("");
|
||||
let target_triple = config.target_triple();
|
||||
let target_machine = config.target_machine();
|
||||
let target_machine = &self.target_machine;
|
||||
let target_triple = target_machine.get_triple();
|
||||
module.set_triple(&target_triple);
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||
@@ -92,24 +94,45 @@ impl FuncTrampoline {
|
||||
}
|
||||
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
let (function, sections) =
|
||||
load_object_file(mem_buf_slice, FUNCTION_SECTION, None, |name: &String| {
|
||||
let CompiledFunction {
|
||||
compiled_function,
|
||||
custom_sections,
|
||||
eh_frame_section_indices,
|
||||
} = load_object_file(
|
||||
mem_buf_slice,
|
||||
FUNCTION_SECTION,
|
||||
RelocationTarget::LocalFunc(LocalFunctionIndex::from_u32(0)),
|
||||
|name: &String| {
|
||||
Err(CompileError::Codegen(format!(
|
||||
"trampoline generation produced reference to unknown function {}",
|
||||
name
|
||||
)))
|
||||
})?;
|
||||
if !sections.is_empty() {
|
||||
},
|
||||
)?;
|
||||
let mut all_sections_are_eh_sections = true;
|
||||
if eh_frame_section_indices.len() != custom_sections.len() {
|
||||
all_sections_are_eh_sections = false;
|
||||
} else {
|
||||
let mut eh_frame_section_indices = eh_frame_section_indices;
|
||||
eh_frame_section_indices.sort_unstable();
|
||||
for (idx, section_idx) in eh_frame_section_indices.iter().enumerate() {
|
||||
if idx as u32 != section_idx.as_u32() {
|
||||
all_sections_are_eh_sections = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !all_sections_are_eh_sections {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced custom sections".into(),
|
||||
"trampoline generation produced non-eh custom sections".into(),
|
||||
));
|
||||
}
|
||||
if !function.relocations.is_empty() {
|
||||
if !compiled_function.relocations.is_empty() {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced relocations".into(),
|
||||
));
|
||||
}
|
||||
if !function.jt_offsets.is_empty() {
|
||||
if !compiled_function.jt_offsets.is_empty() {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced jump tables".into(),
|
||||
));
|
||||
@@ -117,21 +140,21 @@ impl FuncTrampoline {
|
||||
// Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
|
||||
|
||||
Ok(FunctionBody {
|
||||
body: function.body.body,
|
||||
unwind_info: function.body.unwind_info,
|
||||
body: compiled_function.body.body,
|
||||
unwind_info: compiled_function.body.unwind_info,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dynamic_trampoline(
|
||||
&mut self,
|
||||
ty: &FunctionType,
|
||||
config: &LLVMConfig,
|
||||
config: &LLVM,
|
||||
) -> Result<FunctionBody, CompileError> {
|
||||
// The function type, used for the callbacks
|
||||
let function = CompiledFunctionKind::DynamicFunctionTrampoline(ty.clone());
|
||||
let module = self.ctx.create_module("");
|
||||
let target_triple = config.target_triple();
|
||||
let target_machine = config.target_machine();
|
||||
let target_machine = &self.target_machine;
|
||||
let target_triple = target_machine.get_triple();
|
||||
module.set_triple(&target_triple);
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||
@@ -173,24 +196,45 @@ impl FuncTrampoline {
|
||||
}
|
||||
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
let (function, sections) =
|
||||
load_object_file(mem_buf_slice, FUNCTION_SECTION, None, |name: &String| {
|
||||
let CompiledFunction {
|
||||
compiled_function,
|
||||
custom_sections,
|
||||
eh_frame_section_indices,
|
||||
} = load_object_file(
|
||||
mem_buf_slice,
|
||||
FUNCTION_SECTION,
|
||||
RelocationTarget::LocalFunc(LocalFunctionIndex::from_u32(0)),
|
||||
|name: &String| {
|
||||
Err(CompileError::Codegen(format!(
|
||||
"trampoline generation produced reference to unknown function {}",
|
||||
name
|
||||
)))
|
||||
})?;
|
||||
if !sections.is_empty() {
|
||||
},
|
||||
)?;
|
||||
let mut all_sections_are_eh_sections = true;
|
||||
if eh_frame_section_indices.len() != custom_sections.len() {
|
||||
all_sections_are_eh_sections = false;
|
||||
} else {
|
||||
let mut eh_frame_section_indices = eh_frame_section_indices;
|
||||
eh_frame_section_indices.sort_unstable();
|
||||
for (idx, section_idx) in eh_frame_section_indices.iter().enumerate() {
|
||||
if idx as u32 != section_idx.as_u32() {
|
||||
all_sections_are_eh_sections = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !all_sections_are_eh_sections {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced custom sections".into(),
|
||||
"trampoline generation produced non-eh custom sections".into(),
|
||||
));
|
||||
}
|
||||
if !function.relocations.is_empty() {
|
||||
if !compiled_function.relocations.is_empty() {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced relocations".into(),
|
||||
));
|
||||
}
|
||||
if !function.jt_offsets.is_empty() {
|
||||
if !compiled_function.jt_offsets.is_empty() {
|
||||
return Err(CompileError::Codegen(
|
||||
"trampoline generation produced jump tables".into(),
|
||||
));
|
||||
@@ -198,8 +242,8 @@ impl FuncTrampoline {
|
||||
// Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
|
||||
|
||||
Ok(FunctionBody {
|
||||
body: function.body.body,
|
||||
unwind_info: function.body.unwind_info,
|
||||
body: compiled_function.body.body,
|
||||
unwind_info: compiled_function.body.unwind_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use inkwell::{
|
||||
context::Context,
|
||||
module::{Linkage, Module},
|
||||
passes::PassManager,
|
||||
targets::FileType,
|
||||
targets::{FileType, TargetMachine},
|
||||
types::{BasicType, BasicTypeEnum, FloatMathType, IntType, PointerType, VectorType},
|
||||
values::{
|
||||
BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionOpcode, InstructionValue,
|
||||
@@ -22,8 +22,8 @@ use inkwell::{
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::config::{CompiledFunctionKind, LLVMConfig};
|
||||
use crate::object_file::load_object_file;
|
||||
use crate::config::{CompiledFunctionKind, LLVM};
|
||||
use crate::object_file::{load_object_file, CompiledFunction};
|
||||
use wasm_common::entity::{PrimaryMap, SecondaryMap};
|
||||
use wasm_common::{
|
||||
FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex,
|
||||
@@ -31,9 +31,8 @@ use wasm_common::{
|
||||
};
|
||||
use wasmer_compiler::wasmparser::{MemoryImmediate, Operator};
|
||||
use wasmer_compiler::{
|
||||
to_wasm_error, wptype_to_type, CompileError, CompiledFunction, CustomSections,
|
||||
FunctionBodyData, GenerateMiddlewareChain, MiddlewareBinaryReader, ModuleTranslationState,
|
||||
RelocationTarget,
|
||||
to_wasm_error, wptype_to_type, CompileError, FunctionBodyData, GenerateMiddlewareChain,
|
||||
MiddlewareBinaryReader, ModuleTranslationState, RelocationTarget,
|
||||
};
|
||||
use wasmer_runtime::{MemoryPlan, ModuleInfo, TablePlan};
|
||||
|
||||
@@ -55,12 +54,14 @@ fn const_zero(ty: BasicTypeEnum) -> BasicValueEnum {
|
||||
|
||||
pub struct FuncTranslator {
|
||||
ctx: Context,
|
||||
target_machine: TargetMachine,
|
||||
}
|
||||
|
||||
impl FuncTranslator {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(target_machine: TargetMachine) -> Self {
|
||||
Self {
|
||||
ctx: Context::create(),
|
||||
target_machine,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,11 +71,11 @@ impl FuncTranslator {
|
||||
module_translation: &ModuleTranslationState,
|
||||
local_func_index: &LocalFunctionIndex,
|
||||
function_body: &FunctionBodyData,
|
||||
config: &LLVMConfig,
|
||||
config: &LLVM,
|
||||
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
_table_plans: &PrimaryMap<TableIndex, TablePlan>,
|
||||
func_names: &SecondaryMap<FunctionIndex, String>,
|
||||
) -> Result<(CompiledFunction, CustomSections), CompileError> {
|
||||
) -> Result<CompiledFunction, CompileError> {
|
||||
// The function type, used for the callbacks.
|
||||
let function = CompiledFunctionKind::Local(*local_func_index);
|
||||
let func_index = wasm_module.func_index(*local_func_index);
|
||||
@@ -85,8 +86,8 @@ impl FuncTranslator {
|
||||
};
|
||||
let module = self.ctx.create_module(module_name.as_str());
|
||||
|
||||
let target_triple = config.target_triple();
|
||||
let target_machine = config.target_machine();
|
||||
let target_machine = &self.target_machine;
|
||||
let target_triple = target_machine.get_triple();
|
||||
module.set_triple(&target_triple);
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
let wasm_fn_type = wasm_module
|
||||
@@ -272,7 +273,7 @@ impl FuncTranslator {
|
||||
load_object_file(
|
||||
mem_buf_slice,
|
||||
".wasmer_function",
|
||||
Some(RelocationTarget::LocalFunc(*local_func_index)),
|
||||
RelocationTarget::LocalFunc(*local_func_index),
|
||||
|name: &String| {
|
||||
if let Some((index, _)) = func_names
|
||||
.iter()
|
||||
|
||||
@@ -3,6 +3,27 @@
|
||||
This is the `wasmer-compiler-singlepass` crate, which contains a
|
||||
compiler implementation based on Singlepass.
|
||||
|
||||
## Usage
|
||||
|
||||
Add this crate into your `Cargo.toml` dependencies:
|
||||
|
||||
```toml
|
||||
wasmer-compiler-singlepass = "1.0.0-alpha.1"
|
||||
```
|
||||
|
||||
And then:
|
||||
|
||||
```rust
|
||||
use wasmer::{Store, JIT};
|
||||
use wasmer_compiler_singlepass::Singlepass;
|
||||
|
||||
let compiler = Singlepass::new();
|
||||
// Put it into an engine and add it to the store
|
||||
let store = Store::new(&JIT::new(&compiler).engine());
|
||||
```
|
||||
|
||||
## When to use Singlepass
|
||||
|
||||
Singlepass is designed to emit compiled code at linear time, as such
|
||||
is not prone to JIT bombs and also offers great compilation performance
|
||||
orders of magnitude faster than `wasmer-compiler-cranelift` and
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::{
|
||||
common_decl::*, config::SinglepassConfig, emitter_x64::*, machine::Machine, x64_decl::*,
|
||||
};
|
||||
use crate::{common_decl::*, config::Singlepass, emitter_x64::*, machine::Machine, x64_decl::*};
|
||||
use dynasmrt::{x64::Assembler, DynamicLabel};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::collections::BTreeMap;
|
||||
@@ -32,7 +30,7 @@ pub struct FuncGen<'a> {
|
||||
module: &'a ModuleInfo,
|
||||
|
||||
/// ModuleInfo compilation config.
|
||||
config: &'a SinglepassConfig,
|
||||
config: &'a Singlepass,
|
||||
|
||||
/// Offsets of vmctx fields.
|
||||
vmoffsets: &'a VMOffsets,
|
||||
@@ -410,7 +408,7 @@ impl<'a> FuncGen<'a> {
|
||||
let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst {
|
||||
Location::Imm32(_) | Location::Imm64(_) => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_zx_sx dst Imm: unreachable code"),
|
||||
message: "emit_relaxed_zx_sx dst Imm: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
Location::Memory(_, _) => {
|
||||
@@ -427,7 +425,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_zx_sx dst: unreachable code"),
|
||||
message: "emit_relaxed_zx_sx dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -448,7 +446,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_zx_sx src: unreachable code"),
|
||||
message: "emit_relaxed_zx_sx src: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -584,7 +582,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_avx_base src1: unreachable code"),
|
||||
message: "emit_relaxed_avx_base src1: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -613,7 +611,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_avx_base src2: unreachable code"),
|
||||
message: "emit_relaxed_avx_base src2: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -628,7 +626,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_relaxed_avx_base dst: unreachable code"),
|
||||
message: "emit_relaxed_avx_base dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -703,7 +701,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_cmpop_i32_dynamic_b ret: unreachable code"),
|
||||
message: "emit_cmpop_i32_dynamic_b ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -750,7 +748,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_cmpop_i64_dynamic_b ret: unreachable code"),
|
||||
message: "emit_cmpop_i64_dynamic_b ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -810,7 +808,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_xcnt_i32 loc: unreachable code"),
|
||||
message: "emit_xcnt_i32 loc: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -863,7 +861,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_xcnt_i64 loc: unreachable code"),
|
||||
message: "emit_xcnt_i64 loc: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -967,7 +965,7 @@ impl<'a> FuncGen<'a> {
|
||||
self.machine.state.register_values[X64Register::GPR(*r).to_index().0].clone();
|
||||
if content == MachineValue::Undefined {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv: Undefined used_gprs content"),
|
||||
message: "emit_call_sysv: Undefined used_gprs content".to_string(),
|
||||
});
|
||||
}
|
||||
self.machine.state.stack_values.push(content);
|
||||
@@ -994,7 +992,7 @@ impl<'a> FuncGen<'a> {
|
||||
self.machine.state.register_values[X64Register::XMM(*r).to_index().0].clone();
|
||||
if content == MachineValue::Undefined {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv: Undefined used_xmms content"),
|
||||
message: "emit_call_sysv: Undefined used_xmms content".to_string(),
|
||||
});
|
||||
}
|
||||
self.machine.state.stack_values.push(content);
|
||||
@@ -1005,12 +1003,8 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
// Calculate stack offset.
|
||||
for (i, _param) in params.iter().enumerate() {
|
||||
let loc = Machine::get_param_location(1 + i);
|
||||
match loc {
|
||||
Location::Memory(_, _) => {
|
||||
stack_offset += 8;
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
|
||||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1062,7 +1056,8 @@ impl<'a> FuncGen<'a> {
|
||||
Location::Memory(reg, offset) => {
|
||||
if reg != GPR::RBP {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv loc param: unreachable code"),
|
||||
message: "emit_call_sysv loc param: unreachable code"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
self.machine
|
||||
@@ -1112,7 +1107,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv loc: unreachable code"),
|
||||
message: "emit_call_sysv loc: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1137,7 +1132,7 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
if (self.machine.state.stack_values.len() % 2) != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv: explicit shadow takes one slot"),
|
||||
message: "emit_call_sysv: explicit shadow takes one slot".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1171,7 +1166,7 @@ impl<'a> FuncGen<'a> {
|
||||
);
|
||||
if (stack_offset % 8) != 0 {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv: Bad restoring stack alignement"),
|
||||
message: "emit_call_sysv: Bad restoring stack alignement".to_string(),
|
||||
});
|
||||
}
|
||||
for _ in 0..stack_offset / 8 {
|
||||
@@ -1180,7 +1175,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
|
||||
// Restore XMMs.
|
||||
if used_xmms.len() > 0 {
|
||||
if !used_xmms.is_empty() {
|
||||
for (i, r) in used_xmms.iter().enumerate() {
|
||||
self.assembler.emit_mov(
|
||||
Size::S64,
|
||||
@@ -1206,7 +1201,7 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
if self.machine.state.stack_values.pop().unwrap() != MachineValue::ExplicitShadow {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_call_sysv: Popped value is not ExplicitShadow"),
|
||||
message: "emit_call_sysv: Popped value is not ExplicitShadow".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
@@ -1330,7 +1325,7 @@ impl<'a> FuncGen<'a> {
|
||||
3 => 8,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_memory_op align: unreachable value"),
|
||||
message: "emit_memory_op align: unreachable value".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -1371,7 +1366,7 @@ impl<'a> FuncGen<'a> {
|
||||
) -> Result<(), CodegenError> {
|
||||
if memory_sz > stack_sz {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_compare_and_swap: memory size > stac size"),
|
||||
message: "emit_compare_and_swap: memory size > stack size".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1773,7 +1768,7 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
if self.machine.state.wasm_inst_offset != std::usize::MAX {
|
||||
return Err(CodegenError {
|
||||
message: format!("emit_head: wasm_inst_offset not std::usize::MAX"),
|
||||
message: "emit_head: wasm_inst_offset not std::usize::MAX".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
@@ -1781,7 +1776,7 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
pub fn new(
|
||||
module: &'a ModuleInfo,
|
||||
config: &'a SinglepassConfig,
|
||||
config: &'a Singlepass,
|
||||
vmoffsets: &'a VMOffsets,
|
||||
memory_plans: &'a PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
_table_plans: &'a PrimaryMap<TableIndex, TablePlan>,
|
||||
@@ -1842,7 +1837,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
|
||||
pub fn has_control_frames(&self) -> bool {
|
||||
self.control_stack.len() > 0
|
||||
!self.control_stack.is_empty()
|
||||
}
|
||||
|
||||
pub fn feed_operator(&mut self, op: Operator) -> Result<(), CodegenError> {
|
||||
@@ -2160,7 +2155,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I32Clz src: unreachable code"),
|
||||
message: "I32Clz src: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2177,7 +2172,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I32Clz dst: unreachable code"),
|
||||
message: "I32Clz dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2211,12 +2206,9 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = ret {
|
||||
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
};
|
||||
}
|
||||
Operator::I32Ctz => {
|
||||
@@ -2230,7 +2222,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I32Ctz src: unreachable code"),
|
||||
message: "I32Ctz src: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2247,7 +2239,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I32Ctz dst: unreachable code"),
|
||||
message: "I32Ctz dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2279,12 +2271,9 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = ret {
|
||||
self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
};
|
||||
}
|
||||
Operator::I32Popcnt => self.emit_xcnt_i32(Assembler::emit_popcnt)?,
|
||||
@@ -2405,7 +2394,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64Clz src: unreachable code"),
|
||||
message: "I64Clz src: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2422,7 +2411,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64Clz dst: unreachable code"),
|
||||
message: "I64Clz dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2456,12 +2445,9 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = ret {
|
||||
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
};
|
||||
}
|
||||
Operator::I64Ctz => {
|
||||
@@ -2475,7 +2461,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64Ctz src: unreachable code"),
|
||||
message: "I64Ctz src: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2492,7 +2478,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::GPR(reg) => reg,
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64Ctz dst: unreachable code"),
|
||||
message: "I64Ctz dst: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2524,12 +2510,9 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = ret {
|
||||
self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
};
|
||||
}
|
||||
Operator::I64Popcnt => self.emit_xcnt_i64(Assembler::emit_popcnt)?,
|
||||
@@ -2721,7 +2704,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Max src1: unreachable code"),
|
||||
message: "F32Max src1: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2754,7 +2737,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Max src2: unreachable code"),
|
||||
message: "F32Max src2: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2811,7 +2794,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Max ret: unreachable code"),
|
||||
message: "F32Max ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2865,7 +2848,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Min src1: unreachable code"),
|
||||
message: "F32Min src1: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2898,7 +2881,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Min src2: unreachable code"),
|
||||
message: "F32Min src2: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2964,7 +2947,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F32Min ret: unreachable code"),
|
||||
message: "F32Min ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3211,7 +3194,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Max src1: unreachable code"),
|
||||
message: "F64Max src1: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -3244,7 +3227,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Max src2: unreachable code"),
|
||||
message: "F64Max src2: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -3301,7 +3284,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Max ret: unreachable code"),
|
||||
message: "F64Max ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3356,7 +3339,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Min src1: unreachable code"),
|
||||
message: "F64Min src1: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -3389,7 +3372,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Min src2: unreachable code"),
|
||||
message: "F64Min src2: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -3455,7 +3438,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("F64Min ret: unreachable code"),
|
||||
message: "F64Min ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5187,13 +5170,13 @@ impl<'a> FuncGen<'a> {
|
||||
|this| {
|
||||
this.assembler.emit_call_location(Location::GPR(GPR::RAX));
|
||||
},
|
||||
params.iter().map(|x| *x),
|
||||
params.iter().copied(),
|
||||
)?;
|
||||
|
||||
self.machine
|
||||
.release_locations_only_stack(&mut self.assembler, ¶ms);
|
||||
|
||||
if return_types.len() > 0 {
|
||||
if !return_types.is_empty() {
|
||||
let ret = self.machine.acquire_locations(
|
||||
&mut self.assembler,
|
||||
&[(
|
||||
@@ -5217,7 +5200,7 @@ impl<'a> FuncGen<'a> {
|
||||
Operator::CallIndirect { index, table_index } => {
|
||||
if table_index != 0 {
|
||||
return Err(CodegenError {
|
||||
message: format!("CallIndirect: table_index is not 0"),
|
||||
message: "CallIndirect: table_index is not 0".to_string(),
|
||||
});
|
||||
}
|
||||
let table_index = TableIndex::new(table_index as _);
|
||||
@@ -5387,13 +5370,13 @@ impl<'a> FuncGen<'a> {
|
||||
));
|
||||
}
|
||||
},
|
||||
params.iter().map(|x| *x),
|
||||
params.iter().copied(),
|
||||
)?;
|
||||
|
||||
self.machine
|
||||
.release_locations_only_stack(&mut self.assembler, ¶ms);
|
||||
|
||||
if return_types.len() > 0 {
|
||||
if !return_types.is_empty() {
|
||||
let ret = self.machine.acquire_locations(
|
||||
&mut self.assembler,
|
||||
&[(
|
||||
@@ -5429,7 +5412,7 @@ impl<'a> FuncGen<'a> {
|
||||
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("If: multi-value returns not yet implemented"),
|
||||
message: "If: multi-value returns not yet implemented".to_string(),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -5445,7 +5428,7 @@ impl<'a> FuncGen<'a> {
|
||||
Operator::Else => {
|
||||
let frame = self.control_stack.last_mut().unwrap();
|
||||
|
||||
if !was_unreachable && frame.returns.len() > 0 {
|
||||
if !was_unreachable && !frame.returns.is_empty() {
|
||||
let first_return = frame.returns[0];
|
||||
let loc = *self.value_stack.last().unwrap();
|
||||
if first_return.is_float() {
|
||||
@@ -5497,7 +5480,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("Else: frame.if_else unreachable code"),
|
||||
message: "Else: frame.if_else unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5570,7 +5553,8 @@ impl<'a> FuncGen<'a> {
|
||||
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("Block: multi-value returns not yet implemented"),
|
||||
message: "Block: multi-value returns not yet implemented"
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -5598,7 +5582,7 @@ impl<'a> FuncGen<'a> {
|
||||
let _activate_offset = self.assembler.get_offset().0;
|
||||
|
||||
self.control_stack.push(ControlFrame {
|
||||
label: label,
|
||||
label,
|
||||
loop_like: true,
|
||||
if_else: IfElseState::None,
|
||||
returns: match ty {
|
||||
@@ -5606,7 +5590,8 @@ impl<'a> FuncGen<'a> {
|
||||
WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("Loop: multi-value returns not yet implemented"),
|
||||
message: "Loop: multi-value returns not yet implemented"
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -5627,7 +5612,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
self.vmoffsets.vmctx_builtin_function(
|
||||
if let Some(_) = self.module.local_memory_index(memory_index) {
|
||||
if self.module.local_memory_index(memory_index).is_some() {
|
||||
VMBuiltinFunctionIndex::get_memory32_size_index()
|
||||
} else {
|
||||
VMBuiltinFunctionIndex::get_imported_memory32_size_index()
|
||||
@@ -5669,7 +5654,7 @@ impl<'a> FuncGen<'a> {
|
||||
Location::Memory(
|
||||
Machine::get_vmctx_reg(),
|
||||
self.vmoffsets.vmctx_builtin_function(
|
||||
if let Some(_) = self.module.local_memory_index(memory_index) {
|
||||
if self.module.local_memory_index(memory_index).is_some() {
|
||||
VMBuiltinFunctionIndex::get_memory32_grow_index()
|
||||
} else {
|
||||
VMBuiltinFunctionIndex::get_imported_memory32_grow_index()
|
||||
@@ -6035,7 +6020,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64Load32U ret: unreachable code"),
|
||||
message: "I64Load32U ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6158,10 +6143,10 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
Operator::Return => {
|
||||
let frame = &self.control_stack[0];
|
||||
if frame.returns.len() > 0 {
|
||||
if !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("Return: incorrect frame.returns"),
|
||||
message: "Return: incorrect frame.returns".to_string(),
|
||||
});
|
||||
}
|
||||
let first_return = frame.returns[0];
|
||||
@@ -6208,10 +6193,10 @@ impl<'a> FuncGen<'a> {
|
||||
Operator::Br { relative_depth } => {
|
||||
let frame =
|
||||
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
|
||||
if !frame.loop_like && frame.returns.len() > 0 {
|
||||
if !frame.loop_like && !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("Br: incorrect frame.returns"),
|
||||
message: "Br: incorrect frame.returns".to_string(),
|
||||
});
|
||||
}
|
||||
let first_return = frame.returns[0];
|
||||
@@ -6262,10 +6247,10 @@ impl<'a> FuncGen<'a> {
|
||||
|
||||
let frame =
|
||||
&self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
|
||||
if !frame.loop_like && frame.returns.len() > 0 {
|
||||
if !frame.loop_like && !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("BrIf: incorrect frame.returns"),
|
||||
message: "BrIf: incorrect frame.returns".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6345,7 +6330,7 @@ impl<'a> FuncGen<'a> {
|
||||
table.push(label);
|
||||
let frame =
|
||||
&self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
|
||||
if !frame.loop_like && frame.returns.len() > 0 {
|
||||
if !frame.loop_like && !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!(
|
||||
@@ -6397,10 +6382,10 @@ impl<'a> FuncGen<'a> {
|
||||
{
|
||||
let frame = &self.control_stack
|
||||
[self.control_stack.len() - 1 - (default_target as usize)];
|
||||
if !frame.loop_like && frame.returns.len() > 0 {
|
||||
if !frame.loop_like && !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("BrTable: incorrect frame.returns"),
|
||||
message: "BrTable: incorrect frame.returns".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6459,7 +6444,7 @@ impl<'a> FuncGen<'a> {
|
||||
Operator::End => {
|
||||
let frame = self.control_stack.pop().unwrap();
|
||||
|
||||
if !was_unreachable && frame.returns.len() > 0 {
|
||||
if !was_unreachable && !frame.returns.is_empty() {
|
||||
let loc = *self.value_stack.last().unwrap();
|
||||
if frame.returns[0].is_float() {
|
||||
let fp = self.fp_stack.peek1()?;
|
||||
@@ -6494,7 +6479,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if self.control_stack.len() == 0 {
|
||||
if self.control_stack.is_empty() {
|
||||
self.assembler.emit_label(frame.label);
|
||||
self.machine
|
||||
.finalize_locals(&mut self.assembler, &self.locals);
|
||||
@@ -6532,10 +6517,10 @@ impl<'a> FuncGen<'a> {
|
||||
self.assembler.emit_label(label);
|
||||
}
|
||||
|
||||
if frame.returns.len() > 0 {
|
||||
if !frame.returns.is_empty() {
|
||||
if frame.returns.len() != 1 {
|
||||
return Err(CodegenError {
|
||||
message: format!("End: incorrect frame.returns"),
|
||||
message: "End: incorrect frame.returns".to_string(),
|
||||
});
|
||||
}
|
||||
let loc = self.machine.acquire_locations(
|
||||
@@ -6747,7 +6732,7 @@ impl<'a> FuncGen<'a> {
|
||||
}
|
||||
_ => {
|
||||
return Err(CodegenError {
|
||||
message: format!("I64AtomicLoad32U ret: unreachable code"),
|
||||
message: "I64AtomicLoad32U ret: unreachable code".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8262,12 +8247,8 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
|
||||
// Calculate stack offset.
|
||||
let mut stack_offset: u32 = 0;
|
||||
for (i, _param) in sig.params().iter().enumerate() {
|
||||
let loc = Machine::get_param_location(1 + i);
|
||||
match loc {
|
||||
Location::Memory(_, _) => {
|
||||
stack_offset += 8;
|
||||
}
|
||||
_ => {}
|
||||
if let Location::Memory(_, _) = Machine::get_param_location(1 + i) {
|
||||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8338,7 +8319,7 @@ pub fn gen_std_trampoline(sig: &FunctionType) -> FunctionBody {
|
||||
);
|
||||
|
||||
// Write return value.
|
||||
if sig.results().len() > 0 {
|
||||
if !sig.results().is_empty() {
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::GPR(GPR::RAX),
|
||||
@@ -8374,7 +8355,7 @@ pub fn gen_std_dynamic_import_trampoline(
|
||||
);
|
||||
|
||||
// Copy arguments.
|
||||
if sig.params().len() > 0 {
|
||||
if !sig.params().is_empty() {
|
||||
let mut argalloc = ArgumentRegisterAllocator::default();
|
||||
argalloc.next(Type::I64).unwrap(); // skip VMContext
|
||||
|
||||
@@ -8426,7 +8407,7 @@ pub fn gen_std_dynamic_import_trampoline(
|
||||
a.emit_call_location(Location::GPR(GPR::RAX));
|
||||
|
||||
// Fetch return value.
|
||||
if sig.results().len() > 0 {
|
||||
if !sig.results().is_empty() {
|
||||
assert_eq!(sig.results().len(), 1);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
@@ -8470,8 +8451,7 @@ pub fn gen_import_call_trampoline(
|
||||
if sig
|
||||
.params()
|
||||
.iter()
|
||||
.find(|&&x| x == Type::F32 || x == Type::F64)
|
||||
.is_some()
|
||||
.any(|&x| x == Type::F32 || x == Type::F64)
|
||||
{
|
||||
let mut param_locations: Vec<Location> = vec![];
|
||||
|
||||
@@ -8493,8 +8473,7 @@ pub fn gen_import_call_trampoline(
|
||||
for i in 0..sig.params().len() {
|
||||
let loc = match i {
|
||||
0..=4 => {
|
||||
static PARAM_REGS: &'static [GPR] =
|
||||
&[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
|
||||
static PARAM_REGS: &[GPR] = &[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
|
||||
let loc = Location::Memory(GPR::RSP, (i * 8) as i32);
|
||||
a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc);
|
||||
loc
|
||||
|
||||
@@ -161,7 +161,7 @@ impl MachineState {
|
||||
.enumerate()
|
||||
.find(|&(_, (a, b))| a != b)
|
||||
.map(|x| x.0)
|
||||
.unwrap_or(old.stack_values.len().min(self.stack_values.len()));
|
||||
.unwrap_or_else(|| old.stack_values.len().min(self.stack_values.len()));
|
||||
assert_eq!(self.register_values.len(), old.register_values.len());
|
||||
let reg_diff: Vec<_> = self
|
||||
.register_values
|
||||
@@ -196,7 +196,7 @@ impl MachineState {
|
||||
.enumerate()
|
||||
.find(|&(_, (a, b))| a != b)
|
||||
.map(|x| x.0)
|
||||
.unwrap_or(old.wasm_stack.len().min(self.wasm_stack.len()));
|
||||
.unwrap_or_else(|| old.wasm_stack.len().min(self.wasm_stack.len()));
|
||||
MachineStateDiff {
|
||||
last: None,
|
||||
stack_push: self.stack_values[first_diff_stack_depth..].to_vec(),
|
||||
|
||||
@@ -6,65 +6,58 @@ use crate::codegen_x64::{
|
||||
gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline,
|
||||
CodegenError, FuncGen,
|
||||
};
|
||||
use crate::config::SinglepassConfig;
|
||||
use crate::config::Singlepass;
|
||||
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
use std::sync::Arc;
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap};
|
||||
use wasm_common::Features;
|
||||
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
|
||||
use wasmer_compiler::wasmparser::BinaryReaderError;
|
||||
use wasmer_compiler::TrapInformation;
|
||||
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler, SectionIndex};
|
||||
use wasmer_compiler::{
|
||||
CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader, ModuleTranslationState, Target,
|
||||
CompileModuleInfo, CompilerConfig, GenerateMiddlewareChain, MiddlewareBinaryReader,
|
||||
ModuleTranslationState, Target,
|
||||
};
|
||||
use wasmer_compiler::{FunctionBody, FunctionBodyData};
|
||||
use wasmer_runtime::ModuleInfo;
|
||||
use wasmer_runtime::TrapCode;
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan, VMOffsets};
|
||||
use wasmer_runtime::{ModuleInfo, TrapCode, VMOffsets};
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with Singlepass.
|
||||
/// It does the compilation in one pass
|
||||
pub struct SinglepassCompiler {
|
||||
config: SinglepassConfig,
|
||||
config: Singlepass,
|
||||
}
|
||||
|
||||
impl SinglepassCompiler {
|
||||
/// Creates a new Singlepass compiler
|
||||
pub fn new(config: &SinglepassConfig) -> Self {
|
||||
pub fn new(config: &Singlepass) -> Self {
|
||||
Self {
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn config(&self) -> &SinglepassConfig {
|
||||
/// Gets the config for this Compiler
|
||||
fn config(&self) -> &Singlepass {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for SinglepassCompiler {
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> &Features {
|
||||
self.config.features()
|
||||
}
|
||||
|
||||
/// Gets the target associated to this Compiler.
|
||||
fn target(&self) -> &Target {
|
||||
self.config.target()
|
||||
}
|
||||
|
||||
/// Compile the module using Singlepass, producing a compilation result with
|
||||
/// associated relocations.
|
||||
fn compile_module(
|
||||
&self,
|
||||
module: &ModuleInfo,
|
||||
_target: &Target,
|
||||
compile_info: &CompileModuleInfo,
|
||||
_module_translation: &ModuleTranslationState,
|
||||
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError> {
|
||||
let vmoffsets = VMOffsets::new(8, module);
|
||||
if compile_info.features.multi_value {
|
||||
return Err(CompileError::UnsupportedFeature("multivalue".to_string()));
|
||||
}
|
||||
let vmoffsets = VMOffsets::new(8, &compile_info.module);
|
||||
let memory_plans = &compile_info.memory_plans;
|
||||
let table_plans = &compile_info.table_plans;
|
||||
let module = &compile_info.module;
|
||||
let import_trampolines: PrimaryMap<SectionIndex, _> = (0..module.num_imported_funcs)
|
||||
.map(FunctionIndex::new)
|
||||
.collect::<Vec<_>>()
|
||||
|
||||
@@ -3,17 +3,27 @@
|
||||
|
||||
use crate::compiler::SinglepassCompiler;
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler::{
|
||||
Compiler, CompilerConfig, CpuFeature, Features, FunctionMiddlewareGenerator, Target,
|
||||
};
|
||||
use wasm_common::Features;
|
||||
use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, FunctionMiddlewareGenerator, Target};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SinglepassConfig {
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub enable_nan_canonicalization: bool,
|
||||
pub struct Singlepass {
|
||||
pub(crate) enable_nan_canonicalization: bool,
|
||||
pub(crate) enable_stack_check: bool,
|
||||
/// The middleware chain.
|
||||
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
|
||||
}
|
||||
|
||||
impl Singlepass {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enable_nan_canonicalization: true,
|
||||
enable_stack_check: false,
|
||||
middlewares: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable stack check.
|
||||
///
|
||||
@@ -22,62 +32,47 @@ pub struct SinglepassConfig {
|
||||
///
|
||||
/// Note that this doesn't guarantee deterministic execution across
|
||||
/// different platforms.
|
||||
pub enable_stack_check: bool,
|
||||
pub fn enable_stack_check(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_stack_check = enable;
|
||||
self
|
||||
}
|
||||
|
||||
features: Features,
|
||||
target: Target,
|
||||
|
||||
/// The middleware chain.
|
||||
pub(crate) middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
|
||||
}
|
||||
|
||||
impl SinglepassConfig {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new(mut features: Features, target: Target) -> Self {
|
||||
// Override the default multi-value switch
|
||||
features.multi_value = false;
|
||||
|
||||
Self {
|
||||
enable_nan_canonicalization: true,
|
||||
enable_stack_check: false,
|
||||
features,
|
||||
target,
|
||||
middlewares: vec![],
|
||||
}
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_nan_canonicalization = enable;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig for SinglepassConfig {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
impl CompilerConfig for Singlepass {
|
||||
fn enable_pic(&mut self) {
|
||||
// Do nothing, since singlepass already emits
|
||||
// PIC code.
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Transform it into the compiler
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
Box::new(SinglepassCompiler::new(&self))
|
||||
}
|
||||
|
||||
/// Gets the default features for this compiler in the given target
|
||||
fn default_features_for_target(&self, _target: &Target) -> Features {
|
||||
let mut features = Features::default();
|
||||
features.multi_value(false);
|
||||
features
|
||||
}
|
||||
|
||||
/// Pushes a middleware onto the back of the middleware chain.
|
||||
fn push_middleware(&mut self, middleware: Arc<dyn FunctionMiddlewareGenerator>) {
|
||||
self.middlewares.push(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SinglepassConfig {
|
||||
fn default() -> SinglepassConfig {
|
||||
Self::new(Default::default(), Default::default())
|
||||
impl Default for Singlepass {
|
||||
fn default() -> Singlepass {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,12 +702,9 @@ impl Emitter for Assembler {
|
||||
|
||||
fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) {
|
||||
// fast path
|
||||
match (src, dst) {
|
||||
(Location::Imm32(0), Location::GPR(x)) => {
|
||||
dynasm!(self ; xor Rd(x as u8), Rd(x as u8));
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
if let (Location::Imm32(0), Location::GPR(x)) = (src, dst) {
|
||||
dynasm!(self ; xor Rd(x as u8), Rd(x as u8));
|
||||
return;
|
||||
}
|
||||
|
||||
binop_all_nofp!(mov, self, sz, src, dst, {
|
||||
|
||||
@@ -21,4 +21,4 @@ mod machine;
|
||||
mod x64_decl;
|
||||
|
||||
pub use crate::compiler::SinglepassCompiler;
|
||||
pub use crate::config::SinglepassConfig;
|
||||
pub use crate::config::Singlepass;
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Machine {
|
||||
/// This method does not mark the register as used.
|
||||
pub fn pick_gpr(&self) -> Option<GPR> {
|
||||
use GPR::*;
|
||||
static REGS: &'static [GPR] = &[RSI, RDI, R8, R9, R10, R11];
|
||||
static REGS: &[GPR] = &[RSI, RDI, R8, R9, R10, R11];
|
||||
for r in REGS {
|
||||
if !self.used_gprs.contains(r) {
|
||||
return Some(*r);
|
||||
@@ -64,7 +64,7 @@ impl Machine {
|
||||
/// This method does not mark the register as used.
|
||||
pub fn pick_temp_gpr(&self) -> Option<GPR> {
|
||||
use GPR::*;
|
||||
static REGS: &'static [GPR] = &[RAX, RCX, RDX];
|
||||
static REGS: &[GPR] = &[RAX, RCX, RDX];
|
||||
for r in REGS {
|
||||
if !self.used_gprs.contains(r) {
|
||||
return Some(*r);
|
||||
@@ -99,7 +99,7 @@ impl Machine {
|
||||
/// This method does not mark the register as used.
|
||||
pub fn pick_xmm(&self) -> Option<XMM> {
|
||||
use XMM::*;
|
||||
static REGS: &'static [XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7];
|
||||
static REGS: &[XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7];
|
||||
for r in REGS {
|
||||
if !self.used_xmms.contains(r) {
|
||||
return Some(*r);
|
||||
@@ -113,7 +113,7 @@ impl Machine {
|
||||
/// This method does not mark the register as used.
|
||||
pub fn pick_temp_xmm(&self) -> Option<XMM> {
|
||||
use XMM::*;
|
||||
static REGS: &'static [XMM] = &[XMM0, XMM1, XMM2];
|
||||
static REGS: &[XMM] = &[XMM0, XMM1, XMM2];
|
||||
for r in REGS {
|
||||
if !self.used_xmms.contains(r) {
|
||||
return Some(*r);
|
||||
@@ -260,20 +260,17 @@ impl Machine {
|
||||
let mut delta_stack_offset: usize = 0;
|
||||
|
||||
for loc in locs.iter().rev() {
|
||||
match *loc {
|
||||
Location::Memory(GPR::RBP, x) => {
|
||||
if x >= 0 {
|
||||
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();
|
||||
if let Location::Memory(GPR::RBP, x) = *loc {
|
||||
if x >= 0 {
|
||||
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();
|
||||
}
|
||||
// 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;
|
||||
|
||||
for loc in locs.iter().rev() {
|
||||
match *loc {
|
||||
Location::Memory(GPR::RBP, x) => {
|
||||
if x >= 0 {
|
||||
unreachable!();
|
||||
}
|
||||
let offset = (-x) as usize;
|
||||
if offset != stack_offset {
|
||||
unreachable!();
|
||||
}
|
||||
stack_offset -= 8;
|
||||
delta_stack_offset += 8;
|
||||
if let Location::Memory(GPR::RBP, x) = *loc {
|
||||
if x >= 0 {
|
||||
unreachable!();
|
||||
}
|
||||
_ => {}
|
||||
let offset = (-x) as usize;
|
||||
if offset != stack_offset {
|
||||
unreachable!();
|
||||
}
|
||||
stack_offset -= 8;
|
||||
delta_stack_offset += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,41 @@ This crate is the base for Compiler implementations.
|
||||
It performs the translation from a Wasm module into a basic ModuleInfo,
|
||||
but leaves the Wasm function bytecode translation to the compiler implementor.
|
||||
|
||||
Here are some of the Compilers provided by Wasmer:
|
||||
* [Singlepass](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-singlepass)
|
||||
* [Cranelift](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-cranelift)
|
||||
* [LLVM](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-llvm)
|
||||
|
||||
## How to create a compiler
|
||||
|
||||
Creating a new compiler is quite easy, you just need to impement two traits: `CompilerConfig` and `Compiler`:
|
||||
|
||||
```rust
|
||||
/// The compiler configuration options.
|
||||
pub trait CompilerConfig {
|
||||
/// Gets the custom compiler config
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send>;
|
||||
}
|
||||
|
||||
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
|
||||
pub trait Compiler {
|
||||
/// Compiles a parsed module.
|
||||
///
|
||||
/// It returns the [`Compilation`] or a [`CompileError`].
|
||||
fn compile_module<'data, 'module>(
|
||||
&self,
|
||||
target: &Target,
|
||||
compile_info: &'module CompileModuleInfo,
|
||||
module_translation: &ModuleTranslationState,
|
||||
// The list of function bodies
|
||||
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
|
||||
) -> Result<Compilation, CompileError>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
This project borrowed some of the code strucutre from the [cranelift-wasm](https://crates.io/crates/cranelift-wasm), however it's been adapted to not depend on any specific IR and be abstract of any compiler.
|
||||
This project borrowed some of the code strucutre from the [cranelift-wasm](https://crates.io/crates/cranelift-wasm) crate, however it's been adapted to not depend on any specific IR and be abstract of any compiler.
|
||||
|
||||
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.
|
||||
|
||||
@@ -5,55 +5,49 @@ use crate::error::CompileError;
|
||||
use crate::function::Compilation;
|
||||
use crate::lib::std::boxed::Box;
|
||||
use crate::lib::std::sync::Arc;
|
||||
use crate::module::CompileModuleInfo;
|
||||
use crate::target::Target;
|
||||
use crate::translator::FunctionMiddlewareGenerator;
|
||||
use crate::FunctionBodyData;
|
||||
use crate::ModuleTranslationState;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{Features, LocalFunctionIndex, MemoryIndex, TableIndex};
|
||||
use wasmer_runtime::ModuleInfo;
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
use wasm_common::{Features, LocalFunctionIndex};
|
||||
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
|
||||
|
||||
/// The compiler configuration options.
|
||||
///
|
||||
/// This options must have WebAssembly `Features` and a specific
|
||||
/// `Target` to compile to.
|
||||
pub trait CompilerConfig {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features;
|
||||
|
||||
/// Should Position Independent Code (PIC) be enabled.
|
||||
/// Enable Position Independent Code (PIC).
|
||||
///
|
||||
/// This is required for shared object generation (Native Engine),
|
||||
/// but will make the JIT Engine to fail, since PIC is not yet
|
||||
/// supported in the JIT linking phase.
|
||||
fn enable_pic(&mut self);
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target;
|
||||
fn enable_pic(&mut self) {
|
||||
// By default we do nothing, each backend will need to customize this
|
||||
// in case they do something special for emitting PIC code.
|
||||
}
|
||||
|
||||
/// Gets the custom compiler config
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send>;
|
||||
|
||||
/// Gets the default features for this compiler in the given target
|
||||
fn default_features_for_target(&self, _target: &Target) -> Features {
|
||||
Features::default()
|
||||
}
|
||||
|
||||
/// Pushes a middleware onto the back of the middleware chain.
|
||||
fn push_middleware(&mut self, middleware: Arc<dyn FunctionMiddlewareGenerator>);
|
||||
}
|
||||
|
||||
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
|
||||
pub trait Compiler {
|
||||
/// Gets the target associated with this compiler
|
||||
fn target(&self) -> &Target;
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> &Features;
|
||||
|
||||
/// Validates a module.
|
||||
///
|
||||
/// It returns the a succesful Result in case is valid, `CompileError` in case is not.
|
||||
fn validate_module<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
||||
let features = self.features();
|
||||
fn validate_module<'data>(
|
||||
&self,
|
||||
features: &Features,
|
||||
data: &'data [u8],
|
||||
) -> Result<(), CompileError> {
|
||||
let config = ValidatingParserConfig {
|
||||
operator_config: OperatorValidatorConfig {
|
||||
enable_threads: features.threads,
|
||||
@@ -72,13 +66,10 @@ pub trait Compiler {
|
||||
/// It returns the [`Compilation`] or a [`CompileError`].
|
||||
fn compile_module<'data, 'module>(
|
||||
&self,
|
||||
module: &'module ModuleInfo,
|
||||
target: &Target,
|
||||
module: &'module CompileModuleInfo,
|
||||
module_translation: &ModuleTranslationState,
|
||||
// The list of function bodies
|
||||
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
|
||||
// The plans for the module memories (imported and local)
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
// The plans for the module tables (imported and local)
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError>;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ pub enum CompileError {
|
||||
#[error("Validation error: {0}")]
|
||||
Validate(String),
|
||||
|
||||
/// The compiler doesn't support a Wasm feature
|
||||
#[error("Feature {0} is not yet supported")]
|
||||
UnsupportedFeature(String),
|
||||
|
||||
/// Insufficient resources available for execution.
|
||||
#[error("Insufficient resources: {0}")]
|
||||
Resource(String),
|
||||
|
||||
@@ -57,6 +57,7 @@ mod compiler;
|
||||
mod error;
|
||||
mod function;
|
||||
mod jump_table;
|
||||
mod module;
|
||||
mod relocation;
|
||||
mod target;
|
||||
mod trap;
|
||||
@@ -76,6 +77,7 @@ pub use crate::function::{
|
||||
Functions,
|
||||
};
|
||||
pub use crate::jump_table::{JumpTable, JumpTableOffsets};
|
||||
pub use crate::module::CompileModuleInfo;
|
||||
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
|
||||
pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex};
|
||||
pub use crate::sourceloc::SourceLoc;
|
||||
|
||||
27
lib/compiler/src/module.rs
Normal file
27
lib/compiler/src/module.rs
Normal 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>,
|
||||
}
|
||||
@@ -29,6 +29,8 @@ pub enum RelocationKind {
|
||||
Abs8,
|
||||
/// x86 PC-relative 4-byte
|
||||
X86PCRel4,
|
||||
/// x86 PC-relative 8-byte
|
||||
X86PCRel8,
|
||||
/// x86 PC-relative 4-byte offset to trailing rodata
|
||||
X86PCRelRodata4,
|
||||
/// x86 call to PC-relative 4-byte
|
||||
@@ -62,6 +64,7 @@ impl fmt::Display for RelocationKind {
|
||||
Self::Abs4 => write!(f, "Abs4"),
|
||||
Self::Abs8 => write!(f, "Abs8"),
|
||||
Self::X86PCRel4 => write!(f, "PCRel4"),
|
||||
Self::X86PCRel8 => write!(f, "PCRel8"),
|
||||
Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
|
||||
Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
||||
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
||||
@@ -112,7 +115,7 @@ impl Relocation {
|
||||
RelocationKind::Abs8 => {
|
||||
let reloc_address = start + self.offset as usize;
|
||||
let reloc_addend = self.addend as isize;
|
||||
let reloc_abs = (target_func_address)
|
||||
let reloc_abs = target_func_address
|
||||
.checked_add(reloc_addend as u64)
|
||||
.unwrap();
|
||||
(reloc_address, reloc_abs)
|
||||
@@ -126,6 +129,15 @@ impl Relocation {
|
||||
.unwrap();
|
||||
(reloc_address, reloc_delta_u32 as u64)
|
||||
}
|
||||
RelocationKind::X86PCRel8 => {
|
||||
let reloc_address = start + self.offset as usize;
|
||||
let reloc_addend = self.addend as isize;
|
||||
let reloc_delta = target_func_address
|
||||
.wrapping_sub(reloc_address as u64)
|
||||
.checked_add(reloc_addend as u64)
|
||||
.unwrap();
|
||||
(reloc_address, reloc_delta)
|
||||
}
|
||||
RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
|
||||
let reloc_address = start + self.offset as usize;
|
||||
let reloc_addend = self.addend as isize;
|
||||
|
||||
@@ -11,8 +11,7 @@ use wasmparser::{BinaryReader, Operator, Result as WpResult, Type};
|
||||
/// A shared builder for function middlewares.
|
||||
pub trait FunctionMiddlewareGenerator: Debug + Send + Sync {
|
||||
/// Generates a `FunctionMiddleware` for a given function.
|
||||
fn generate<'a>(&self, local_function_index: LocalFunctionIndex)
|
||||
-> Box<dyn FunctionMiddleware>;
|
||||
fn generate(&self, local_function_index: LocalFunctionIndex) -> Box<dyn FunctionMiddleware>;
|
||||
}
|
||||
|
||||
/// A function middleware specialized for a single function.
|
||||
|
||||
@@ -61,27 +61,24 @@ pub fn parse_type_section(
|
||||
environ.reserve_signatures(count)?;
|
||||
|
||||
for entry in types {
|
||||
match entry.map_err(to_wasm_error)? {
|
||||
WPFunctionType { params, returns } => {
|
||||
let sig_params: Vec<Type> = params
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig_returns: Vec<Type> = returns
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig = FunctionType::new(sig_params, sig_returns);
|
||||
environ.declare_signature(sig)?;
|
||||
module_translation_state.wasm_types.push((params, returns));
|
||||
}
|
||||
}
|
||||
let WPFunctionType { params, returns } = entry.map_err(to_wasm_error)?;
|
||||
let sig_params: Vec<Type> = params
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig_returns: Vec<Type> = returns
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig = FunctionType::new(sig_params, sig_returns);
|
||||
environ.declare_signature(sig)?;
|
||||
module_translation_state.wasm_types.push((params, returns));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
4
lib/emscripten/src/env/unix/mod.rs
vendored
4
lib/emscripten/src/env/unix/mod.rs
vendored
@@ -263,7 +263,7 @@ pub fn _getaddrinfo(
|
||||
.get_mut();
|
||||
|
||||
guest_sockaddr.sa_family = (*host_sockaddr_ptr).sa_family as i16;
|
||||
guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data.clone();
|
||||
guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data;
|
||||
guest_sockaddr_ptr
|
||||
};
|
||||
|
||||
@@ -308,7 +308,7 @@ pub fn _getaddrinfo(
|
||||
}
|
||||
// this frees all connected nodes on the linked list
|
||||
freeaddrinfo(out_ptr);
|
||||
head_of_list.unwrap_or(WasmPtr::new(0))
|
||||
head_of_list.unwrap_or_else(|| WasmPtr::new(0))
|
||||
};
|
||||
|
||||
res_val_ptr.deref(ctx.memory(0)).unwrap().set(head_of_list);
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn sigdelset(ctx: &mut EmEnv, set: i32, signum: i32) -> i32 {
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let ptr = emscripten_memory_pointer!(memory, set) as *mut i32;
|
||||
|
||||
unsafe { *ptr = *ptr & !(1 << (signum - 1)) }
|
||||
unsafe { *ptr &= !(1 << (signum - 1)) }
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ const STATIC_BUMP: u32 = 215_536;
|
||||
|
||||
lazy_static! {
|
||||
static ref OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG: FunctionType =
|
||||
{ FunctionType::new(vec![], vec![ValType::I32]) };
|
||||
FunctionType::new(vec![], vec![ValType::I32]);
|
||||
}
|
||||
|
||||
// The address globals begin at. Very low in memory, for code size and optimization opportunities.
|
||||
@@ -465,7 +465,7 @@ pub fn emscripten_call_main(
|
||||
}
|
||||
|
||||
/// Top level function to execute emscripten
|
||||
pub fn run_emscripten_instance<'a>(
|
||||
pub fn run_emscripten_instance(
|
||||
instance: &mut Instance,
|
||||
env: &mut EmEnv,
|
||||
globals: &mut EmscriptenGlobals,
|
||||
@@ -1105,7 +1105,7 @@ pub fn generate_emscripten_env(
|
||||
// Compatibility with newer versions of Emscripten
|
||||
let mut to_insert: Vec<(String, _)> = vec![];
|
||||
for (k, v) in env_ns.iter() {
|
||||
if k.starts_with("_") {
|
||||
if k.starts_with('_') {
|
||||
let k = &k[1..];
|
||||
if !env_ns.contains(k) {
|
||||
to_insert.push((k.to_string(), v.clone()));
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn sbrk(ctx: &mut EmEnv, increment: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
ctx.memory(0).view::<u32>()[dynamictop_ptr].set(new_dynamic_top as u32);
|
||||
return old_dynamic_top as _;
|
||||
old_dynamic_top as _
|
||||
}
|
||||
|
||||
/// emscripten: getTotalMemory
|
||||
|
||||
@@ -33,11 +33,11 @@ pub fn _pthread_attr_setstacksize(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn _pthread_cleanup_pop(_ctx: &mut EmEnv, _a: i32) -> () {
|
||||
pub fn _pthread_cleanup_pop(_ctx: &mut EmEnv, _a: i32) {
|
||||
trace!("emscripten::_pthread_cleanup_pop");
|
||||
}
|
||||
|
||||
pub fn _pthread_cleanup_push(_ctx: &mut EmEnv, _a: i32, _b: i32) -> () {
|
||||
pub fn _pthread_cleanup_push(_ctx: &mut EmEnv, _a: i32, _b: i32) {
|
||||
trace!("emscripten::_pthread_cleanup_push");
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ pub fn _pthread_equal(_ctx: &mut EmEnv, _a: i32, _b: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) -> () {
|
||||
pub fn _pthread_exit(_ctx: &mut EmEnv, _a: i32) {
|
||||
trace!("emscripten::_pthread_exit");
|
||||
}
|
||||
|
||||
|
||||
@@ -646,7 +646,7 @@ pub fn ___syscall102(ctx: &mut EmEnv, _which: c_int, mut varargs: VarArgs) -> c_
|
||||
let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
|
||||
address_addr.sa_family = host_address.sa_family as _;
|
||||
address_addr.sa_data = host_address.sa_data.clone();
|
||||
address_addr.sa_data = host_address.sa_data;
|
||||
|
||||
// why is this here?
|
||||
// set_cloexec
|
||||
@@ -686,7 +686,7 @@ pub fn ___syscall102(ctx: &mut EmEnv, _which: c_int, mut varargs: VarArgs) -> c_
|
||||
// translate from host data into emscripten data
|
||||
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
address_mut.sa_family = sock_addr_host.sa_family as _;
|
||||
address_mut.sa_data = sock_addr_host.sa_data.clone();
|
||||
address_mut.sa_data = sock_addr_host.sa_data;
|
||||
|
||||
debug!(
|
||||
"=> socket: {}, address, {:?}, address_len: {}, result = {}",
|
||||
|
||||
@@ -241,7 +241,7 @@ pub fn _localtime(ctx: &mut EmEnv, time_p: u32) -> c_int {
|
||||
|
||||
let timespec = unsafe {
|
||||
let time_p_addr = emscripten_memory_pointer!(ctx.memory(0), time_p) as *mut i64;
|
||||
let seconds = *time_p_addr.clone();
|
||||
let seconds = *time_p_addr;
|
||||
time::Timespec::new(seconds, 0)
|
||||
};
|
||||
let result_tm = time::at(timespec);
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn get_emscripten_table_size(module: &Module) -> Result<(u32, Option<u32>),
|
||||
let ty = import.ty();
|
||||
Ok((ty.minimum, ty.maximum))
|
||||
} else {
|
||||
return Err("Emscripten requires at least one imported table".to_string());
|
||||
Err("Emscripten requires at least one imported table".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn get_emscripten_memory_size(module: &Module) -> Result<(Pages, Option<Page
|
||||
let ty = import.ty();
|
||||
Ok((ty.minimum, ty.maximum, ty.shared))
|
||||
} else {
|
||||
return Err("Emscripten requires at least one imported memory".to_string());
|
||||
Err("Emscripten requires at least one imported memory".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,12 @@ use wasm_common::{
|
||||
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::ModuleEnvironment;
|
||||
use wasmer_compiler::{CompileError, Features, Triple};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment};
|
||||
use wasmer_engine::{
|
||||
register_frame_info, Artifact, DeserializeError, GlobalFrameInfoRegistration, SerializeError,
|
||||
Tunables,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_engine::{Engine, SerializableFunctionFrameInfo};
|
||||
@@ -43,10 +44,14 @@ impl JITArtifact {
|
||||
|
||||
/// Compile a data buffer into a `JITArtifact`, which may then be instantiated.
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
|
||||
pub fn new(
|
||||
jit: &JITEngine,
|
||||
data: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Self, CompileError> {
|
||||
let environ = ModuleEnvironment::new();
|
||||
let mut inner_jit = jit.inner_mut();
|
||||
let tunables = jit.tunables();
|
||||
let features = inner_jit.features();
|
||||
|
||||
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
|
||||
|
||||
@@ -63,15 +68,21 @@ impl JITArtifact {
|
||||
.map(|table_type| tunables.table_plan(*table_type))
|
||||
.collect();
|
||||
|
||||
let compile_info = CompileModuleInfo {
|
||||
module: Arc::new(translation.module),
|
||||
features: features.clone(),
|
||||
memory_plans,
|
||||
table_plans,
|
||||
};
|
||||
|
||||
let compiler = inner_jit.compiler()?;
|
||||
|
||||
// Compile the Module
|
||||
let compilation = compiler.compile_module(
|
||||
&translation.module,
|
||||
&jit.target(),
|
||||
&compile_info,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
memory_plans.clone(),
|
||||
table_plans.clone(),
|
||||
)?;
|
||||
let function_call_trampolines = compilation.get_function_call_trampolines();
|
||||
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
|
||||
@@ -102,11 +113,8 @@ impl JITArtifact {
|
||||
};
|
||||
let serializable = SerializableModule {
|
||||
compilation: serializable_compilation,
|
||||
module: Arc::new(translation.module),
|
||||
features: inner_jit.compiler()?.features().clone(),
|
||||
compile_info,
|
||||
data_initializers,
|
||||
memory_plans,
|
||||
table_plans,
|
||||
};
|
||||
Self::from_parts(&mut inner_jit, serializable)
|
||||
}
|
||||
@@ -150,7 +158,7 @@ impl JITArtifact {
|
||||
finished_dynamic_function_trampolines,
|
||||
) = inner_jit.allocate(
|
||||
&mut unwind_registry,
|
||||
&serializable.module,
|
||||
&serializable.compile_info.module,
|
||||
&serializable.compilation.function_bodies,
|
||||
&serializable.compilation.function_call_trampolines,
|
||||
&serializable.compilation.dynamic_function_trampolines,
|
||||
@@ -159,7 +167,7 @@ impl JITArtifact {
|
||||
inner_jit.allocate_custom_sections(&serializable.compilation.custom_sections)?;
|
||||
|
||||
link_module(
|
||||
&serializable.module,
|
||||
&serializable.compile_info.module,
|
||||
&finished_functions,
|
||||
&serializable.compilation.function_jt_offsets,
|
||||
serializable.compilation.function_relocations.clone(),
|
||||
@@ -171,6 +179,7 @@ impl JITArtifact {
|
||||
let signatures = {
|
||||
let signature_registry = inner_jit.signatures();
|
||||
serializable
|
||||
.compile_info
|
||||
.module
|
||||
.signatures
|
||||
.values()
|
||||
@@ -227,15 +236,15 @@ impl JITArtifact {
|
||||
|
||||
impl Artifact for JITArtifact {
|
||||
fn module(&self) -> Arc<ModuleInfo> {
|
||||
self.serializable.module.clone()
|
||||
self.serializable.compile_info.module.clone()
|
||||
}
|
||||
|
||||
fn module_ref(&self) -> &ModuleInfo {
|
||||
&self.serializable.module
|
||||
&self.serializable.compile_info.module
|
||||
}
|
||||
|
||||
fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
|
||||
Arc::get_mut(&mut self.serializable.module)
|
||||
Arc::get_mut(&mut self.serializable.compile_info.module)
|
||||
}
|
||||
|
||||
fn register_frame_info(&self) {
|
||||
@@ -248,14 +257,14 @@ impl Artifact for JITArtifact {
|
||||
let frame_infos = &self.serializable.compilation.function_frame_info;
|
||||
let finished_functions = &self.finished_functions;
|
||||
*info = register_frame_info(
|
||||
self.serializable.module.clone(),
|
||||
self.serializable.compile_info.module.clone(),
|
||||
finished_functions,
|
||||
frame_infos.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
fn features(&self) -> &Features {
|
||||
&self.serializable.features
|
||||
&self.serializable.compile_info.features
|
||||
}
|
||||
|
||||
fn data_initializers(&self) -> &[OwnedDataInitializer] {
|
||||
@@ -263,11 +272,11 @@ impl Artifact for JITArtifact {
|
||||
}
|
||||
|
||||
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
|
||||
&self.serializable.memory_plans
|
||||
&self.serializable.compile_info.memory_plans
|
||||
}
|
||||
|
||||
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
|
||||
&self.serializable.table_plans
|
||||
&self.serializable.compile_info.table_plans
|
||||
}
|
||||
|
||||
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]> {
|
||||
|
||||
55
lib/engine-jit/src/builder.rs
Normal file
55
lib/engine-jit/src/builder.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
//! Memory management for executable code.
|
||||
use crate::unwind::UnwindRegistry;
|
||||
use region;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::Arc;
|
||||
use std::{cmp, mem};
|
||||
@@ -150,7 +149,7 @@ impl CodeMemory {
|
||||
Ok(self
|
||||
.read_sections
|
||||
.last_mut()
|
||||
.ok_or("Can't get last section".to_string())?)
|
||||
.ok_or_else(|| "Can't get last section".to_string())?)
|
||||
}
|
||||
|
||||
/// Make all allocated memory executable.
|
||||
|
||||
@@ -5,12 +5,13 @@ use crate::{CodeMemory, JITArtifact};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::Features;
|
||||
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, SignatureIndex};
|
||||
use wasmer_compiler::{
|
||||
CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{Compiler, CompilerConfig};
|
||||
use wasmer_compiler::Compiler;
|
||||
use wasmer_compiler::{
|
||||
CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target,
|
||||
};
|
||||
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
|
||||
use wasmer_runtime::{
|
||||
ModuleInfo, SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
||||
@@ -20,26 +21,24 @@ use wasmer_runtime::{
|
||||
#[derive(Clone)]
|
||||
pub struct JITEngine {
|
||||
inner: Arc<Mutex<JITEngineInner>>,
|
||||
tunables: Arc<dyn Tunables + Send + Sync>,
|
||||
/// The target for the compiler
|
||||
target: Arc<Target>,
|
||||
engine_id: EngineId,
|
||||
}
|
||||
|
||||
impl JITEngine {
|
||||
/// Create a new `JITEngine` with the given config
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(
|
||||
config: Box<dyn CompilerConfig>,
|
||||
tunables: impl Tunables + 'static + Send + Sync,
|
||||
) -> Self {
|
||||
let compiler = config.compiler();
|
||||
pub fn new(compiler: Box<dyn Compiler + Send>, target: Target, features: Features) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(JITEngineInner {
|
||||
compiler: Some(compiler),
|
||||
function_call_trampolines: HashMap::new(),
|
||||
code_memory: CodeMemory::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
features,
|
||||
})),
|
||||
tunables: Arc::new(tunables),
|
||||
target: Arc::new(target),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
@@ -57,7 +56,7 @@ impl JITEngine {
|
||||
///
|
||||
/// Headless engines can't compile or validate any modules,
|
||||
/// they just take already processed Modules (via `Module::serialize`).
|
||||
pub fn headless(tunables: impl Tunables + 'static + Send + Sync) -> Self {
|
||||
pub fn headless() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(JITEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
@@ -65,8 +64,9 @@ impl JITEngine {
|
||||
function_call_trampolines: HashMap::new(),
|
||||
code_memory: CodeMemory::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
features: Features::default(),
|
||||
})),
|
||||
tunables: Arc::new(tunables),
|
||||
target: Arc::new(Target::default()),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
@@ -81,9 +81,9 @@ impl JITEngine {
|
||||
}
|
||||
|
||||
impl Engine for JITEngine {
|
||||
/// Get the tunables
|
||||
fn tunables(&self) -> &dyn Tunables {
|
||||
&*self.tunables
|
||||
/// The target
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Register a signature
|
||||
@@ -109,8 +109,12 @@ impl Engine for JITEngine {
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Ok(Arc::new(JITArtifact::new(&self, binary)?))
|
||||
fn compile(
|
||||
&self,
|
||||
binary: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Ok(Arc::new(JITArtifact::new(&self, binary, tunables)?))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module
|
||||
@@ -121,6 +125,10 @@ impl Engine for JITEngine {
|
||||
fn id(&self) -> &EngineId {
|
||||
&self.engine_id
|
||||
}
|
||||
|
||||
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// The inner contents of `JITEngine`
|
||||
@@ -130,6 +138,8 @@ pub struct JITEngineInner {
|
||||
compiler: Option<Box<dyn Compiler + Send>>,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
function_call_trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
/// The features to compile the Wasm module with
|
||||
features: Features,
|
||||
/// The code memory is responsible of publishing the compiled
|
||||
/// functions to memory.
|
||||
code_memory: CodeMemory,
|
||||
@@ -151,7 +161,7 @@ impl JITEngineInner {
|
||||
/// Validate the module
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
||||
self.compiler()?.validate_module(data)
|
||||
self.compiler()?.validate_module(self.features(), data)
|
||||
}
|
||||
|
||||
/// Validate the module
|
||||
@@ -163,6 +173,11 @@ impl JITEngineInner {
|
||||
))
|
||||
}
|
||||
|
||||
/// The Wasm features
|
||||
pub fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Allocate custom sections into memory
|
||||
pub(crate) fn allocate_custom_sections(
|
||||
&mut self,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
)]
|
||||
|
||||
mod artifact;
|
||||
mod builder;
|
||||
mod code_memory;
|
||||
mod engine;
|
||||
mod link;
|
||||
@@ -33,6 +34,7 @@ mod serialize;
|
||||
mod unwind;
|
||||
|
||||
pub use crate::artifact::JITArtifact;
|
||||
pub use crate::builder::JIT;
|
||||
pub use crate::code_memory::CodeMemory;
|
||||
pub use crate::engine::JITEngine;
|
||||
pub use crate::link::link_module;
|
||||
|
||||
@@ -47,6 +47,11 @@ fn apply_relocation(
|
||||
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
|
||||
write_unaligned(reloc_address as *mut u32, reloc_delta as _);
|
||||
},
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
RelocationKind::X86PCRel8 => unsafe {
|
||||
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
|
||||
write_unaligned(reloc_address as *mut u64, reloc_delta);
|
||||
},
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
RelocationKind::X86CallPCRel4 => unsafe {
|
||||
let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{
|
||||
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
};
|
||||
use wasm_common::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
|
||||
use wasmer_compiler::{
|
||||
CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation, SectionIndex,
|
||||
CompileModuleInfo, CustomSection, Dwarf, FunctionBody, JumpTableOffsets, Relocation,
|
||||
SectionIndex,
|
||||
};
|
||||
use wasmer_engine::SerializableFunctionFrameInfo;
|
||||
use wasmer_runtime::ModuleInfo;
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
|
||||
// /// The serializable function data
|
||||
// #[derive(Serialize, Deserialize)]
|
||||
@@ -45,10 +40,6 @@ pub struct SerializableCompilation {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SerializableModule {
|
||||
pub compilation: SerializableCompilation,
|
||||
pub features: Features,
|
||||
pub module: Arc<ModuleInfo>,
|
||||
pub compile_info: CompileModuleInfo,
|
||||
pub data_initializers: Box<[OwnedDataInitializer]>,
|
||||
// Plans for that module
|
||||
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
|
||||
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
|
||||
wasmer-runtime = { path = "../runtime", version = "1.0.0-alpha.1" }
|
||||
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
|
||||
object = { version = "0.19", default-features = false, features = ["write"] }
|
||||
wasmer-object = { path = "../object", version = "1.0.0-alpha.1" }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_bytes = { version = "0.11" }
|
||||
cfg-if = "0.1"
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
use crate::engine::{NativeEngine, NativeEngineInner};
|
||||
use crate::serialize::ModuleMetadata;
|
||||
use libloading::{Library, Symbol as LibrarySymbol};
|
||||
#[cfg(feature = "compiler")]
|
||||
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
|
||||
#[cfg(feature = "compiler")]
|
||||
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
@@ -20,20 +16,22 @@ use tempfile::NamedTempFile;
|
||||
use tracing::trace;
|
||||
use wasm_common::entity::{BoxedSlice, PrimaryMap};
|
||||
use wasm_common::{
|
||||
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer,
|
||||
SignatureIndex, TableIndex,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{
|
||||
Architecture, BinaryFormat, CustomSectionProtection, Endianness, ModuleEnvironment,
|
||||
OperatingSystem, RelocationTarget, Triple,
|
||||
Compilation, CompileModuleInfo, Compiler, ModuleEnvironment, OperatingSystem, Target, Triple,
|
||||
};
|
||||
use wasmer_compiler::{CompileError, Features};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_engine::Engine;
|
||||
use wasmer_engine::{
|
||||
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
|
||||
Tunables,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_object::{emit_compilation, emit_data, get_object_for_target, CompilationNamer};
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
use wasmer_runtime::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline};
|
||||
|
||||
@@ -93,15 +91,16 @@ impl NativeArtifact {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> {
|
||||
/// Generate a compilation
|
||||
pub fn generate_compilation<'data>(
|
||||
data: &'data [u8],
|
||||
compiler: &dyn Compiler,
|
||||
target: &Target,
|
||||
features: &Features,
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<(CompileModuleInfo, Compilation, Vec<DataInitializer<'data>>), CompileError> {
|
||||
let environ = ModuleEnvironment::new();
|
||||
let mut engine_inner = engine.inner_mut();
|
||||
let tunables = engine.tunables();
|
||||
|
||||
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
|
||||
|
||||
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
|
||||
.module
|
||||
.memories
|
||||
@@ -115,272 +114,73 @@ impl NativeArtifact {
|
||||
.map(|table_type| tunables.table_plan(*table_type))
|
||||
.collect();
|
||||
|
||||
let compiler = engine_inner.compiler()?;
|
||||
let compile_info = CompileModuleInfo {
|
||||
module: Arc::new(translation.module),
|
||||
features: features.clone(),
|
||||
memory_plans,
|
||||
table_plans,
|
||||
};
|
||||
|
||||
// Compile the Module
|
||||
let compilation = compiler.compile_module(
|
||||
&translation.module,
|
||||
&target,
|
||||
&compile_info,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
memory_plans.clone(),
|
||||
table_plans.clone(),
|
||||
)?;
|
||||
let function_call_trampolines = compilation.get_function_call_trampolines();
|
||||
let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines();
|
||||
Ok((compile_info, compilation, translation.data_initializers))
|
||||
}
|
||||
|
||||
let data_initializers = translation
|
||||
.data_initializers
|
||||
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(
|
||||
engine: &NativeEngine,
|
||||
data: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Self, CompileError> {
|
||||
let mut engine_inner = engine.inner_mut();
|
||||
let target = engine.target();
|
||||
let (compile_info, compilation, data_initializers) = Self::generate_compilation(
|
||||
data,
|
||||
engine_inner.compiler()?,
|
||||
target,
|
||||
engine_inner.features(),
|
||||
tunables,
|
||||
)?;
|
||||
|
||||
let data_initializers = data_initializers
|
||||
.iter()
|
||||
.map(OwnedDataInitializer::new)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let target = compiler.target();
|
||||
let target_triple = target.triple().clone();
|
||||
|
||||
let obj_binary_format = match target_triple.binary_format {
|
||||
BinaryFormat::Elf => object::BinaryFormat::Elf,
|
||||
BinaryFormat::Macho => object::BinaryFormat::MachO,
|
||||
BinaryFormat::Coff => object::BinaryFormat::Coff,
|
||||
format => {
|
||||
return Err(CompileError::Codegen(format!(
|
||||
"Binary format {} not supported",
|
||||
format
|
||||
)))
|
||||
}
|
||||
};
|
||||
let obj_architecture = match target_triple.architecture {
|
||||
Architecture::X86_64 => object::Architecture::X86_64,
|
||||
Architecture::Aarch64(_) => object::Architecture::Aarch64,
|
||||
architecture => {
|
||||
return Err(CompileError::Codegen(format!(
|
||||
"Architecture {} not supported",
|
||||
architecture
|
||||
)))
|
||||
}
|
||||
};
|
||||
let obj_endianness = match target_triple.endianness() {
|
||||
Ok(Endianness::Little) => object::Endianness::Little,
|
||||
Ok(Endianness::Big) => object::Endianness::Big,
|
||||
Err(e) => {
|
||||
return Err(CompileError::Codegen(format!(
|
||||
"Can't detect the endianness for the target: {:?}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
let mut obj = Object::new(obj_binary_format, obj_architecture, obj_endianness);
|
||||
let function_bodies = compilation.get_function_bodies();
|
||||
let custom_sections = compilation.get_custom_sections();
|
||||
let custom_section_relocations = compilation.get_custom_section_relocations();
|
||||
|
||||
// We construct the function body lengths
|
||||
let function_body_lengths = function_bodies
|
||||
let function_body_lengths = compilation
|
||||
.get_function_bodies()
|
||||
.values()
|
||||
.map(|function_body| function_body.body.len() as u64)
|
||||
.collect::<PrimaryMap<LocalFunctionIndex, u64>>();
|
||||
|
||||
let metadata = ModuleMetadata {
|
||||
features: compiler.features().clone(),
|
||||
module: Arc::new(translation.module),
|
||||
compile_info,
|
||||
prefix: engine_inner.get_prefix(&data),
|
||||
data_initializers,
|
||||
function_body_lengths,
|
||||
memory_plans,
|
||||
table_plans,
|
||||
};
|
||||
|
||||
let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?;
|
||||
let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
|
||||
|
||||
let mut metadata_length = vec![0; 10];
|
||||
let mut writable = &mut metadata_length[..];
|
||||
let mut metadata_binary = vec![0; 10];
|
||||
let mut writable = &mut metadata_binary[..];
|
||||
leb128::write::unsigned(&mut writable, serialized_data.len() as u64)
|
||||
.expect("Should write number");
|
||||
metadata_length.extend(serialized_data);
|
||||
metadata_binary.extend(serialized_data);
|
||||
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: b"WASMER_METADATA".to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Data,
|
||||
scope: SymbolScope::Dynamic,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
});
|
||||
let section_id = obj.section_id(StandardSection::Data);
|
||||
obj.add_symbol_data(symbol_id, section_id, &metadata_length, 1);
|
||||
|
||||
let function_relocations = compilation.get_relocations();
|
||||
|
||||
// Add sections
|
||||
for (section_index, custom_section) in custom_sections.iter() {
|
||||
// TODO: We need to rename the sections corresponding to the DWARF information
|
||||
// to the proper names (like `.eh_frame`)
|
||||
let section_name = metadata.get_section_name(section_index);
|
||||
let (section_kind, standard_section) = match custom_section.protection {
|
||||
CustomSectionProtection::ReadExecute => (SymbolKind::Text, StandardSection::Text),
|
||||
// TODO: Fix this to be StandardSection::Data
|
||||
CustomSectionProtection::Read => (SymbolKind::Data, StandardSection::Text),
|
||||
};
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: section_name.as_bytes().to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: section_kind,
|
||||
scope: SymbolScope::Dynamic,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
});
|
||||
let section_id = obj.section_id(standard_section);
|
||||
obj.add_symbol_data(symbol_id, section_id, custom_section.bytes.as_slice(), 1);
|
||||
}
|
||||
|
||||
// Add functions
|
||||
for (function_local_index, function) in function_bodies.into_iter() {
|
||||
let function_name = metadata.get_function_name(function_local_index);
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: function_name.as_bytes().to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Text,
|
||||
scope: SymbolScope::Dynamic,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
});
|
||||
|
||||
let section_id = obj.section_id(StandardSection::Text);
|
||||
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
|
||||
}
|
||||
|
||||
// Add function call trampolines
|
||||
for (signature_index, function) in function_call_trampolines.into_iter() {
|
||||
let function_name = metadata.get_function_call_trampoline_name(signature_index);
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: function_name.as_bytes().to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Text,
|
||||
scope: SymbolScope::Dynamic,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
});
|
||||
let section_id = obj.section_id(StandardSection::Text);
|
||||
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
|
||||
}
|
||||
|
||||
// Add dynamic function trampolines
|
||||
for (func_index, function) in dynamic_function_trampolines.into_iter() {
|
||||
let function_name = metadata.get_dynamic_function_trampoline_name(func_index);
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: function_name.as_bytes().to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Text,
|
||||
scope: SymbolScope::Dynamic,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
});
|
||||
let section_id = obj.section_id(StandardSection::Text);
|
||||
obj.add_symbol_data(symbol_id, section_id, &function.body, 1);
|
||||
}
|
||||
|
||||
// Add relocations (function and sections)
|
||||
let mut all_relocations = Vec::new();
|
||||
for (function_local_index, relocations) in function_relocations.into_iter() {
|
||||
let function_name = metadata.get_function_name(function_local_index);
|
||||
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
|
||||
all_relocations.push((symbol_id, relocations))
|
||||
}
|
||||
for (section_index, relocations) in custom_section_relocations.into_iter() {
|
||||
let function_name = metadata.get_section_name(section_index);
|
||||
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
|
||||
all_relocations.push((symbol_id, relocations))
|
||||
}
|
||||
for (symbol_id, relocations) in all_relocations.into_iter() {
|
||||
let (_symbol_id, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap();
|
||||
let section_id = obj.section_id(StandardSection::Text);
|
||||
for r in relocations {
|
||||
let relocation_address = section_offset + r.offset as u64;
|
||||
match r.reloc_target {
|
||||
RelocationTarget::LocalFunc(index) => {
|
||||
let target_name = metadata.get_function_name(index);
|
||||
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
|
||||
obj.add_relocation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
)
|
||||
.map_err(to_compile_error)?;
|
||||
}
|
||||
RelocationTarget::LibCall(libcall) => {
|
||||
let libcall_fn_name = libcall.to_function_name().as_bytes();
|
||||
// We add the symols lazily as we see them
|
||||
let target_symbol = obj.symbol_id(libcall_fn_name).unwrap_or_else(|| {
|
||||
obj.add_symbol(Symbol {
|
||||
name: libcall_fn_name.to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Unknown,
|
||||
scope: SymbolScope::Unknown,
|
||||
weak: false,
|
||||
section: SymbolSection::Undefined,
|
||||
flags: SymbolFlags::None,
|
||||
})
|
||||
});
|
||||
obj.add_relocation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
)
|
||||
.map_err(to_compile_error)?;
|
||||
}
|
||||
RelocationTarget::CustomSection(section_index) => {
|
||||
let target_name = metadata.get_section_name(section_index);
|
||||
let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
|
||||
obj.add_relocation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
)
|
||||
.map_err(to_compile_error)?;
|
||||
}
|
||||
RelocationTarget::JumpTable(_func_index, _jt) => {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
emit_data(&mut obj, b"WASMER_METADATA", &metadata_binary).map_err(to_compile_error)?;
|
||||
emit_compilation(&mut obj, compilation, &metadata).map_err(to_compile_error)?;
|
||||
|
||||
let filepath = {
|
||||
let file = tempfile::Builder::new()
|
||||
@@ -511,7 +311,7 @@ impl NativeArtifact {
|
||||
let mut finished_functions: PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]> =
|
||||
PrimaryMap::new();
|
||||
for (function_local_index, function_len) in metadata.function_body_lengths.iter() {
|
||||
let function_name = metadata.get_function_name(function_local_index);
|
||||
let function_name = metadata.get_function_name(&function_local_index);
|
||||
unsafe {
|
||||
// We use a fake function signature `fn()` because we just
|
||||
// want to get the function address.
|
||||
@@ -530,8 +330,8 @@ impl NativeArtifact {
|
||||
}
|
||||
|
||||
// Retrieve function call trampolines (for all signatures in the module)
|
||||
for (sig_index, func_type) in metadata.module.signatures.iter() {
|
||||
let function_name = metadata.get_function_call_trampoline_name(sig_index);
|
||||
for (sig_index, func_type) in metadata.compile_info.module.signatures.iter() {
|
||||
let function_name = metadata.get_function_call_trampoline_name(&sig_index);
|
||||
unsafe {
|
||||
let trampoline: LibrarySymbol<VMTrampoline> = lib
|
||||
.get(function_name.as_bytes())
|
||||
@@ -544,14 +344,15 @@ impl NativeArtifact {
|
||||
let mut finished_dynamic_function_trampolines: PrimaryMap<
|
||||
FunctionIndex,
|
||||
*mut [VMFunctionBody],
|
||||
> = PrimaryMap::with_capacity(metadata.module.num_imported_funcs);
|
||||
> = PrimaryMap::with_capacity(metadata.compile_info.module.num_imported_funcs);
|
||||
for func_index in metadata
|
||||
.compile_info
|
||||
.module
|
||||
.functions
|
||||
.keys()
|
||||
.take(metadata.module.num_imported_funcs)
|
||||
.take(metadata.compile_info.module.num_imported_funcs)
|
||||
{
|
||||
let function_name = metadata.get_dynamic_function_trampoline_name(func_index);
|
||||
let function_name = metadata.get_dynamic_function_trampoline_name(&func_index);
|
||||
unsafe {
|
||||
let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib
|
||||
.get(function_name.as_bytes())
|
||||
@@ -582,6 +383,7 @@ impl NativeArtifact {
|
||||
let signatures = {
|
||||
let signature_registry = engine_inner.signatures();
|
||||
metadata
|
||||
.compile_info
|
||||
.module
|
||||
.signatures
|
||||
.values()
|
||||
@@ -697,15 +499,15 @@ impl NativeArtifact {
|
||||
|
||||
impl Artifact for NativeArtifact {
|
||||
fn module(&self) -> Arc<ModuleInfo> {
|
||||
self.metadata.module.clone()
|
||||
self.metadata.compile_info.module.clone()
|
||||
}
|
||||
|
||||
fn module_ref(&self) -> &ModuleInfo {
|
||||
&self.metadata.module
|
||||
&self.metadata.compile_info.module
|
||||
}
|
||||
|
||||
fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
|
||||
Arc::get_mut(&mut self.metadata.module)
|
||||
Arc::get_mut(&mut self.metadata.compile_info.module)
|
||||
}
|
||||
|
||||
fn register_frame_info(&self) {
|
||||
@@ -713,7 +515,7 @@ impl Artifact for NativeArtifact {
|
||||
}
|
||||
|
||||
fn features(&self) -> &Features {
|
||||
&self.metadata.features
|
||||
&self.metadata.compile_info.features
|
||||
}
|
||||
|
||||
fn data_initializers(&self) -> &[OwnedDataInitializer] {
|
||||
@@ -721,11 +523,11 @@ impl Artifact for NativeArtifact {
|
||||
}
|
||||
|
||||
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
|
||||
&self.metadata.memory_plans
|
||||
&self.metadata.compile_info.memory_plans
|
||||
}
|
||||
|
||||
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
|
||||
&self.metadata.table_plans
|
||||
&self.metadata.compile_info.table_plans
|
||||
}
|
||||
|
||||
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]> {
|
||||
|
||||
56
lib/engine-native/src/builder.rs
Normal file
56
lib/engine-native/src/builder.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use wasm_common::FunctionType;
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasm_common::{Features, FunctionType};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{Compiler, CompilerConfig};
|
||||
use wasmer_compiler::Compiler;
|
||||
use wasmer_compiler::{CompileError, Target};
|
||||
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
|
||||
use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
|
||||
|
||||
@@ -16,27 +16,24 @@ use wasmer_runtime::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
|
||||
#[derive(Clone)]
|
||||
pub struct NativeEngine {
|
||||
inner: Arc<Mutex<NativeEngineInner>>,
|
||||
tunables: Arc<dyn Tunables + Send + Sync>,
|
||||
/// The target for the compiler
|
||||
target: Arc<Target>,
|
||||
engine_id: EngineId,
|
||||
}
|
||||
|
||||
impl NativeEngine {
|
||||
/// Create a new `NativeEngine` with the given config
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn new(
|
||||
mut config: Box<dyn CompilerConfig>,
|
||||
tunables: impl Tunables + 'static + Send + Sync,
|
||||
) -> Self {
|
||||
config.enable_pic();
|
||||
let compiler = config.compiler();
|
||||
pub fn new(compiler: Box<dyn Compiler + Send>, target: Target, features: Features) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(NativeEngineInner {
|
||||
compiler: Some(compiler),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
features,
|
||||
})),
|
||||
tunables: Arc::new(tunables),
|
||||
target: Arc::new(target),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
@@ -54,7 +51,7 @@ impl NativeEngine {
|
||||
///
|
||||
/// Headless engines can't compile or validate any modules,
|
||||
/// they just take already processed Modules (via `Module::serialize`).
|
||||
pub fn headless(tunables: impl Tunables + 'static + Send + Sync) -> Self {
|
||||
pub fn headless() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(NativeEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
@@ -62,8 +59,9 @@ impl NativeEngine {
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
features: Features::default(),
|
||||
})),
|
||||
tunables: Arc::new(tunables),
|
||||
target: Arc::new(Target::default()),
|
||||
engine_id: EngineId::default(),
|
||||
}
|
||||
}
|
||||
@@ -96,9 +94,9 @@ impl NativeEngine {
|
||||
}
|
||||
|
||||
impl Engine for NativeEngine {
|
||||
/// Get the tunables
|
||||
fn tunables(&self) -> &dyn Tunables {
|
||||
&*self.tunables
|
||||
/// The target
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Register a signature
|
||||
@@ -124,8 +122,12 @@ impl Engine for NativeEngine {
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Ok(Arc::new(NativeArtifact::new(&self, binary)?))
|
||||
fn compile(
|
||||
&self,
|
||||
binary: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Ok(Arc::new(NativeArtifact::new(&self, binary, tunables)?))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module (binary content of a Shared Object file)
|
||||
@@ -147,6 +149,10 @@ impl Engine for NativeEngine {
|
||||
fn id(&self) -> &EngineId {
|
||||
&self.engine_id
|
||||
}
|
||||
|
||||
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// The inner contents of `NativeEngine`
|
||||
@@ -154,6 +160,8 @@ pub struct NativeEngineInner {
|
||||
/// The compiler
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: Option<Box<dyn Compiler + Send>>,
|
||||
/// The WebAssembly features to use
|
||||
features: Features,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
/// The signature registry is used mainly to operate with trampolines
|
||||
@@ -186,10 +194,14 @@ impl NativeEngineInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Validate the module
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
||||
self.compiler()?.validate_module(data)
|
||||
self.compiler()?.validate_module(self.features(), data)
|
||||
}
|
||||
|
||||
/// Validate the module
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
)]
|
||||
|
||||
mod artifact;
|
||||
mod builder;
|
||||
mod engine;
|
||||
mod serialize;
|
||||
|
||||
pub use crate::artifact::NativeArtifact;
|
||||
pub use crate::builder::Native;
|
||||
pub use crate::engine::NativeEngine;
|
||||
|
||||
/// Version number of this crate.
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap};
|
||||
use wasm_common::{
|
||||
Features, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
};
|
||||
use wasmer_compiler::SectionIndex;
|
||||
use wasmer_runtime::ModuleInfo;
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
use wasm_common::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex};
|
||||
use wasmer_compiler::{CompileModuleInfo, SectionIndex};
|
||||
use wasmer_object::CompilationNamer;
|
||||
|
||||
/// Serializable struct that represents the compiled metadata.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ModuleMetadata {
|
||||
pub features: Features,
|
||||
pub module: Arc<ModuleInfo>,
|
||||
pub compile_info: CompileModuleInfo,
|
||||
pub prefix: String,
|
||||
pub data_initializers: Box<[OwnedDataInitializer]>,
|
||||
// The function body lengths (used for reverse-locate traps in the function)
|
||||
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
|
||||
// Plans for that module
|
||||
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
}
|
||||
|
||||
impl ModuleMetadata {
|
||||
impl CompilationNamer for ModuleMetadata {
|
||||
/// Gets the function name given a local function index
|
||||
pub fn get_function_name(&self, index: LocalFunctionIndex) -> String {
|
||||
fn get_function_name(&self, index: &LocalFunctionIndex) -> String {
|
||||
format!("wasmer_function_{}_{}", self.prefix, index.index())
|
||||
}
|
||||
|
||||
/// Gets the section name given a section index
|
||||
pub fn get_section_name(&self, index: SectionIndex) -> String {
|
||||
fn get_section_name(&self, index: &SectionIndex) -> String {
|
||||
format!("wasmer_section_{}_{}", self.prefix, index.index())
|
||||
}
|
||||
|
||||
/// Gets the function call trampoline name given a signature index
|
||||
pub fn get_function_call_trampoline_name(&self, index: SignatureIndex) -> String {
|
||||
fn get_function_call_trampoline_name(&self, index: &SignatureIndex) -> String {
|
||||
format!(
|
||||
"wasmer_trampoline_function_call_{}_{}",
|
||||
self.prefix,
|
||||
@@ -44,7 +35,7 @@ impl ModuleMetadata {
|
||||
}
|
||||
|
||||
/// Gets the dynamic function trampoline name given a function index
|
||||
pub fn get_dynamic_function_trampoline_name(&self, index: FunctionIndex) -> String {
|
||||
fn get_dynamic_function_trampoline_name(&self, index: &FunctionIndex) -> String {
|
||||
format!(
|
||||
"wasmer_trampoline_dynamic_function_{}_{}",
|
||||
self.prefix,
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::path::Path;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
use std::sync::Arc;
|
||||
use wasm_common::FunctionType;
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_compiler::{CompileError, Target};
|
||||
use wasmer_runtime::{VMSharedSignatureIndex, VMTrampoline};
|
||||
|
||||
/// A unimplemented Wasmer `Engine`.
|
||||
@@ -16,8 +16,8 @@ use wasmer_runtime::{VMSharedSignatureIndex, VMTrampoline};
|
||||
///
|
||||
/// The product that an `Engine` produces and consumes is the [`Artifact`].
|
||||
pub trait Engine {
|
||||
/// Get the tunables
|
||||
fn tunables(&self) -> &dyn Tunables;
|
||||
/// Gets the target
|
||||
fn target(&self) -> &Target;
|
||||
|
||||
/// Register a signature
|
||||
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex;
|
||||
@@ -32,7 +32,11 @@ pub trait Engine {
|
||||
fn validate(&self, binary: &[u8]) -> Result<(), CompileError>;
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError>;
|
||||
fn compile(
|
||||
&self,
|
||||
binary: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError>;
|
||||
|
||||
/// Deserializes a WebAssembly module
|
||||
///
|
||||
@@ -60,6 +64,9 @@ pub trait Engine {
|
||||
/// comparing two trait objects unsafely relies on implementation details
|
||||
/// of trait representation.
|
||||
fn id(&self) -> &EngineId;
|
||||
|
||||
/// Clone the engine
|
||||
fn cloned(&self) -> Arc<dyn Engine + Send + Sync>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
||||
@@ -11,10 +11,10 @@ use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
|
||||
/// Tunables for an engine
|
||||
pub trait Tunables {
|
||||
/// Get a `MemoryPlan` for the provided `MemoryType`
|
||||
/// Construct a `MemoryPlan` for the provided `MemoryType`
|
||||
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan;
|
||||
|
||||
/// Get a `TablePlan` for the provided `TableType`
|
||||
/// Construct a `TablePlan` for the provided `TableType`
|
||||
fn table_plan(&self, table: TableType) -> TablePlan;
|
||||
|
||||
/// Create a memory given a memory type
|
||||
|
||||
20
lib/object/Cargo.toml
Normal file
20
lib/object/Cargo.toml
Normal 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
0
lib/object/README.md
Normal file
20
lib/object/src/error.rs
Normal file
20
lib/object/src/error.rs
Normal 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
28
lib/object/src/lib.rs
Normal 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
262
lib/object/src/module.rs
Normal 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(§ion_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(§ion_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(§ion_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(())
|
||||
}
|
||||
@@ -155,7 +155,7 @@ impl ModuleInfo {
|
||||
.iter()
|
||||
.filter_map(|(_name, export_index)| match export_index {
|
||||
ExportIndex::Function(i) => {
|
||||
let signature = self.functions.get(i.clone()).unwrap();
|
||||
let signature = self.functions.get(*i).unwrap();
|
||||
let func_type = self.signatures.get(signature.clone()).unwrap();
|
||||
Some(func_type.clone())
|
||||
}
|
||||
@@ -169,20 +169,20 @@ impl ModuleInfo {
|
||||
let iter = self.exports.iter().map(move |(name, export_index)| {
|
||||
let extern_type = match export_index {
|
||||
ExportIndex::Function(i) => {
|
||||
let signature = self.functions.get(i.clone()).unwrap();
|
||||
let func_type = self.signatures.get(signature.clone()).unwrap();
|
||||
let signature = self.functions.get(*i).unwrap();
|
||||
let func_type = self.signatures.get(*signature).unwrap();
|
||||
ExternType::Function(func_type.clone())
|
||||
}
|
||||
ExportIndex::Table(i) => {
|
||||
let table_type = self.tables.get(i.clone()).unwrap();
|
||||
let table_type = self.tables.get(*i).unwrap();
|
||||
ExternType::Table(*table_type)
|
||||
}
|
||||
ExportIndex::Memory(i) => {
|
||||
let memory_type = self.memories.get(i.clone()).unwrap();
|
||||
let memory_type = self.memories.get(*i).unwrap();
|
||||
ExternType::Memory(*memory_type)
|
||||
}
|
||||
ExportIndex::Global(i) => {
|
||||
let global_type = self.globals.get(i.clone()).unwrap();
|
||||
let global_type = self.globals.get(*i).unwrap();
|
||||
ExternType::Global(*global_type)
|
||||
}
|
||||
};
|
||||
@@ -202,20 +202,20 @@ impl ModuleInfo {
|
||||
.map(move |((module, field, _), import_index)| {
|
||||
let extern_type = match import_index {
|
||||
ImportIndex::Function(i) => {
|
||||
let signature = self.functions.get(i.clone()).unwrap();
|
||||
let func_type = self.signatures.get(signature.clone()).unwrap();
|
||||
let signature = self.functions.get(*i).unwrap();
|
||||
let func_type = self.signatures.get(*signature).unwrap();
|
||||
ExternType::Function(func_type.clone())
|
||||
}
|
||||
ImportIndex::Table(i) => {
|
||||
let table_type = self.tables.get(i.clone()).unwrap();
|
||||
let table_type = self.tables.get(*i).unwrap();
|
||||
ExternType::Table(*table_type)
|
||||
}
|
||||
ImportIndex::Memory(i) => {
|
||||
let memory_type = self.memories.get(i.clone()).unwrap();
|
||||
let memory_type = self.memories.get(*i).unwrap();
|
||||
ExternType::Memory(*memory_type)
|
||||
}
|
||||
ImportIndex::Global(i) => {
|
||||
let global_type = self.globals.get(i.clone()).unwrap();
|
||||
let global_type = self.globals.get(*i).unwrap();
|
||||
ExternType::Global(*global_type)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ mod test_vmfunction_body {
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub enum VMFunctionKind {
|
||||
/// A function is static when it's address matches the signature:
|
||||
/// A function is static when its address matches the signature:
|
||||
/// (vmctx, vmctx, arg1, arg2...) -> (result1, result2, ...)
|
||||
///
|
||||
/// This is the default for functions that are defined:
|
||||
@@ -126,7 +126,7 @@ pub enum VMFunctionKind {
|
||||
/// 2. In the WebAssembly file
|
||||
Static,
|
||||
|
||||
/// A function is dynamic when it's address matches the signature:
|
||||
/// A function is dynamic when its address matches the signature:
|
||||
/// (ctx, &[Type]) -> Vec<Type>
|
||||
///
|
||||
/// This is the default for functions that are defined:
|
||||
|
||||
@@ -21,13 +21,19 @@ mod utils;
|
||||
|
||||
use crate::syscalls::*;
|
||||
|
||||
pub use crate::state::{Fd, WasiFile, WasiFs, WasiFsError, WasiState, ALL_RIGHTS, VIRTUAL_ROOT_FD};
|
||||
pub use crate::state::{
|
||||
Fd, WasiFile, WasiFs, WasiFsError, WasiState, WasiStateCreationError, ALL_RIGHTS,
|
||||
VIRTUAL_ROOT_FD,
|
||||
};
|
||||
pub use crate::syscalls::types;
|
||||
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
|
||||
|
||||
use thiserror::Error;
|
||||
use wasmer::{imports, Function, ImportObject, Memory, Module, Store};
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
/// This is returned in `RuntimeError`.
|
||||
@@ -45,14 +51,88 @@ pub enum WasiError {
|
||||
#[derive(Clone)]
|
||||
pub struct WasiEnv {
|
||||
state: Arc<Mutex<WasiState>>,
|
||||
memory: Option<Arc<Memory>>,
|
||||
memory: Arc<WasiMemory>,
|
||||
}
|
||||
|
||||
/// Wrapper type around `Memory` used to delay initialization of the memory.
|
||||
///
|
||||
/// The `initialized` field is used to indicate if it's safe to read `memory` as `Memory`.
|
||||
///
|
||||
/// The `mutate_lock` is used to prevent access from multiple threads during initialization.
|
||||
struct WasiMemory {
|
||||
initialized: AtomicBool,
|
||||
memory: UnsafeCell<MaybeUninit<Memory>>,
|
||||
mutate_lock: Mutex<()>,
|
||||
}
|
||||
|
||||
impl WasiMemory {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
initialized: AtomicBool::new(false),
|
||||
memory: UnsafeCell::new(MaybeUninit::zeroed()),
|
||||
mutate_lock: Mutex::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the memory, making it safe to read from.
|
||||
///
|
||||
/// Returns whether or not the set was successful. If the set failed then
|
||||
/// the memory has already been initialized.
|
||||
fn set_memory(&self, memory: Memory) -> bool {
|
||||
// synchronize it
|
||||
let _guard = self.mutate_lock.lock();
|
||||
if self.initialized.load(Ordering::Acquire) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ptr = self.memory.get();
|
||||
let mem_inner: &mut MaybeUninit<Memory> = &mut *ptr;
|
||||
mem_inner.as_mut_ptr().write(memory);
|
||||
}
|
||||
self.initialized.store(true, Ordering::Release);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns `None` if the memory has not been initialized yet.
|
||||
/// Otherwise returns the memory that was used to initialize it.
|
||||
fn get_memory(&self) -> Option<&Memory> {
|
||||
// Based on normal usage, `Relaxed` is fine...
|
||||
// TODO: investigate if it's possible to use the API in a way where `Relaxed`
|
||||
// is not fine
|
||||
if self.initialized.load(Ordering::Relaxed) {
|
||||
unsafe {
|
||||
let maybe_mem = self.memory.get();
|
||||
Some(&*(*maybe_mem).as_ptr())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WasiMemory {
|
||||
fn drop(&mut self) {
|
||||
if self.initialized.load(Ordering::Acquire) {
|
||||
unsafe {
|
||||
// We want to get the internal value in memory, so we need to consume
|
||||
// the `UnsafeCell` and assume the `MapbeInit` is initialized, but because
|
||||
// we only have a `&mut self` we can't do this directly, so we swap the data
|
||||
// out so we can drop it (via `assume_init`).
|
||||
let mut maybe_uninit = UnsafeCell::new(MaybeUninit::zeroed());
|
||||
std::mem::swap(&mut self.memory, &mut maybe_uninit);
|
||||
maybe_uninit.into_inner().assume_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiEnv {
|
||||
pub fn new(state: WasiState) -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(state)),
|
||||
memory: None,
|
||||
memory: Arc::new(WasiMemory::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +146,8 @@ impl WasiEnv {
|
||||
}
|
||||
|
||||
/// Set the memory
|
||||
pub fn set_memory(&mut self, memory: Arc<Memory>) {
|
||||
self.memory = Some(memory);
|
||||
pub fn set_memory(&mut self, memory: Memory) -> bool {
|
||||
self.memory.set_memory(memory)
|
||||
}
|
||||
|
||||
/// Get the WASI state
|
||||
@@ -82,14 +162,14 @@ impl WasiEnv {
|
||||
|
||||
/// Get a reference to the memory
|
||||
pub fn memory(&self) -> &Memory {
|
||||
self.memory.as_ref().expect("The expected Memory is not attached to the `WasiEnv`. Did you forgot to call wasi_env.set_memory(...)?")
|
||||
self.memory.get_memory().expect("The expected Memory is not attached to the `WasiEnv`. Did you forgot to call wasi_env.set_memory(...)?")
|
||||
}
|
||||
|
||||
pub(crate) fn get_memory_and_wasi_state(
|
||||
&mut self,
|
||||
_mem_index: u32,
|
||||
) -> (&Memory, MutexGuard<WasiState>) {
|
||||
let memory = self.memory.as_ref().unwrap();
|
||||
let memory = self.memory.get_memory().unwrap();
|
||||
let state = self.state.lock().unwrap();
|
||||
(memory, state)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(crate) fn create_wasi_state(program_name: &str) -> WasiStateBuilder {
|
||||
///
|
||||
/// Usage:
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// let mut state_builder = WasiState::new("wasi-prog-name");
|
||||
/// state_builder
|
||||
@@ -196,7 +196,7 @@ impl WasiStateBuilder {
|
||||
/// Usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// WasiState::new("program_name")
|
||||
/// .preopen(|p| p.directory("src").read(true).write(true).create(true))?
|
||||
|
||||
@@ -1452,7 +1452,7 @@ impl WasiFs {
|
||||
/// Usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use wasmer_wasi::state::{WasiState, WasiStateCreationError};
|
||||
/// # use wasmer_wasi::{WasiState, WasiStateCreationError};
|
||||
/// # fn main() -> Result<(), WasiStateCreationError> {
|
||||
/// WasiState::new("program_name")
|
||||
/// .env(b"HOME", "/home/home".to_string())
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/// types for use in the WASI filesystem
|
||||
use crate::syscalls::types::*;
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
#[cfg(unix)]
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Seek, Write},
|
||||
@@ -148,7 +150,7 @@ impl WasiFsError {
|
||||
|
||||
/// This trait relies on your file closing when it goes out of scope via `Drop`
|
||||
#[typetag::serde(tag = "type")]
|
||||
pub trait WasiFile: std::fmt::Debug + Send + Write + Read + Seek {
|
||||
pub trait WasiFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable {
|
||||
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
|
||||
fn last_accessed(&self) -> __wasi_timestamp_t;
|
||||
|
||||
@@ -210,6 +212,40 @@ pub trait WasiFile: std::fmt::Debug + Send + Write + Read + Seek {
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 .
|
||||
/// Trait needed to get downcasting from `WasiFile` to work.
|
||||
pub trait Upcastable {
|
||||
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any;
|
||||
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any;
|
||||
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
impl<T: Any + fmt::Debug + 'static> Upcastable for T {
|
||||
#[inline]
|
||||
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline]
|
||||
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn WasiFile + 'static {
|
||||
#[inline]
|
||||
pub fn downcast_ref<T: 'static>(self: &'_ Self) -> Option<&'_ T> {
|
||||
self.upcast_any_ref().downcast_ref::<T>()
|
||||
}
|
||||
#[inline]
|
||||
pub fn downcast_mut<T: 'static>(self: &'_ mut Self) -> Option<&'_ mut T> {
|
||||
self.upcast_any_mut().downcast_mut::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PollEvent {
|
||||
/// Data available to read
|
||||
|
||||
@@ -43,10 +43,7 @@ pub use crate::indexes::{
|
||||
LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
|
||||
SignatureIndex, TableIndex,
|
||||
};
|
||||
pub use crate::native::{
|
||||
Func, HostFunction, NativeWasmType, ValueType, WasmExternType, WasmTypeList, WithEnv,
|
||||
WithoutEnv,
|
||||
};
|
||||
pub use crate::native::{NativeWasmType, ValueType};
|
||||
pub use crate::r#ref::{ExternRef, HostInfo, HostRef};
|
||||
pub use crate::units::{Bytes, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE};
|
||||
pub use crate::values::Value;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
//! This module permits to create native functions
|
||||
//! easily in Rust, thanks to its advanced typing system.
|
||||
|
||||
use crate::types::{FunctionType, Type};
|
||||
use crate::types::Type;
|
||||
use crate::values::Value;
|
||||
use std::convert::Infallible;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// `NativeWasmType` represents a native Wasm type.
|
||||
/// It uses the Rust Type system to automatically detect the
|
||||
@@ -154,53 +152,6 @@ mod test_native_type {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to represent a wasm extern type.
|
||||
pub unsafe trait WasmExternType: Copy
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Native wasm type for this `WasmExternType`.
|
||||
type Native: NativeWasmType;
|
||||
|
||||
/// Convert from given `Native` type to self.
|
||||
fn from_native(native: Self::Native) -> Self;
|
||||
|
||||
/// Convert self to `Native` type.
|
||||
fn to_native(self) -> Self::Native;
|
||||
}
|
||||
|
||||
macro_rules! wasm_extern_type {
|
||||
($type:ty => $native_type:ty) => {
|
||||
#[allow(clippy::use_self)]
|
||||
unsafe impl WasmExternType for $type {
|
||||
type Native = $native_type;
|
||||
|
||||
#[inline]
|
||||
fn from_native(native: Self::Native) -> Self {
|
||||
native as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_native(self) -> Self::Native {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wasm_extern_type!(i8 => i32);
|
||||
wasm_extern_type!(u8 => i32);
|
||||
wasm_extern_type!(i16 => i32);
|
||||
wasm_extern_type!(u16 => i32);
|
||||
wasm_extern_type!(i32 => i32);
|
||||
wasm_extern_type!(u32 => i32);
|
||||
wasm_extern_type!(i64 => i64);
|
||||
wasm_extern_type!(u64 => i64);
|
||||
wasm_extern_type!(f32 => f32);
|
||||
wasm_extern_type!(f64 => f64);
|
||||
// wasm_extern_type!(u128 => i128);
|
||||
// wasm_extern_type!(i128 => i128);
|
||||
|
||||
// pub trait IntegerAtomic
|
||||
// where
|
||||
// Self: Sized
|
||||
@@ -243,514 +194,3 @@ macro_rules! impl_value_type_for {
|
||||
}
|
||||
|
||||
impl_value_type_for!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
|
||||
|
||||
/// Represents a list of WebAssembly values.
|
||||
pub trait WasmTypeList {
|
||||
/// CStruct type.
|
||||
type CStruct;
|
||||
|
||||
/// Array of return values.
|
||||
type Array: AsMut<[i128]>;
|
||||
|
||||
/// Construct `Self` based on an array of returned values.
|
||||
fn from_array(array: Self::Array) -> Self;
|
||||
|
||||
/// Transforms Rust values into an Array
|
||||
fn into_array(self) -> Self::Array;
|
||||
|
||||
/// Generates an empty array that will hold the returned values of
|
||||
/// the WebAssembly function.
|
||||
fn empty_array() -> Self::Array;
|
||||
|
||||
/// Transforms C values into Rust values.
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self;
|
||||
|
||||
/// Transforms Rust values into C values.
|
||||
fn into_c_struct(self) -> Self::CStruct;
|
||||
|
||||
/// Get types of the current values.
|
||||
fn wasm_types() -> &'static [Type];
|
||||
}
|
||||
|
||||
/// Represents a TrapEarly type.
|
||||
pub trait TrapEarly<Rets>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// The error type for this trait.
|
||||
type Error: Send + 'static;
|
||||
|
||||
/// Get returns or error result.
|
||||
fn report(self) -> Result<Rets, Self::Error>;
|
||||
}
|
||||
|
||||
impl<Rets> TrapEarly<Rets> for Rets
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
type Error = Infallible;
|
||||
|
||||
fn report(self) -> Result<Self, Infallible> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
E: Send + 'static,
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn report(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty trait to specify the kind of `HostFunction`: With or
|
||||
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
|
||||
/// `ImplicitVmCtx` structures.
|
||||
///
|
||||
/// This trait is never aimed to be used by a user. It is used by the
|
||||
/// trait system to automatically generate an appropriate `wrap`
|
||||
/// function.
|
||||
#[doc(hidden)]
|
||||
pub trait HostFunctionKind {}
|
||||
|
||||
/// An empty struct to help Rust typing to determine
|
||||
/// when a `HostFunction` doesn't take an Environment
|
||||
pub struct WithEnv {}
|
||||
|
||||
impl HostFunctionKind for WithEnv {}
|
||||
|
||||
/// An empty struct to help Rust typing to determine
|
||||
/// when a `HostFunction` takes an Environment
|
||||
pub struct WithoutEnv {}
|
||||
|
||||
impl HostFunctionKind for WithoutEnv {}
|
||||
|
||||
/// Represents a function that can be converted to a `vm::Func`
|
||||
/// (function pointer) that can be called within WebAssembly.
|
||||
pub trait HostFunction<Args, Rets, Kind, T>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Kind: HostFunctionKind,
|
||||
T: Sized,
|
||||
Self: Sized,
|
||||
{
|
||||
/// Convert to function pointer.
|
||||
fn to_raw(self) -> *const FunctionBody;
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FunctionBody(*mut u8);
|
||||
|
||||
/// Represents a function that can be used by WebAssembly.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Func<Args = (), Rets = ()> {
|
||||
address: *const FunctionBody,
|
||||
_phantom: PhantomData<(Args, Rets)>,
|
||||
}
|
||||
|
||||
unsafe impl<Args, Rets> Send for Func<Args, Rets> {}
|
||||
|
||||
impl<Args, Rets> Func<Args, Rets>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Creates a new `Func`.
|
||||
pub fn new<F, T, E>(func: F) -> Self
|
||||
where
|
||||
F: HostFunction<Args, Rets, T, E>,
|
||||
T: HostFunctionKind,
|
||||
E: Sized,
|
||||
{
|
||||
Self {
|
||||
address: func.to_raw(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the type of the Func
|
||||
pub fn ty(&self) -> FunctionType {
|
||||
FunctionType::new(Args::wasm_types(), Rets::wasm_types())
|
||||
}
|
||||
|
||||
/// Get the address of the Func
|
||||
pub fn address(&self) -> *const FunctionBody {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmTypeList for Infallible {
|
||||
type CStruct = Self;
|
||||
type Array = [i128; 0];
|
||||
|
||||
fn from_array(_: Self::Array) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn into_array(self) -> Self::Array {
|
||||
[]
|
||||
}
|
||||
|
||||
fn empty_array() -> Self::Array {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn from_c_struct(_: Self::CStruct) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn wasm_types() -> &'static [Type] {
|
||||
&[]
|
||||
}
|
||||
|
||||
// #[allow(non_snake_case)]
|
||||
// unsafe fn call<Rets>(
|
||||
// self,
|
||||
// ) -> Result<Rets>
|
||||
// where
|
||||
// Rets: WasmTypeList,
|
||||
// {
|
||||
// unreachable!()
|
||||
// }
|
||||
}
|
||||
|
||||
macro_rules! impl_traits {
|
||||
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||
/// Struct for typed funcs.
|
||||
#[repr($repr)]
|
||||
pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*;
|
||||
|
||||
#[allow(unused_parens, dead_code)]
|
||||
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*
|
||||
{
|
||||
type CStruct = $struct_name<$( $x ),*>;
|
||||
|
||||
type Array = [i128; count_idents!( $( $x ),* )];
|
||||
|
||||
fn from_array(array: Self::Array) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
}
|
||||
|
||||
fn into_array(self) -> Self::Array {
|
||||
#[allow(non_snake_case)]
|
||||
let ( $( $x ),* ) = self;
|
||||
[ $( WasmExternType::to_native($x).to_binary() ),* ]
|
||||
}
|
||||
|
||||
fn empty_array() -> Self::Array {
|
||||
[0; count_idents!( $( $x ),* )]
|
||||
}
|
||||
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let $struct_name ( $( $x ),* ) = c_struct;
|
||||
|
||||
( $( WasmExternType::from_native($x) ),* )
|
||||
}
|
||||
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
let ( $( $x ),* ) = self;
|
||||
|
||||
$struct_name ( $( WasmExternType::to_native($x) ),* )
|
||||
}
|
||||
|
||||
fn wasm_types() -> &'static [Type] {
|
||||
&[$( $x::Native::WASM_TYPE ),*]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, FN > HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
FN: Fn($( $x , )*) -> Rets + 'static + Send
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(self) -> *const FunctionBody {
|
||||
// unimplemented!("");
|
||||
extern fn wrap<$( $x, )* Rets, FN>( _: usize, $($x: $x::Native, )* ) -> Rets::CStruct
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
$($x: WasmExternType,)*
|
||||
FN: Fn( $( $x ),* ) -> Rets + 'static
|
||||
{
|
||||
// println!("WRAP");
|
||||
// println!("Struct {:?}", (($( $x ),*) as WasmTypeList).into_c_struct());
|
||||
// $( println!("X: {:?}", $x); )*
|
||||
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
|
||||
f( $( WasmExternType::from_native($x) ),* ).into_c_struct()
|
||||
}
|
||||
wrap::<$( $x, )* Rets, Self> as *const FunctionBody
|
||||
|
||||
// extern fn wrap<$( $x: WasmExternType, )* Rets>(a: &dyn Any, b: &dyn Any, $($x: $x, )* ) -> Rets::CStruct
|
||||
// where
|
||||
// Rets: WasmTypeList
|
||||
// {
|
||||
// println!("WRAP");
|
||||
// let f: &fn( &dyn Any, &dyn Any, $( $x ),* ) -> Rets = unsafe { std::mem::transmute(&()) };
|
||||
// f( a, b, $( $x ),* ).into_c_struct()
|
||||
// }
|
||||
// wrap::<$( $x, )* Rets> as *const FunctionBody
|
||||
|
||||
// extern fn wrap<$( $x, )* Rets, FN>(
|
||||
// $($x: <$x as WasmExternType>::Native , )*
|
||||
// ) -> Rets::CStruct
|
||||
// where
|
||||
// $( $x: WasmExternType, )*
|
||||
// Rets: WasmTypeList,
|
||||
// FN: Fn($( $x, )*) -> Rets::CStruct,
|
||||
// {
|
||||
// // let self_pointer = wrap::<$( $x, )* Rets, FN> as *const FunctionBody;
|
||||
// let f: &FN = unsafe {
|
||||
// std::mem::transmute(&())
|
||||
// };
|
||||
// f($( $x, )*)
|
||||
// }
|
||||
// unimplemented!("");
|
||||
// extern fn wrap<Args, Rets>(
|
||||
// env: &FuncEnv,
|
||||
// args: Args::Array,
|
||||
// returns: Rets::Array
|
||||
// )
|
||||
// where
|
||||
// Args: WasmTypeList,
|
||||
// Rets: WasmTypeList,
|
||||
// {
|
||||
// let self_pointer = wrap::<Args, Rets> as *const FunctionBody;
|
||||
// self_pointer($( $x , )*);
|
||||
// }
|
||||
// unimplemented!("");
|
||||
// $( $x: WasmExternType, )*
|
||||
// Rets: WasmTypeList,
|
||||
// Trap: TrapEarly<Rets>,
|
||||
// FN: Fn($( $x, )*) -> Trap,
|
||||
// {
|
||||
// let x = |args: <(i32, i32) as WasmTypeList>::Array, rets: &mut <(i32, i32) as WasmTypeList>::Array| {
|
||||
// let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _);
|
||||
// rets[0] = result.0 as _;
|
||||
// rets[1] = result.1 as _;
|
||||
// };
|
||||
|
||||
// &self as *const _ as *const FunctionBody
|
||||
// let x: *const FunctionBody = unsafe { std::mem::transmute(self) };
|
||||
// unimplemented!("");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl< $( $x, )* Rets, FN, T > HostFunction<( $( $x ),* ), Rets, WithEnv, T> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
T: Sized,
|
||||
FN: Fn(&mut T, $( $x , )*) -> Rets + 'static + Send
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(self) -> *const FunctionBody {
|
||||
extern fn wrap<$( $x, )* Rets, FN, T>( ctx: &mut T, $($x: $x::Native, )* ) -> Rets::CStruct
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
$($x: WasmExternType,)*
|
||||
T: Sized,
|
||||
FN: Fn(&mut T, $( $x ),* ) -> Rets + 'static
|
||||
{
|
||||
let f: &FN = unsafe { &*(&() as *const () as *const FN) };
|
||||
f(ctx, $( WasmExternType::from_native($x) ),* ).into_c_struct()
|
||||
}
|
||||
wrap::<$( $x, )* Rets, Self, T> as *const FunctionBody
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! count_idents {
|
||||
( $($idents:ident),* ) => {{
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
enum Idents { $($idents,)* __CountIdentsLast }
|
||||
const COUNT: usize = Idents::__CountIdentsLast as usize;
|
||||
COUNT
|
||||
}};
|
||||
}
|
||||
|
||||
impl_traits!([C] S0,);
|
||||
impl_traits!([transparent] S1, A1);
|
||||
impl_traits!([C] S2, A1, A2);
|
||||
impl_traits!([C] S3, A1, A2, A3);
|
||||
impl_traits!([C] S4, A1, A2, A3, A4);
|
||||
impl_traits!([C] S5, A1, A2, A3, A4, A5);
|
||||
impl_traits!([C] S6, A1, A2, A3, A4, A5, A6);
|
||||
impl_traits!([C] S7, A1, A2, A3, A4, A5, A6, A7);
|
||||
impl_traits!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
|
||||
impl_traits!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
|
||||
impl_traits!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
|
||||
impl_traits!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
|
||||
impl_traits!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
|
||||
impl_traits!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
|
||||
impl_traits!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
|
||||
impl_traits!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
|
||||
impl_traits!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
|
||||
impl_traits!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
|
||||
impl_traits!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
|
||||
impl_traits!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
|
||||
impl_traits!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
|
||||
// impl_traits!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
|
||||
// impl_traits!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
|
||||
// impl_traits!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
|
||||
// impl_traits!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
|
||||
// impl_traits!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
|
||||
// impl_traits!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_wasm_type_list {
|
||||
use super::*;
|
||||
use crate::types::Type;
|
||||
// WasmTypeList
|
||||
|
||||
#[test]
|
||||
fn test_simple_values() {
|
||||
// Simple values
|
||||
assert_eq!(<i32>::wasm_types(), [Type::I32]);
|
||||
assert_eq!(<i64>::wasm_types(), [Type::I64]);
|
||||
assert_eq!(<f32>::wasm_types(), [Type::F32]);
|
||||
assert_eq!(<f64>::wasm_types(), [Type::F64]);
|
||||
|
||||
// Multi values
|
||||
assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]);
|
||||
assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]);
|
||||
assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]);
|
||||
assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]);
|
||||
|
||||
// Mixed values
|
||||
// assert_eq!(<(i32, i64, f32, f64)>::wasm_types(), [Type::I32, Type::I64, Type::F32, Type::F64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_array() {
|
||||
assert_eq!(<()>::empty_array().len(), 0);
|
||||
assert_eq!(<i32>::empty_array().len(), 1);
|
||||
assert_eq!(<(i32, i64)>::empty_array().len(), 2);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_from_array() {
|
||||
// assert_eq!(<()>::from_array([]), ());
|
||||
// assert_eq!(<(i32)>::from_array([1]), (1));
|
||||
// assert_eq!(<(i32, i32)>::from_array([1, 1]), (1, 1));
|
||||
// // This doesn't work
|
||||
// // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64));
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_into_array() {
|
||||
// assert_eq!(().into_array(), []);
|
||||
// assert_eq!((1).into_array(), [1]);
|
||||
// assert_eq!((1, 2).into_array(), [1, 2]);
|
||||
// assert_eq!((1, 2, 3).into_array(), [1, 2, 3]);
|
||||
// // This doesn't work
|
||||
// // assert_eq!(<(i32, i64, f32, f64)>::from_array([1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()]), (1, 2, 3.1f32, 4.2f64));
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_into_c_struct() {
|
||||
// assert_eq!(<()>::into_c_struct(), &[]);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(test)]
|
||||
mod test_func {
|
||||
use super::*;
|
||||
use crate::types::Type;
|
||||
use std::ptr;
|
||||
// WasmTypeList
|
||||
|
||||
fn func() {}
|
||||
fn func__i32() -> i32 {
|
||||
0
|
||||
}
|
||||
fn func_i32(_a: i32) {}
|
||||
fn func_i32__i32(a: i32) -> i32 {
|
||||
a * 2
|
||||
}
|
||||
fn func_i32_i32__i32(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) {
|
||||
(a, b)
|
||||
}
|
||||
fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) {
|
||||
(b, a)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_types() {
|
||||
assert_eq!(Func::new(func).ty(), FunctionType::new(vec![], vec![]));
|
||||
assert_eq!(
|
||||
Func::new(func__i32).ty(),
|
||||
FunctionType::new(vec![], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Func::new(func_i32).ty(),
|
||||
FunctionType::new(vec![Type::I32], vec![])
|
||||
);
|
||||
assert_eq!(
|
||||
Func::new(func_i32__i32).ty(),
|
||||
FunctionType::new(vec![Type::I32], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Func::new(func_i32_i32__i32).ty(),
|
||||
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Func::new(func_i32_i32__i32_i32).ty(),
|
||||
FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32])
|
||||
);
|
||||
assert_eq!(
|
||||
Func::new(func_f32_i32__i32_f32).ty(),
|
||||
FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_pointer() {
|
||||
let f = Func::new(func_i32__i32);
|
||||
let function =
|
||||
unsafe { std::mem::transmute::<*const FunctionBody, fn(usize, i32) -> i32>(f.address) };
|
||||
assert_eq!(function(0, 3), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_call() {
|
||||
let f = Func::new(func_i32__i32);
|
||||
let x = |args: <(i32, i32) as WasmTypeList>::Array,
|
||||
rets: &mut <(i32, i32) as WasmTypeList>::Array| {
|
||||
let result = func_i32_i32__i32_i32(args[0] as _, args[1] as _);
|
||||
rets[0] = result.0 as _;
|
||||
rets[1] = result.1 as _;
|
||||
};
|
||||
let mut rets = <(i64, i64)>::empty_array();
|
||||
x([20, 10], &mut rets);
|
||||
// panic!("Rets: {:?}",rets);
|
||||
let mut rets = <(i64)>::empty_array();
|
||||
// let result = f.call([1], &mut rets);
|
||||
// assert_eq!(result.is_err(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Compile {
|
||||
.fold(CpuFeature::set(), |a, b| a | b);
|
||||
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
|
||||
// usage
|
||||
features = features | CpuFeature::SSE2;
|
||||
features |= CpuFeature::SSE2;
|
||||
Target::new(target_triple.clone(), features)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -41,7 +41,7 @@ impl Config {
|
||||
/// Runs logic for the `config` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
self.inner_execute()
|
||||
.context(format!("failed to retrieve the wasmer config"))
|
||||
.context("failed to retrieve the wasmer config".to_string())
|
||||
}
|
||||
fn inner_execute(&self) -> Result<()> {
|
||||
let key = "WASMER_DIR";
|
||||
@@ -64,7 +64,7 @@ impl Config {
|
||||
println!("exec_prefix={}", bindir);
|
||||
println!("includedir={}", includedir);
|
||||
println!("libdir={}", libdir);
|
||||
println!("");
|
||||
println!();
|
||||
println!("Name: wasmer");
|
||||
println!("Description: The Wasmer library for running WebAssembly");
|
||||
println!("Version: {}", VERSION);
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::warning;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use wasmer::*;
|
||||
#[cfg(feature = "cache")]
|
||||
use wasmer_cache::{Cache, FileSystemCache, WasmHash};
|
||||
@@ -79,10 +78,10 @@ impl Run {
|
||||
format!(
|
||||
"failed to run `{}`{}",
|
||||
self.path.display(),
|
||||
if compilers.len() > 0 {
|
||||
""
|
||||
} else {
|
||||
if compilers.is_empty() {
|
||||
" (no compilers enabled)"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -173,9 +172,8 @@ impl Run {
|
||||
#[cfg(feature = "native")]
|
||||
{
|
||||
if wasmer_engine_native::NativeArtifact::is_deserializable(&contents) {
|
||||
let tunables = Tunables::default();
|
||||
let engine = wasmer_engine_native::NativeEngine::headless(tunables);
|
||||
let store = Store::new(Arc::new(engine));
|
||||
let engine = wasmer_engine_native::Native::headless().engine();
|
||||
let store = Store::new(&engine);
|
||||
let module = unsafe { Module::deserialize_from_file(&store, &self.path)? };
|
||||
return Ok(module);
|
||||
}
|
||||
@@ -183,9 +181,8 @@ impl Run {
|
||||
#[cfg(feature = "jit")]
|
||||
{
|
||||
if wasmer_engine_jit::JITArtifact::is_deserializable(&contents) {
|
||||
let tunables = Tunables::default();
|
||||
let engine = wasmer_engine_jit::JITEngine::headless(tunables);
|
||||
let store = Store::new(Arc::new(engine));
|
||||
let engine = wasmer_engine_jit::JIT::headless().engine();
|
||||
let store = Store::new(&engine);
|
||||
let module = unsafe { Module::deserialize_from_file(&store, &self.path)? };
|
||||
return Ok(module);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::utils::{parse_envvar, parse_mapdir};
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use wasmer::{Instance, Module};
|
||||
use wasmer_wasi::{get_wasi_version, WasiState, WasiVersion};
|
||||
|
||||
@@ -59,7 +58,7 @@ impl Wasi {
|
||||
let import_object = wasi_env.import_object(&module)?;
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
wasi_env.set_memory(Arc::new(instance.exports.get_memory("memory")?.clone()));
|
||||
wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());
|
||||
|
||||
let start = instance.exports.get_function("_start")?;
|
||||
start
|
||||
|
||||
73
src/store.rs
73
src/store.rs
@@ -6,6 +6,7 @@ use anyhow::{Error, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::Arc;
|
||||
use structopt::StructOpt;
|
||||
use wasmer::*;
|
||||
@@ -174,22 +175,18 @@ impl StoreOptions {
|
||||
|
||||
/// Get the Compiler Config for the current options
|
||||
#[allow(unused_variables)]
|
||||
fn get_compiler_config(
|
||||
&self,
|
||||
target: Target,
|
||||
) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
|
||||
fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
|
||||
let compiler = self.get_compiler()?;
|
||||
let features = self.get_features()?;
|
||||
let compiler_config: Box<dyn CompilerConfig> = match compiler {
|
||||
CompilerType::Headless => bail!("The headless engine can't be chosen"),
|
||||
#[cfg(feature = "singlepass")]
|
||||
CompilerType::Singlepass => {
|
||||
let config = wasmer_compiler_singlepass::SinglepassConfig::new(features, target);
|
||||
let config = wasmer_compiler_singlepass::Singlepass::new();
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "cranelift")]
|
||||
CompilerType::Cranelift => {
|
||||
let config = wasmer_compiler_cranelift::CraneliftConfig::new(features, target);
|
||||
let config = wasmer_compiler_cranelift::Cranelift::new();
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "llvm")]
|
||||
@@ -198,10 +195,9 @@ impl StoreOptions {
|
||||
use std::io::Write;
|
||||
use wasm_common::entity::EntityRef;
|
||||
use wasmer_compiler_llvm::{
|
||||
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks,
|
||||
LLVMConfig,
|
||||
CompiledFunctionKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
|
||||
};
|
||||
let mut config = LLVMConfig::new(features, target);
|
||||
let mut config = LLVM::new();
|
||||
struct Callbacks {
|
||||
debug_dir: PathBuf,
|
||||
}
|
||||
@@ -278,7 +274,7 @@ impl StoreOptions {
|
||||
}
|
||||
}
|
||||
if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
|
||||
config.callbacks = Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?));
|
||||
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
@@ -291,11 +287,6 @@ impl StoreOptions {
|
||||
Ok((compiler_config, compiler))
|
||||
}
|
||||
|
||||
/// Gets the tunables for the compiler target
|
||||
pub fn get_tunables(&self, compiler_config: &dyn CompilerConfig) -> Tunables {
|
||||
Tunables::for_target(compiler_config.target().triple())
|
||||
}
|
||||
|
||||
/// Gets the store for the host target, with the engine name and compiler name selected
|
||||
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
|
||||
let target = Target::default();
|
||||
@@ -307,29 +298,34 @@ impl StoreOptions {
|
||||
&self,
|
||||
target: Target,
|
||||
) -> Result<(Store, EngineType, CompilerType)> {
|
||||
let (compiler_config, compiler_type) = self.get_compiler_config(target)?;
|
||||
let tunables = self.get_tunables(&*compiler_config);
|
||||
let (engine, engine_type) = self.get_engine_with_compiler(tunables, compiler_config)?;
|
||||
let store = Store::new(engine);
|
||||
let (compiler_config, compiler_type) = self.get_compiler_config()?;
|
||||
let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?;
|
||||
let store = Store::new(&*engine);
|
||||
Ok((store, engine_type, compiler_type))
|
||||
}
|
||||
|
||||
fn get_engine_with_compiler(
|
||||
&self,
|
||||
tunables: Tunables,
|
||||
compiler_config: Box<dyn CompilerConfig>,
|
||||
) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
|
||||
target: Target,
|
||||
mut compiler_config: Box<dyn CompilerConfig>,
|
||||
) -> Result<(Box<dyn Engine + Send + Sync>, EngineType)> {
|
||||
let engine_type = self.get_engine()?;
|
||||
let engine: Arc<dyn Engine + Send + Sync> = match engine_type {
|
||||
let features = self.get_features()?;
|
||||
let engine: Box<dyn Engine + Send + Sync> = match engine_type {
|
||||
#[cfg(feature = "jit")]
|
||||
EngineType::JIT => {
|
||||
Arc::new(wasmer_engine_jit::JITEngine::new(compiler_config, tunables))
|
||||
}
|
||||
EngineType::JIT => Box::new(
|
||||
wasmer_engine_jit::JIT::new(&*compiler_config)
|
||||
.features(features)
|
||||
.target(target)
|
||||
.engine(),
|
||||
),
|
||||
#[cfg(feature = "native")]
|
||||
EngineType::Native => Arc::new(wasmer_engine_native::NativeEngine::new(
|
||||
compiler_config,
|
||||
tunables,
|
||||
)),
|
||||
EngineType::Native => Box::new(
|
||||
wasmer_engine_native::Native::new(&mut *compiler_config)
|
||||
.target(target)
|
||||
.features(features)
|
||||
.engine(),
|
||||
),
|
||||
#[cfg(not(all(feature = "jit", feature = "native",)))]
|
||||
engine => bail!(
|
||||
"The `{}` engine is not included in this binary.",
|
||||
@@ -363,16 +359,13 @@ impl StoreOptions {
|
||||
// If we don't have a compiler, but we have an engine
|
||||
#[cfg(all(not(feature = "compiler"), feature = "engine"))]
|
||||
impl StoreOptions {
|
||||
fn get_engine_headless(
|
||||
&self,
|
||||
tunables: Tunables,
|
||||
) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
|
||||
fn get_engine_headless(&self) -> Result<(Arc<dyn Engine + Send + Sync>, EngineType)> {
|
||||
let engine_type = self.get_engine()?;
|
||||
let engine: Arc<dyn Engine + Send + Sync> = match engine_type {
|
||||
#[cfg(feature = "jit")]
|
||||
EngineType::JIT => Arc::new(wasmer_engine_jit::JITEngine::headless(tunables)),
|
||||
EngineType::JIT => Arc::new(wasmer_engine_jit::JIT::headless().engine()),
|
||||
#[cfg(feature = "native")]
|
||||
EngineType::Native => Arc::new(wasmer_engine_native::NativeEngine::headless(tunables)),
|
||||
EngineType::Native => Arc::new(wasmer_engine_native::Native::headless().engine()),
|
||||
#[cfg(not(all(feature = "jit", feature = "native",)))]
|
||||
engine => bail!(
|
||||
"The `{}` engine is not included in this binary.",
|
||||
@@ -384,10 +377,8 @@ impl StoreOptions {
|
||||
|
||||
/// Get the store (headless engine)
|
||||
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
|
||||
// Get the tunables for the current host
|
||||
let tunables = Tunables::default();
|
||||
let (engine, engine_type) = self.get_engine_headless(tunables)?;
|
||||
let store = Store::new(engine);
|
||||
let (engine, engine_type) = self.get_engine_headless()?;
|
||||
let store = Store::new(&engine);
|
||||
Ok((store, engine_type, CompilerType::Headless))
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
|
||||
/// Parses a mapdir from an env var
|
||||
pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
|
||||
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
|
||||
return Ok((env_var.to_string(), value.to_string()));
|
||||
Ok((env_var.to_string(), value.to_string()))
|
||||
} else {
|
||||
bail!(
|
||||
"Env vars must be of the form <var_name>=<value>. Found {}",
|
||||
|
||||
@@ -11,3 +11,9 @@ mod multi_value_imports;
|
||||
mod serialize;
|
||||
mod traps;
|
||||
mod utils;
|
||||
mod wasi;
|
||||
mod wast;
|
||||
|
||||
pub use crate::utils::get_compiler;
|
||||
pub use crate::wasi::run_wasi;
|
||||
pub use crate::wast::run_wast;
|
||||
|
||||
@@ -39,7 +39,7 @@ fn test_trap_return() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
|
||||
#[cfg_attr(feature = "test-singlepass", ignore)]
|
||||
fn test_trap_trace() -> Result<()> {
|
||||
let store = get_store();
|
||||
let wat = r#"
|
||||
@@ -119,7 +119,7 @@ fn test_trap_trace_cb() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
|
||||
#[cfg_attr(feature = "test-singlepass", ignore)]
|
||||
fn test_trap_stack_overflow() -> Result<()> {
|
||||
let store = get_store();
|
||||
let wat = r#"
|
||||
@@ -419,7 +419,7 @@ RuntimeError: indirect call type mismatch
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[cfg_attr(any(feature = "test-singlepass", feature = "test-llvm"), ignore)]
|
||||
#[cfg_attr(feature = "test-singlepass", ignore)]
|
||||
fn start_trap_pretty() -> Result<()> {
|
||||
let store = get_store();
|
||||
let wat = r#"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
use test_utils::get_compiler_config_from_str;
|
||||
use wasmer::{Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
|
||||
use wasmer_engine_jit::JITEngine;
|
||||
use wasmer::{CompilerConfig, Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
|
||||
use wasmer_engine::Engine;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
fn get_compiler_str() -> &'static str {
|
||||
pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
all(feature = "test-llvm", any(feature = "test-cranelift", feature = "test-singlepass")),
|
||||
@@ -11,44 +11,43 @@ fn get_compiler_str() -> &'static str {
|
||||
))] {
|
||||
compile_error!("Only one compiler can be selected")
|
||||
} else if #[cfg(feature = "test-cranelift")] {
|
||||
"cranelift"
|
||||
let mut compiler = wasmer_compiler_cranelift::Cranelift::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-llvm")] {
|
||||
"llvm"
|
||||
let mut compiler = wasmer_compiler_llvm::LLVM::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-singlepass")] {
|
||||
"singlepass"
|
||||
let mut compiler = wasmer_compiler_singlepass::Singlepass::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler
|
||||
} else {
|
||||
compile_error!("No compiler chosen for the tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_engine() -> impl Engine {
|
||||
let compiler_config = get_compiler(false);
|
||||
JIT::new(&compiler_config).engine()
|
||||
}
|
||||
|
||||
pub fn get_store() -> Store {
|
||||
let features = Features::default();
|
||||
let try_nan_canonicalization = false;
|
||||
let compiler_config =
|
||||
get_compiler_config_from_str(get_compiler_str(), try_nan_canonicalization, features);
|
||||
let tunables = Tunables::for_target(compiler_config.target().triple());
|
||||
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
|
||||
store
|
||||
Store::new(&get_engine())
|
||||
}
|
||||
|
||||
pub fn get_store_with_middlewares<I: Iterator<Item = Arc<dyn FunctionMiddlewareGenerator>>>(
|
||||
middlewares: I,
|
||||
) -> Store {
|
||||
let features = Features::default();
|
||||
let try_nan_canonicalization = false;
|
||||
let mut compiler_config =
|
||||
get_compiler_config_from_str(get_compiler_str(), try_nan_canonicalization, features);
|
||||
let mut compiler_config = get_compiler(false);
|
||||
for x in middlewares {
|
||||
compiler_config.push_middleware(x);
|
||||
}
|
||||
let tunables = Tunables::for_target(compiler_config.target().triple());
|
||||
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
|
||||
store
|
||||
let engine = JIT::new(&compiler_config).engine();
|
||||
Store::new(&engine)
|
||||
}
|
||||
|
||||
pub fn get_headless_store() -> Store {
|
||||
let tunables = Tunables::for_target(&Triple::host());
|
||||
let store = Store::new(Arc::new(JITEngine::headless(tunables)));
|
||||
store
|
||||
Store::new(&JIT::headless().engine())
|
||||
}
|
||||
|
||||
44
tests/compilers/wasi.rs
Normal file
44
tests/compilers/wasi.rs
Normal 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(())
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
#![cfg(all(feature = "compiler", feature = "engine"))]
|
||||
|
||||
use crate::utils::get_compiler;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use test_utils::get_compiler_config_from_str;
|
||||
use wasmer::{Features, Store, Tunables};
|
||||
use wasmer::{Features, Store};
|
||||
#[cfg(feature = "jit")]
|
||||
use wasmer_engine_jit::JITEngine;
|
||||
use wasmer_engine_jit::JIT;
|
||||
#[cfg(feature = "native")]
|
||||
use wasmer_engine_native::NativeEngine;
|
||||
use wasmer_wast::Wast;
|
||||
@@ -29,7 +28,7 @@ fn native_prefixer(bytes: &[u8]) -> String {
|
||||
format!("{}", hash.to_hex())
|
||||
}
|
||||
|
||||
fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
println!(
|
||||
"Running wast `{}` with the {} compiler",
|
||||
wast_path, compiler
|
||||
@@ -39,13 +38,13 @@ fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
if wast_path.contains("bulk-memory") {
|
||||
features.bulk_memory(true);
|
||||
}
|
||||
let compiler_config =
|
||||
get_compiler_config_from_str(compiler, try_nan_canonicalization, features);
|
||||
let tunables = Tunables::for_target(compiler_config.target().triple());
|
||||
let store = Store::new(Arc::new(JITEngine::new(compiler_config, tunables)));
|
||||
#[cfg(feature = "test-singlepass")]
|
||||
features.multi_value(false);
|
||||
let compiler_config = get_compiler(true);
|
||||
let store = Store::new(&JIT::new(&compiler_config).features(features).engine());
|
||||
// let mut native = NativeEngine::new(compiler_config, tunables);
|
||||
// native.set_deterministic_prefixer(native_prefixer);
|
||||
// let store = Store::new(Arc::new(native));
|
||||
// let store = Store::new(&native);
|
||||
let mut wast = Wast::new_with_spectest(store);
|
||||
if compiler == "singlepass" {
|
||||
// We don't support multivalue yet in singlepass
|
||||
@@ -7,10 +7,44 @@ singlepass on windows # Singlepass is not yet supported on Windows
|
||||
|
||||
## WASI
|
||||
|
||||
wasi::snapshot1::fd_read
|
||||
wasi::snapshot1::poll_oneoff
|
||||
wasi::snapshot1::fd_pread
|
||||
wasi::snapshot1::fd_close
|
||||
wasi::snapshot1::fd_allocate
|
||||
wasi::snapshot1::close_preopen_fd
|
||||
wasi::snapshot1::envvar
|
||||
### due to hard-coded direct calls into WASI for wasi unstable
|
||||
|
||||
wasitests::snapshot1::fd_read
|
||||
wasitests::snapshot1::poll_oneoff
|
||||
wasitests::snapshot1::fd_pread
|
||||
wasitests::snapshot1::fd_close
|
||||
wasitests::snapshot1::fd_allocate
|
||||
wasitests::snapshot1::close_preopen_fd
|
||||
wasitests::snapshot1::envvar
|
||||
|
||||
### TODO: resolve the disabled tests below. These are newly disabled tests from the migration:
|
||||
|
||||
### due to git clone not preserving symlinks:
|
||||
wasitests::snapshot1::readlink
|
||||
wasitests::unstable::readlink
|
||||
|
||||
### failing due to `remove_dir_all`. this test is also bad for parallelism
|
||||
wasitests::snapshot1::create_dir
|
||||
wasitests::unstable::create_dir
|
||||
|
||||
### failing because it closes `stdout` which breaks our testing system
|
||||
wasitests::unstable::fd_close
|
||||
|
||||
### failing because we're operating on stdout which is now overridden.
|
||||
### TODO: check WasiFile implementation
|
||||
### Alterative: split test into 2 parts, one printing to stderr, the other printing to stdout to test the real versions
|
||||
wasitests::unstable::poll_oneoff
|
||||
|
||||
## Failing due to different line endings on Windows
|
||||
## we need a better solution to this problem:
|
||||
|
||||
wasitests::snapshot1::file_metadata on windows
|
||||
wasitests::snapshot1::fseek on windows
|
||||
wasitests::snapshot1::path_link on windows
|
||||
wasitests::snapshot1::path_symlink on windows
|
||||
wasitests::unstable::fd_pread on windows
|
||||
wasitests::unstable::fd_read on windows
|
||||
wasitests::unstable::file_metadata on windows
|
||||
wasitests::unstable::fseek on windows
|
||||
wasitests::unstable::path_link on windows
|
||||
wasitests::unstable::path_symlink on windows
|
||||
|
||||
@@ -13,7 +13,7 @@ use wasm_common::{
|
||||
use wasmer_compiler::CompileError;
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::ModuleEnvironment;
|
||||
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError};
|
||||
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables};
|
||||
use wasmer_runtime::{
|
||||
MemoryPlan, ModuleInfo, TablePlan, VMContext, VMFunctionBody, VMSharedSignatureIndex,
|
||||
};
|
||||
@@ -55,9 +55,12 @@ impl DummyArtifact {
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
/// Compile a data buffer into a `DummyArtifact`, which may then be instantiated.
|
||||
pub fn new(engine: &DummyEngine, data: &[u8]) -> Result<Self, CompileError> {
|
||||
pub fn new(
|
||||
engine: &DummyEngine,
|
||||
data: &[u8],
|
||||
tunables: &dyn Tunables,
|
||||
) -> Result<Self, CompileError> {
|
||||
let environ = ModuleEnvironment::new();
|
||||
let tunables = engine.tunables();
|
||||
|
||||
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user