Merge branch 'master' into feat-interface-types-better-error

This commit is contained in:
Ivan Enderlin 2020-03-12 14:52:15 +01:00
commit 03910a51fb
44 changed files with 714 additions and 219 deletions

View File

@ -3,6 +3,19 @@
## **[Unreleased]**
- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types`
- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s.
## 0.16.2 - 2020-03-11
- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again.
## 0.16.1 - 2020-03-11
- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command.
## 0.16.0 - 2020-03-11
- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax
- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types`
- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None`

38
Cargo.lock generated
View File

@ -1820,7 +1820,7 @@ dependencies = [
[[package]]
name = "wasmer"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"atty",
"byteorder",
@ -1851,7 +1851,7 @@ dependencies = [
[[package]]
name = "wasmer-clif-backend"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"byteorder",
"cranelift-codegen",
@ -1902,14 +1902,14 @@ dependencies = [
[[package]]
name = "wasmer-dev-utils"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"libc",
]
[[package]]
name = "wasmer-emscripten"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"byteorder",
"getrandom",
@ -1922,7 +1922,7 @@ dependencies = [
[[package]]
name = "wasmer-emscripten-tests"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"glob 0.3.0",
"wabt",
@ -1936,7 +1936,7 @@ dependencies = [
[[package]]
name = "wasmer-interface-types"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"nom",
"wast",
@ -1952,7 +1952,7 @@ dependencies = [
[[package]]
name = "wasmer-llvm-backend"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"byteorder",
"cc",
@ -1983,14 +1983,14 @@ dependencies = [
[[package]]
name = "wasmer-middleware-common"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"wasmer-runtime-core",
]
[[package]]
name = "wasmer-middleware-common-tests"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"criterion",
"wabt",
@ -2003,7 +2003,7 @@ dependencies = [
[[package]]
name = "wasmer-runtime"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"criterion",
"lazy_static",
@ -2020,7 +2020,7 @@ dependencies = [
[[package]]
name = "wasmer-runtime-c-api"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"cbindgen",
"libc",
@ -2032,7 +2032,7 @@ dependencies = [
[[package]]
name = "wasmer-runtime-core"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"bincode",
"blake3",
@ -2060,7 +2060,7 @@ dependencies = [
[[package]]
name = "wasmer-runtime-core-tests"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"wabt",
"wasmer-clif-backend",
@ -2071,7 +2071,7 @@ dependencies = [
[[package]]
name = "wasmer-singlepass-backend"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"bincode",
"byteorder",
@ -2088,7 +2088,7 @@ dependencies = [
[[package]]
name = "wasmer-spectests"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"glob 0.3.0",
"wabt",
@ -2100,7 +2100,7 @@ dependencies = [
[[package]]
name = "wasmer-wasi"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"bincode",
"byteorder",
@ -2117,7 +2117,7 @@ dependencies = [
[[package]]
name = "wasmer-wasi-experimental-io-devices"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"log",
"minifb",
@ -2130,7 +2130,7 @@ dependencies = [
[[package]]
name = "wasmer-wasi-tests"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"glob 0.3.0",
"wasmer-clif-backend",
@ -2143,7 +2143,7 @@ dependencies = [
[[package]]
name = "wasmer-win-exception-handler"
version = "0.15.0"
version = "0.16.2"
dependencies = [
"cmake",
"libc",

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer"
version = "0.15.0"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"

View File

@ -287,7 +287,9 @@ build-install-package:
mkdir -p ./install/bin
cp ./wapm-cli/target/release/wapm ./install/bin/
cp ./target/release/wasmer ./install/bin/
tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer
# Create the wax binary as symlink to wapm
cd ./install/bin/ && ln -sf wapm wax && chmod +x wax
tar -C ./install -zcvf wasmer.tar.gz bin
UNAME_S := $(shell uname -s)
@ -315,7 +317,7 @@ endif
cp lib/runtime-c-api/doc/index.md ./capi/README.md
tar -C ./capi -zcvf wasmer-c-api.tar.gz lib include README.md LICENSE
WAPM_VERSION = 0.4.3
WAPM_VERSION = v0.5.0
build-wapm:
git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"

View File

@ -300,6 +300,7 @@ jobs:
isDraft: false
isPreRelease: false
assets: '$(Build.ArtifactStagingDirectory)/**'
assetUploadMode: 'replace' # Don't delete previously uploaded assets (default)
- job: Publish_Docs
dependsOn:

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-clif-backend"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime Cranelift compiler backend"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -11,7 +11,7 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
cranelift-native = "0.59.0"
cranelift-codegen = "0.59.0"
cranelift-entity = "0.59.0"
@ -38,7 +38,7 @@ version = "0.0.7"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.15.0" }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.16.2" }
[features]
generate-debug-information = ["wasm-debug"]

View File

@ -209,7 +209,7 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
Ok(())
}
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> {
Ok(())
}

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-dev-utils"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten-tests"
version = "0.15.0"
version = "0.16.2"
description = "Tests for our Emscripten implementation"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,15 +9,15 @@ publish = false
build = "build/mod.rs"
[dependencies]
wasmer-emscripten = { path = "../emscripten", version = "0.15.0" }
wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false }
wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true}
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", optional = true, features = ["test"] }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-emscripten = { path = "../emscripten", version = "0.16.2" }
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false }
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", optional = true, features = ["test"] }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
[dev-dependencies]
wabt = "0.9.1"
wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"}
wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"}
[build-dependencies]
glob = "0.3"

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime emscripten implementation library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -15,7 +15,7 @@ lazy_static = "1.4"
libc = "0.2.60"
log = "0.4"
time = "0.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
[target.'cfg(windows)'.dependencies]
getrandom = "0.1"

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-interface-types"
version = "0.15.0"
version = "0.16.2"
description = "WebAssembly Interface Types library for Wasmer"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]

View File

@ -9,8 +9,8 @@ edition = "2018"
[dependencies]
wabt = "0.9.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime = { path = "../runtime", version = "0.15.0" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"] }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"] }
[features]

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-llvm-backend"
version = "0.15.0"
version = "0.16.2"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
@ -10,7 +10,7 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0", features = ["generate-debug-information-no-export-symbols"] }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2", features = ["generate-debug-information-no-export-symbols"] }
wasmparser = "0.51.3"
smallvec = "0.6"
goblin = "0.0.24"

View File

@ -8984,7 +8984,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
Ok(())
}
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), CodegenError> {
self.func_import_count += 1;
Ok(())
}

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-middleware-common-tests"
version = "0.15.0"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
@ -8,11 +8,11 @@ license = "MIT"
publish = false
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-middleware-common = { path = "../middleware-common", version = "0.15.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
wasmer-middleware-common = { path = "../middleware-common", version = "0.16.2" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
[features]
clif = ["wasmer-clif-backend"]

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-middleware-common"
version = "0.15.0"
version = "0.16.2"
repository = "https://github.com/wasmerio/wasmer"
description = "Wasmer runtime common middlewares"
license = "MIT"
@ -10,4 +10,4 @@ categories = ["wasm"]
edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-c-api"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer C API library"
documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/"
license = "MIT"
@ -20,22 +20,22 @@ libc = "0.2.60"
[dependencies.wasmer-runtime]
default-features = false
path = "../runtime"
version = "0.15.0"
version = "0.16.2"
[dependencies.wasmer-runtime-core]
default-features = false
path = "../runtime-core"
version = "0.15.0"
version = "0.16.2"
[dependencies.wasmer-wasi]
default-features = false
path = "../wasi"
version = "0.15.0"
version = "0.16.2"
optional = true
[dependencies.wasmer-emscripten]
path = "../emscripten"
version = "0.15.0"
version = "0.16.2"
optional = true
[features]

View File

@ -34,6 +34,8 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline
}
/// Adds a callinfo trampoline to the builder.
///
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
@ -42,8 +44,14 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampolin
ctx: *const c_void,
num_params: u32,
) -> usize {
use wasmer_runtime_core::types::Type;
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
builder.add_callinfo_trampoline(mem::transmute(func), ctx as *const CallContext, num_params)
builder.add_callinfo_trampoline(
mem::transmute(func),
ctx as *const CallContext,
&vec![Type::I64; num_params as usize],
&[Type::I64],
)
}
/// Finalizes the trampoline builder into an executable buffer.

View File

@ -1386,6 +1386,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits)
#if (!defined(_WIN32) && defined(ARCH_X86_64))
/**
* Adds a callinfo trampoline to the builder.
*
* Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
*/
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
const wasmer_trampoline_callable_t *func,

View File

@ -1146,6 +1146,8 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits)
#if (!defined(_WIN32) && defined(ARCH_X86_64))
/// Adds a callinfo trampoline to the builder.
///
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder,
const wasmer_trampoline_callable_t *func,
const void *ctx,

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-core-tests"
version = "0.15.0"
version = "0.16.2"
description = "Tests for the Wasmer runtime core crate"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,10 +9,10 @@ publish = false
[dependencies]
wabt = "0.9.1"
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
[features]
default = ["backend-cranelift"]

View File

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{convert::TryInto, sync::Arc};
use wasmer_runtime_core::{
compile_with,
error::RuntimeError,
@ -12,10 +12,11 @@ use wasmer_runtime_core::{
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
macro_rules! call_and_assert {
($instance:ident, $function:ident, $expected_value:expr) => {
let $function: Func<i32, i32> = $instance.func(stringify!($function)).unwrap();
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
#[allow(unused_parens)]
let $function: Func<( $( $inputs ),* ), $output> = $instance.func(stringify!($function)).expect(concat!("Failed to get the `", stringify!($function), "` export function."));
let result = $function.call(1);
let result = $function.call( $( $arguments ),* );
match (result, $expected_value) {
(Ok(value), expected_value) => assert_eq!(
@ -75,7 +76,12 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
(import "env" "memory" (memory 1 1))
(import "env" "callback_fn" (func $callback_fn (type $type)))
(import "env" "callback_closure" (func $callback_closure (type $type)))
(import "env" "callback_closure_dynamic" (func $callback_closure_dynamic (type $type)))
(import "env" "callback_fn_dynamic" (func $callback_fn_dynamic (type $type)))
(import "env" "callback_closure_dynamic_0" (func $callback_closure_dynamic_0))
(import "env" "callback_closure_dynamic_1" (func $callback_closure_dynamic_1 (param i32) (result i32)))
(import "env" "callback_closure_dynamic_2" (func $callback_closure_dynamic_2 (param i32 i64) (result i64)))
(import "env" "callback_closure_dynamic_3" (func $callback_closure_dynamic_3 (param i32 i64 f32) (result f32)))
(import "env" "callback_closure_dynamic_4" (func $callback_closure_dynamic_4 (param i32 i64 f32 f64) (result f64)))
(import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type)))
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
(import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type)))
@ -94,9 +100,34 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
get_local 0
call $callback_closure)
(func (export "function_closure_dynamic") (type $type)
(func (export "function_fn_dynamic") (type $type)
get_local 0
call $callback_closure_dynamic)
call $callback_fn_dynamic)
(func (export "function_closure_dynamic_0")
call $callback_closure_dynamic_0)
(func (export "function_closure_dynamic_1") (param i32) (result i32)
get_local 0
call $callback_closure_dynamic_1)
(func (export "function_closure_dynamic_2") (param i32 i64) (result i64)
get_local 0
get_local 1
call $callback_closure_dynamic_2)
(func (export "function_closure_dynamic_3") (param i32 i64 f32) (result f32)
get_local 0
get_local 1
get_local 2
call $callback_closure_dynamic_3)
(func (export "function_closure_dynamic_4") (param i32 i64 f32 f64) (result f64)
get_local 0
get_local 1
get_local 2
get_local 3
call $callback_closure_dynamic_4)
(func (export "function_closure_with_env") (type $type)
get_local 0
@ -154,13 +185,73 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
Ok(n + 1)
}),
"callback_closure_dynamic" => DynamicFunc::new(
// Regular polymorphic function.
"callback_fn_dynamic" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
|_, params| -> Vec<Value> {
match params[0] {
Value::I32(x) => vec![Value::I32(x + 1)],
_ => unreachable!()
}
callback_fn_dynamic,
),
// Polymorphic closure “closures”.
"callback_closure_dynamic_0" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![], vec![])),
|_, inputs: &[Value]| -> Vec<Value> {
assert!(inputs.is_empty());
vec![]
}
),
"callback_closure_dynamic_1" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
assert_eq!(inputs.len(), 1);
let memory = vmctx.memory(0);
let shift_ = shift + memory.view::<i32>()[0].get();
let n: i32 = (&inputs[0]).try_into().unwrap();
vec![Value::I32(shift_ + n)]
}
),
"callback_closure_dynamic_2" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![Type::I32, Type::I64], vec![Type::I64])),
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
assert_eq!(inputs.len(), 2);
let memory = vmctx.memory(0);
let shift_ = shift + memory.view::<i32>()[0].get();
let i: i32 = (&inputs[0]).try_into().unwrap();
let j: i64 = (&inputs[1]).try_into().unwrap();
vec![Value::I64(shift_ as i64 + i as i64 + j)]
}
),
"callback_closure_dynamic_3" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32], vec![Type::F32])),
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
assert_eq!(inputs.len(), 3);
let memory = vmctx.memory(0);
let shift_ = shift + memory.view::<i32>()[0].get();
let i: i32 = (&inputs[0]).try_into().unwrap();
let j: i64 = (&inputs[1]).try_into().unwrap();
let k: f32 = (&inputs[2]).try_into().unwrap();
vec![Value::F32(shift_ as f32 + i as f32 + j as f32 + k)]
}
),
"callback_closure_dynamic_4" => DynamicFunc::new(
Arc::new(FuncSig::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![Type::F64])),
move |vmctx: &mut vm::Ctx, inputs: &[Value]| -> Vec<Value> {
assert_eq!(inputs.len(), 4);
let memory = vmctx.memory(0);
let shift_ = shift + memory.view::<i32>()[0].get();
let i: i32 = (&inputs[0]).try_into().unwrap();
let j: i64 = (&inputs[1]).try_into().unwrap();
let k: f32 = (&inputs[2]).try_into().unwrap();
let l: f64 = (&inputs[3]).try_into().unwrap();
vec![Value::F64(shift_ as f64 + i as f64 + j as f64 + k as f64 + l)]
}
),
@ -227,6 +318,13 @@ fn callback_fn(n: i32) -> Result<i32, ()> {
Ok(n + 1)
}
fn callback_fn_dynamic(_: &mut vm::Ctx, inputs: &[Value]) -> Vec<Value> {
match inputs[0] {
Value::I32(x) => vec![Value::I32(x + 1)],
_ => unreachable!(),
}
}
fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, ()> {
let memory = vmctx.memory(0);
let shift_: i32 = memory.view()[0].get();
@ -246,57 +344,82 @@ fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, Strin
}
macro_rules! test {
($test_name:ident, $function:ident, $expected_value:expr) => {
($test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
#[test]
fn $test_name() {
imported_functions_forms(&|instance| {
call_and_assert!(instance, $function, $expected_value);
call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value);
});
}
};
}
test!(test_fn, function_fn, Ok(2));
test!(test_closure, function_closure, Ok(2));
test!(test_closure_dynamic, function_closure_dynamic, Ok(2));
test!(test_fn, function_fn(i32) -> i32, (1) == Ok(2));
test!(test_closure, function_closure(i32) -> i32, (1) == Ok(2));
test!(test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
test!(
test_closure_dynamic_0,
function_closure_dynamic_0(()) -> (),
() == Ok(())
);
test!(
test_closure_dynamic_1,
function_closure_dynamic_1(i32) -> i32,
(1) == Ok(1 + shift + SHIFT)
);
test!(
test_closure_dynamic_2,
function_closure_dynamic_2(i32, i64) -> i64,
(1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64)
);
test!(
test_closure_dynamic_3,
function_closure_dynamic_3(i32, i64, f32) -> f32,
(1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32)
);
test!(
test_closure_dynamic_4,
function_closure_dynamic_4(i32, i64, f32, f64) -> f64,
(1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64)
);
test!(
test_closure_with_env,
function_closure_with_env,
Ok(2 + shift + SHIFT)
function_closure_with_env(i32) -> i32,
(1) == Ok(2 + shift + SHIFT)
);
test!(test_fn_with_vmctx, function_fn_with_vmctx, Ok(2 + SHIFT));
test!(test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
test!(
test_closure_with_vmctx,
function_closure_with_vmctx,
Ok(2 + SHIFT)
function_closure_with_vmctx(i32) -> i32,
(1) == Ok(2 + SHIFT)
);
test!(
test_closure_with_vmctx_and_env,
function_closure_with_vmctx_and_env,
Ok(2 + shift + SHIFT)
function_closure_with_vmctx_and_env(i32) -> i32,
(1) == Ok(2 + shift + SHIFT)
);
test!(
test_fn_trap,
function_fn_trap,
Err(RuntimeError(Box::new(format!("foo {}", 2))))
function_fn_trap(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("foo {}", 2))))
);
test!(
test_closure_trap,
function_closure_trap,
Err(RuntimeError(Box::new(format!("bar {}", 2))))
function_closure_trap(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("bar {}", 2))))
);
test!(
test_fn_trap_with_vmctx,
function_fn_trap_with_vmctx,
Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
function_fn_trap_with_vmctx(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
);
test!(
test_closure_trap_with_vmctx,
function_closure_trap_with_vmctx,
Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
function_closure_trap_with_vmctx(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
);
test!(
test_closure_trap_with_vmctx_and_env,
function_closure_trap_with_vmctx_and_env,
Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
function_closure_trap_with_vmctx_and_env(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
);

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-core"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -59,3 +59,5 @@ generate-debug-information = ["wasm-debug"]
# don't export symbols related to the GDB JIT interafce, LLVM or some other native
# code will be providing them
generate-debug-information-no-export-symbols = []
# enable DynamicFunc's for closures with captured environment.
dynamicfunc-fat-closures = []

View File

@ -143,7 +143,7 @@ pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule,
Ok(())
}
/// Adds an import function.
fn feed_import_function(&mut self) -> Result<(), E>;
fn feed_import_function(&mut self, _sigindex: SigIndex) -> Result<(), E>;
/// Sets the signatures.
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
/// Sets function signatures.

View File

@ -130,9 +130,10 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
let slice_full_len = index as usize + length as usize;
let memory_size = memory.size().bytes().0;
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|| length == 0
if (self.offset as usize) + (item_size * slice_full_len) > memory_size
|| self.offset as usize >= memory_size
|| mem::size_of::<T>() == 0
{
return None;
@ -167,9 +168,10 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
let slice_full_len = index as usize + length as usize;
let memory_size = memory.size().bytes().0;
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|| length == 0
|| self.offset as usize >= memory_size
|| mem::size_of::<T>() == 0
{
return None;
@ -190,7 +192,11 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
/// underlying data can be mutated if the Wasm is allowed to execute or
/// an aliasing `WasmPtr` is used to mutate memory.
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
if self.offset as usize + str_len as usize > memory.size().bytes().0 || str_len == 0 {
let memory_size = memory.size().bytes().0;
if self.offset as usize + str_len as usize > memory.size().bytes().0
|| self.offset as usize >= memory_size
{
return None;
}
let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
@ -271,15 +277,15 @@ mod test {
memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
let memory = memory::Memory::new(memory_descriptor).unwrap();
// test that basic access works and that len = 0 is caught correctly
// test that basic access works and that len = 0 works, but oob does not
let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
assert!(start_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_none());
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_none());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_none() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
@ -293,7 +299,8 @@ mod test {
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 3] = [(0, 0), (0, 2), (1, 1)];
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.into_iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
@ -323,7 +330,8 @@ mod test {
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 4] = [(0, 0), (1, 0), (0, 2), (1, 1)];
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.into_iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
@ -339,8 +347,6 @@ mod test {
for oob_end_array_ptr in end_wasm_ptr_array_oob_array.into_iter() {
assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
assert!(oob_end_array_ptr.deref(&memory, 0, 0).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 0).is_none() });
assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
}

View File

@ -6,8 +6,8 @@ use crate::{
backend::{CompilerConfig, RunnableModule},
error::CompileError,
module::{
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
TableInitializer,
DataInitializer, ExportIndex, ImportName, ModuleInfo, NameIndex, NamespaceIndex,
StringTable, StringTableBuilder, TableInitializer,
},
structures::{Map, TypedIndex},
types::{
@ -110,11 +110,36 @@ pub fn read_module<
let mut namespace_builder = Some(StringTableBuilder::new());
let mut name_builder = Some(StringTableBuilder::new());
let mut func_count: usize = 0;
let mut mcg_info_fed = false;
let mut feed_mcg_signatures: Option<_> = Some(|mcg: &mut MCG| -> Result<(), LoadError> {
let info_read = info.read().unwrap();
mcg.feed_signatures(info_read.signatures.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
Ok(())
});
let mut feed_mcg_info: Option<_> = Some(
|mcg: &mut MCG,
ns_builder: StringTableBuilder<NamespaceIndex>,
name_builder: StringTableBuilder<NameIndex>|
-> Result<(), LoadError> {
{
let mut info_write = info.write().unwrap();
info_write.namespace_table = ns_builder.finish();
info_write.name_table = name_builder.finish();
}
let info_read = info.read().unwrap();
mcg.feed_function_signatures(info_read.func_assoc.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
mcg.check_precondition(&info_read)
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
Ok(())
},
);
loop {
use wasmparser::ParserState;
let state = parser.read();
match *state {
ParserState::Error(ref err) => return Err(err.clone().into()),
ParserState::TypeSectionEntry(ref ty) => {
@ -124,6 +149,10 @@ pub fn read_module<
.push(func_type_to_func_sig(ty)?);
}
ParserState::ImportSectionEntry { module, field, ty } => {
if let Some(f) = feed_mcg_signatures.take() {
f(mcg)?;
}
let namespace_index = namespace_builder.as_mut().unwrap().register(module);
let name_index = name_builder.as_mut().unwrap().register(field);
let import_name = ImportName {
@ -136,7 +165,7 @@ pub fn read_module<
let sigindex = SigIndex::new(sigindex as usize);
info.write().unwrap().imported_functions.push(import_name);
info.write().unwrap().func_assoc.push(sigindex);
mcg.feed_import_function()
mcg.feed_import_function(sigindex)
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
}
ImportSectionEntryType::Table(table_ty) => {
@ -217,23 +246,17 @@ pub fn read_module<
info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize));
}
ParserState::BeginFunctionBody { range } => {
let id = func_count;
if !mcg_info_fed {
mcg_info_fed = true;
{
let mut info_write = info.write().unwrap();
info_write.namespace_table = namespace_builder.take().unwrap().finish();
info_write.name_table = name_builder.take().unwrap().finish();
}
let info_read = info.read().unwrap();
mcg.feed_signatures(info_read.signatures.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
mcg.feed_function_signatures(info_read.func_assoc.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
mcg.check_precondition(&info_read)
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
if let Some(f) = feed_mcg_signatures.take() {
f(mcg)?;
}
if let Some(f) = feed_mcg_info.take() {
f(
mcg,
namespace_builder.take().unwrap(),
name_builder.take().unwrap(),
)?;
}
let id = func_count;
let fcg = mcg
.next_function(
Arc::clone(&info),
@ -432,17 +455,15 @@ pub fn read_module<
info.write().unwrap().globals.push(global_init);
}
ParserState::EndWasm => {
// TODO Consolidate with BeginFunction body if possible
if !mcg_info_fed {
info.write().unwrap().namespace_table =
namespace_builder.take().unwrap().finish();
info.write().unwrap().name_table = name_builder.take().unwrap().finish();
mcg.feed_signatures(info.read().unwrap().signatures.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
mcg.feed_function_signatures(info.read().unwrap().func_assoc.clone())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
mcg.check_precondition(&info.read().unwrap())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
if let Some(f) = feed_mcg_signatures.take() {
f(mcg)?;
}
if let Some(f) = feed_mcg_info.take() {
f(
mcg,
namespace_builder.take().unwrap(),
name_builder.take().unwrap(),
)?;
}
break;
}

View File

@ -480,10 +480,11 @@ impl InstanceImage {
}
}
/// Declarations for x86-64 registers.
/// X64-specific structures and methods that do not depend on an x64 machine to run.
#[cfg(unix)]
pub mod x64_decl {
use super::*;
use crate::types::Type;
/// General-purpose registers.
#[repr(u8)]
@ -610,9 +611,88 @@ pub mod x64_decl {
_ => return None,
})
}
/// Returns the instruction prefix for `movq %this_reg, ?(%rsp)`.
///
/// To build an instruction, append the memory location as a 32-bit
/// offset to the stack pointer to this prefix.
pub fn prefix_mov_to_stack(&self) -> Option<&'static [u8]> {
Some(match *self {
X64Register::GPR(gpr) => match gpr {
GPR::RDI => &[0x48, 0x89, 0xbc, 0x24],
GPR::RSI => &[0x48, 0x89, 0xb4, 0x24],
GPR::RDX => &[0x48, 0x89, 0x94, 0x24],
GPR::RCX => &[0x48, 0x89, 0x8c, 0x24],
GPR::R8 => &[0x4c, 0x89, 0x84, 0x24],
GPR::R9 => &[0x4c, 0x89, 0x8c, 0x24],
_ => return None,
},
X64Register::XMM(xmm) => match xmm {
XMM::XMM0 => &[0x66, 0x0f, 0xd6, 0x84, 0x24],
XMM::XMM1 => &[0x66, 0x0f, 0xd6, 0x8c, 0x24],
XMM::XMM2 => &[0x66, 0x0f, 0xd6, 0x94, 0x24],
XMM::XMM3 => &[0x66, 0x0f, 0xd6, 0x9c, 0x24],
XMM::XMM4 => &[0x66, 0x0f, 0xd6, 0xa4, 0x24],
XMM::XMM5 => &[0x66, 0x0f, 0xd6, 0xac, 0x24],
XMM::XMM6 => &[0x66, 0x0f, 0xd6, 0xb4, 0x24],
XMM::XMM7 => &[0x66, 0x0f, 0xd6, 0xbc, 0x24],
_ => return None,
},
})
}
}
/// An allocator that allocates registers for function arguments according to the System V ABI.
#[derive(Default)]
pub struct ArgumentRegisterAllocator {
n_gprs: usize,
n_xmms: usize,
}
impl ArgumentRegisterAllocator {
/// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type.
pub fn next(&mut self, ty: Type) -> Option<X64Register> {
static GPR_SEQ: &'static [GPR] =
&[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9];
static XMM_SEQ: &'static [XMM] = &[
XMM::XMM0,
XMM::XMM1,
XMM::XMM2,
XMM::XMM3,
XMM::XMM4,
XMM::XMM5,
XMM::XMM6,
XMM::XMM7,
];
match ty {
Type::I32 | Type::I64 => {
if self.n_gprs < GPR_SEQ.len() {
let gpr = GPR_SEQ[self.n_gprs];
self.n_gprs += 1;
Some(X64Register::GPR(gpr))
} else {
None
}
}
Type::F32 | Type::F64 => {
if self.n_xmms < XMM_SEQ.len() {
let xmm = XMM_SEQ[self.n_xmms];
self.n_xmms += 1;
Some(X64Register::XMM(xmm))
} else {
None
}
}
_ => todo!(
"ArgumentRegisterAllocator::next: Unsupported type: {:?}",
ty
),
}
}
}
}
/// X64-specific structures and methods that only work on an x64 machine.
#[cfg(unix)]
pub mod x64 {
//! The x64 state module contains functions to generate state and code for x64 targets.

View File

@ -7,6 +7,8 @@
//! Variadic functions are not supported because `rax` is used by the trampoline code.
use crate::loader::CodeMemory;
use crate::state::x64_decl::ArgumentRegisterAllocator;
use crate::types::Type;
use crate::vm::Ctx;
use std::collections::BTreeMap;
use std::fmt;
@ -246,44 +248,50 @@ impl TrampolineBufferBuilder {
&mut self,
target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64,
context: *const CallContext,
num_params: u32,
params: &[Type],
_returns: &[Type],
) -> usize {
let idx = self.offsets.len();
self.offsets.push(self.code.len());
let mut stack_offset: u32 = num_params.checked_mul(8).unwrap();
let mut stack_offset: u32 = params.len().checked_mul(8).unwrap() as u32;
if stack_offset % 16 == 0 {
stack_offset += 8;
}
self.code.extend_from_slice(&[0x48, 0x81, 0xec]); // sub ?, %rsp
self.code.extend_from_slice(value_to_bytes(&stack_offset));
for i in 0..num_params {
match i {
0..=5 => {
// mov %?, ?(%rsp)
let prefix: &[u8] = match i {
0 => &[0x48, 0x89, 0xbc, 0x24], // rdi
1 => &[0x48, 0x89, 0xb4, 0x24], // rsi
2 => &[0x48, 0x89, 0x94, 0x24], // rdx
3 => &[0x48, 0x89, 0x8c, 0x24], // rcx
4 => &[0x4c, 0x89, 0x84, 0x24], // r8
5 => &[0x4c, 0x89, 0x8c, 0x24], // r9
_ => unreachable!(),
};
let mut allocator = ArgumentRegisterAllocator::default();
let mut source_stack_count: u32 = 0; // # of allocated slots in the source stack.
for (i, ty) in params.iter().enumerate() {
match allocator.next(*ty) {
Some(reg) => {
// This argument is allocated to a register.
let prefix = reg
.prefix_mov_to_stack()
.expect("cannot get instruction prefix for argument register");
self.code.extend_from_slice(prefix);
self.code.extend_from_slice(value_to_bytes(&(i * 8u32)));
self.code
.extend_from_slice(value_to_bytes(&((i as u32) * 8u32)));
}
_ => {
None => {
// This argument is allocated to the stack.
self.code.extend_from_slice(&[
0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax
]);
self.code.extend_from_slice(value_to_bytes(
&((i - 6) * 8u32 + stack_offset + 8/* ret addr */),
&(source_stack_count * 8u32 + stack_offset + 8/* ret addr */),
));
// mov %rax, ?(%rsp)
self.code.extend_from_slice(&[0x48, 0x89, 0x84, 0x24]);
self.code.extend_from_slice(value_to_bytes(&(i * 8u32)));
self.code
.extend_from_slice(value_to_bytes(&((i as u32) * 8u32)));
source_stack_count += 1;
}
}
}
@ -395,8 +403,13 @@ mod tests {
}
let mut builder = TrampolineBufferBuilder::new();
let ctx = TestContext { value: 100 };
let idx =
builder.add_callinfo_trampoline(do_add, &ctx as *const TestContext as *const _, 8);
let param_types: Vec<Type> = vec![Type::I32; 8];
let idx = builder.add_callinfo_trampoline(
do_add,
&ctx as *const TestContext as *const _,
&param_types,
&[Type::I32],
);
let buf = builder.build();
let t = buf.get_trampoline(idx);
let ret = unsafe {
@ -407,9 +420,49 @@ mod tests {
assert_eq!(ret, 136);
}
#[test]
fn test_trampolines_with_floating_point() {
unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 {
// `n` is not really a pointer. It is the length of the argument list, casted into the pointer type.
let n = n as usize;
let mut result: u64 = 0;
for i in 0..n {
result += *args.offset(i as _);
}
result
}
let buffer = TrampBuffer::new(4096);
let mut builder = TrampolineBufferBuilder::new();
builder.add_callinfo_trampoline(
inner,
8 as _,
&[
Type::I32,
Type::I32,
Type::I32,
Type::F32,
Type::I32,
Type::I32,
Type::I32,
Type::I32,
],
&[Type::I32],
);
let ptr = buffer.insert(builder.code()).unwrap();
let ret = unsafe {
let f = std::mem::transmute::<
_,
extern "C" fn(i32, i32, i32, f32, i32, i32, i32, i32) -> i32,
>(ptr);
f(1, 2, 3, f32::from_bits(4), 5, 6, 7, 8)
};
assert_eq!(ret, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8);
}
#[test]
fn test_many_global_trampolines() {
unsafe extern "C" fn inner(n: *const CallContext, args: *const u64) -> u64 {
// `n` is not really a pointer. It is the length of the argument list, casted into the pointer type.
let n = n as usize;
let mut result: u64 = 0;
for i in 0..n {
@ -427,7 +480,8 @@ mod tests {
for i in 0..5000usize {
let mut builder = TrampolineBufferBuilder::new();
let n = i % 8;
builder.add_callinfo_trampoline(inner, n as _, n as _);
let param_types: Vec<_> = (0..n).map(|_| Type::I64).collect();
builder.add_callinfo_trampoline(inner, n as _, &param_types, &[Type::I64]);
let ptr = buffer
.insert(builder.code())
.expect("cannot insert new code into global buffer");

View File

@ -306,16 +306,15 @@ impl<'a> DynamicFunc<'a> {
{
use crate::trampoline_x64::{CallContext, TrampolineBufferBuilder};
use crate::types::Value;
use std::convert::TryFrom;
struct PolymorphicContext {
arg_types: Vec<Type>,
func: Box<dyn Fn(&mut vm::Ctx, &[Value]) -> Vec<Value>>,
}
unsafe extern "C" fn enter_host_polymorphic(
unsafe fn do_enter_host_polymorphic(
ctx: *const CallContext,
args: *const u64,
) -> u64 {
) -> Vec<Value> {
let ctx = &*(ctx as *const PolymorphicContext);
let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx);
let args: Vec<Value> = ctx
@ -335,13 +334,40 @@ impl<'a> DynamicFunc<'a> {
}
})
.collect();
let rets = (ctx.func)(vmctx, &args);
(ctx.func)(vmctx, &args)
}
unsafe extern "C" fn enter_host_polymorphic_i(
ctx: *const CallContext,
args: *const u64,
) -> u64 {
let rets = do_enter_host_polymorphic(ctx, args);
if rets.len() == 0 {
0
} else if rets.len() == 1 {
u64::try_from(rets[0].to_u128()).expect(
"128-bit return value from polymorphic host functions is not yet supported",
)
match rets[0] {
Value::I32(x) => x as u64,
Value::I64(x) => x as u64,
_ => panic!("enter_host_polymorphic_i: invalid return type"),
}
} else {
panic!(
"multiple return values from polymorphic host functions is not yet supported"
);
}
}
unsafe extern "C" fn enter_host_polymorphic_f(
ctx: *const CallContext,
args: *const u64,
) -> f64 {
let rets = do_enter_host_polymorphic(ctx, args);
if rets.len() == 0 {
0.0
} else if rets.len() == 1 {
match rets[0] {
Value::F32(x) => f64::from_bits(x.to_bits() as u64),
Value::F64(x) => x,
_ => panic!("enter_host_polymorphic_f: invalid return type"),
}
} else {
panic!(
"multiple return values from polymorphic host functions is not yet supported"
@ -349,9 +375,8 @@ impl<'a> DynamicFunc<'a> {
}
}
// Disable "fat" closures for possible future changes.
if mem::size_of::<F>() != 0 {
unimplemented!("DynamicFunc with captured environment is not yet supported");
if cfg!(not(feature = "dynamicfunc-fat-closures")) && mem::size_of::<F>() != 0 {
unimplemented!("DynamicFunc with captured environment is disabled");
}
let mut builder = TrampolineBufferBuilder::new();
@ -360,11 +385,29 @@ impl<'a> DynamicFunc<'a> {
func: Box::new(func),
});
let ctx = Box::into_raw(ctx);
builder.add_callinfo_trampoline(
enter_host_polymorphic,
ctx as *const _,
(signature.params().len() + 1) as u32, // +vmctx
);
let mut native_param_types = vec![Type::I64]; // vm::Ctx is the first parameter.
native_param_types.extend_from_slice(signature.params());
match signature.returns() {
[x] if *x == Type::F32 || *x == Type::F64 => {
builder.add_callinfo_trampoline(
unsafe { std::mem::transmute(enter_host_polymorphic_f as usize) },
ctx as *const _,
&native_param_types,
signature.returns(),
);
}
_ => {
builder.add_callinfo_trampoline(
enter_host_polymorphic_i,
ctx as *const _,
&native_param_types,
signature.returns(),
);
}
}
let ptr = builder
.insert_global()
.expect("cannot bump-allocate global trampoline memory");

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -11,17 +11,17 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
lazy_static = "1.4"
memmap = "0.7"
[dependencies.wasmer-runtime-core]
path = "../runtime-core"
version = "0.15.0"
version = "0.16.2"
[dependencies.wasmer-clif-backend]
path = "../clif-backend"
version = "0.15.0"
version = "0.16.2"
optional = true
# Dependencies for caching.

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-singlepass-backend"
version = "0.15.0"
version = "0.16.2"
repository = "https://github.com/wasmerio/wasmer"
description = "Wasmer runtime single pass compiler backend"
license = "MIT"
@ -11,7 +11,7 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
dynasm = "0.5"
dynasmrt = "0.5"
lazy_static = "1.4"

View File

@ -32,8 +32,9 @@ use wasmer_runtime_core::{
memory::MemoryType,
module::{ModuleInfo, ModuleInner},
state::{
x64::new_machine_state, x64::X64Register, FunctionStateMap, MachineState, MachineValue,
ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue,
x64::new_machine_state, x64::X64Register, x64_decl::ArgumentRegisterAllocator,
FunctionStateMap, MachineState, MachineValue, ModuleStateMap, OffsetInfo, SuspendOffset,
WasmAbstractValue,
},
structures::{Map, TypedIndex},
typed_func::{Trampoline, Wasm},
@ -204,6 +205,7 @@ pub struct X64FunctionCode {
signatures: Arc<Map<SigIndex, FuncSig>>,
function_signatures: Arc<Map<FuncIndex, SigIndex>>,
signature: FuncSig,
fsm: FunctionStateMap,
offset: usize,
@ -712,11 +714,22 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
machine.track_state = self.config.as_ref().unwrap().track_state;
assembler.emit_label(begin_label);
let signatures = self.signatures.as_ref().unwrap();
let function_signatures = self.function_signatures.as_ref().unwrap();
let sig_index = function_signatures
.get(FuncIndex::new(
self.functions.len() + self.func_import_count,
))
.unwrap()
.clone();
let sig = signatures.get(sig_index).unwrap().clone();
let code = X64FunctionCode {
local_function_id: self.functions.len(),
signatures: self.signatures.as_ref().unwrap().clone(),
function_signatures: self.function_signatures.as_ref().unwrap().clone(),
signatures: signatures.clone(),
function_signatures: function_signatures.clone(),
signature: sig,
fsm: FunctionStateMap::new(new_machine_state(), self.functions.len(), 32, vec![]), // only a placeholder; this is initialized later in `begin_body`
offset: begin_offset.0,
@ -869,7 +882,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
Ok(())
}
fn feed_import_function(&mut self) -> Result<(), CodegenError> {
fn feed_import_function(&mut self, sigindex: SigIndex) -> Result<(), CodegenError> {
let labels = self.function_labels.as_mut().unwrap();
let id = labels.len();
@ -880,6 +893,92 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
a.emit_label(label);
labels.insert(id, (label, Some(offset)));
// Singlepass internally treats all arguments as integers, but the standard System V calling convention requires
// floating point arguments to be passed in XMM registers.
//
// FIXME: This is only a workaround. We should fix singlepass to use the standard CC.
let sig = self
.signatures
.as_ref()
.expect("signatures itself")
.get(sigindex)
.expect("signatures");
// Translation is expensive, so only do it if needed.
if sig
.params()
.iter()
.find(|&&x| x == Type::F32 || x == Type::F64)
.is_some()
{
let mut param_locations: Vec<Location> = vec![];
// Allocate stack space for arguments.
let stack_offset: i32 = if sig.params().len() > 5 {
5 * 8
} else {
(sig.params().len() as i32) * 8
};
if stack_offset > 0 {
a.emit_sub(
Size::S64,
Location::Imm32(stack_offset as u32),
Location::GPR(GPR::RSP),
);
}
// Store all arguments to the stack to prevent overwrite.
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];
let loc = Location::Memory(GPR::RSP, (i * 8) as i32);
a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc);
loc
}
_ => Location::Memory(GPR::RSP, stack_offset + 8 + ((i - 5) * 8) as i32),
};
param_locations.push(loc);
}
// Copy arguments.
let mut argalloc = ArgumentRegisterAllocator::default();
argalloc.next(Type::I32).unwrap(); // skip vm::Ctx
let mut caller_stack_offset: i32 = 0;
for (i, ty) in sig.params().iter().enumerate() {
let prev_loc = param_locations[i];
let target = match argalloc.next(*ty) {
Some(X64Register::GPR(gpr)) => Location::GPR(gpr),
Some(X64Register::XMM(xmm)) => Location::XMM(xmm),
None => {
// No register can be allocated. Put this argument on the stack.
//
// Since here we never use fewer registers than by the original call, on the caller's frame
// we always have enough space to store the rearranged arguments, and the copy "backward" between different
// slots in the caller argument region will always work.
a.emit_mov(Size::S64, prev_loc, Location::GPR(GPR::RAX));
a.emit_mov(
Size::S64,
Location::GPR(GPR::RAX),
Location::Memory(GPR::RSP, stack_offset + 8 + caller_stack_offset),
);
caller_stack_offset += 8;
continue;
}
};
a.emit_mov(Size::S64, prev_loc, target);
}
// Restore stack pointer.
if stack_offset > 0 {
a.emit_add(
Size::S64,
Location::Imm32(stack_offset as u32),
Location::GPR(GPR::RSP),
);
}
}
// Emits a tail call trampoline that loads the address of the target import function
// from Ctx and jumps to it.
@ -6260,7 +6359,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
false,
)[0];
self.value_stack.push(ret);
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
match return_types[0] {
WpType::F32 | WpType::F64 => {
a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret);
}
_ => {
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
}
}
}
}
Operator::CallIndirect { index, table_index } => {
@ -6399,7 +6505,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
false,
)[0];
self.value_stack.push(ret);
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
match return_types[0] {
WpType::F32 | WpType::F64 => {
a.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret);
}
_ => {
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret);
}
}
}
}
Operator::If { ty } => {
@ -7614,6 +7727,18 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
self.machine.finalize_locals(a, &self.locals);
a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP));
a.emit_pop(Size::S64, Location::GPR(GPR::RBP));
// Make a copy of the return value in XMM0, as required by the SysV CC.
match self.signature.returns() {
[x] if *x == Type::F32 || *x == Type::F64 => {
a.emit_mov(
Size::S64,
Location::GPR(GPR::RAX),
Location::XMM(XMM::XMM0),
);
}
_ => {}
}
a.emit_ret();
} else {
let released = &self.value_stack[frame.value_stack_depth..];

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-spectests"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer spectests library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,10 +9,10 @@ edition = "2018"
[dependencies]
glob = "0.3"
wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false}
wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true}
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false}
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
[build-dependencies]
wabt = "0.9.1"

View File

@ -256,6 +256,16 @@ mod tests {
Memory, Table,
};
fn format_panic(e: &dyn std::any::Any) -> String {
if let Some(s) = e.downcast_ref::<&str>() {
format!("{}", s)
} else if let Some(s) = e.downcast_ref::<String>() {
format!("{}", s)
} else {
"(unknown)".into()
}
}
fn parse_and_run(
path: &PathBuf,
file_excludes: &HashSet<String>,
@ -342,7 +352,7 @@ mod tests {
file: filename.to_string(),
line: line,
kind: format!("{}", "Module"),
message: format!("caught panic {:?}", e),
message: format!("caught panic {}", format_panic(&e)),
},
&test_key,
excludes,
@ -798,7 +808,7 @@ mod tests {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertInvalid"),
message: format!("caught panic {:?}", p),
message: format!("caught panic {}", format_panic(&p)),
},
&test_key,
excludes,
@ -851,7 +861,7 @@ mod tests {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertMalformed"),
message: format!("caught panic {:?}", p),
message: format!("caught panic {}", format_panic(&p)),
},
&test_key,
excludes,
@ -975,7 +985,7 @@ mod tests {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertUnlinkable"),
message: format!("caught panic {:?}", e),
message: format!("caught panic {}", format_panic(&e)),
},
&test_key,
excludes,

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-wasi-experimental-io-devices"
version = "0.15.0"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
@ -14,8 +14,8 @@ maintenance = { status = "experimental" }
[dependencies]
log = "0.4"
minifb = "0.13"
wasmer-wasi = { version = "0.15.0", path = "../wasi" }
wasmer-runtime-core = { version = "0.15.0", path = "../runtime-core" }
wasmer-wasi = { version = "0.16.2", path = "../wasi" }
wasmer-runtime-core = { version = "0.16.2", path = "../runtime-core" }
ref_thread_local = "0.0"
serde = "1"
typetag = "0.1"

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-wasi-tests"
version = "0.15.0"
version = "0.16.2"
description = "Tests for our WASI implementation"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -10,18 +10,18 @@ build = "build/mod.rs"
[dependencies]
# We set default features to false to be able to use the singlepass backend properly
wasmer-runtime = { path = "../runtime", version = "0.15.0", default-features = false }
wasmer-wasi = { path = "../wasi", version = "0.15.0" }
wasmer-runtime = { path = "../runtime", version = "0.16.2", default-features = false }
wasmer-wasi = { path = "../wasi", version = "0.16.2" }
# hack to get tests to work
wasmer-clif-backend = { path = "../clif-backend", version = "0.15.0", optional = true}
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.15.0", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.15.0", features = ["test"], optional = true }
wasmer-clif-backend = { path = "../clif-backend", version = "0.16.2", optional = true}
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.16.2", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.16.2", features = ["test"], optional = true }
[build-dependencies]
glob = "0.3"
[dev-dependencies]
wasmer-dev-utils = { path = "../dev-utils", version = "0.15.0"}
wasmer-dev-utils = { path = "../dev-utils", version = "0.16.2"}
[features]
clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"]

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-wasi"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime WASI implementation library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -19,7 +19,7 @@ getrandom = "0.1"
time = "0.1"
typetag = "0.1"
serde = { version = "1", features = ["derive"] }
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-win-exception-handler"
version = "0.15.0"
version = "0.16.2"
description = "Wasmer runtime exception handling for Windows"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[target.'cfg(windows)'.dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.15.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.16.2" }
winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
libc = "0.2.60"

View File

@ -1,5 +1,5 @@
PREVIOUS_VERSION='0.14.1'
NEXT_VERSION='0.15.0'
PREVIOUS_VERSION='0.16.1'
NEXT_VERSION='0.16.2'
# quick hack
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,6 +1,6 @@
[Setup]
AppName=Wasmer
AppVersion=0.15.0
AppVersion=0.16.2
DefaultDirName={pf}\Wasmer
DefaultGroupName=Wasmer
Compression=lzma2
@ -23,6 +23,7 @@ Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName: "WASMER_CACHE_DI
[Files]
Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin"
Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin"
Source: "wax.cmd"; DestDir: "{app}\bin"
[Dirs]
Name: "{%USERPROFILE}\.wasmer"

2
src/installer/wax.cmd Normal file
View File

@ -0,0 +1,2 @@
@echo off
wapm.exe execute %*