mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-19 01:20:51 +00:00
Merge branch 'master' into ventuzelo/fix-857-panic-loader-parameter
This commit is contained in:
commit
006bce0ca1
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,11 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
All PRs to the Wasmer repository must add to this file.
|
||||
|
||||
Blocks of changes will separated by version increments.
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#921](https://github.com/wasmerio/wasmer/pull/921) In LLVM backend, annotate all memory accesses with TBAA metadata.
|
||||
- [#883](https://github.com/wasmerio/wasmer/pull/883) Allow floating point operations to have arbitrary inputs, even including SNaNs.
|
||||
- [#856](https://github.com/wasmerio/wasmer/pull/856) Expose methods in the runtime C API to get a WASI import object
|
||||
|
||||
## 0.9.0 - 2019-10-23
|
||||
|
||||
Special thanks to @alocquet for their contributions!
|
||||
|
||||
- [#898](https://github.com/wasmerio/wasmer/pull/898) State tracking is now disabled by default in the LLVM backend. It can be enabled with `--track-state`.
|
||||
- [#861](https://github.com/wasmerio/wasmer/pull/861) Add descriptions to `unimplemented!` macro in various places
|
||||
- [#897](https://github.com/wasmerio/wasmer/pull/897) Removes special casing of stdin, stdout, and stderr in WASI. Closing these files now works. Removes `stdin`, `stdout`, and `stderr` from `WasiFS`, replaced by the methods `stdout`, `stdout_mut`, and so on.
|
||||
- [#863](https://github.com/wasmerio/wasmer/pull/863) Fix min and max for cases involving NaN and negative zero when using the LLVM backend.
|
||||
|
||||
- [#858](https://github.com/wasmerio/wasmer/pull/858) Minor panic fix when wasmer binary with `loader` option run a module without exported `_start` function
|
||||
|
||||
## 0.8.0 - 2019-10-02
|
||||
|
764
Cargo.lock
generated
764
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
@ -43,6 +43,7 @@ members = [
|
||||
"lib/singlepass-backend",
|
||||
"lib/runtime",
|
||||
"lib/runtime-core",
|
||||
"lib/runtime-core-tests",
|
||||
"lib/emscripten",
|
||||
"lib/spectests",
|
||||
"lib/win-exception-handler",
|
||||
|
73
Makefile
73
Makefile
@ -89,12 +89,15 @@ wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llv
|
||||
# Backends
|
||||
singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasitests-singlepass
|
||||
cargo test -p wasmer-singlepass-backend --release
|
||||
cargo test -p wasmer-runtime-core-tests --release --no-default-features --features backend-singlepass
|
||||
|
||||
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
|
||||
cargo test -p wasmer-clif-backend --release
|
||||
cargo test -p wasmer-runtime-core-tests --release
|
||||
|
||||
llvm: spectests-llvm emtests-llvm wasitests-llvm
|
||||
cargo test -p wasmer-llvm-backend --release
|
||||
cargo test -p wasmer-runtime-core-tests --release --no-default-features --features backend-llvm
|
||||
|
||||
|
||||
# All tests
|
||||
@ -105,8 +108,23 @@ capi:
|
||||
test-capi: capi
|
||||
cargo test -p wasmer-runtime-c-api --release
|
||||
|
||||
capi-test: test-capi
|
||||
|
||||
test-rest:
|
||||
cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests
|
||||
cargo test --release \
|
||||
--all \
|
||||
--exclude wasmer-runtime-c-api \
|
||||
--exclude wasmer-emscripten \
|
||||
--exclude wasmer-spectests \
|
||||
--exclude wasmer-wasi \
|
||||
--exclude wasmer-middleware-common \
|
||||
--exclude wasmer-middleware-common-tests \
|
||||
--exclude wasmer-singlepass-backend \
|
||||
--exclude wasmer-clif-backend \
|
||||
--exclude wasmer-llvm-backend \
|
||||
--exclude wasmer-wasi-tests \
|
||||
--exclude wasmer-emscripten-tests \
|
||||
--exclude wasmer-runtime-core-tests
|
||||
|
||||
circleci-clean:
|
||||
@if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi;
|
||||
@ -140,20 +158,65 @@ install:
|
||||
|
||||
# Checks
|
||||
check-bench-singlepass:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-singlepass" \
|
||||
cargo check --benches --all --no-default-features --features "backend-singlepass" \
|
||||
--exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader
|
||||
check-bench-clif:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-cranelift" \
|
||||
cargo check --benches --all --no-default-features --features "backend-cranelift" \
|
||||
--exclude wasmer-singlepass-backend --exclude wasmer-llvm-backend --exclude wasmer-kernel-loader \
|
||||
--exclude wasmer-middleware-common-tests
|
||||
check-bench-llvm:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-llvm" \
|
||||
cargo check --benches --all --no-default-features --features "backend-llvm" \
|
||||
--exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-kernel-loader
|
||||
|
||||
check-bench: check-bench-singlepass check-bench-llvm
|
||||
|
||||
# TODO: We wanted `--workspace --exclude wasmer-runtime`, but can't due
|
||||
# to https://github.com/rust-lang/cargo/issues/6745 .
|
||||
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests
|
||||
RUNTIME_CHECK = cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features
|
||||
check: check-bench
|
||||
cargo check --release --features backend-singlepass,backend-llvm,loader-kernel,debug
|
||||
cargo check $(NOT_RUNTIME_CRATES)
|
||||
cargo check --release $(NOT_RUNTIME_CRATES)
|
||||
cargo check --all-features $(NOT_RUNTIME_CRATES)
|
||||
cargo check --release --all-features $(NOT_RUNTIME_CRATES)
|
||||
# wasmer-runtime doesn't work with all backends enabled at once.
|
||||
#
|
||||
# We test using manifest-path directly so as to disable the default.
|
||||
# `--no-default-features` only disables the default features in the
|
||||
# current package, not the package specified by `-p`. This is
|
||||
# intentional.
|
||||
#
|
||||
# Test default features, test 'debug' feature only in non-release
|
||||
# builds, test as many combined features as possible with each backend
|
||||
# as default, and test a minimal set of features with only one backend
|
||||
# at a time.
|
||||
cargo check --manifest-path lib/runtime/Cargo.toml
|
||||
cargo check --release --manifest-path lib/runtime/Cargo.toml
|
||||
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=cranelift,cache,debug,llvm,singlepass,default-backend-singlepass
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=cranelift,cache,llvm,singlepass,default-backend-singlepass
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=cranelift,cache,debug,llvm,singlepass,default-backend-cranelift
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=cranelift,cache,llvm,singlepass,default-backend-cranelift
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=cranelift,cache,debug,llvm,singlepass,default-backend-llvm
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=cranelift,cache,llvm,singlepass,default-backend-llvm
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=singlepass,default-backend-singlepass,debug
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=singlepass,default-backend-singlepass
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=cranelift,default-backend-cranelift,debug
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=cranelift,default-backend-cranelift
|
||||
$(RUNTIME_CHECK) \
|
||||
--features=llvm,default-backend-llvm,debug
|
||||
$(RUNTIME_CHECK) --release \
|
||||
--features=llvm,default-backend-llvm
|
||||
|
||||
# Release
|
||||
release:
|
||||
|
@ -44,6 +44,7 @@ Wasmer runtime can be used as a library embedded in different languages, so you
|
||||
|  | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed | <a href="https://pecl.php.net/package/wasm" target="_blank"></a> |  |
|
||||
|  | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed | <a href="https://rubygems.org/gems/wasmer" target="_blank"></a> |  |
|
||||
|  | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed | <a href="https://github.com/wasmerio/postgres-ext-wasm" target="_blank"></a> |  |
|
||||
|  | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | Wasmer | actively developed | <a href="https://www.npmjs.com/package/@wasmer/wasi" target="_blank"></a> |  |
|
||||
|  | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | <a href="https://www.nuget.org/packages/WasmerSharp/" target="_blank"></a> |  |
|
||||
|  | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | |  |
|
||||
|  | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintained | |  |
|
||||
|
@ -60,6 +60,10 @@ jobs:
|
||||
cat /proc/meminfo
|
||||
displayName: System info - Extended (Linux)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||
- bash: |
|
||||
sysctl -a | grep machdep.cpu
|
||||
displayName: System info - Extended (Mac)
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
|
||||
- bash: make test
|
||||
displayName: Tests (*nix)
|
||||
condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT')))
|
||||
|
8
docs/assets/languages/js.svg
Executable file
8
docs/assets/languages/js.svg
Executable file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<svg width="20" height="20" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M0,0 L256,0 L256,256 L0,256 L0,0 Z" fill="#F7DF1E"></path>
|
||||
<path d="M67.311746,213.932292 L86.902654,202.076241 C90.6821079,208.777346 94.1202286,214.447137 102.367086,214.447137 C110.272203,214.447137 115.256076,211.354819 115.256076,199.326883 L115.256076,117.528787 L139.313575,117.528787 L139.313575,199.666997 C139.313575,224.58433 124.707759,235.925943 103.3984,235.925943 C84.1532952,235.925943 72.9819429,225.958603 67.3113397,213.93026" fill="#000000"></path>
|
||||
<path d="M152.380952,211.354413 L171.969422,200.0128 C177.125994,208.433981 183.827911,214.619835 195.684368,214.619835 C205.652521,214.619835 212.009041,209.635962 212.009041,202.762159 C212.009041,194.513676 205.479416,191.592025 194.481168,186.78207 L188.468419,184.202565 C171.111213,176.81473 159.597308,167.53534 159.597308,147.944838 C159.597308,129.901308 173.344508,116.153295 194.825752,116.153295 C210.119924,116.153295 221.117765,121.48094 229.021663,135.400432 L210.29059,147.428775 C206.166146,140.040127 201.699556,137.119289 194.826159,137.119289 C187.78047,137.119289 183.312254,141.587098 183.312254,147.428775 C183.312254,154.646349 187.78047,157.568406 198.089956,162.036622 L204.103924,164.614095 C224.553448,173.378641 236.067352,182.313448 236.067352,202.418387 C236.067352,224.071924 219.055137,235.927975 196.200432,235.927975 C173.860978,235.927975 159.425829,225.274311 152.381359,211.354413" fill="#000000"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -11,6 +11,17 @@
|
||||
| OSR | 🔄 | ❓ | ❓ |
|
||||
| SIMD | ⬜ | ⬜ | ✅ |
|
||||
| WASI | ✅ | ✅ | ✅ |
|
||||
| WASMER_BACKTRACE | ✅ | ⬜ | ⬜ |
|
||||
|
||||
## Operating System
|
||||
| | GNU Linux | Mac OSX | Windows NT |
|
||||
| - | :-: | :-: | :-: |
|
||||
| Cranelift Backend | ✅ | ✅ | ✅ |
|
||||
| LLVM Backend | ✅ | ✅ | ✅ |
|
||||
| Singlepass Backend | [#347](https://github.com/wasmerio/wasmer/issues/347) | ✅ | ✅ |
|
||||
| WASI | ✅ | ✅ | ✅* |
|
||||
|
||||
* `poll_fd` is not fully implemented for Windows yet
|
||||
|
||||
## Language integration
|
||||
|
||||
@ -18,8 +29,14 @@ TODO: define a set of features that are relevant and mark them here
|
||||
|
||||
Current ideas:
|
||||
|
||||
- WASI FS API
|
||||
- Callbacks
|
||||
- Exiting early in hostcall
|
||||
- Metering
|
||||
- Caching
|
||||
|
||||
> TODO: expand this table, it's focused on new features that we haven't implemented yet and doesn't list all language integrations
|
||||
|
||||
| | Rust | C / C++ | Go | Python | Ruby |
|
||||
| - | :-: | :-: | :-: | :-: | :-: |
|
||||
| Terminate in host call | ✅ | ⬜ | ⬜ | ⬜ | ⬜ |
|
||||
| WASI | ✅ | ✅ | 🔄 | ⬜ | ⬜ |
|
||||
| WASI FS API | ✅ | ⬜ | ⬜ | ⬜ | ⬜ |
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-clif-backend"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime Cranelift compiler backend"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,7 +9,7 @@ edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
cranelift-native = "0.44.0"
|
||||
cranelift-codegen = "0.44.0"
|
||||
cranelift-entity = "0.44.0"
|
||||
@ -35,7 +35,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.8.0" }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.9.0" }
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
@ -128,166 +128,6 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
|
||||
.state
|
||||
.initialize(&builder.func.signature, exit_block);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
use cranelift_codegen::cursor::{Cursor, FuncCursor};
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
let entry_ebb = func.layout.entry_block().unwrap();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
func.layout.insert_ebb(ebb, entry_ebb);
|
||||
let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(ebb);
|
||||
let params = pos.func.dfg.ebb_params(entry_ebb).to_vec();
|
||||
|
||||
let new_ebb_params: Vec<_> = params
|
||||
.iter()
|
||||
.map(|¶m| {
|
||||
pos.func
|
||||
.dfg
|
||||
.append_ebb_param(ebb, pos.func.dfg.value_type(param))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let start_debug = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![
|
||||
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
|
||||
ir::AbiParam::new(ir::types::I32),
|
||||
],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("strtdbug");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let end_debug = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![ir::AbiParam::special(
|
||||
ir::types::I64,
|
||||
ir::ArgumentPurpose::VMContext,
|
||||
)],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("enddbug");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let i32_print = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![
|
||||
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
|
||||
ir::AbiParam::new(ir::types::I32),
|
||||
],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("i32print");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let i64_print = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![
|
||||
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
|
||||
ir::AbiParam::new(ir::types::I64),
|
||||
],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("i64print");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let f32_print = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![
|
||||
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
|
||||
ir::AbiParam::new(ir::types::F32),
|
||||
],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("f32print");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let f64_print = {
|
||||
let signature = pos.func.import_signature(ir::Signature {
|
||||
call_conv: self.target_config().default_call_conv,
|
||||
params: vec![
|
||||
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
|
||||
ir::AbiParam::new(ir::types::F64),
|
||||
],
|
||||
returns: vec![],
|
||||
});
|
||||
|
||||
let name = ir::ExternalName::testcase("f64print");
|
||||
|
||||
pos.func.import_function(ir::ExtFuncData {
|
||||
name,
|
||||
signature,
|
||||
colocated: false,
|
||||
})
|
||||
};
|
||||
|
||||
let vmctx = pos
|
||||
.func
|
||||
.special_param(ir::ArgumentPurpose::VMContext)
|
||||
.expect("missing vmctx parameter");
|
||||
|
||||
let func_index = pos.ins().iconst(
|
||||
ir::types::I32,
|
||||
func_index.index() as i64 + self.module.info.imported_functions.len() as i64,
|
||||
);
|
||||
|
||||
pos.ins().call(start_debug, &[vmctx, func_index]);
|
||||
|
||||
for param in new_ebb_params.iter().cloned() {
|
||||
match pos.func.dfg.value_type(param) {
|
||||
ir::types::I32 => pos.ins().call(i32_print, &[vmctx, param]),
|
||||
ir::types::I64 => pos.ins().call(i64_print, &[vmctx, param]),
|
||||
ir::types::F32 => pos.ins().call(f32_print, &[vmctx, param]),
|
||||
ir::types::F64 => pos.ins().call(f64_print, &[vmctx, param]),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
|
||||
pos.ins().call(end_debug, &[vmctx]);
|
||||
|
||||
pos.ins().jump(entry_ebb, new_ebb_params.as_slice());
|
||||
}
|
||||
|
||||
self.functions.push(func_env);
|
||||
Ok(self.functions.last_mut().unwrap())
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl binemit::RelocSink for RelocSink {
|
||||
_ebb_offset: binemit::CodeOffset,
|
||||
) {
|
||||
// This should use the `offsets` field of `ir::Function`.
|
||||
unimplemented!();
|
||||
unimplemented!("RelocSink::reloc_ebb");
|
||||
}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
@ -146,7 +146,7 @@ impl binemit::RelocSink for RelocSink {
|
||||
|
||||
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
|
||||
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
|
||||
_ => unimplemented!(),
|
||||
_ => unimplemented!("reloc_external VmCall::Local {}", index),
|
||||
})),
|
||||
IMPORT_NAMESPACE => RelocationType::VmCall(VmCall::Import(match index {
|
||||
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
|
||||
@ -157,10 +157,10 @@ impl binemit::RelocSink for RelocSink {
|
||||
|
||||
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
|
||||
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
|
||||
_ => unimplemented!(),
|
||||
_ => unimplemented!("reloc_external VmCall::Import {}", index),
|
||||
})),
|
||||
SIG_NAMESPACE => RelocationType::Signature(SigIndex::new(index as usize)),
|
||||
_ => unimplemented!(),
|
||||
_ => unimplemented!("reloc_external SigIndex {}", index),
|
||||
};
|
||||
self.external_relocs.push(ExternalRelocation {
|
||||
reloc,
|
||||
@ -204,7 +204,7 @@ impl binemit::RelocSink for RelocSink {
|
||||
}
|
||||
|
||||
fn reloc_constant(&mut self, _: u32, _: cranelift_codegen::binemit::Reloc, _: u32) {
|
||||
unimplemented!()
|
||||
unimplemented!("RelocSink::reloc_constant")
|
||||
}
|
||||
|
||||
fn reloc_jt(
|
||||
@ -213,7 +213,7 @@ impl binemit::RelocSink for RelocSink {
|
||||
_reloc: binemit::Reloc,
|
||||
_jt: ir::JumpTable,
|
||||
) {
|
||||
unimplemented!();
|
||||
unimplemented!("RelocSink::reloc_jt");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,31 @@
|
||||
use crate::{cache::BackendCache, trampoline::Trampolines};
|
||||
use crate::{
|
||||
cache::BackendCache,
|
||||
libcalls,
|
||||
relocation::{
|
||||
ExternalRelocation, LibCall, LocalRelocation, LocalTrapSink, Reloc, RelocSink,
|
||||
RelocationType, TrapSink, VmCall, VmCallKind,
|
||||
},
|
||||
signal::HandlerData,
|
||||
trampoline::Trampolines,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use cranelift_codegen::{
|
||||
binemit::{Stackmap, StackmapSink},
|
||||
ir, isa, Context,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
mem,
|
||||
ptr::{write_unaligned, NonNull},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use wasmer_runtime_core::cache::Error as CacheError;
|
||||
use wasmer_runtime_core::{
|
||||
self,
|
||||
backend::{
|
||||
sys::{Memory, Protect},
|
||||
SigRegistry,
|
||||
},
|
||||
cache::Error as CacheError,
|
||||
error::{CompileError, CompileResult},
|
||||
module::ModuleInfo,
|
||||
structures::{Map, SliceMap, TypedIndex},
|
||||
@ -250,17 +249,9 @@ impl FuncResolverBuilder {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
LibCall::Probestack => __rust_probestack as isize,
|
||||
},
|
||||
RelocationType::Intrinsic(ref name) => match name.as_str() {
|
||||
"i32print" => i32_print as isize,
|
||||
"i64print" => i64_print as isize,
|
||||
"f32print" => f32_print as isize,
|
||||
"f64print" => f64_print as isize,
|
||||
"strtdbug" => start_debug as isize,
|
||||
"enddbug" => end_debug as isize,
|
||||
_ => Err(CompileError::InternalError {
|
||||
msg: format!("unexpected intrinsic: {}", name),
|
||||
})?,
|
||||
},
|
||||
RelocationType::Intrinsic(ref name) => Err(CompileError::InternalError {
|
||||
msg: format!("unexpected intrinsic: {}", name),
|
||||
})?,
|
||||
RelocationType::VmCall(vmcall) => match vmcall {
|
||||
VmCall::Local(kind) => match kind {
|
||||
VmCallKind::StaticMemoryGrow | VmCallKind::SharedStaticMemoryGrow => {
|
||||
@ -371,28 +362,3 @@ impl FuncResolver {
|
||||
fn round_up(n: usize, multiple: usize) -> usize {
|
||||
(n + multiple - 1) & !(multiple - 1)
|
||||
}
|
||||
|
||||
extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) {
|
||||
eprint!(" i32: {},", n);
|
||||
}
|
||||
extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) {
|
||||
eprint!(" i64: {},", n);
|
||||
}
|
||||
extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) {
|
||||
eprint!(" f32: {},", n);
|
||||
}
|
||||
extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) {
|
||||
eprint!(" f64: {},", n);
|
||||
}
|
||||
extern "C" fn start_debug(ctx: &mut vm::Ctx, func_index: u32) {
|
||||
if let Some(symbol_map) = unsafe { ctx.borrow_symbol_map() } {
|
||||
if let Some(fn_name) = symbol_map.get(&func_index) {
|
||||
eprint!("func ({} ({})), args: [", fn_name, func_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
eprint!("func ({}), args: [", func_index);
|
||||
}
|
||||
extern "C" fn end_debug(_ctx: &mut vm::Ctx) {
|
||||
eprintln!(" ]");
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::relocation::{TrapData, TrapSink};
|
||||
use crate::resolver::FuncResolver;
|
||||
use crate::trampoline::Trampolines;
|
||||
use crate::{
|
||||
relocation::{TrapData, TrapSink},
|
||||
resolver::FuncResolver,
|
||||
trampoline::Trampolines,
|
||||
};
|
||||
use libc::c_void;
|
||||
use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
backend::RunnableModule,
|
||||
module::ModuleInfo,
|
||||
typed_func::{Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
};
|
||||
@ -59,7 +61,7 @@ impl RunnableModule for Caller {
|
||||
|
||||
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
|
||||
unsafe extern "C" fn invoke(
|
||||
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64),
|
||||
trampoline: Trampoline,
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
|
@ -98,7 +98,10 @@ pub fn call_protected<T>(
|
||||
},
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
|
||||
_ => unimplemented!(),
|
||||
_ => unimplemented!(
|
||||
"WasmTrapInfo::Unknown signal:{:?}",
|
||||
Signal::from_c_int(signum)
|
||||
),
|
||||
}))
|
||||
} else {
|
||||
let signal = match Signal::from_c_int(signum) {
|
||||
|
@ -1,24 +1,30 @@
|
||||
use crate::relocation::{TrapCode, TrapData};
|
||||
use crate::signal::{CallProtError, HandlerData};
|
||||
use crate::trampoline::Trampoline;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::c_void;
|
||||
use std::ptr::{self, NonNull};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_runtime_core::vm::Func;
|
||||
use crate::{
|
||||
relocation::{TrapCode, TrapData},
|
||||
signal::{CallProtError, HandlerData},
|
||||
};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::c_void,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
typed_func::{Trampoline, WasmTrapInfo},
|
||||
vm::{Ctx, Func},
|
||||
};
|
||||
use wasmer_win_exception_handler::CallProtectedData;
|
||||
pub use wasmer_win_exception_handler::_call_protected;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::minwinbase::{
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW,
|
||||
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW,
|
||||
EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION,
|
||||
EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP,
|
||||
EXCEPTION_STACK_OVERFLOW,
|
||||
use winapi::{
|
||||
shared::minwindef::DWORD,
|
||||
um::minwinbase::{
|
||||
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
|
||||
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND,
|
||||
EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT,
|
||||
EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_FLT_STACK_CHECK,
|
||||
EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_ILLEGAL_INSTRUCTION,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_INVALID_HANDLE,
|
||||
EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, EXCEPTION_POSSIBLE_DEADLOCK,
|
||||
EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_STACK_OVERFLOW,
|
||||
},
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
@ -110,5 +116,5 @@ pub fn call_protected(
|
||||
|
||||
pub unsafe fn trigger_trap() -> ! {
|
||||
// TODO
|
||||
unimplemented!();
|
||||
unimplemented!("windows::trigger_trap");
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
use crate::cache::TrampolineCache;
|
||||
use crate::resolver::NoopStackmapSink;
|
||||
use crate::{cache::TrampolineCache, resolver::NoopStackmapSink};
|
||||
use cranelift_codegen::{
|
||||
binemit::{NullTrapSink, Reloc, RelocSink},
|
||||
cursor::{Cursor, FuncCursor},
|
||||
ir::{self, InstBuilder},
|
||||
isa, Context,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::{iter, mem, ptr::NonNull};
|
||||
use std::{collections::HashMap, iter, mem};
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::{Memory, Protect},
|
||||
module::{ExportIndex, ModuleInfo},
|
||||
typed_func::Trampoline,
|
||||
types::{FuncSig, SigIndex, Type},
|
||||
vm,
|
||||
};
|
||||
|
||||
struct NullRelocSink {}
|
||||
@ -22,14 +20,12 @@ impl RelocSink for NullRelocSink {
|
||||
fn reloc_external(&mut self, _: u32, _: Reloc, _: &ir::ExternalName, _: i64) {}
|
||||
|
||||
fn reloc_constant(&mut self, _: u32, _: Reloc, _: u32) {
|
||||
unimplemented!()
|
||||
unimplemented!("RelocSink::reloc_constant")
|
||||
}
|
||||
|
||||
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
|
||||
}
|
||||
|
||||
pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64);
|
||||
|
||||
pub struct Trampolines {
|
||||
memory: Memory,
|
||||
offsets: HashMap<SigIndex, usize>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-dev-utils"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten-tests"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
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.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.8.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.8.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8.0", optional = true }
|
||||
wasmer-emscripten = { path = "../emscripten", version = "0.9.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.9.1"
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.8.0"}
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.9.0"}
|
||||
|
||||
[build-dependencies]
|
||||
glob = "0.3"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime emscripten implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -12,10 +12,10 @@ byteorder = "1.3"
|
||||
lazy_static = "1.4"
|
||||
libc = "0.2.60"
|
||||
time = "0.1"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rand = "0.7"
|
||||
getrandom = "0.1"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
@ -10,22 +10,22 @@ pub fn ___cxa_allocate_exception(ctx: &mut Ctx, size: u32) -> u32 {
|
||||
|
||||
pub fn ___cxa_current_primary_exception(_ctx: &mut Ctx) -> u32 {
|
||||
debug!("emscripten::___cxa_current_primary_exception");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___cxa_current_primary_exception")
|
||||
}
|
||||
|
||||
pub fn ___cxa_decrement_exception_refcount(_ctx: &mut Ctx, _a: u32) {
|
||||
debug!("emscripten::___cxa_decrement_exception_refcount({})", _a);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___cxa_decrement_exception_refcount({})", _a)
|
||||
}
|
||||
|
||||
pub fn ___cxa_increment_exception_refcount(_ctx: &mut Ctx, _a: u32) {
|
||||
debug!("emscripten::___cxa_increment_exception_refcount({})", _a);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___cxa_increment_exception_refcount({})", _a)
|
||||
}
|
||||
|
||||
pub fn ___cxa_rethrow_primary_exception(_ctx: &mut Ctx, _a: u32) {
|
||||
debug!("emscripten::___cxa_rethrow_primary_exception({})", _a);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___cxa_rethrow_primary_exception({})", _a)
|
||||
}
|
||||
|
||||
/// emscripten: ___cxa_throw
|
||||
|
@ -15,13 +15,13 @@ use wasmer_runtime_core::vm::Ctx;
|
||||
/// getprotobyname
|
||||
pub fn getprotobyname(_ctx: &mut Ctx, _name_ptr: i32) -> i32 {
|
||||
debug!("emscripten::getprotobyname");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::getprotobyname")
|
||||
}
|
||||
|
||||
/// getprotobynumber
|
||||
pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 {
|
||||
debug!("emscripten::getprotobynumber");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::getprotobynumber")
|
||||
}
|
||||
|
||||
/// sigdelset
|
||||
@ -53,11 +53,11 @@ pub fn sigfillset(ctx: &mut Ctx, set: i32) -> i32 {
|
||||
/// tzset
|
||||
pub fn tzset(_ctx: &mut Ctx) {
|
||||
debug!("emscripten::tzset - stub");
|
||||
//unimplemented!()
|
||||
//unimplemented!("emscripten::tzset - stub")
|
||||
}
|
||||
|
||||
/// strptime
|
||||
pub fn strptime(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
|
||||
debug!("emscripten::strptime");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::strptime")
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ pub fn printf(_ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 {
|
||||
/// chroot
|
||||
pub fn chroot(_ctx: &mut Ctx, _name_ptr: i32) -> i32 {
|
||||
debug!("emscripten::chroot");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::chroot")
|
||||
}
|
||||
|
||||
/// getpwuid
|
||||
pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 {
|
||||
debug!("emscripten::getpwuid");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::getpwuid")
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
ftruncate64(_fd, _length)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which)
|
||||
}
|
||||
|
||||
/// lchown
|
||||
@ -1111,6 +1111,6 @@ pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall324 (fallocate) {}", _which)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use crate::utils::{copy_cstr_into_wasm, get_cstr_path};
|
||||
use crate::varargs::VarArgs;
|
||||
use libc::mkdir;
|
||||
use libc::open;
|
||||
use rand::Rng;
|
||||
use std::env;
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
@ -39,7 +38,8 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
|
||||
let ptr = tmp_dir_c_str.as_ptr() as *const i8;
|
||||
let mut urandom_file = File::create(tmp_dir).unwrap();
|
||||
// create some random bytes and put them into the file
|
||||
let random_bytes = rand::thread_rng().gen::<[u8; 32]>();
|
||||
let mut random_bytes = [0u8; 32];
|
||||
getrandom::getrandom(&mut random_bytes).unwrap();
|
||||
let _ = urandom_file.write_all(&random_bytes).unwrap();
|
||||
// put the file path string into wasm memory
|
||||
let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) };
|
||||
@ -66,13 +66,13 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
|
||||
/// link
|
||||
pub fn ___syscall9(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall9 (link) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall9 (link) {}", _which);
|
||||
}
|
||||
|
||||
/// ftruncate64
|
||||
pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall194 - stub");
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall194 - stub")
|
||||
}
|
||||
|
||||
// chown
|
||||
@ -86,13 +86,13 @@ pub fn ___syscall212(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
|
||||
/// access
|
||||
pub fn ___syscall33(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall33 (access) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall33 (access) {}", _which);
|
||||
}
|
||||
|
||||
/// nice
|
||||
pub fn ___syscall34(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall34 (nice) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall34 (nice) {}", _which);
|
||||
}
|
||||
|
||||
// mkdir
|
||||
@ -113,19 +113,19 @@ pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int
|
||||
/// dup
|
||||
pub fn ___syscall41(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall41 (dup) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall41 (dup) {}", _which);
|
||||
}
|
||||
|
||||
/// getrusage
|
||||
pub fn ___syscall77(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall77 (getrusage) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall77 (getrusage) {}", _which);
|
||||
}
|
||||
|
||||
/// symlink
|
||||
pub fn ___syscall83(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall83 (symlink) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall83 (symlink) {}", _which);
|
||||
}
|
||||
|
||||
/// readlink
|
||||
@ -143,38 +143,38 @@ pub fn ___syscall132(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_
|
||||
/// lchown
|
||||
pub fn ___syscall198(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall198 (lchown) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall198 (lchown) {}", _which);
|
||||
}
|
||||
|
||||
/// getgid32
|
||||
pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall200 (getgid32)");
|
||||
unimplemented!();
|
||||
unimplemented!("emscripten::___syscall200 (getgid32)");
|
||||
}
|
||||
|
||||
// geteuid32
|
||||
pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall201 (geteuid32)");
|
||||
unimplemented!();
|
||||
unimplemented!("emscripten::___syscall201 (geteuid32)");
|
||||
}
|
||||
|
||||
// getegid32
|
||||
pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
// gid_t
|
||||
debug!("emscripten::___syscall202 (getegid32)");
|
||||
unimplemented!();
|
||||
unimplemented!("emscripten::___syscall202 (getegid32)");
|
||||
}
|
||||
|
||||
/// getgroups
|
||||
pub fn ___syscall205(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall205 (getgroups) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall205 (getgroups) {}", _which);
|
||||
}
|
||||
|
||||
/// madvise
|
||||
pub fn ___syscall219(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall212 (chown) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall212 (chown) {}", _which);
|
||||
}
|
||||
|
||||
/// dup3
|
||||
@ -194,7 +194,7 @@ pub fn ___syscall54(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_in
|
||||
/// fchmod
|
||||
pub fn ___syscall94(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall118 (fchmod) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall118 (fchmod) {}", _which);
|
||||
}
|
||||
|
||||
// socketcall
|
||||
@ -209,7 +209,7 @@ pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
|
||||
/// fsync
|
||||
pub fn ___syscall118(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall118 (fsync) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall118 (fsync) {}", _which);
|
||||
}
|
||||
|
||||
// pread
|
||||
@ -247,7 +247,7 @@ pub fn ___syscall142(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
|
||||
/// fdatasync
|
||||
pub fn ___syscall148(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall148 (fdatasync) {}", _which);
|
||||
unimplemented!();
|
||||
unimplemented!("emscripten::___syscall148 (fdatasync) {}", _which);
|
||||
}
|
||||
|
||||
// setpgid
|
||||
@ -300,11 +300,11 @@ pub fn ___syscall221(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_
|
||||
/// fchown
|
||||
pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall207 (fchown) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall207 (fchown) {}", _which)
|
||||
}
|
||||
|
||||
/// fallocate
|
||||
pub fn ___syscall324(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
|
||||
debug!("emscripten::___syscall324 (fallocate) {}", _which);
|
||||
unimplemented!()
|
||||
unimplemented!("emscripten::___syscall324 (fallocate) {}", _which)
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
[package]
|
||||
name = "wasmer-llvm-backend"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
wasmparser = "0.39.1"
|
||||
smallvec = "0.6"
|
||||
goblin = "0.0.24"
|
||||
libc = "0.2.60"
|
||||
capstone = { version = "0.6", optional = true }
|
||||
byteorder = "1"
|
||||
|
||||
[dependencies.inkwell]
|
||||
@ -38,4 +37,3 @@ wabt = "0.9.1"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
disasm = ["capstone"]
|
||||
|
@ -1,21 +1,24 @@
|
||||
use super::stackmap::StackmapRegistry;
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect};
|
||||
use crate::{
|
||||
intrinsics::Intrinsics,
|
||||
structs::{Callbacks, LLVMModule, LLVMResult, MemProtect},
|
||||
};
|
||||
use inkwell::{
|
||||
memory_buffer::MemoryBuffer,
|
||||
module::Module,
|
||||
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
|
||||
OptimizationLevel,
|
||||
targets::{FileType, TargetMachine},
|
||||
};
|
||||
use libc::c_char;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
ffi::{c_void, CString},
|
||||
fs::File,
|
||||
io::Write,
|
||||
mem,
|
||||
ops::Deref,
|
||||
ptr::{self, NonNull},
|
||||
rc::Rc,
|
||||
slice, str,
|
||||
sync::{Arc, Once},
|
||||
};
|
||||
@ -28,7 +31,7 @@ use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
state::ModuleStateMap,
|
||||
structures::TypedIndex,
|
||||
typed_func::{Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm, vmcalls,
|
||||
};
|
||||
@ -58,7 +61,7 @@ extern "C" {
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
fn invoke_trampoline(
|
||||
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64),
|
||||
trampoline: Trampoline,
|
||||
vmctx_ptr: *mut vm::Ctx,
|
||||
func_ptr: NonNull<vm::Func>,
|
||||
params: *const u64,
|
||||
@ -168,34 +171,14 @@ pub struct LLVMBackend {
|
||||
|
||||
impl LLVMBackend {
|
||||
pub fn new(
|
||||
module: Module,
|
||||
module: Rc<RefCell<Module>>,
|
||||
_intrinsics: Intrinsics,
|
||||
_stackmaps: &StackmapRegistry,
|
||||
_module_info: &ModuleInfo,
|
||||
target_machine: &TargetMachine,
|
||||
) -> (Self, LLVMCache) {
|
||||
Target::initialize_x86(&InitializationConfig {
|
||||
asm_parser: true,
|
||||
asm_printer: true,
|
||||
base: true,
|
||||
disassembler: true,
|
||||
info: true,
|
||||
machine_code: true,
|
||||
});
|
||||
let triple = TargetMachine::get_default_triple().to_string();
|
||||
let target = Target::from_triple(&triple).unwrap();
|
||||
let target_machine = target
|
||||
.create_target_machine(
|
||||
&triple,
|
||||
&TargetMachine::get_host_cpu_name().to_string(),
|
||||
&TargetMachine::get_host_cpu_features().to_string(),
|
||||
OptimizationLevel::Aggressive,
|
||||
RelocMode::Static,
|
||||
CodeModel::Large,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let memory_buffer = target_machine
|
||||
.write_to_memory_buffer(&module, FileType::Object)
|
||||
.write_to_memory_buffer(&module.borrow_mut(), FileType::Object)
|
||||
.unwrap();
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
|
||||
@ -408,12 +391,7 @@ impl RunnableModule for LLVMBackend {
|
||||
}
|
||||
|
||||
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
|
||||
let trampoline: unsafe extern "C" fn(
|
||||
*mut vm::Ctx,
|
||||
NonNull<vm::Func>,
|
||||
*const u64,
|
||||
*mut u64,
|
||||
) = unsafe {
|
||||
let trampoline: Trampoline = unsafe {
|
||||
let name = if cfg!(target_os = "macos") {
|
||||
format!("_trmp{}", sig_index.index())
|
||||
} else {
|
||||
@ -477,33 +455,3 @@ impl CacheGen for LLVMCache {
|
||||
Ok(([].as_ref().into(), memory))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
|
||||
use capstone::arch::BuildsCapstone;
|
||||
let mut cs = capstone::Capstone::new() // Call builder-pattern
|
||||
.x86() // X86 architecture
|
||||
.mode(capstone::arch::x86::ArchMode::Mode64) // 64-bit mode
|
||||
.detail(true) // Generate extra instruction details
|
||||
.build()
|
||||
.expect("Failed to create Capstone object");
|
||||
|
||||
// Get disassembled instructions
|
||||
let insns = cs
|
||||
.disasm_count(
|
||||
std::slice::from_raw_parts(ptr, size),
|
||||
ptr as u64,
|
||||
inst_count,
|
||||
)
|
||||
.expect("Failed to disassemble");
|
||||
|
||||
println!("count = {}", insns.len());
|
||||
for insn in insns.iter() {
|
||||
println!(
|
||||
"0x{:x}: {:6} {}",
|
||||
insn.address(),
|
||||
insn.mnemonic().unwrap_or(""),
|
||||
insn.op_str().unwrap_or("")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,11 +6,16 @@ use inkwell::{
|
||||
types::{
|
||||
BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType,
|
||||
},
|
||||
values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue},
|
||||
values::{
|
||||
BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionValue, IntValue,
|
||||
PointerValue, VectorValue,
|
||||
},
|
||||
AddressSpace,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use wasmer_runtime_core::{
|
||||
memory::MemoryType,
|
||||
module::ModuleInfo,
|
||||
@ -19,6 +24,7 @@ use wasmer_runtime_core::{
|
||||
GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
units::Pages,
|
||||
vm::{Ctx, INTERNALS_SIZE},
|
||||
};
|
||||
|
||||
@ -47,16 +53,6 @@ pub struct Intrinsics {
|
||||
pub sqrt_f32x4: FunctionValue,
|
||||
pub sqrt_f64x2: FunctionValue,
|
||||
|
||||
pub minimum_f32: FunctionValue,
|
||||
pub minimum_f64: FunctionValue,
|
||||
pub minimum_f32x4: FunctionValue,
|
||||
pub minimum_f64x2: FunctionValue,
|
||||
|
||||
pub maximum_f32: FunctionValue,
|
||||
pub maximum_f64: FunctionValue,
|
||||
pub maximum_f32x4: FunctionValue,
|
||||
pub maximum_f64x2: FunctionValue,
|
||||
|
||||
pub ceil_f32: FunctionValue,
|
||||
pub ceil_f64: FunctionValue,
|
||||
|
||||
@ -125,6 +121,8 @@ pub struct Intrinsics {
|
||||
pub i128_zero: IntValue,
|
||||
pub f32_zero: FloatValue,
|
||||
pub f64_zero: FloatValue,
|
||||
pub f32x4_zero: VectorValue,
|
||||
pub f64x2_zero: VectorValue,
|
||||
|
||||
pub trap_unreachable: BasicValueEnum,
|
||||
pub trap_call_indirect_sig: BasicValueEnum,
|
||||
@ -191,6 +189,8 @@ impl Intrinsics {
|
||||
let i128_zero = i128_ty.const_int(0, false);
|
||||
let f32_zero = f32_ty.const_float(0.0);
|
||||
let f64_zero = f64_ty.const_float(0.0);
|
||||
let f32x4_zero = f32x4_ty.const_zero();
|
||||
let f64x2_zero = f64x2_ty.const_zero();
|
||||
|
||||
let i1_ty_basic = i1_ty.as_basic_type_enum();
|
||||
let i32_ty_basic = i32_ty.as_basic_type_enum();
|
||||
@ -303,8 +303,6 @@ impl Intrinsics {
|
||||
|
||||
let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false);
|
||||
let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false);
|
||||
let ret_f32x4_take_f32x4_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic, f32x4_ty_basic], false);
|
||||
let ret_f64x2_take_f64x2_f64x2 = f64x2_ty.fn_type(&[f64x2_ty_basic, f64x2_ty_basic], false);
|
||||
|
||||
let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type(
|
||||
&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic],
|
||||
@ -329,32 +327,6 @@ impl Intrinsics {
|
||||
sqrt_f32x4: module.add_function("llvm.sqrt.v4f32", ret_f32x4_take_f32x4, None),
|
||||
sqrt_f64x2: module.add_function("llvm.sqrt.v2f64", ret_f64x2_take_f64x2, None),
|
||||
|
||||
minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None),
|
||||
minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None),
|
||||
minimum_f32x4: module.add_function(
|
||||
"llvm.minnum.v4f32",
|
||||
ret_f32x4_take_f32x4_f32x4,
|
||||
None,
|
||||
),
|
||||
minimum_f64x2: module.add_function(
|
||||
"llvm.minnum.v2f64",
|
||||
ret_f64x2_take_f64x2_f64x2,
|
||||
None,
|
||||
),
|
||||
|
||||
maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None),
|
||||
maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None),
|
||||
maximum_f32x4: module.add_function(
|
||||
"llvm.maxnum.v4f32",
|
||||
ret_f32x4_take_f32x4_f32x4,
|
||||
None,
|
||||
),
|
||||
maximum_f64x2: module.add_function(
|
||||
"llvm.maxnum.v2f64",
|
||||
ret_f64x2_take_f64x2_f64x2,
|
||||
None,
|
||||
),
|
||||
|
||||
ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None),
|
||||
ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None),
|
||||
|
||||
@ -455,6 +427,8 @@ impl Intrinsics {
|
||||
i128_zero,
|
||||
f32_zero,
|
||||
f64_zero,
|
||||
f32x4_zero,
|
||||
f64x2_zero,
|
||||
|
||||
trap_unreachable: i32_zero.as_basic_value_enum(),
|
||||
trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(),
|
||||
@ -589,11 +563,15 @@ pub enum MemoryCache {
|
||||
Dynamic {
|
||||
ptr_to_base_ptr: PointerValue,
|
||||
ptr_to_bounds: PointerValue,
|
||||
minimum: Pages,
|
||||
maximum: Option<Pages>,
|
||||
},
|
||||
/// The memory is always in the same place.
|
||||
Static {
|
||||
base_ptr: PointerValue,
|
||||
bounds: IntValue,
|
||||
minimum: Pages,
|
||||
maximum: Option<Pages>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -683,7 +661,12 @@ impl<'a> CtxType<'a> {
|
||||
ptr
|
||||
}
|
||||
|
||||
pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache {
|
||||
pub fn memory(
|
||||
&mut self,
|
||||
index: MemoryIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
) -> MemoryCache {
|
||||
let (cached_memories, info, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_memories,
|
||||
self.info,
|
||||
@ -692,34 +675,48 @@ impl<'a> CtxType<'a> {
|
||||
);
|
||||
|
||||
*cached_memories.entry(index).or_insert_with(|| {
|
||||
let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_mem_index) => (
|
||||
unsafe {
|
||||
cache_builder.build_struct_gep(
|
||||
ctx_ptr_value,
|
||||
offset_to_index(Ctx::offset_memories()),
|
||||
"memory_array_ptr_ptr",
|
||||
)
|
||||
},
|
||||
local_mem_index.index() as u64,
|
||||
info.memories[local_mem_index].memory_type(),
|
||||
),
|
||||
LocalOrImport::Import(import_mem_index) => (
|
||||
unsafe {
|
||||
cache_builder.build_struct_gep(
|
||||
ctx_ptr_value,
|
||||
offset_to_index(Ctx::offset_imported_memories()),
|
||||
"memory_array_ptr_ptr",
|
||||
)
|
||||
},
|
||||
import_mem_index.index() as u64,
|
||||
info.imported_memories[import_mem_index].1.memory_type(),
|
||||
),
|
||||
};
|
||||
let (memory_array_ptr_ptr, index, memory_type, minimum, maximum, field_name) =
|
||||
match index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_mem_index) => (
|
||||
unsafe {
|
||||
cache_builder.build_struct_gep(
|
||||
ctx_ptr_value,
|
||||
offset_to_index(Ctx::offset_memories()),
|
||||
"memory_array_ptr_ptr",
|
||||
)
|
||||
},
|
||||
local_mem_index.index() as u64,
|
||||
info.memories[local_mem_index].memory_type(),
|
||||
info.memories[local_mem_index].minimum,
|
||||
info.memories[local_mem_index].maximum,
|
||||
"context_field_ptr_to_local_memory",
|
||||
),
|
||||
LocalOrImport::Import(import_mem_index) => (
|
||||
unsafe {
|
||||
cache_builder.build_struct_gep(
|
||||
ctx_ptr_value,
|
||||
offset_to_index(Ctx::offset_imported_memories()),
|
||||
"memory_array_ptr_ptr",
|
||||
)
|
||||
},
|
||||
import_mem_index.index() as u64,
|
||||
info.imported_memories[import_mem_index].1.memory_type(),
|
||||
info.imported_memories[import_mem_index].1.minimum,
|
||||
info.imported_memories[import_mem_index].1.maximum,
|
||||
"context_field_ptr_to_imported_memory",
|
||||
),
|
||||
};
|
||||
|
||||
let memory_array_ptr = cache_builder
|
||||
.build_load(memory_array_ptr_ptr, "memory_array_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
field_name,
|
||||
memory_array_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
let const_index = intrinsics.i32_ty.const_int(index, false);
|
||||
let memory_ptr_ptr = unsafe {
|
||||
cache_builder.build_in_bounds_gep(
|
||||
@ -731,6 +728,13 @@ impl<'a> CtxType<'a> {
|
||||
let memory_ptr = cache_builder
|
||||
.build_load(memory_ptr_ptr, "memory_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"memory_ptr",
|
||||
memory_ptr.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
|
||||
let (ptr_to_base_ptr, ptr_to_bounds) = unsafe {
|
||||
(
|
||||
@ -743,15 +747,37 @@ impl<'a> CtxType<'a> {
|
||||
MemoryType::Dynamic => MemoryCache::Dynamic {
|
||||
ptr_to_base_ptr,
|
||||
ptr_to_bounds,
|
||||
minimum,
|
||||
maximum,
|
||||
},
|
||||
MemoryType::Static | MemoryType::SharedStatic => MemoryCache::Static {
|
||||
base_ptr: cache_builder
|
||||
MemoryType::Static | MemoryType::SharedStatic => {
|
||||
let base_ptr = cache_builder
|
||||
.build_load(ptr_to_base_ptr, "base")
|
||||
.into_pointer_value(),
|
||||
bounds: cache_builder
|
||||
.into_pointer_value();
|
||||
let bounds = cache_builder
|
||||
.build_load(ptr_to_bounds, "bounds")
|
||||
.into_int_value(),
|
||||
},
|
||||
.into_int_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"static_memory_base",
|
||||
base_ptr.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"static_memory_bounds",
|
||||
bounds.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
MemoryCache::Static {
|
||||
base_ptr,
|
||||
bounds,
|
||||
minimum,
|
||||
maximum,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -760,6 +786,7 @@ impl<'a> CtxType<'a> {
|
||||
&mut self,
|
||||
index: TableIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
) -> (PointerValue, PointerValue) {
|
||||
let (cached_tables, info, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_tables,
|
||||
@ -772,7 +799,7 @@ impl<'a> CtxType<'a> {
|
||||
ptr_to_base_ptr,
|
||||
ptr_to_bounds,
|
||||
} = *cached_tables.entry(index).or_insert_with(|| {
|
||||
let (table_array_ptr_ptr, index) = match index.local_or_import(info) {
|
||||
let (table_array_ptr_ptr, index, field_name) = match index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_table_index) => (
|
||||
unsafe {
|
||||
cache_builder.build_struct_gep(
|
||||
@ -782,6 +809,7 @@ impl<'a> CtxType<'a> {
|
||||
)
|
||||
},
|
||||
local_table_index.index() as u64,
|
||||
"context_field_ptr_to_local_table",
|
||||
),
|
||||
LocalOrImport::Import(import_table_index) => (
|
||||
unsafe {
|
||||
@ -792,12 +820,20 @@ impl<'a> CtxType<'a> {
|
||||
)
|
||||
},
|
||||
import_table_index.index() as u64,
|
||||
"context_field_ptr_to_import_table",
|
||||
),
|
||||
};
|
||||
|
||||
let table_array_ptr = cache_builder
|
||||
.build_load(table_array_ptr_ptr, "table_array_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
field_name,
|
||||
table_array_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
let const_index = intrinsics.i32_ty.const_int(index, false);
|
||||
let table_ptr_ptr = unsafe {
|
||||
cache_builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr")
|
||||
@ -805,6 +841,13 @@ impl<'a> CtxType<'a> {
|
||||
let table_ptr = cache_builder
|
||||
.build_load(table_ptr_ptr, "table_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"table_ptr",
|
||||
table_array_ptr.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
|
||||
let (ptr_to_base_ptr, ptr_to_bounds) = unsafe {
|
||||
(
|
||||
@ -826,15 +869,30 @@ impl<'a> CtxType<'a> {
|
||||
&mut self,
|
||||
index: TableIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
builder: &Builder,
|
||||
) -> (PointerValue, IntValue) {
|
||||
let (ptr_to_base_ptr, ptr_to_bounds) = self.table_prepare(index, intrinsics);
|
||||
(
|
||||
builder
|
||||
.build_load(ptr_to_base_ptr, "base_ptr")
|
||||
.into_pointer_value(),
|
||||
builder.build_load(ptr_to_bounds, "bounds").into_int_value(),
|
||||
)
|
||||
let (ptr_to_base_ptr, ptr_to_bounds) =
|
||||
self.table_prepare(index, intrinsics, module.clone());
|
||||
let base_ptr = builder
|
||||
.build_load(ptr_to_base_ptr, "base_ptr")
|
||||
.into_pointer_value();
|
||||
let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"table_base_ptr",
|
||||
base_ptr.as_instruction_value().unwrap(),
|
||||
Some(index.index() as u32),
|
||||
);
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"table_bounds",
|
||||
bounds.as_instruction_value().unwrap(),
|
||||
Some(index.index() as u32),
|
||||
);
|
||||
(base_ptr, bounds)
|
||||
}
|
||||
|
||||
pub fn local_func(
|
||||
@ -842,6 +900,7 @@ impl<'a> CtxType<'a> {
|
||||
index: LocalFuncIndex,
|
||||
fn_ty: FunctionType,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
builder: &Builder,
|
||||
) -> PointerValue {
|
||||
let local_func_array_ptr_ptr = unsafe {
|
||||
@ -854,6 +913,13 @@ impl<'a> CtxType<'a> {
|
||||
let local_func_array_ptr = builder
|
||||
.build_load(local_func_array_ptr_ptr, "local_func_array_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"context_field_ptr_to_local_funcs",
|
||||
local_func_array_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
let local_func_ptr_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
local_func_array_ptr,
|
||||
@ -864,6 +930,13 @@ impl<'a> CtxType<'a> {
|
||||
let local_func_ptr = builder
|
||||
.build_load(local_func_ptr_ptr, "local_func_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"local_func_ptr",
|
||||
local_func_ptr.as_instruction_value().unwrap(),
|
||||
Some(index.index() as u32),
|
||||
);
|
||||
builder.build_pointer_cast(
|
||||
local_func_ptr,
|
||||
fn_ty.ptr_type(AddressSpace::Generic),
|
||||
@ -905,7 +978,12 @@ impl<'a> CtxType<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache {
|
||||
pub fn global_cache(
|
||||
&mut self,
|
||||
index: GlobalIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
) -> GlobalCache {
|
||||
let (cached_globals, ctx_ptr_value, info, cache_builder) = (
|
||||
&mut self.cached_globals,
|
||||
self.ctx_ptr_value,
|
||||
@ -914,7 +992,7 @@ impl<'a> CtxType<'a> {
|
||||
);
|
||||
|
||||
*cached_globals.entry(index).or_insert_with(|| {
|
||||
let (globals_array_ptr_ptr, index, mutable, wasmer_ty) =
|
||||
let (globals_array_ptr_ptr, index, mutable, wasmer_ty, field_name) =
|
||||
match index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_global_index) => {
|
||||
let desc = info.globals[local_global_index].desc;
|
||||
@ -929,6 +1007,7 @@ impl<'a> CtxType<'a> {
|
||||
local_global_index.index() as u64,
|
||||
desc.mutable,
|
||||
desc.ty,
|
||||
"context_field_ptr_to_local_globals",
|
||||
)
|
||||
}
|
||||
LocalOrImport::Import(import_global_index) => {
|
||||
@ -944,6 +1023,7 @@ impl<'a> CtxType<'a> {
|
||||
import_global_index.index() as u64,
|
||||
desc.mutable,
|
||||
desc.ty,
|
||||
"context_field_ptr_to_imported_globals",
|
||||
)
|
||||
}
|
||||
};
|
||||
@ -953,6 +1033,13 @@ impl<'a> CtxType<'a> {
|
||||
let global_array_ptr = cache_builder
|
||||
.build_load(globals_array_ptr_ptr, "global_array_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
field_name,
|
||||
globals_array_ptr_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
let const_index = intrinsics.i32_ty.const_int(index, false);
|
||||
let global_ptr_ptr = unsafe {
|
||||
cache_builder.build_in_bounds_gep(
|
||||
@ -964,6 +1051,13 @@ impl<'a> CtxType<'a> {
|
||||
let global_ptr = cache_builder
|
||||
.build_load(global_ptr_ptr, "global_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"global_ptr",
|
||||
globals_array_ptr_ptr.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
|
||||
let global_ptr_typed =
|
||||
cache_builder.build_pointer_cast(global_ptr, llvm_ptr_ty, "global_ptr_typed");
|
||||
@ -973,9 +1067,15 @@ impl<'a> CtxType<'a> {
|
||||
ptr_to_value: global_ptr_typed,
|
||||
}
|
||||
} else {
|
||||
GlobalCache::Const {
|
||||
value: cache_builder.build_load(global_ptr_typed, "global_value"),
|
||||
}
|
||||
let value = cache_builder.build_load(global_ptr_typed, "global_value");
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"global",
|
||||
value.as_instruction_value().unwrap(),
|
||||
Some(index as u32),
|
||||
);
|
||||
GlobalCache::Const { value }
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -984,6 +1084,7 @@ impl<'a> CtxType<'a> {
|
||||
&mut self,
|
||||
index: ImportedFuncIndex,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
) -> (PointerValue, PointerValue) {
|
||||
let (cached_imported_functions, ctx_ptr_value, cache_builder) = (
|
||||
&mut self.cached_imported_functions,
|
||||
@ -1002,6 +1103,13 @@ impl<'a> CtxType<'a> {
|
||||
let func_array_ptr = cache_builder
|
||||
.build_load(func_array_ptr_ptr, "func_array_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"context_field_ptr_to_imported_funcs",
|
||||
func_array_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false);
|
||||
let imported_func_ptr = unsafe {
|
||||
cache_builder.build_in_bounds_gep(
|
||||
@ -1023,6 +1131,20 @@ impl<'a> CtxType<'a> {
|
||||
let ctx_ptr = cache_builder
|
||||
.build_load(ctx_ptr_ptr, "ctx_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"imported_func_ptr",
|
||||
func_ptr.as_instruction_value().unwrap(),
|
||||
Some(index.index() as u32),
|
||||
);
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"imported_func_ctx_ptr",
|
||||
ctx_ptr.as_instruction_value().unwrap(),
|
||||
Some(index.index() as u32),
|
||||
);
|
||||
|
||||
ImportedFuncCache { func_ptr, ctx_ptr }
|
||||
});
|
||||
@ -1034,6 +1156,7 @@ impl<'a> CtxType<'a> {
|
||||
&mut self,
|
||||
index: usize,
|
||||
intrinsics: &Intrinsics,
|
||||
module: Rc<RefCell<Module>>,
|
||||
builder: &Builder,
|
||||
) -> PointerValue {
|
||||
assert!(index < INTERNALS_SIZE);
|
||||
@ -1048,6 +1171,13 @@ impl<'a> CtxType<'a> {
|
||||
let local_internals_ptr = builder
|
||||
.build_load(local_internals_ptr_ptr, "local_internals_ptr")
|
||||
.into_pointer_value();
|
||||
tbaa_label(
|
||||
module.clone(),
|
||||
intrinsics,
|
||||
"context_field_ptr_to_internals",
|
||||
local_internals_ptr_ptr.as_instruction_value().unwrap(),
|
||||
None,
|
||||
);
|
||||
unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
local_internals_ptr,
|
||||
@ -1057,3 +1187,83 @@ impl<'a> CtxType<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given an instruction that operates on memory, mark the access as not aliasing
|
||||
// other memory accesses which have a different (label, index) pair.
|
||||
pub fn tbaa_label(
|
||||
module: Rc<RefCell<Module>>,
|
||||
intrinsics: &Intrinsics,
|
||||
label: &str,
|
||||
instruction: InstructionValue,
|
||||
index: Option<u32>,
|
||||
) {
|
||||
// To convey to LLVM that two pointers must be pointing to distinct memory,
|
||||
// we use LLVM's Type Based Aliasing Analysis, or TBAA, to mark the memory
|
||||
// operations as having different types whose pointers may not alias.
|
||||
//
|
||||
// See the LLVM documentation at
|
||||
// https://llvm.org/docs/LangRef.html#tbaa-metadata
|
||||
//
|
||||
// LLVM TBAA supports many features, but we use it in a simple way, with
|
||||
// only scalar types that are children of the root node. Every TBAA type we
|
||||
// declare is NoAlias with the others. See NoAlias, PartialAlias,
|
||||
// MayAlias and MustAlias in the LLVM documentation:
|
||||
// https://llvm.org/docs/AliasAnalysis.html#must-may-and-no-alias-responses
|
||||
|
||||
let module = module.borrow_mut();
|
||||
let context = module.get_context();
|
||||
|
||||
// `!wasmer_tbaa_root = {}`, the TBAA root node for wasmer.
|
||||
let tbaa_root = module
|
||||
.get_global_metadata("wasmer_tbaa_root")
|
||||
.pop()
|
||||
.unwrap_or_else(|| {
|
||||
module.add_global_metadata("wasmer_tbaa_root", &context.metadata_node(&[]));
|
||||
module.get_global_metadata("wasmer_tbaa_root")[0]
|
||||
});
|
||||
|
||||
// Construct (or look up) the type descriptor, for example
|
||||
// `!"local 0" = !{!"local 0", !wasmer_tbaa_root}`.
|
||||
let label = if let Some(idx) = index {
|
||||
format!("{}{}", label, idx)
|
||||
} else {
|
||||
label.to_string()
|
||||
};
|
||||
let type_label = context.metadata_string(label.as_str());
|
||||
let type_tbaa = module
|
||||
.get_global_metadata(label.as_str())
|
||||
.pop()
|
||||
.unwrap_or_else(|| {
|
||||
module.add_global_metadata(
|
||||
label.as_str(),
|
||||
&context.metadata_node(&[type_label.into(), tbaa_root.into()]),
|
||||
);
|
||||
module.get_global_metadata(label.as_str())[0]
|
||||
});
|
||||
|
||||
// Construct (or look up) the access tag, which is a struct of the form
|
||||
// (base type, access type, offset).
|
||||
//
|
||||
// "If BaseTy is a scalar type, Offset must be 0 and BaseTy and AccessTy
|
||||
// must be the same".
|
||||
// -- https://llvm.org/docs/LangRef.html#tbaa-metadata
|
||||
let label = label + "_memop";
|
||||
let type_tbaa = module
|
||||
.get_global_metadata(label.as_str())
|
||||
.pop()
|
||||
.unwrap_or_else(|| {
|
||||
module.add_global_metadata(
|
||||
label.as_str(),
|
||||
&context.metadata_node(&[
|
||||
type_tbaa.into(),
|
||||
type_tbaa.into(),
|
||||
intrinsics.i64_zero.into(),
|
||||
]),
|
||||
);
|
||||
module.get_global_metadata(label.as_str())[0]
|
||||
});
|
||||
|
||||
// Attach the access tag to the instruction.
|
||||
let tbaa_kind = context.get_kind_id("tbaa");
|
||||
instruction.set_metadata(type_tbaa, tbaa_kind);
|
||||
}
|
||||
|
@ -67,9 +67,29 @@ impl ControlFrame {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum ExtraInfo {
|
||||
None,
|
||||
|
||||
// This values is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
|
||||
// machine, but which might not be in the LLVM value. The conversion to
|
||||
// arithmetic NaN is pending. It is required for correctness.
|
||||
PendingF32NaN,
|
||||
|
||||
// This values is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
|
||||
// machine, but which might not be in the LLVM value. The conversion to
|
||||
// arithmetic NaN is pending. It is required for correctness.
|
||||
PendingF64NaN,
|
||||
}
|
||||
impl Default for ExtraInfo {
|
||||
fn default() -> Self {
|
||||
ExtraInfo::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
pub stack: Vec<BasicValueEnum>,
|
||||
pub stack: Vec<(BasicValueEnum, ExtraInfo)>,
|
||||
control_stack: Vec<ControlFrame>,
|
||||
value_counter: Cell<usize>,
|
||||
|
||||
@ -145,10 +165,18 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn push1<T: BasicValue>(&mut self, value: T) {
|
||||
self.stack.push(value.as_basic_value_enum())
|
||||
self.push1_extra(value, ExtraInfo::None);
|
||||
}
|
||||
|
||||
pub fn push1_extra<T: BasicValue>(&mut self, value: T, info: ExtraInfo) {
|
||||
self.stack.push((value.as_basic_value_enum(), info));
|
||||
}
|
||||
|
||||
pub fn pop1(&mut self) -> Result<BasicValueEnum, BinaryReaderError> {
|
||||
Ok(self.pop1_extra()?.0)
|
||||
}
|
||||
|
||||
pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> {
|
||||
self.stack.pop().ok_or(BinaryReaderError {
|
||||
message: "invalid value stack",
|
||||
offset: -1isize as usize,
|
||||
@ -161,6 +189,14 @@ impl State {
|
||||
Ok((v1, v2))
|
||||
}
|
||||
|
||||
pub fn pop2_extra(
|
||||
&mut self,
|
||||
) -> Result<((BasicValueEnum, ExtraInfo), (BasicValueEnum, ExtraInfo)), BinaryReaderError> {
|
||||
let v2 = self.pop1_extra()?;
|
||||
let v1 = self.pop1_extra()?;
|
||||
Ok((v1, v2))
|
||||
}
|
||||
|
||||
pub fn pop3(
|
||||
&mut self,
|
||||
) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> {
|
||||
@ -170,7 +206,23 @@ impl State {
|
||||
Ok((v1, v2, v3))
|
||||
}
|
||||
|
||||
pub fn peek1(&self) -> Result<BasicValueEnum, BinaryReaderError> {
|
||||
pub fn pop3_extra(
|
||||
&mut self,
|
||||
) -> Result<
|
||||
(
|
||||
(BasicValueEnum, ExtraInfo),
|
||||
(BasicValueEnum, ExtraInfo),
|
||||
(BasicValueEnum, ExtraInfo),
|
||||
),
|
||||
BinaryReaderError,
|
||||
> {
|
||||
let v3 = self.pop1_extra()?;
|
||||
let v2 = self.pop1_extra()?;
|
||||
let v1 = self.pop1_extra()?;
|
||||
Ok((v1, v2, v3))
|
||||
}
|
||||
|
||||
pub fn peek1_extra(&self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> {
|
||||
self.stack
|
||||
.get(self.stack.len() - 1)
|
||||
.ok_or(BinaryReaderError {
|
||||
@ -180,7 +232,14 @@ impl State {
|
||||
.map(|v| *v)
|
||||
}
|
||||
|
||||
pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> {
|
||||
pub fn peekn(&self, n: usize) -> Result<Vec<BasicValueEnum>, BinaryReaderError> {
|
||||
Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect())
|
||||
}
|
||||
|
||||
pub fn peekn_extra(
|
||||
&self,
|
||||
n: usize,
|
||||
) -> Result<&[(BasicValueEnum, ExtraInfo)], BinaryReaderError> {
|
||||
self.stack
|
||||
.get(self.stack.len() - n..)
|
||||
.ok_or(BinaryReaderError {
|
||||
@ -189,8 +248,11 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn popn_save(&mut self, n: usize) -> Result<Vec<BasicValueEnum>, BinaryReaderError> {
|
||||
let v = self.peekn(n)?.to_vec();
|
||||
pub fn popn_save_extra(
|
||||
&mut self,
|
||||
n: usize,
|
||||
) -> Result<Vec<(BasicValueEnum, ExtraInfo)>, BinaryReaderError> {
|
||||
let v = self.peekn_extra(n)?.to_vec();
|
||||
self.popn(n)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-middleware-common-tests"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
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.8.0" }
|
||||
wasmer-middleware-common = { path = "../middleware-common", version = "0.8.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.8.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.8.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8.0", optional = true }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
wasmer-middleware-common = { path = "../middleware-common", version = "0.9.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
|
||||
|
||||
[features]
|
||||
clif = []
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-middleware-common"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "Wasmer runtime common middlewares"
|
||||
license = "MIT"
|
||||
@ -8,4 +8,4 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-c-api"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer C API library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -17,19 +17,26 @@ libc = "0.2.60"
|
||||
[dependencies.wasmer-runtime]
|
||||
default-features = false
|
||||
path = "../runtime"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
default-features = false
|
||||
path = "../runtime-core"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
|
||||
[dependencies.wasmer-wasi]
|
||||
default-features = false
|
||||
path = "../wasi"
|
||||
version = "0.9.0"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["cranelift-backend"]
|
||||
default = ["cranelift-backend", "wasi"]
|
||||
debug = ["wasmer-runtime/debug"]
|
||||
cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend-cranelift"]
|
||||
llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"]
|
||||
singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"]
|
||||
wasi = ["wasmer-wasi"]
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.9"
|
||||
|
@ -36,6 +36,7 @@ The C and C++ header files can be found in the source tree of this
|
||||
crate, respectively [`wasmer.h`][wasmer_h] and
|
||||
[`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
|
||||
up-to-date in this repository.
|
||||
The runtime shared library (so, dll, dylib) can also be downloaded in Wasmer [release page](https://github.com/wasmerio/wasmer/releases).
|
||||
|
||||
Here is a simple example to use the C API:
|
||||
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
};
|
||||
use libc::{c_int, c_uint};
|
||||
use std::{ptr, slice};
|
||||
use wasmer_runtime::{Instance, Memory, Module, Value};
|
||||
use wasmer_runtime::{Instance, Module, Value};
|
||||
use wasmer_runtime_core::{export::Export, module::ExportIndex};
|
||||
|
||||
/// Intermediate representation of an `Export` instance that is
|
||||
@ -85,12 +85,41 @@ pub union wasmer_import_export_value {
|
||||
/// List of export/import kinds.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
// ================
|
||||
// ! DANGER !
|
||||
// ================
|
||||
// Do not modify these values without updating the `TryFrom` implementation below
|
||||
pub enum wasmer_import_export_kind {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
}
|
||||
|
||||
impl wasmer_import_export_kind {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::WASM_FUNCTION => "function",
|
||||
Self::WASM_GLOBAL => "global",
|
||||
Self::WASM_MEMORY => "memory",
|
||||
Self::WASM_TABLE => "table",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u32> for wasmer_import_export_kind {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => Self::WASM_FUNCTION,
|
||||
1 => Self::WASM_GLOBAL,
|
||||
2 => Self::WASM_MEMORY,
|
||||
3 => Self::WASM_TABLE,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets export descriptors for the given module
|
||||
@ -355,7 +384,8 @@ pub unsafe extern "C" fn wasmer_export_to_memory(
|
||||
let export = &named_export.export;
|
||||
|
||||
if let Export::Memory(exported_memory) = export {
|
||||
*memory = exported_memory as *const Memory as *mut wasmer_memory_t;
|
||||
let mem = Box::new(exported_memory.clone());
|
||||
*memory = Box::into_raw(mem) as *mut wasmer_memory_t;
|
||||
wasmer_result_t::WASMER_OK
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
|
@ -9,11 +9,11 @@ use crate::{
|
||||
wasmer_byte_array, wasmer_result_t,
|
||||
};
|
||||
use libc::c_uint;
|
||||
use std::{ffi::c_void, ptr, slice, sync::Arc};
|
||||
use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc};
|
||||
use wasmer_runtime::{Global, Memory, Module, Table};
|
||||
use wasmer_runtime_core::{
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::ImportObject,
|
||||
import::{ImportObject, ImportObjectIterator},
|
||||
module::ImportName,
|
||||
types::{FuncSig, Type},
|
||||
};
|
||||
@ -41,6 +41,10 @@ pub struct wasmer_import_descriptor_t;
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_descriptors_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_object_iter_t;
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
@ -51,12 +55,318 @@ pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasi")]
|
||||
mod wasi;
|
||||
|
||||
#[cfg(feature = "wasi")]
|
||||
pub use self::wasi::*;
|
||||
|
||||
/// Gets an entry from an ImportObject at the name and namespace.
|
||||
/// Stores `name`, `namespace`, and `import_export_value` in `import`.
|
||||
/// Thus these must remain valid for the lifetime of `import`.
|
||||
///
|
||||
/// The caller owns all data involved.
|
||||
/// `import_export_value` will be written to based on `tag`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_get_import(
|
||||
import_object: *const wasmer_import_object_t,
|
||||
namespace: wasmer_byte_array,
|
||||
name: wasmer_byte_array,
|
||||
import: *mut wasmer_import_t,
|
||||
import_export_value: *mut wasmer_import_export_value,
|
||||
tag: u32,
|
||||
) -> wasmer_result_t {
|
||||
let tag: wasmer_import_export_kind = if let Ok(t) = TryFrom::try_from(tag) {
|
||||
t
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "wasmer_import_export_tag out of range".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
||||
let namespace_str = if let Ok(ns) = namespace.as_str() {
|
||||
ns
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting namespace to UTF-8 string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
let name_str = if let Ok(name) = name.as_str() {
|
||||
name
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting name to UTF-8 string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
if import.is_null() || import_export_value.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "pointers to import and import_export_value must not be null".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
let import_out = &mut *import;
|
||||
let import_export_value_out = &mut *import_export_value;
|
||||
if let Some(export) =
|
||||
import_object.maybe_with_namespace(namespace_str, |ns| ns.get_export(name_str))
|
||||
{
|
||||
match export {
|
||||
Export::Function { .. } => {
|
||||
if tag != wasmer_import_export_kind::WASM_FUNCTION {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found function, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_FUNCTION;
|
||||
let writer = import_export_value_out.func as *mut Export;
|
||||
*writer = export.clone();
|
||||
}
|
||||
Export::Memory(memory) => {
|
||||
if tag != wasmer_import_export_kind::WASM_MEMORY {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found memory, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_MEMORY;
|
||||
let writer = import_export_value_out.func as *mut Memory;
|
||||
*writer = memory.clone();
|
||||
}
|
||||
Export::Table(table) => {
|
||||
if tag != wasmer_import_export_kind::WASM_TABLE {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found table, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_TABLE;
|
||||
let writer = import_export_value_out.func as *mut Table;
|
||||
*writer = table.clone();
|
||||
}
|
||||
Export::Global(global) => {
|
||||
if tag != wasmer_import_export_kind::WASM_GLOBAL {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Found global, expected {}", tag.to_str()),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
import_out.tag = wasmer_import_export_kind::WASM_GLOBAL;
|
||||
let writer = import_export_value_out.func as *mut Global;
|
||||
*writer = global.clone();
|
||||
}
|
||||
}
|
||||
|
||||
import_out.value = *import_export_value;
|
||||
import_out.module_name = namespace;
|
||||
import_out.import_name = name;
|
||||
|
||||
wasmer_result_t::WASMER_OK
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: format!("Export {} {} not found", namespace_str, name_str),
|
||||
});
|
||||
wasmer_result_t::WASMER_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
/// private wrapper data type used for casting
|
||||
#[repr(C)]
|
||||
struct WasmerImportObjectIterator(
|
||||
std::iter::Peekable<Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>>,
|
||||
);
|
||||
|
||||
/// Create an iterator over the functions in the import object.
|
||||
/// Get the next import with `wasmer_import_object_iter_next`
|
||||
/// Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iterate_functions(
|
||||
import_object: *const wasmer_import_object_t,
|
||||
) -> *mut wasmer_import_object_iter_t {
|
||||
if import_object.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object must not be null".to_owned(),
|
||||
});
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let import_object: &ImportObject = &*(import_object as *const ImportObject);
|
||||
let iter_inner = Box::new(import_object.clone_ref().into_iter().filter(|(_, _, e)| {
|
||||
if let Export::Function { .. } = e {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})) as Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>;
|
||||
let iterator = Box::new(WasmerImportObjectIterator(iter_inner.peekable()));
|
||||
|
||||
Box::into_raw(iterator) as *mut wasmer_import_object_iter_t
|
||||
}
|
||||
|
||||
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
/// was an error or there's nothing left to return.
|
||||
///
|
||||
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_next(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
import: *mut wasmer_import_t,
|
||||
) -> wasmer_result_t {
|
||||
if import_object_iter.is_null() || import.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object_iter and import must not be null".to_owned(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
|
||||
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
let out = &mut *import;
|
||||
// TODO: the copying here can be optimized away, we just need to use a different type of
|
||||
// iterator internally
|
||||
if let Some((namespace, name, export)) = iter.0.next() {
|
||||
let ns = {
|
||||
let mut n = namespace.clone();
|
||||
n.shrink_to_fit();
|
||||
n.into_bytes()
|
||||
};
|
||||
let ns_bytes = wasmer_byte_array {
|
||||
bytes: ns.as_ptr(),
|
||||
bytes_len: ns.len() as u32,
|
||||
};
|
||||
|
||||
let name = {
|
||||
let mut n = name.clone();
|
||||
n.shrink_to_fit();
|
||||
n.into_bytes()
|
||||
};
|
||||
let name_bytes = wasmer_byte_array {
|
||||
bytes: name.as_ptr(),
|
||||
bytes_len: name.len() as u32,
|
||||
};
|
||||
|
||||
out.module_name = ns_bytes;
|
||||
out.import_name = name_bytes;
|
||||
|
||||
std::mem::forget(ns);
|
||||
std::mem::forget(name);
|
||||
|
||||
match export {
|
||||
Export::Function { .. } => {
|
||||
let func = Box::new(export.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_FUNCTION;
|
||||
out.value = wasmer_import_export_value {
|
||||
func: Box::into_raw(func) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Global(global) => {
|
||||
let glbl = Box::new(global.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_GLOBAL;
|
||||
out.value = wasmer_import_export_value {
|
||||
global: Box::into_raw(glbl) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Memory(memory) => {
|
||||
let mem = Box::new(memory.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_MEMORY;
|
||||
out.value = wasmer_import_export_value {
|
||||
memory: Box::into_raw(mem) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
Export::Table(table) => {
|
||||
let tbl = Box::new(table.clone());
|
||||
|
||||
out.tag = wasmer_import_export_kind::WASM_TABLE;
|
||||
out.value = wasmer_import_export_value {
|
||||
memory: Box::into_raw(tbl) as *mut _ as *const _,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wasmer_result_t::WASMER_OK
|
||||
} else {
|
||||
wasmer_result_t::WASMER_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
/// not return any new data
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_at_end(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
) -> bool {
|
||||
if import_object_iter.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "import_object_iter must not be null".to_owned(),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
|
||||
iter.0.peek().is_none()
|
||||
}
|
||||
|
||||
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_iter_destroy(
|
||||
import_object_iter: *mut wasmer_import_object_iter_t,
|
||||
) {
|
||||
if !import_object_iter.is_null() {
|
||||
let _ = Box::from_raw(import_object_iter as *mut WasmerImportObjectIterator);
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
///
|
||||
/// This function does not free the memory in `wasmer_import_object_t`;
|
||||
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_imports_destroy(
|
||||
imports: *mut wasmer_import_t,
|
||||
imports_len: u32,
|
||||
) {
|
||||
if imports.is_null() {
|
||||
return;
|
||||
}
|
||||
let imports: &[wasmer_import_t] = &*slice::from_raw_parts_mut(imports, imports_len as usize);
|
||||
for import in imports {
|
||||
let _namespace: Vec<u8> = Vec::from_raw_parts(
|
||||
import.module_name.bytes as *mut u8,
|
||||
import.module_name.bytes_len as usize,
|
||||
import.module_name.bytes_len as usize,
|
||||
);
|
||||
let _name: Vec<u8> = Vec::from_raw_parts(
|
||||
import.import_name.bytes as *mut u8,
|
||||
import.import_name.bytes_len as usize,
|
||||
import.import_name.bytes_len as usize,
|
||||
);
|
||||
match import.tag {
|
||||
wasmer_import_export_kind::WASM_FUNCTION => {
|
||||
let _: Box<Export> = Box::from_raw(import.value.func as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_GLOBAL => {
|
||||
let _: Box<Global> = Box::from_raw(import.value.global as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let _: Box<Memory> = Box::from_raw(import.value.memory as *mut _);
|
||||
}
|
||||
wasmer_import_export_kind::WASM_TABLE => {
|
||||
let _: Box<Table> = Box::from_raw(import.value.table as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_extend(
|
||||
import_object: *mut wasmer_import_object_t,
|
||||
imports: *mut wasmer_import_t,
|
||||
imports: *const wasmer_import_t,
|
||||
imports_len: c_uint,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
101
lib/runtime-c-api/src/import/wasi.rs
Normal file
101
lib/runtime-c-api/src/import/wasi.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use super::*;
|
||||
use crate::get_slice_checked;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Opens a directory that's visible to the WASI module as `alias` but
|
||||
/// is backed by the host file at `host_file_path`
|
||||
#[repr(C)]
|
||||
pub struct wasmer_wasi_map_dir_entry_t {
|
||||
/// What the WASI module will see in its virtual root
|
||||
pub alias: wasmer_byte_array,
|
||||
/// The backing file that the WASI module will interact with via the alias
|
||||
pub host_file_path: wasmer_byte_array,
|
||||
}
|
||||
|
||||
impl wasmer_wasi_map_dir_entry_t {
|
||||
/// Converts the data into owned, Rust types
|
||||
pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), std::str::Utf8Error> {
|
||||
let alias = self.alias.as_str()?.to_owned();
|
||||
let host_path = std::path::PathBuf::from(self.host_file_path.as_str()?);
|
||||
|
||||
Ok((alias, host_path))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a WASI import object.
|
||||
///
|
||||
/// This function treats null pointers as empty collections.
|
||||
/// For example, passing null for a string in `args`, will lead to a zero
|
||||
/// length argument in that position.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_wasi_generate_import_object(
|
||||
args: *const wasmer_byte_array,
|
||||
args_len: c_uint,
|
||||
envs: *const wasmer_byte_array,
|
||||
envs_len: c_uint,
|
||||
preopened_files: *const wasmer_byte_array,
|
||||
preopened_files_len: c_uint,
|
||||
mapped_dirs: *const wasmer_wasi_map_dir_entry_t,
|
||||
mapped_dirs_len: c_uint,
|
||||
) -> *mut wasmer_import_object_t {
|
||||
let arg_list = get_slice_checked(args, args_len as usize);
|
||||
let env_list = get_slice_checked(envs, envs_len as usize);
|
||||
let preopened_file_list = get_slice_checked(preopened_files, preopened_files_len as usize);
|
||||
let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize);
|
||||
|
||||
wasmer_wasi_generate_import_object_inner(
|
||||
arg_list,
|
||||
env_list,
|
||||
preopened_file_list,
|
||||
mapped_dir_list,
|
||||
)
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Inner function that wraps error handling
|
||||
fn wasmer_wasi_generate_import_object_inner(
|
||||
arg_list: &[wasmer_byte_array],
|
||||
env_list: &[wasmer_byte_array],
|
||||
preopened_file_list: &[wasmer_byte_array],
|
||||
mapped_dir_list: &[wasmer_wasi_map_dir_entry_t],
|
||||
) -> Result<*mut wasmer_import_object_t, std::str::Utf8Error> {
|
||||
let arg_vec = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect();
|
||||
let env_vec = env_list
|
||||
.iter()
|
||||
.map(|env_var| unsafe { env_var.as_vec() })
|
||||
.collect();
|
||||
let po_file_vec = preopened_file_list
|
||||
.iter()
|
||||
.map(|po_file| Ok(unsafe { PathBuf::from(po_file.as_str()?) }.to_owned()))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mapped_dir_vec = mapped_dir_list
|
||||
.iter()
|
||||
.map(|entry| unsafe { entry.as_tuple() })
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let import_object = Box::new(wasmer_wasi::generate_import_object(
|
||||
arg_vec,
|
||||
env_vec,
|
||||
po_file_vec,
|
||||
mapped_dir_vec,
|
||||
));
|
||||
Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t)
|
||||
}
|
||||
|
||||
/// Convenience function that creates a WASI import object with no arguments,
|
||||
/// environment variables, preopened files, or mapped directories.
|
||||
///
|
||||
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
/// empty values.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wasmer_import_object_t
|
||||
{
|
||||
let import_object = Box::new(wasmer_wasi::generate_import_object(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
));
|
||||
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
@ -75,6 +75,7 @@ pub unsafe extern "C" fn wasmer_instantiate(
|
||||
|
||||
let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new);
|
||||
|
||||
// TODO check that tag is actually in bounds here
|
||||
let export = match import.tag {
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let mem = import.value.memory as *mut Memory;
|
||||
|
@ -129,3 +129,34 @@ pub struct wasmer_byte_array {
|
||||
pub bytes: *const u8,
|
||||
pub bytes_len: u32,
|
||||
}
|
||||
|
||||
impl wasmer_byte_array {
|
||||
/// Get the data as a slice
|
||||
pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
|
||||
get_slice_checked(self.bytes, self.bytes_len as usize)
|
||||
}
|
||||
|
||||
/// Copy the data into an owned Vec
|
||||
pub unsafe fn as_vec(&self) -> Vec<u8> {
|
||||
let mut out = Vec::with_capacity(self.bytes_len as usize);
|
||||
out.extend_from_slice(self.as_slice());
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Read the data as a &str, returns an error if the string is not valid UTF8
|
||||
pub unsafe fn as_str<'a>(&self) -> Result<&'a str, std::str::Utf8Error> {
|
||||
std::str::from_utf8(self.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a slice from a pointer and a length, returning an empty slice if the
|
||||
/// pointer is null
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
|
||||
if ptr.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
2
lib/runtime-c-api/tests/.gitignore
vendored
2
lib/runtime-c-api/tests/.gitignore
vendored
@ -25,3 +25,5 @@ test-tables
|
||||
test-validate
|
||||
test-context
|
||||
test-module-import-instantiate
|
||||
test-wasi-import-object
|
||||
|
||||
|
@ -6,6 +6,8 @@ add_executable(test-exports test-exports.c)
|
||||
add_executable(test-globals test-globals.c)
|
||||
add_executable(test-import-function test-import-function.c)
|
||||
add_executable(test-imports test-imports.c)
|
||||
add_executable(test-import-object test-import-object.c)
|
||||
add_executable(test-wasi-import-object test-wasi-import-object.c)
|
||||
add_executable(test-instantiate test-instantiate.c)
|
||||
add_executable(test-memory test-memory.c)
|
||||
add_executable(test-module test-module.c)
|
||||
@ -58,6 +60,14 @@ target_link_libraries(test-imports general ${WASMER_LIB})
|
||||
target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-imports test-imports)
|
||||
|
||||
target_link_libraries(test-import-object general ${WASMER_LIB})
|
||||
target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-import-object test-import-object)
|
||||
|
||||
target_link_libraries(test-wasi-import-object general ${WASMER_LIB})
|
||||
target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-wasi-import-object test-wasi-import-object)
|
||||
|
||||
target_link_libraries(test-instantiate general ${WASMER_LIB})
|
||||
target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-instantiate test-instantiate)
|
||||
|
3
lib/runtime-c-api/tests/assets/README.md
Normal file
3
lib/runtime-c-api/tests/assets/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
These are used in tests in the parent directory.
|
||||
|
||||
To keep the generated wasm small, use `wasm-opt` and `wasm-strip` from wabt-tools (can be installed via wapm). Addtionally, consider passing the `-C opt-level=z` flag to `rustc` to optimize for size.
|
31
lib/runtime-c-api/tests/assets/extended_wasi.rs
Normal file
31
lib/runtime-c-api/tests/assets/extended_wasi.rs
Normal file
@ -0,0 +1,31 @@
|
||||
extern "C" {
|
||||
fn host_print(ptr: u32, len: u32);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
|
||||
println!("Found {} args on program {}", args.len(), args[0]);
|
||||
|
||||
let env_vars = std::env::vars()
|
||||
.map(|(arg, val)| format!("{}={}", arg, val))
|
||||
.collect::<Vec<String>>();
|
||||
let env_var_list = env_vars.join(", ");
|
||||
|
||||
println!("Found {} env vars: {}", env_vars.len(), env_var_list);
|
||||
|
||||
let dirs_in_root = std::fs::read_dir("/")
|
||||
.unwrap()
|
||||
.map(|e| e.map(|inner| format!("{:?}", inner)))
|
||||
.collect::<Result<Vec<String>, _>>()
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Found {} pre opened dirs: {}",
|
||||
dirs_in_root.len(),
|
||||
dirs_in_root.join(", ")
|
||||
);
|
||||
|
||||
const HOST_STR: &str = "This string came from a WASI module";
|
||||
unsafe { host_print(HOST_STR.as_ptr() as u32, HOST_STR.len() as u32) };
|
||||
}
|
BIN
lib/runtime-c-api/tests/assets/extended_wasi.wasm
Executable file
BIN
lib/runtime-c-api/tests/assets/extended_wasi.wasm
Executable file
Binary file not shown.
@ -1,3 +1,4 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
@ -242,7 +243,7 @@ int main()
|
||||
|
||||
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
|
||||
|
||||
printf("Result: %ld\n", outputs[0].value.I64);
|
||||
printf("Result: %" PRId64 "\n", outputs[0].value.I64);
|
||||
|
||||
assert(outputs[0].value.I64 == 7);
|
||||
assert(call_result == WASMER_OK);
|
||||
|
BIN
lib/runtime-c-api/tests/test-import-object
Executable file
BIN
lib/runtime-c-api/tests/test-import-object
Executable file
Binary file not shown.
172
lib/runtime-c-api/tests/test-import-object.c
Normal file
172
lib/runtime-c-api/tests/test-import-object.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
bool static print_str_called = false;
|
||||
|
||||
// Host function that will be imported into the Web Assembly Instance
|
||||
void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
|
||||
{
|
||||
print_str_called = true;
|
||||
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
|
||||
uint32_t mem_len = wasmer_memory_length(memory);
|
||||
uint8_t *mem_bytes = wasmer_memory_data(memory);
|
||||
printf("%.*s", len, mem_bytes + ptr);
|
||||
}
|
||||
|
||||
// Use the last_error API to retrieve error messages
|
||||
void print_wasmer_error()
|
||||
{
|
||||
int error_len = wasmer_last_error_length();
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char *error_str = malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create a new func to hold the parameter and signature
|
||||
// of our `print_str` host function
|
||||
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
|
||||
wasmer_value_tag returns_sig[] = {};
|
||||
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
|
||||
|
||||
// Create module name for our imports
|
||||
// represented in bytes for UTF-8 compatability
|
||||
const char *module_name = "env";
|
||||
wasmer_byte_array module_name_bytes;
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
// Define a function import
|
||||
const char *import_name = "_print_str";
|
||||
wasmer_byte_array import_name_bytes;
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
wasmer_import_t func_import;
|
||||
func_import.module_name = module_name_bytes;
|
||||
func_import.import_name = import_name_bytes;
|
||||
func_import.tag = WASM_FUNCTION;
|
||||
func_import.value.func = func;
|
||||
|
||||
// Define a memory import
|
||||
const char *import_memory_name = "memory";
|
||||
wasmer_byte_array import_memory_name_bytes;
|
||||
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
|
||||
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
|
||||
wasmer_import_t memory_import;
|
||||
memory_import.module_name = module_name_bytes;
|
||||
memory_import.import_name = import_memory_name_bytes;
|
||||
memory_import.tag = WASM_MEMORY;
|
||||
wasmer_memory_t *memory = NULL;
|
||||
wasmer_limits_t descriptor;
|
||||
descriptor.min = 256;
|
||||
wasmer_limit_option_t max;
|
||||
max.has_some = true;
|
||||
max.some = 256;
|
||||
descriptor.max = max;
|
||||
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
|
||||
if (memory_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
memory_import.value.memory = memory;
|
||||
|
||||
// Define a global import
|
||||
const char *import_global_name = "__memory_base";
|
||||
wasmer_byte_array import_global_name_bytes;
|
||||
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
|
||||
import_global_name_bytes.bytes_len = strlen(import_global_name);
|
||||
wasmer_import_t global_import;
|
||||
global_import.module_name = module_name_bytes;
|
||||
global_import.import_name = import_global_name_bytes;
|
||||
global_import.tag = WASM_GLOBAL;
|
||||
wasmer_value_t val;
|
||||
val.tag = WASM_I32;
|
||||
val.value.I32 = 1024;
|
||||
wasmer_global_t *global = wasmer_global_new(val, false);
|
||||
global_import.value.global = global;
|
||||
|
||||
// Define a table import
|
||||
const char *import_table_name = "table";
|
||||
wasmer_byte_array import_table_name_bytes;
|
||||
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
|
||||
import_table_name_bytes.bytes_len = strlen(import_table_name);
|
||||
wasmer_import_t table_import;
|
||||
table_import.module_name = module_name_bytes;
|
||||
table_import.import_name = import_table_name_bytes;
|
||||
table_import.tag = WASM_TABLE;
|
||||
wasmer_table_t *table = NULL;
|
||||
wasmer_limits_t table_descriptor;
|
||||
table_descriptor.min = 256;
|
||||
wasmer_limit_option_t table_max;
|
||||
table_max.has_some = true;
|
||||
table_max.some = 256;
|
||||
table_descriptor.max = table_max;
|
||||
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
|
||||
if (table_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
table_import.value.table = table;
|
||||
|
||||
// Define an empty import object
|
||||
wasmer_import_object_t *import_object = wasmer_import_object_new();
|
||||
// Create our imports
|
||||
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
|
||||
int imports_len = sizeof(imports) / sizeof(imports[0]);
|
||||
// Add our imports to the import object
|
||||
wasmer_import_object_extend(import_object, imports, imports_len);
|
||||
|
||||
// Read the wasm file bytes
|
||||
FILE *file = fopen("assets/hello_wasm.wasm", "r");
|
||||
fseek(file, 0, SEEK_END);
|
||||
long len = ftell(file);
|
||||
uint8_t *bytes = malloc(len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(bytes, 1, len, file);
|
||||
fclose(file);
|
||||
|
||||
wasmer_module_t *module = NULL;
|
||||
// Compile the WebAssembly module
|
||||
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
|
||||
printf("Compile result: %d\n", compile_result);
|
||||
if (compile_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
assert(compile_result == WASMER_OK);
|
||||
|
||||
// Instantiatoe the module with our import_object
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object);
|
||||
printf("Instantiate result: %d\n", instantiate_result);
|
||||
if (instantiate_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
assert(instantiate_result == WASMER_OK);
|
||||
|
||||
// Call the exported "hello_wasm" function of our instance
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t results[] = {result_one};
|
||||
wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call_result);
|
||||
assert(call_result == WASMER_OK);
|
||||
assert(print_str_called);
|
||||
|
||||
// Use *_destroy methods to cleanup as specified in the header documentation
|
||||
wasmer_import_func_destroy(func);
|
||||
wasmer_global_destroy(global);
|
||||
wasmer_memory_destroy(memory);
|
||||
wasmer_table_destroy(table);
|
||||
wasmer_instance_destroy(instance);
|
||||
wasmer_import_object_destroy(import_object);
|
||||
wasmer_module_destroy(module);
|
||||
|
||||
return 0;
|
||||
}
|
BIN
lib/runtime-c-api/tests/test-wasi-import-object
Executable file
BIN
lib/runtime-c-api/tests/test-wasi-import-object
Executable file
Binary file not shown.
250
lib/runtime-c-api/tests/test-wasi-import-object.c
Normal file
250
lib/runtime-c-api/tests/test-wasi-import-object.c
Normal file
@ -0,0 +1,250 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool host_print_called = false;
|
||||
|
||||
// Host function that will be imported into the Web Assembly Instance
|
||||
void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
|
||||
{
|
||||
host_print_called = true;
|
||||
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
|
||||
uint32_t mem_len = wasmer_memory_length(memory);
|
||||
uint8_t *mem_bytes = wasmer_memory_data(memory);
|
||||
printf("%.*s", len, mem_bytes + ptr);
|
||||
}
|
||||
|
||||
// Use the last_error API to retrieve error messages
|
||||
void print_wasmer_error()
|
||||
{
|
||||
int error_len = wasmer_last_error_length();
|
||||
printf("Error len: `%d`\n", error_len);
|
||||
char *error_str = malloc(error_len);
|
||||
wasmer_last_error_message(error_str, error_len);
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
}
|
||||
|
||||
// helper function to print byte array to stdout
|
||||
void print_byte_array(wasmer_byte_array *arr) {
|
||||
for (int i = 0; i < arr->bytes_len; ++i) {
|
||||
putchar(arr->bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create a new func to hold the parameter and signature
|
||||
// of our `host_print` host function
|
||||
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
|
||||
wasmer_value_tag returns_sig[] = {};
|
||||
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0);
|
||||
|
||||
// Create module name for our imports
|
||||
// represented in bytes for UTF-8 compatability
|
||||
const char *module_name = "env";
|
||||
wasmer_byte_array module_name_bytes;
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
// Define a function import
|
||||
const char *import_name = "host_print";
|
||||
wasmer_byte_array import_name_bytes;
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
wasmer_import_t func_import;
|
||||
func_import.module_name = module_name_bytes;
|
||||
func_import.import_name = import_name_bytes;
|
||||
func_import.tag = WASM_FUNCTION;
|
||||
func_import.value.func = func;
|
||||
|
||||
// Define a memory import
|
||||
const char *import_memory_name = "memory";
|
||||
wasmer_byte_array import_memory_name_bytes;
|
||||
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
|
||||
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
|
||||
wasmer_import_t memory_import;
|
||||
memory_import.module_name = module_name_bytes;
|
||||
memory_import.import_name = import_memory_name_bytes;
|
||||
memory_import.tag = WASM_MEMORY;
|
||||
wasmer_memory_t *memory = NULL;
|
||||
wasmer_limits_t descriptor;
|
||||
descriptor.min = 256;
|
||||
wasmer_limit_option_t max;
|
||||
max.has_some = true;
|
||||
max.some = 256;
|
||||
descriptor.max = max;
|
||||
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
|
||||
if (memory_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
memory_import.value.memory = memory;
|
||||
|
||||
// Define a global import
|
||||
const char *import_global_name = "__memory_base";
|
||||
wasmer_byte_array import_global_name_bytes;
|
||||
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
|
||||
import_global_name_bytes.bytes_len = strlen(import_global_name);
|
||||
wasmer_import_t global_import;
|
||||
global_import.module_name = module_name_bytes;
|
||||
global_import.import_name = import_global_name_bytes;
|
||||
global_import.tag = WASM_GLOBAL;
|
||||
wasmer_value_t val;
|
||||
val.tag = WASM_I32;
|
||||
val.value.I32 = 1024;
|
||||
wasmer_global_t *global = wasmer_global_new(val, false);
|
||||
global_import.value.global = global;
|
||||
|
||||
// Define a table import
|
||||
const char *import_table_name = "table";
|
||||
wasmer_byte_array import_table_name_bytes;
|
||||
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
|
||||
import_table_name_bytes.bytes_len = strlen(import_table_name);
|
||||
wasmer_import_t table_import;
|
||||
table_import.module_name = module_name_bytes;
|
||||
table_import.import_name = import_table_name_bytes;
|
||||
table_import.tag = WASM_TABLE;
|
||||
wasmer_table_t *table = NULL;
|
||||
wasmer_limits_t table_descriptor;
|
||||
table_descriptor.min = 256;
|
||||
wasmer_limit_option_t table_max;
|
||||
table_max.has_some = true;
|
||||
table_max.some = 256;
|
||||
table_descriptor.max = table_max;
|
||||
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
|
||||
if (table_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
table_import.value.table = table;
|
||||
|
||||
|
||||
// Create arbitrary arguments for our program
|
||||
|
||||
// Set up data for our WASI import object
|
||||
//
|
||||
// Environment variables and program arguments are processed by the WASI
|
||||
// program. They will not have any effects unless the program includes
|
||||
// logic to process them.
|
||||
const char *wasi_prog_name = "wasi_test_program";
|
||||
const char *wasi_first_arg = "--help";
|
||||
wasmer_byte_array args[] = {
|
||||
{ .bytes = (const uint8_t *) wasi_prog_name,
|
||||
.bytes_len = strlen(wasi_prog_name) },
|
||||
{ .bytes = (const uint8_t *) wasi_first_arg,
|
||||
.bytes_len = strlen(wasi_first_arg) }
|
||||
};
|
||||
int wasi_argc = sizeof(args) / sizeof(args[0]);
|
||||
|
||||
// Create arbitrary environment variables for our program;
|
||||
const char *wasi_color_env = "COLOR=TRUE";
|
||||
const char *wasi_app_should_log = "APP_SHOULD_LOG=FALSE";
|
||||
wasmer_byte_array envs[] = {
|
||||
{ .bytes = (const uint8_t *) wasi_color_env,
|
||||
.bytes_len = strlen(wasi_color_env) },
|
||||
{ .bytes = (const uint8_t *) wasi_app_should_log,
|
||||
.bytes_len = strlen(wasi_app_should_log) }
|
||||
};
|
||||
int wasi_env_len = sizeof(args) / sizeof(args[0]);
|
||||
|
||||
// Open the host's current directory under a different name.
|
||||
// WARNING: this gives the WASI module limited access to your host's file system,
|
||||
// use caution when granting these permissions to untrusted Wasm modules.
|
||||
const char *wasi_map_dir_alias = "the_host_current_dir";
|
||||
const char *wasi_map_dir_host_path = ".";
|
||||
wasmer_wasi_map_dir_entry_t mapped_dirs[] = {
|
||||
{ .alias =
|
||||
{ .bytes = (const uint8_t *) wasi_map_dir_alias,
|
||||
.bytes_len = strlen(wasi_map_dir_alias) },
|
||||
.host_file_path =
|
||||
{ .bytes = (const uint8_t *) wasi_map_dir_host_path,
|
||||
.bytes_len = strlen(wasi_map_dir_host_path) } }
|
||||
};
|
||||
int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]);
|
||||
|
||||
// Create the WASI import object
|
||||
wasmer_import_object_t *import_object =
|
||||
wasmer_wasi_generate_import_object(args, wasi_argc,
|
||||
envs, wasi_env_len,
|
||||
NULL, 0,
|
||||
mapped_dirs, mapped_dir_len);
|
||||
|
||||
// Create our imports
|
||||
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
|
||||
int imports_len = sizeof(imports) / sizeof(imports[0]);
|
||||
// Add our imports to the import object
|
||||
wasmer_import_object_extend(import_object, imports, imports_len);
|
||||
|
||||
// Read the wasm file bytes
|
||||
FILE *file = fopen("assets/extended_wasi.wasm", "r");
|
||||
assert(file);
|
||||
fseek(file, 0, SEEK_END);
|
||||
long len = ftell(file);
|
||||
uint8_t *bytes = malloc(len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(bytes, 1, len, file);
|
||||
fclose(file);
|
||||
|
||||
wasmer_module_t *module = NULL;
|
||||
// Compile the WebAssembly module
|
||||
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
|
||||
printf("Compile result: %d\n", compile_result);
|
||||
if (compile_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
assert(compile_result == WASMER_OK);
|
||||
|
||||
// Instantiatoe the module with our import_object
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object);
|
||||
printf("Instantiate result: %d\n", instantiate_result);
|
||||
if (instantiate_result != WASMER_OK)
|
||||
{
|
||||
print_wasmer_error();
|
||||
}
|
||||
assert(instantiate_result == WASMER_OK);
|
||||
|
||||
// Call the exported "hello_wasm" function of our instance
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t results[] = {result_one};
|
||||
// _start runs before main for WASI programs
|
||||
wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call_result);
|
||||
assert(call_result == WASMER_OK);
|
||||
assert(host_print_called);
|
||||
|
||||
wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object);
|
||||
|
||||
puts("Functions in import object:");
|
||||
while ( !wasmer_import_object_iter_at_end(func_iter) ) {
|
||||
wasmer_import_t import;
|
||||
wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import);
|
||||
assert(result == WASMER_OK);
|
||||
|
||||
print_byte_array(&import.module_name);
|
||||
putchar(' ');
|
||||
print_byte_array(&import.import_name);
|
||||
putchar('\n');
|
||||
|
||||
assert(import.tag == WASM_FUNCTION);
|
||||
assert(import.value.func);
|
||||
wasmer_import_object_imports_destroy(&import, 1);
|
||||
}
|
||||
wasmer_import_object_iter_destroy(func_iter);
|
||||
|
||||
// Use *_destroy methods to cleanup as specified in the header documentation
|
||||
wasmer_import_func_destroy(func);
|
||||
wasmer_global_destroy(global);
|
||||
wasmer_memory_destroy(memory);
|
||||
wasmer_table_destroy(table);
|
||||
wasmer_instance_destroy(instance);
|
||||
wasmer_import_object_destroy(import_object);
|
||||
wasmer_module_destroy(module);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
* List of export/import kinds.
|
||||
*/
|
||||
enum wasmer_import_export_kind {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
};
|
||||
typedef uint32_t wasmer_import_export_kind;
|
||||
|
||||
@ -138,6 +138,10 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_import_object_iter_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
@ -170,6 +174,21 @@ typedef struct {
|
||||
|
||||
} wasmer_trampoline_buffer_t;
|
||||
|
||||
/**
|
||||
* Opens a directory that's visible to the WASI module as `alias` but
|
||||
* is backed by the host file at `host_file_path`
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* What the WASI module will see in its virtual root
|
||||
*/
|
||||
wasmer_byte_array alias;
|
||||
/**
|
||||
* The backing file that the WASI module will interact with via the alias
|
||||
*/
|
||||
wasmer_byte_array host_file_path;
|
||||
} wasmer_wasi_map_dir_entry_t;
|
||||
|
||||
/**
|
||||
* Creates a new Module from the given wasm bytes.
|
||||
*
|
||||
@ -451,9 +470,60 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
* Extends an existing import object with new imports
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
const wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/**
|
||||
* Gets an entry from an ImportObject at the name and namespace.
|
||||
* Stores `name`, `namespace`, and `import_export_value` in `import`.
|
||||
* Thus these must remain valid for the lifetime of `import`.
|
||||
*
|
||||
* The caller owns all data involved.
|
||||
* `import_export_value` will be written to based on `tag`.
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
|
||||
wasmer_byte_array namespace_,
|
||||
wasmer_byte_array name,
|
||||
wasmer_import_t *import,
|
||||
wasmer_import_export_value *import_export_value,
|
||||
uint32_t tag);
|
||||
|
||||
/**
|
||||
* Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
*
|
||||
* This function does not free the memory in `wasmer_import_object_t`;
|
||||
* it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
*/
|
||||
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
|
||||
|
||||
/**
|
||||
* Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
* not return any new data
|
||||
*/
|
||||
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/**
|
||||
* Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
*/
|
||||
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/**
|
||||
* Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
* was an error or there's nothing left to return.
|
||||
*
|
||||
* To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
* To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
|
||||
wasmer_import_t *import);
|
||||
|
||||
/**
|
||||
* Create an iterator over the functions in the import object.
|
||||
* Get the next import with `wasmer_import_object_iter_next`
|
||||
* Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
*/
|
||||
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Creates a new empty import object.
|
||||
* See also `wasmer_import_object_append`
|
||||
@ -756,4 +826,29 @@ void *wasmer_trampoline_get_context(void);
|
||||
*/
|
||||
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
||||
|
||||
/**
|
||||
* Convenience function that creates a WASI import object with no arguments,
|
||||
* environment variables, preopened files, or mapped directories.
|
||||
*
|
||||
* This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
* empty values.
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_wasi_generate_default_import_object(void);
|
||||
|
||||
/**
|
||||
* Creates a WASI import object.
|
||||
*
|
||||
* This function treats null pointers as empty collections.
|
||||
* For example, passing null for a string in `args`, will lead to a zero
|
||||
* length argument in that position.
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
|
||||
unsigned int args_len,
|
||||
const wasmer_byte_array *envs,
|
||||
unsigned int envs_len,
|
||||
const wasmer_byte_array *preopened_files,
|
||||
unsigned int preopened_files_len,
|
||||
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
|
||||
unsigned int mapped_dirs_len);
|
||||
|
||||
#endif /* WASMER_H */
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
/// List of export/import kinds.
|
||||
enum class wasmer_import_export_kind : uint32_t {
|
||||
WASM_FUNCTION,
|
||||
WASM_GLOBAL,
|
||||
WASM_MEMORY,
|
||||
WASM_TABLE,
|
||||
WASM_FUNCTION = 0,
|
||||
WASM_GLOBAL = 1,
|
||||
WASM_MEMORY = 2,
|
||||
WASM_TABLE = 3,
|
||||
};
|
||||
|
||||
enum class wasmer_result_t {
|
||||
@ -120,6 +120,10 @@ struct wasmer_import_t {
|
||||
wasmer_import_export_value value;
|
||||
};
|
||||
|
||||
struct wasmer_import_object_iter_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
@ -154,6 +158,15 @@ struct wasmer_trampoline_buffer_t {
|
||||
|
||||
};
|
||||
|
||||
/// Opens a directory that's visible to the WASI module as `alias` but
|
||||
/// is backed by the host file at `host_file_path`
|
||||
struct wasmer_wasi_map_dir_entry_t {
|
||||
/// What the WASI module will see in its virtual root
|
||||
wasmer_byte_array alias;
|
||||
/// The backing file that the WASI module will interact with via the alias
|
||||
wasmer_byte_array host_file_path;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
/// Creates a new Module from the given wasm bytes.
|
||||
@ -359,9 +372,48 @@ void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
const wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/// Gets an entry from an ImportObject at the name and namespace.
|
||||
/// Stores `name`, `namespace`, and `import_export_value` in `import`.
|
||||
/// Thus these must remain valid for the lifetime of `import`.
|
||||
///
|
||||
/// The caller owns all data involved.
|
||||
/// `import_export_value` will be written to based on `tag`.
|
||||
wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *import_object,
|
||||
wasmer_byte_array namespace_,
|
||||
wasmer_byte_array name,
|
||||
wasmer_import_t *import,
|
||||
wasmer_import_export_value *import_export_value,
|
||||
uint32_t tag);
|
||||
|
||||
/// Frees the memory allocated in `wasmer_import_object_iter_next`
|
||||
///
|
||||
/// This function does not free the memory in `wasmer_import_object_t`;
|
||||
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
|
||||
void wasmer_import_object_imports_destroy(wasmer_import_t *imports, uint32_t imports_len);
|
||||
|
||||
/// Returns true if further calls to `wasmer_import_object_iter_next` will
|
||||
/// not return any new data
|
||||
bool wasmer_import_object_iter_at_end(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
|
||||
void wasmer_import_object_iter_destroy(wasmer_import_object_iter_t *import_object_iter);
|
||||
|
||||
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
|
||||
/// was an error or there's nothing left to return.
|
||||
///
|
||||
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
|
||||
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
|
||||
wasmer_result_t wasmer_import_object_iter_next(wasmer_import_object_iter_t *import_object_iter,
|
||||
wasmer_import_t *import);
|
||||
|
||||
/// Create an iterator over the functions in the import object.
|
||||
/// Get the next import with `wasmer_import_object_iter_next`
|
||||
/// Free the iterator with `wasmer_import_object_iter_destroy`
|
||||
wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer_import_object_t *import_object);
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
wasmer_import_object_t *wasmer_import_object_new();
|
||||
@ -590,6 +642,27 @@ void *wasmer_trampoline_get_context();
|
||||
/// Returns true for valid wasm bytes and false for invalid bytes
|
||||
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
||||
|
||||
/// Convenience function that creates a WASI import object with no arguments,
|
||||
/// environment variables, preopened files, or mapped directories.
|
||||
///
|
||||
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
|
||||
/// empty values.
|
||||
wasmer_import_object_t *wasmer_wasi_generate_default_import_object();
|
||||
|
||||
/// Creates a WASI import object.
|
||||
///
|
||||
/// This function treats null pointers as empty collections.
|
||||
/// For example, passing null for a string in `args`, will lead to a zero
|
||||
/// length argument in that position.
|
||||
wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_array *args,
|
||||
unsigned int args_len,
|
||||
const wasmer_byte_array *envs,
|
||||
unsigned int envs_len,
|
||||
const wasmer_byte_array *preopened_files,
|
||||
unsigned int preopened_files_len,
|
||||
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
|
||||
unsigned int mapped_dirs_len);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // WASMER_H
|
||||
|
21
lib/runtime-core-tests/Cargo.toml
Normal file
21
lib/runtime-core-tests/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-core-tests"
|
||||
version = "0.9.0"
|
||||
description = "Tests for the Wasmer runtime core crate"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
wabt = "0.9.1"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.9", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["backend-cranelift"]
|
||||
backend-cranelift = ["wasmer-clif-backend"]
|
||||
backend-singlepass = ["wasmer-singlepass-backend"]
|
||||
backend-llvm = ["wasmer-llvm-backend"]
|
21
lib/runtime-core-tests/src/lib.rs
Normal file
21
lib/runtime-core-tests/src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
||||
pub use wabt::wat2wasm;
|
||||
use wasmer_runtime_core::backend::Compiler;
|
||||
|
||||
#[cfg(feature = "backend-cranelift")]
|
||||
pub fn get_compiler() -> impl Compiler {
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-singlepass")]
|
||||
pub fn get_compiler() -> impl Compiler {
|
||||
use wasmer_singlepass_backend::SinglePassCompiler;
|
||||
SinglePassCompiler::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
pub fn get_compiler() -> impl Compiler {
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
LLVMCompiler::new()
|
||||
}
|
137
lib/runtime-core-tests/tests/imports.rs
Normal file
137
lib/runtime-core-tests/tests/imports.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use wasmer_runtime_core::{
|
||||
compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func,
|
||||
types::MemoryDescriptor, units::Pages, vm,
|
||||
};
|
||||
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
||||
|
||||
#[test]
|
||||
fn imported_functions_forms() {
|
||||
const MODULE: &str = r#"
|
||||
(module
|
||||
(type $type (func (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(import "env" "callback_fn" (func $callback_fn (type $type)))
|
||||
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
|
||||
(import "env" "callback_fn_trap" (func $callback_fn_trap (type $type)))
|
||||
(import "env" "callback_fn_trap_with_vmctx" (func $callback_fn_trap_with_vmctx (type $type)))
|
||||
(func (export "function_fn") (type $type)
|
||||
get_local 0
|
||||
call $callback_fn)
|
||||
(func (export "function_fn_with_vmctx") (type $type)
|
||||
get_local 0
|
||||
call $callback_fn_with_vmctx)
|
||||
(func (export "function_fn_trap") (type $type)
|
||||
get_local 0
|
||||
call $callback_fn_trap)
|
||||
(func (export "function_fn_trap_with_vmctx") (type $type)
|
||||
get_local 0
|
||||
call $callback_fn_trap_with_vmctx))
|
||||
"#;
|
||||
|
||||
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||
let memory_descriptor = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
|
||||
let memory = Memory::new(memory_descriptor).unwrap();
|
||||
|
||||
const SHIFT: i32 = 10;
|
||||
memory.view()[0].set(SHIFT);
|
||||
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"memory" => memory.clone(),
|
||||
"callback_fn" => Func::new(callback_fn),
|
||||
"callback_fn_with_vmctx" => Func::new(callback_fn_with_vmctx),
|
||||
"callback_fn_trap" => Func::new(callback_fn_trap),
|
||||
"callback_fn_trap_with_vmctx" => Func::new(callback_fn_trap_with_vmctx),
|
||||
},
|
||||
};
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
macro_rules! call_and_assert {
|
||||
($function:ident, $expected_value:expr) => {
|
||||
let $function: Func<i32, i32> = instance.func(stringify!($function)).unwrap();
|
||||
|
||||
let result = $function.call(1);
|
||||
|
||||
match (result, $expected_value) {
|
||||
(Ok(value), expected_value) => assert_eq!(
|
||||
Ok(value),
|
||||
expected_value,
|
||||
concat!("Expected right when calling `", stringify!($function), "`.")
|
||||
),
|
||||
(
|
||||
Err(RuntimeError::Error { data }),
|
||||
Err(RuntimeError::Error {
|
||||
data: expected_data,
|
||||
}),
|
||||
) => {
|
||||
if let (Some(data), Some(expected_data)) = (
|
||||
data.downcast_ref::<&str>(),
|
||||
expected_data.downcast_ref::<&str>(),
|
||||
) {
|
||||
assert_eq!(
|
||||
data, expected_data,
|
||||
concat!("Expected right when calling `", stringify!($function), "`.")
|
||||
)
|
||||
} else if let (Some(data), Some(expected_data)) = (
|
||||
data.downcast_ref::<String>(),
|
||||
expected_data.downcast_ref::<String>(),
|
||||
) {
|
||||
assert_eq!(
|
||||
data, expected_data,
|
||||
concat!("Expected right when calling `", stringify!($function), "`.")
|
||||
)
|
||||
} else {
|
||||
assert!(false, "Unexpected error, cannot compare it.")
|
||||
}
|
||||
}
|
||||
(result, expected_value) => assert!(
|
||||
false,
|
||||
format!(
|
||||
"Unexpected assertion for `{}`: left = `{:?}`, right = `{:?}`.",
|
||||
stringify!($function),
|
||||
result,
|
||||
expected_value
|
||||
)
|
||||
),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
call_and_assert!(function_fn, Ok(2));
|
||||
call_and_assert!(function_fn_with_vmctx, Ok(2 + SHIFT));
|
||||
call_and_assert!(
|
||||
function_fn_trap,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("foo {}", 1))
|
||||
})
|
||||
);
|
||||
call_and_assert!(
|
||||
function_fn_trap_with_vmctx,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("baz {}", 2 + SHIFT))
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn callback_fn(n: i32) -> Result<i32, ()> {
|
||||
Ok(n + 1)
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Ok(shift + n + 1)
|
||||
}
|
||||
|
||||
fn callback_fn_trap(n: i32) -> Result<i32, String> {
|
||||
Err(format!("foo {}", n))
|
||||
}
|
||||
|
||||
fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, String> {
|
||||
let memory = vmctx.memory(0);
|
||||
let shift: i32 = memory.view()[0].get();
|
||||
|
||||
Err(format!("baz {}", shift + n + 1))
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
|
@ -41,6 +41,30 @@ add $8, %rsp
|
||||
movq (%rsp), %xmm7
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm8
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm9
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm10
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm11
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm12
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm13
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm14
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm15
|
||||
add $8, %rsp
|
||||
|
||||
popq %rbp
|
||||
popq %rax
|
||||
popq %rbx
|
||||
|
@ -41,6 +41,30 @@ add $8, %rsp
|
||||
movq (%rsp), %xmm7
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm8
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm9
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm10
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm11
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm12
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm13
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm14
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm15
|
||||
add $8, %rsp
|
||||
|
||||
popq %rbp
|
||||
popq %rax
|
||||
popq %rbx
|
||||
|
@ -148,7 +148,7 @@ impl LocalBacking {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "a const initializer must be the i32 type".to_string(),
|
||||
message: "a const initializer must be an i32".to_string(),
|
||||
}]);
|
||||
}
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
@ -209,7 +209,7 @@ impl LocalBacking {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "a const initializer must be the i32 type".to_string(),
|
||||
message: "a const initializer must be an i32".to_string(),
|
||||
}]);
|
||||
}
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
@ -282,7 +282,7 @@ impl LocalBacking {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "a const initializer must be the i32 type".to_string(),
|
||||
message: "a const initializer must be an i32".to_string(),
|
||||
}]);
|
||||
}
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
@ -340,7 +340,7 @@ impl LocalBacking {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "a const initializer must be the i32 type".to_string(),
|
||||
message: "a const initializer be an i32".to_string(),
|
||||
}]);
|
||||
}
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
|
@ -207,7 +207,7 @@ pub fn allocate_and_run<R, F: FnOnce() -> R>(size: usize, f: F) -> R {
|
||||
|
||||
// NOTE: Keep this consistent with `image-loading-*.s`.
|
||||
stack[end_offset - 4 - 10] = &mut ctx as *mut Context<F, R> as usize as u64; // rdi
|
||||
const NUM_SAVED_REGISTERS: usize = 23;
|
||||
const NUM_SAVED_REGISTERS: usize = 31;
|
||||
let stack_begin = stack
|
||||
.as_mut_ptr()
|
||||
.offset((end_offset - 4 - NUM_SAVED_REGISTERS) as isize);
|
||||
@ -347,7 +347,7 @@ unsafe fn install_sighandler() {
|
||||
pub struct FaultInfo {
|
||||
pub faulting_addr: *const c_void,
|
||||
pub ip: *const c_void,
|
||||
pub known_registers: [Option<u64>; 24],
|
||||
pub known_registers: [Option<u64>; 32],
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
@ -378,7 +378,7 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) ->
|
||||
let gregs = &(*ucontext).uc_mcontext.gregs;
|
||||
let fpregs = &*(*ucontext).uc_mcontext.fpregs;
|
||||
|
||||
let mut known_registers: [Option<u64>; 24] = [None; 24];
|
||||
let mut known_registers: [Option<u64>; 32] = [None; 32];
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[REG_R15 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[REG_R14 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[REG_R13 as usize] as _);
|
||||
@ -405,6 +405,14 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) ->
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(read_xmm(&fpregs._xmm[5]));
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(read_xmm(&fpregs._xmm[6]));
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(read_xmm(&fpregs._xmm[7]));
|
||||
known_registers[X64Register::XMM(XMM::XMM8).to_index().0] = Some(read_xmm(&fpregs._xmm[8]));
|
||||
known_registers[X64Register::XMM(XMM::XMM9).to_index().0] = Some(read_xmm(&fpregs._xmm[9]));
|
||||
known_registers[X64Register::XMM(XMM::XMM10).to_index().0] = Some(read_xmm(&fpregs._xmm[10]));
|
||||
known_registers[X64Register::XMM(XMM::XMM11).to_index().0] = Some(read_xmm(&fpregs._xmm[11]));
|
||||
known_registers[X64Register::XMM(XMM::XMM12).to_index().0] = Some(read_xmm(&fpregs._xmm[12]));
|
||||
known_registers[X64Register::XMM(XMM::XMM13).to_index().0] = Some(read_xmm(&fpregs._xmm[13]));
|
||||
known_registers[X64Register::XMM(XMM::XMM14).to_index().0] = Some(read_xmm(&fpregs._xmm[14]));
|
||||
known_registers[X64Register::XMM(XMM::XMM15).to_index().0] = Some(read_xmm(&fpregs._xmm[15]));
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr as usize as _,
|
||||
@ -458,8 +466,17 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) ->
|
||||
}
|
||||
#[repr(C)]
|
||||
struct fpstate {
|
||||
_unused: [u8; 168],
|
||||
xmm: [[u64; 2]; 8],
|
||||
_cwd: u16,
|
||||
_swd: u16,
|
||||
_ftw: u16,
|
||||
_fop: u16,
|
||||
_rip: u64,
|
||||
_rdp: u64,
|
||||
_mxcsr: u32,
|
||||
_mxcr_mask: u32,
|
||||
_st: [[u16; 8]; 8],
|
||||
xmm: [[u64; 2]; 16],
|
||||
_padding: [u32; 24],
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
@ -476,7 +493,7 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) ->
|
||||
let ss = &(*(*ucontext).uc_mcontext).ss;
|
||||
let fs = &(*(*ucontext).uc_mcontext).fs;
|
||||
|
||||
let mut known_registers: [Option<u64>; 24] = [None; 24];
|
||||
let mut known_registers: [Option<u64>; 32] = [None; 32];
|
||||
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(ss.r15);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(ss.r14);
|
||||
@ -504,6 +521,14 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) ->
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(fs.xmm[5][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(fs.xmm[6][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(fs.xmm[7][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM8).to_index().0] = Some(fs.xmm[8][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM9).to_index().0] = Some(fs.xmm[9][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM10).to_index().0] = Some(fs.xmm[10][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM11).to_index().0] = Some(fs.xmm[11][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM12).to_index().0] = Some(fs.xmm[12][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM13).to_index().0] = Some(fs.xmm[13][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM14).to_index().0] = Some(fs.xmm[14][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM15).to_index().0] = Some(fs.xmm[15][0]);
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr,
|
||||
|
@ -22,11 +22,11 @@ pub trait Instance {
|
||||
type Error: Debug;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error>;
|
||||
fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
|
||||
unimplemented!()
|
||||
unimplemented!("Instance::read_memory")
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
unimplemented!("Instance::write_memory")
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,15 +122,15 @@ unsafe impl Sync for CodeMemory {}
|
||||
#[cfg(not(unix))]
|
||||
impl CodeMemory {
|
||||
pub fn new(_size: usize) -> CodeMemory {
|
||||
unimplemented!();
|
||||
unimplemented!("CodeMemory::new");
|
||||
}
|
||||
|
||||
pub fn make_executable(&self) {
|
||||
unimplemented!();
|
||||
unimplemented!("CodeMemory::make_executable");
|
||||
}
|
||||
|
||||
pub fn make_writable(&self) {
|
||||
unimplemented!();
|
||||
unimplemented!("CodeMemory::make_writable");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ pub struct ModuleInner {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ModuleInfo {
|
||||
// This are strictly local and the typsystem ensures that.
|
||||
// This are strictly local and the typesystem ensures that.
|
||||
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
|
||||
pub globals: Map<LocalGlobalIndex, GlobalInit>,
|
||||
pub tables: Map<LocalTableIndex, TableDescriptor>,
|
||||
|
@ -672,6 +672,37 @@ pub mod x64 {
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = stack.as_ptr().offset(last_stack_offset as isize) as usize as u64; // rbp
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM15).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM14).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM13).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM12).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM11).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM10).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM9).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM8).to_index().0].unwrap_or(0);
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0].unwrap_or(0);
|
||||
@ -780,10 +811,10 @@ pub mod x64 {
|
||||
pub unsafe fn read_stack<'a, I: Iterator<Item = &'a CodeVersion>, F: Fn() -> I + 'a>(
|
||||
versions: F,
|
||||
mut stack: *const u64,
|
||||
initially_known_registers: [Option<u64>; 24],
|
||||
initially_known_registers: [Option<u64>; 32],
|
||||
mut initial_address: Option<u64>,
|
||||
) -> ExecutionStateImage {
|
||||
let mut known_registers: [Option<u64>; 24] = initially_known_registers;
|
||||
let mut known_registers: [Option<u64>; 32] = initially_known_registers;
|
||||
let mut results: Vec<WasmFunctionStateDump> = vec![];
|
||||
let mut was_baseline = true;
|
||||
|
||||
@ -1023,6 +1054,14 @@ pub mod x64 {
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
XMM8,
|
||||
XMM9,
|
||||
XMM10,
|
||||
XMM11,
|
||||
XMM12,
|
||||
XMM13,
|
||||
XMM14,
|
||||
XMM15,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@ -1066,6 +1105,14 @@ pub mod x64 {
|
||||
22 => X64Register::XMM(XMM::XMM5),
|
||||
23 => X64Register::XMM(XMM::XMM6),
|
||||
24 => X64Register::XMM(XMM::XMM7),
|
||||
25 => X64Register::XMM(XMM::XMM8),
|
||||
26 => X64Register::XMM(XMM::XMM9),
|
||||
27 => X64Register::XMM(XMM::XMM10),
|
||||
28 => X64Register::XMM(XMM::XMM11),
|
||||
29 => X64Register::XMM(XMM::XMM12),
|
||||
30 => X64Register::XMM(XMM::XMM13),
|
||||
31 => X64Register::XMM(XMM::XMM14),
|
||||
32 => X64Register::XMM(XMM::XMM15),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::IsExport,
|
||||
types::{FuncSig, NativeWasmType, Type, WasmExternType},
|
||||
vm::{self, Ctx},
|
||||
vm,
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
@ -52,16 +52,21 @@ impl fmt::Display for WasmTrapInfo {
|
||||
/// of the `Func` struct.
|
||||
pub trait Kind {}
|
||||
|
||||
pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull<vm::Func>, *const u64, *mut u64);
|
||||
pub type Trampoline = unsafe extern "C" fn(
|
||||
vmctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
);
|
||||
pub type Invoke = unsafe extern "C" fn(
|
||||
Trampoline,
|
||||
*mut Ctx,
|
||||
NonNull<vm::Func>,
|
||||
*const u64,
|
||||
*mut u64,
|
||||
*mut WasmTrapInfo,
|
||||
*mut Option<Box<dyn Any>>,
|
||||
Option<NonNull<c_void>>,
|
||||
trampoline: Trampoline,
|
||||
vmctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any>>,
|
||||
extra: Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
|
||||
/// TODO(lachlan): Naming TBD.
|
||||
@ -91,29 +96,82 @@ impl Wasm {
|
||||
/// This type, as part of the `Func` type signature, represents a function that is created
|
||||
/// by the host.
|
||||
pub struct Host(());
|
||||
|
||||
impl Kind for Wasm {}
|
||||
impl Kind for Host {}
|
||||
|
||||
/// Represents a list of WebAssembly values.
|
||||
pub trait WasmTypeList {
|
||||
type CStruct;
|
||||
|
||||
type RetArray: AsMut<[u64]>;
|
||||
|
||||
/// Construct `Self` based on an array of returned values.
|
||||
fn from_ret_array(array: Self::RetArray) -> Self;
|
||||
|
||||
/// Generates an empty array that will hold the returned values of
|
||||
/// the WebAssembly function.
|
||||
fn empty_ret_array() -> Self::RetArray;
|
||||
|
||||
/// 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 types() -> &'static [Type];
|
||||
|
||||
/// This method is used to distribute the values onto a function,
|
||||
/// e.g. `(1, 2).call(func, …)`. This form is unlikely to be used
|
||||
/// directly in the code, see the `Func:call` implementation.
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
wasm: Wasm,
|
||||
ctx: *mut Ctx,
|
||||
ctx: *mut vm::Ctx,
|
||||
) -> Result<Rets, RuntimeError>
|
||||
where
|
||||
Rets: WasmTypeList;
|
||||
}
|
||||
|
||||
pub trait ExternalFunction<Args, Rets>
|
||||
/// Empty trait to specify the kind of `ExternalFunction`: With or
|
||||
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
|
||||
/// `ImplicitVmCtx` structures.
|
||||
///
|
||||
/// This type is never aimed to be used by a user. It is used by the
|
||||
/// trait system to automatically generate an appropriate `wrap`
|
||||
/// function.
|
||||
pub trait ExternalFunctionKind {}
|
||||
|
||||
/// This empty structure indicates that an external function must
|
||||
/// contain an explicit `vm::Ctx` argument (at first position).
|
||||
///
|
||||
/// ```rs,ignore
|
||||
/// fn add_one(_: mut &vm::Ctx, x: i32) -> i32 {
|
||||
/// x + 1
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ExplicitVmCtx {}
|
||||
|
||||
/// This empty structure indicates that an external function has no
|
||||
/// `vm::Ctx` argument (at first position). Its signature is:
|
||||
///
|
||||
/// ```rs,ignore
|
||||
/// fn add_one(x: i32) -> i32 {
|
||||
/// x + 1
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ImplicitVmCtx {}
|
||||
|
||||
impl ExternalFunctionKind for ExplicitVmCtx {}
|
||||
impl ExternalFunctionKind for ImplicitVmCtx {}
|
||||
|
||||
/// Represents a function that can be converted to a `vm::Func`
|
||||
/// (function pointer) that can be called within WebAssembly.
|
||||
pub trait ExternalFunction<Kind, Args, Rets>
|
||||
where
|
||||
Kind: ExternalFunctionKind,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
@ -149,19 +207,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn Func<'a, Args, Rets, F>(f: F) -> Func<'a, Args, Rets, Unsafe>
|
||||
// where
|
||||
// Args: WasmTypeList,
|
||||
// Rets: WasmTypeList,
|
||||
// F: ExternalFunction<Args, Rets>
|
||||
// {
|
||||
// Func::new(f)
|
||||
// }
|
||||
|
||||
/// Represents a function that can be used by WebAssembly.
|
||||
pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> {
|
||||
inner: Inner,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut Ctx,
|
||||
ctx: *mut vm::Ctx,
|
||||
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
||||
}
|
||||
|
||||
@ -176,7 +226,7 @@ where
|
||||
pub(crate) unsafe fn from_raw_parts(
|
||||
inner: Wasm,
|
||||
f: NonNull<vm::Func>,
|
||||
ctx: *mut Ctx,
|
||||
ctx: *mut vm::Ctx,
|
||||
) -> Func<'a, Args, Rets, Wasm> {
|
||||
Func {
|
||||
inner,
|
||||
@ -196,9 +246,10 @@ where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub fn new<F>(f: F) -> Func<'a, Args, Rets, Host>
|
||||
pub fn new<F, Kind>(f: F) -> Func<'a, Args, Rets, Host>
|
||||
where
|
||||
F: ExternalFunction<Args, Rets>,
|
||||
Kind: ExternalFunctionKind,
|
||||
F: ExternalFunction<Kind, Args, Rets>,
|
||||
{
|
||||
Func {
|
||||
inner: Host(()),
|
||||
@ -215,9 +266,12 @@ where
|
||||
Rets: WasmTypeList,
|
||||
Inner: Kind,
|
||||
{
|
||||
/// Returns the types of the function inputs.
|
||||
pub fn params(&self) -> &'static [Type] {
|
||||
Args::types()
|
||||
}
|
||||
|
||||
/// Returns the types of the function outputs.
|
||||
pub fn returns(&self) -> &'static [Type] {
|
||||
Rets::types()
|
||||
}
|
||||
@ -226,62 +280,83 @@ where
|
||||
impl WasmTypeList for Infallible {
|
||||
type CStruct = Infallible;
|
||||
type RetArray = [u64; 0];
|
||||
|
||||
fn from_ret_array(_: Self::RetArray) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn from_c_struct(_: Self::CStruct) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn types() -> &'static [Type] {
|
||||
&[]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
_: NonNull<vm::Func>,
|
||||
_: Wasm,
|
||||
_: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError> {
|
||||
_: *mut vm::Ctx,
|
||||
) -> Result<Rets, RuntimeError>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: WasmExternType> WasmTypeList for (A,) {
|
||||
impl<A> WasmTypeList for (A,)
|
||||
where
|
||||
A: WasmExternType,
|
||||
{
|
||||
type CStruct = S1<A>;
|
||||
type RetArray = [u64; 1];
|
||||
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
(WasmExternType::from_native(NativeWasmType::from_binary(
|
||||
array[0],
|
||||
)),)
|
||||
}
|
||||
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
[0u64]
|
||||
}
|
||||
|
||||
fn from_c_struct(c_struct: Self::CStruct) -> Self {
|
||||
let S1(a) = c_struct;
|
||||
(WasmExternType::from_native(a),)
|
||||
}
|
||||
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
let (a,) = self;
|
||||
S1(WasmExternType::to_native(a))
|
||||
}
|
||||
|
||||
fn types() -> &'static [Type] {
|
||||
&[A::Native::TYPE]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
wasm: Wasm,
|
||||
ctx: *mut Ctx,
|
||||
) -> Result<Rets, RuntimeError> {
|
||||
ctx: *mut vm::Ctx,
|
||||
) -> Result<Rets, RuntimeError>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
let (a,) = self;
|
||||
let args = [a.to_native().to_binary()];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
@ -323,34 +398,57 @@ where
|
||||
macro_rules! impl_traits {
|
||||
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||
#[repr($repr)]
|
||||
pub struct $struct_name <$( $x: WasmExternType ),*> ( $( <$x as WasmExternType>::Native ),* );
|
||||
pub struct $struct_name< $( $x ),* > ( $( <$x as WasmExternType>::Native ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*;
|
||||
|
||||
impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) {
|
||||
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
|
||||
where
|
||||
$( $x: WasmExternType ),*
|
||||
{
|
||||
type CStruct = $struct_name<$( $x ),*>;
|
||||
|
||||
type RetArray = [u64; count_idents!( $( $x ),* )];
|
||||
|
||||
fn from_ret_array(array: Self::RetArray) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
}
|
||||
|
||||
fn empty_ret_array() -> Self::RetArray {
|
||||
[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) ),* )
|
||||
}
|
||||
|
||||
fn into_c_struct(self) -> Self::CStruct {
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
let ( $( $x ),* ) = self;
|
||||
|
||||
$struct_name ( $( WasmExternType::to_native($x) ),* )
|
||||
}
|
||||
|
||||
fn types() -> &'static [Type] {
|
||||
&[$( $x::Native::TYPE, )*]
|
||||
&[$( $x::Native::TYPE ),*]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: NonNull<vm::Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError> {
|
||||
unsafe fn call<Rets>(
|
||||
self,
|
||||
f: NonNull<vm::Func>,
|
||||
wasm: Wasm,
|
||||
ctx: *mut vm::Ctx,
|
||||
) -> Result<Rets, RuntimeError>
|
||||
where
|
||||
Rets: WasmTypeList
|
||||
{
|
||||
#[allow(unused_parens)]
|
||||
let ( $( $x ),* ) = self;
|
||||
let args = [ $( $x.to_native().to_binary()),* ];
|
||||
@ -358,7 +456,16 @@ macro_rules! impl_traits {
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
|
||||
if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, &mut user_error, wasm.invoke_env) {
|
||||
if (wasm.invoke)(
|
||||
wasm.trampoline,
|
||||
ctx,
|
||||
f,
|
||||
args.as_ptr(),
|
||||
rets.as_mut().as_mut_ptr(),
|
||||
&mut trap,
|
||||
&mut user_error,
|
||||
wasm.invoke_env
|
||||
) {
|
||||
Ok(Rets::from_ret_array(rets))
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
@ -370,18 +477,36 @@ macro_rules! impl_traits {
|
||||
}
|
||||
}
|
||||
|
||||
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
|
||||
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap,
|
||||
{
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
|
||||
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
|
||||
})) {
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
f(vmctx $( , WasmExternType::from_native($x) )* ).report()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
@ -391,28 +516,99 @@ macro_rules! impl_traits {
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(&*ctx.module).runnable_module.do_early_trap(err)
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
}
|
||||
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
} else {
|
||||
assert_eq!(mem::size_of::<Self>(), mem::size_of::<usize>(), "you cannot use a closure that captures state for `Func`.");
|
||||
assert_eq!(
|
||||
mem::size_of::<Self>(),
|
||||
mem::size_of::<usize>(),
|
||||
"you cannot use a closure that captures state for `Func`."
|
||||
);
|
||||
|
||||
NonNull::new(unsafe {
|
||||
::std::mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
|
||||
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn($( $x, )*) -> Trap,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x, )* Rets, Trap, FN>(
|
||||
vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
|
||||
) -> Rets::CStruct
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
Trap: TrapEarly<Rets>,
|
||||
FN: Fn($( $x, )*) -> Trap,
|
||||
{
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
|
||||
let err = match panic::catch_unwind(
|
||||
panic::AssertUnwindSafe(
|
||||
|| {
|
||||
f($( WasmExternType::from_native($x), )* ).report()
|
||||
}
|
||||
)
|
||||
) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
}
|
||||
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
} else {
|
||||
assert_eq!(
|
||||
mem::size_of::<Self>(),
|
||||
mem::size_of::<usize>(),
|
||||
"you cannot use a closure that captures state for `Func`."
|
||||
);
|
||||
|
||||
NonNull::new(unsafe {
|
||||
mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
#[allow(unused_parens)]
|
||||
unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) }
|
||||
unsafe {
|
||||
<( $( $x ),* ) as WasmTypeList>::call(
|
||||
( $( $x ),* ),
|
||||
self.f,
|
||||
self.inner,
|
||||
self.ctx
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -463,9 +659,57 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! test_func_arity_n {
|
||||
($test_name:ident, $($x:ident),*) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
use crate::vm;
|
||||
|
||||
fn with_vmctx(_: &mut vm::Ctx, $($x: i32),*) -> i32 {
|
||||
vec![$($x),*].iter().sum()
|
||||
}
|
||||
|
||||
fn without_vmctx($($x: i32),*) -> i32 {
|
||||
vec![$($x),*].iter().sum()
|
||||
}
|
||||
|
||||
let _func = Func::new(with_vmctx);
|
||||
let _func = Func::new(without_vmctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func_arity_0() {
|
||||
fn foo(_: &mut vm::Ctx) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn bar() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
let _ = Func::new(foo);
|
||||
let _ = Func::new(bar);
|
||||
}
|
||||
|
||||
test_func_arity_n!(test_func_arity_1, a);
|
||||
test_func_arity_n!(test_func_arity_2, a, b);
|
||||
test_func_arity_n!(test_func_arity_3, a, b, c);
|
||||
test_func_arity_n!(test_func_arity_4, a, b, c, d);
|
||||
test_func_arity_n!(test_func_arity_5, a, b, c, d, e);
|
||||
test_func_arity_n!(test_func_arity_6, a, b, c, d, e, f);
|
||||
test_func_arity_n!(test_func_arity_7, a, b, c, d, e, f, g);
|
||||
test_func_arity_n!(test_func_arity_8, a, b, c, d, e, f, g, h);
|
||||
test_func_arity_n!(test_func_arity_9, a, b, c, d, e, f, g, h, i);
|
||||
test_func_arity_n!(test_func_arity_10, a, b, c, d, e, f, g, h, i, j);
|
||||
test_func_arity_n!(test_func_arity_11, a, b, c, d, e, f, g, h, i, j, k);
|
||||
test_func_arity_n!(test_func_arity_12, a, b, c, d, e, f, g, h, i, j, k, l);
|
||||
|
||||
#[test]
|
||||
fn test_call() {
|
||||
fn foo(_ctx: &mut Ctx, a: i32, b: i32) -> (i32, i32) {
|
||||
fn foo(_ctx: &mut vm::Ctx, a: i32, b: i32) -> (i32, i32) {
|
||||
(a, b)
|
||||
}
|
||||
|
||||
@ -476,7 +720,7 @@ mod tests {
|
||||
fn test_imports() {
|
||||
use crate::{func, imports};
|
||||
|
||||
fn foo(_ctx: &mut Ctx, a: i32) -> i32 {
|
||||
fn foo(_ctx: &mut vm::Ctx, a: i32) -> i32 {
|
||||
a
|
||||
}
|
||||
|
||||
|
@ -916,15 +916,15 @@ mod vm_ctx_tests {
|
||||
}
|
||||
|
||||
fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option<Wasm> {
|
||||
unimplemented!()
|
||||
unimplemented!("generate_module::get_trampoline")
|
||||
}
|
||||
unsafe fn do_early_trap(&self, _: Box<dyn Any>) -> ! {
|
||||
unimplemented!()
|
||||
unimplemented!("generate_module::do_early_trap")
|
||||
}
|
||||
}
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> {
|
||||
unimplemented!()
|
||||
unimplemented!("generate_module::generate_cache")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
|
||||
// +*****************************+
|
||||
// | LOCAL MEMORIES |
|
||||
// +****************************+
|
||||
// +*****************************+
|
||||
|
||||
pub unsafe extern "C" fn local_static_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
@ -72,7 +72,7 @@ pub unsafe extern "C" fn local_dynamic_memory_size(
|
||||
|
||||
// +*****************************+
|
||||
// | IMPORTED MEMORIES |
|
||||
// +****************************+
|
||||
// +*****************************+
|
||||
|
||||
pub unsafe extern "C" fn imported_static_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
@ -140,7 +140,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_size(
|
||||
|
||||
// +*****************************+
|
||||
// | LOCAL TABLES |
|
||||
// +****************************+
|
||||
// +*****************************+
|
||||
|
||||
pub unsafe extern "C" fn local_table_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
@ -150,11 +150,11 @@ pub unsafe extern "C" fn local_table_grow(
|
||||
let _ = table_index;
|
||||
let _ = delta;
|
||||
let _ = ctx;
|
||||
unimplemented!()
|
||||
unimplemented!("vmcalls::local_table_grow")
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn local_table_size(ctx: &vm::Ctx, table_index: LocalTableIndex) -> u32 {
|
||||
let _ = table_index;
|
||||
let _ = ctx;
|
||||
unimplemented!()
|
||||
unimplemented!("vmcalls::local_table_size")
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,17 +9,17 @@ edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
|
||||
lazy_static = "1.4"
|
||||
memmap = "0.7"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
path = "../runtime-core"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
|
||||
[dependencies.wasmer-clif-backend]
|
||||
path = "../clif-backend"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -94,8 +94,8 @@ fn main() -> error::Result<()> {
|
||||
|
||||
## Additional Notes
|
||||
|
||||
The `wasmer-runtime` crate is build to support multiple compiler
|
||||
backends. We support have a [Cranelift] backend in the
|
||||
The `wasmer-runtime` crate is built to support multiple compiler
|
||||
backends. We support having a [Cranelift] backend in the
|
||||
[`wasmer-clif-backend`] crate, a [LLVM] backend in the
|
||||
[`wasmer-llvm-backend`] crate, and the [Singlepass] backend in the
|
||||
[`wasmer-singlepass-backend`] crate. Currently, the Cranelift backend
|
||||
|
@ -92,6 +92,7 @@ pub use wasmer_runtime_core::export::Export;
|
||||
pub use wasmer_runtime_core::global::Global;
|
||||
pub use wasmer_runtime_core::import::ImportObject;
|
||||
pub use wasmer_runtime_core::instance::{DynFunc, Instance};
|
||||
pub use wasmer_runtime_core::memory::ptr::{Array, Item, WasmPtr};
|
||||
pub use wasmer_runtime_core::memory::Memory;
|
||||
pub use wasmer_runtime_core::module::Module;
|
||||
pub use wasmer_runtime_core::table::Table;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-singlepass-backend"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "Wasmer runtime single pass compiler backend"
|
||||
license = "MIT"
|
||||
@ -9,8 +9,7 @@ edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmparser = "0.39.1"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
dynasm = "0.3.2"
|
||||
dynasmrt = "0.3.1"
|
||||
lazy_static = "1.4"
|
||||
|
@ -1,16 +1,18 @@
|
||||
#![allow(clippy::forget_copy)] // Used by dynasm.
|
||||
#![warn(unused_imports)]
|
||||
|
||||
use crate::emitter_x64::*;
|
||||
use crate::machine::*;
|
||||
use crate::protect_unix;
|
||||
use crate::{emitter_x64::*, machine::*, protect_unix};
|
||||
use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi};
|
||||
use smallvec::SmallVec;
|
||||
use std::ptr::NonNull;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::{BTreeMap, HashMap},
|
||||
ffi::c_void,
|
||||
iter, mem,
|
||||
ptr::NonNull,
|
||||
slice,
|
||||
sync::{Arc, RwLock},
|
||||
usize,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{
|
||||
@ -27,14 +29,14 @@ use wasmer_runtime_core::{
|
||||
ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::Wasm,
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
vm::{self, LocalGlobal, LocalTable, INTERNALS_SIZE},
|
||||
wasmparser::{MemoryImmediate, Operator, Type as WpType, TypeOrFuncType as WpTypeOrFuncType},
|
||||
};
|
||||
use wasmparser::{MemoryImmediate, Operator, Type as WpType, TypeOrFuncType as WpTypeOrFuncType};
|
||||
|
||||
lazy_static! {
|
||||
/// Performs a System V call to `target` with [stack_top..stack_base] as the argument list, from right to left.
|
||||
@ -118,8 +120,8 @@ lazy_static! {
|
||||
; ret
|
||||
);
|
||||
let buf = assembler.finalize().unwrap();
|
||||
let ret = unsafe { ::std::mem::transmute(buf.ptr(offset)) };
|
||||
::std::mem::forget(buf);
|
||||
let ret = unsafe { mem::transmute(buf.ptr(offset)) };
|
||||
mem::forget(buf);
|
||||
ret
|
||||
};
|
||||
}
|
||||
@ -227,7 +229,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
14: 41 ff e3 jmpq *%r11
|
||||
*/
|
||||
#[repr(packed)]
|
||||
struct Trampoline {
|
||||
struct LocalTrampoline {
|
||||
movabsq_rax: [u8; 2],
|
||||
addr_rax: u64,
|
||||
movabsq_r11: [u8; 2],
|
||||
@ -238,7 +240,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
self.code.make_writable();
|
||||
|
||||
let trampoline = &mut *(self.function_pointers[self.func_import_count + idx].0
|
||||
as *const Trampoline as *mut Trampoline);
|
||||
as *const LocalTrampoline as *mut LocalTrampoline);
|
||||
trampoline.movabsq_rax[0] = 0x48;
|
||||
trampoline.movabsq_rax[1] = 0xb8;
|
||||
trampoline.addr_rax = target_address as u64;
|
||||
@ -255,16 +257,8 @@ impl RunnableModule for X64ExecutionContext {
|
||||
}
|
||||
|
||||
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
|
||||
use std::ffi::c_void;
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
|
||||
unsafe extern "C" fn invoke(
|
||||
_trampoline: unsafe extern "C" fn(
|
||||
*mut vm::Ctx,
|
||||
NonNull<vm::Func>,
|
||||
*const u64,
|
||||
*mut u64,
|
||||
),
|
||||
_trampoline: Trampoline,
|
||||
ctx: *mut vm::Ctx,
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
@ -275,12 +269,10 @@ impl RunnableModule for X64ExecutionContext {
|
||||
) -> bool {
|
||||
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
|
||||
let execution_context =
|
||||
::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm);
|
||||
mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm);
|
||||
|
||||
let args = std::slice::from_raw_parts(
|
||||
args,
|
||||
num_params_plus_one.unwrap().as_ptr() as usize - 1,
|
||||
);
|
||||
let args =
|
||||
slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1);
|
||||
let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect();
|
||||
let ret = match protect_unix::call_protected(
|
||||
|| {
|
||||
@ -1737,7 +1729,7 @@ impl X64FunctionCode {
|
||||
control_stack: &mut [ControlFrame],
|
||||
) -> usize {
|
||||
if !m.track_state {
|
||||
return ::std::usize::MAX;
|
||||
return usize::MAX;
|
||||
}
|
||||
let last_frame = control_stack.last_mut().unwrap();
|
||||
let mut diff = m.state.diff(&last_frame.state);
|
||||
@ -1853,7 +1845,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Location::GPR(GPR::RAX),
|
||||
);
|
||||
|
||||
assert_eq!(self.machine.state.wasm_inst_offset, ::std::usize::MAX);
|
||||
assert_eq!(self.machine.state.wasm_inst_offset, usize::MAX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -2341,18 +2333,109 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Condition::Equal,
|
||||
Location::Imm32(0),
|
||||
),
|
||||
Operator::I32Clz => Self::emit_xcnt_i32(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_lzcnt,
|
||||
),
|
||||
Operator::I32Ctz => Self::emit_xcnt_i32(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_tzcnt,
|
||||
),
|
||||
Operator::I32Clz => {
|
||||
let loc =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src = match loc {
|
||||
Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
let tmp = self.machine.acquire_temp_gpr().unwrap();
|
||||
a.emit_mov(Size::S32, loc, Location::GPR(tmp));
|
||||
tmp
|
||||
}
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let dst = match ret {
|
||||
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let zero_path = a.get_label();
|
||||
let end = a.get_label();
|
||||
|
||||
a.emit_test_gpr_64(src);
|
||||
a.emit_jmp(Condition::Equal, zero_path);
|
||||
a.emit_bsr(Size::S32, Location::GPR(src), Location::GPR(dst));
|
||||
a.emit_xor(Size::S32, Location::Imm32(31), Location::GPR(dst));
|
||||
a.emit_jmp(Condition::None, end);
|
||||
a.emit_label(zero_path);
|
||||
a.emit_mov(Size::S32, Location::Imm32(32), Location::GPR(dst));
|
||||
a.emit_label(end);
|
||||
|
||||
match loc {
|
||||
Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
self.machine.release_temp_gpr(src);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Operator::I32Ctz => {
|
||||
let loc =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src = match loc {
|
||||
Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
let tmp = self.machine.acquire_temp_gpr().unwrap();
|
||||
a.emit_mov(Size::S32, loc, Location::GPR(tmp));
|
||||
tmp
|
||||
}
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let dst = match ret {
|
||||
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let zero_path = a.get_label();
|
||||
let end = a.get_label();
|
||||
|
||||
a.emit_test_gpr_64(src);
|
||||
a.emit_jmp(Condition::Equal, zero_path);
|
||||
a.emit_bsf(Size::S32, Location::GPR(src), Location::GPR(dst));
|
||||
a.emit_jmp(Condition::None, end);
|
||||
a.emit_label(zero_path);
|
||||
a.emit_mov(Size::S32, Location::Imm32(32), Location::GPR(dst));
|
||||
a.emit_label(end);
|
||||
|
||||
match loc {
|
||||
Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
self.machine.release_temp_gpr(src);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S32, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Operator::I32Popcnt => Self::emit_xcnt_i32(
|
||||
a,
|
||||
&mut self.machine,
|
||||
@ -2632,18 +2715,109 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Condition::Equal,
|
||||
Location::Imm64(0),
|
||||
),
|
||||
Operator::I64Clz => Self::emit_xcnt_i64(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_lzcnt,
|
||||
),
|
||||
Operator::I64Ctz => Self::emit_xcnt_i64(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_tzcnt,
|
||||
),
|
||||
Operator::I64Clz => {
|
||||
let loc =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src = match loc {
|
||||
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
let tmp = self.machine.acquire_temp_gpr().unwrap();
|
||||
a.emit_mov(Size::S64, loc, Location::GPR(tmp));
|
||||
tmp
|
||||
}
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let dst = match ret {
|
||||
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let zero_path = a.get_label();
|
||||
let end = a.get_label();
|
||||
|
||||
a.emit_test_gpr_64(src);
|
||||
a.emit_jmp(Condition::Equal, zero_path);
|
||||
a.emit_bsr(Size::S64, Location::GPR(src), Location::GPR(dst));
|
||||
a.emit_xor(Size::S64, Location::Imm32(63), Location::GPR(dst));
|
||||
a.emit_jmp(Condition::None, end);
|
||||
a.emit_label(zero_path);
|
||||
a.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst));
|
||||
a.emit_label(end);
|
||||
|
||||
match loc {
|
||||
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
self.machine.release_temp_gpr(src);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Operator::I64Ctz => {
|
||||
let loc =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src = match loc {
|
||||
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
let tmp = self.machine.acquire_temp_gpr().unwrap();
|
||||
a.emit_mov(Size::S64, loc, Location::GPR(tmp));
|
||||
tmp
|
||||
}
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let dst = match ret {
|
||||
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
|
||||
Location::GPR(reg) => reg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let zero_path = a.get_label();
|
||||
let end = a.get_label();
|
||||
|
||||
a.emit_test_gpr_64(src);
|
||||
a.emit_jmp(Condition::Equal, zero_path);
|
||||
a.emit_bsf(Size::S64, Location::GPR(src), Location::GPR(dst));
|
||||
a.emit_jmp(Condition::None, end);
|
||||
a.emit_label(zero_path);
|
||||
a.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst));
|
||||
a.emit_label(end);
|
||||
|
||||
match loc {
|
||||
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
|
||||
self.machine.release_temp_gpr(src);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match ret {
|
||||
Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, Location::GPR(dst), ret);
|
||||
self.machine.release_temp_gpr(dst);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Operator::I64Popcnt => Self::emit_xcnt_i64(
|
||||
a,
|
||||
&mut self.machine,
|
||||
@ -2812,18 +2986,211 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vdivss,
|
||||
),
|
||||
Operator::F32Max => Self::emit_fp_binop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vmaxss,
|
||||
),
|
||||
Operator::F32Min => Self::emit_fp_binop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vminss,
|
||||
),
|
||||
Operator::F32Max => {
|
||||
let src2 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src1 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let tmp1 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmp2 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmpg1 = self.machine.acquire_temp_gpr().unwrap();
|
||||
let tmpg2 = self.machine.acquire_temp_gpr().unwrap();
|
||||
|
||||
let src1 = match src1 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src1, Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let src2 = match src2 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src2, Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let tmp_xmm1 = XMM::XMM8;
|
||||
let tmp_xmm2 = XMM::XMM9;
|
||||
let tmp_xmm3 = XMM::XMM10;
|
||||
|
||||
static CANONICAL_NAN: u128 = 0x7FC0_0000;
|
||||
a.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2));
|
||||
a.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1));
|
||||
a.emit_vmaxss(src1, XMMOrMemory::XMM(src2), tmp_xmm1);
|
||||
let label1 = a.get_label();
|
||||
let label2 = a.get_label();
|
||||
a.emit_jmp(Condition::NotEqual, label1);
|
||||
a.emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2));
|
||||
a.emit_jmp(Condition::None, label2);
|
||||
a.emit_label(label1);
|
||||
a.emit_vxorps(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2);
|
||||
a.emit_label(label2);
|
||||
a.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3);
|
||||
a.emit_vblendvps(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1);
|
||||
a.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1);
|
||||
// load float canonical nan
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&CANONICAL_NAN as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2));
|
||||
a.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1);
|
||||
match ret {
|
||||
Location::XMM(x) => {
|
||||
a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x));
|
||||
}
|
||||
Location::Memory(_, _) | Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), ret);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.machine.release_temp_gpr(tmpg2);
|
||||
self.machine.release_temp_gpr(tmpg1);
|
||||
self.machine.release_temp_xmm(tmp2);
|
||||
self.machine.release_temp_xmm(tmp1);
|
||||
}
|
||||
Operator::F32Min => {
|
||||
let src2 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src1 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let tmp1 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmp2 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmpg1 = self.machine.acquire_temp_gpr().unwrap();
|
||||
let tmpg2 = self.machine.acquire_temp_gpr().unwrap();
|
||||
|
||||
let src1 = match src1 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src1, Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let src2 = match src2 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src2, Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let tmp_xmm1 = XMM::XMM8;
|
||||
let tmp_xmm2 = XMM::XMM9;
|
||||
let tmp_xmm3 = XMM::XMM10;
|
||||
|
||||
static NEG_ZERO: u128 = 0x8000_0000;
|
||||
static CANONICAL_NAN: u128 = 0x7FC0_0000;
|
||||
a.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2));
|
||||
a.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1));
|
||||
a.emit_vminss(src1, XMMOrMemory::XMM(src2), tmp_xmm1);
|
||||
let label1 = a.get_label();
|
||||
let label2 = a.get_label();
|
||||
a.emit_jmp(Condition::NotEqual, label1);
|
||||
a.emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2));
|
||||
a.emit_jmp(Condition::None, label2);
|
||||
a.emit_label(label1);
|
||||
// load float -0.0
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&NEG_ZERO as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(tmpg1, 0),
|
||||
Location::XMM(tmp_xmm2),
|
||||
);
|
||||
a.emit_label(label2);
|
||||
a.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3);
|
||||
a.emit_vblendvps(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1);
|
||||
a.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1);
|
||||
// load float canonical nan
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&CANONICAL_NAN as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2));
|
||||
a.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1);
|
||||
match ret {
|
||||
Location::XMM(x) => {
|
||||
a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x));
|
||||
}
|
||||
Location::Memory(_, _) | Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), ret);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.machine.release_temp_gpr(tmpg2);
|
||||
self.machine.release_temp_gpr(tmpg1);
|
||||
self.machine.release_temp_xmm(tmp2);
|
||||
self.machine.release_temp_xmm(tmp1);
|
||||
}
|
||||
Operator::F32Eq => Self::emit_fp_cmpop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
@ -2990,18 +3357,211 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vdivsd,
|
||||
),
|
||||
Operator::F64Max => Self::emit_fp_binop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vmaxsd,
|
||||
),
|
||||
Operator::F64Min => Self::emit_fp_binop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
&mut self.value_stack,
|
||||
Assembler::emit_vminsd,
|
||||
),
|
||||
Operator::F64Max => {
|
||||
let src2 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src1 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let tmp1 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmp2 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmpg1 = self.machine.acquire_temp_gpr().unwrap();
|
||||
let tmpg2 = self.machine.acquire_temp_gpr().unwrap();
|
||||
|
||||
let src1 = match src1 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src1, Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let src2 = match src2 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src2, Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let tmp_xmm1 = XMM::XMM8;
|
||||
let tmp_xmm2 = XMM::XMM9;
|
||||
let tmp_xmm3 = XMM::XMM10;
|
||||
|
||||
static CANONICAL_NAN: u128 = 0x7FF8_0000_0000_0000;
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2));
|
||||
a.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1));
|
||||
a.emit_vmaxsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1);
|
||||
let label1 = a.get_label();
|
||||
let label2 = a.get_label();
|
||||
a.emit_jmp(Condition::NotEqual, label1);
|
||||
a.emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2));
|
||||
a.emit_jmp(Condition::None, label2);
|
||||
a.emit_label(label1);
|
||||
a.emit_vxorpd(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2);
|
||||
a.emit_label(label2);
|
||||
a.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3);
|
||||
a.emit_vblendvpd(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1);
|
||||
a.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1);
|
||||
// load float canonical nan
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&CANONICAL_NAN as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2));
|
||||
a.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1);
|
||||
match ret {
|
||||
Location::XMM(x) => {
|
||||
a.emit_vmovapd(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x));
|
||||
}
|
||||
Location::Memory(_, _) | Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), ret);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.machine.release_temp_gpr(tmpg2);
|
||||
self.machine.release_temp_gpr(tmpg1);
|
||||
self.machine.release_temp_xmm(tmp2);
|
||||
self.machine.release_temp_xmm(tmp1);
|
||||
}
|
||||
Operator::F64Min => {
|
||||
let src2 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let src1 =
|
||||
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
|
||||
let ret = self.machine.acquire_locations(
|
||||
a,
|
||||
&[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))],
|
||||
false,
|
||||
)[0];
|
||||
self.value_stack.push(ret);
|
||||
|
||||
let tmp1 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmp2 = self.machine.acquire_temp_xmm().unwrap();
|
||||
let tmpg1 = self.machine.acquire_temp_gpr().unwrap();
|
||||
let tmpg2 = self.machine.acquire_temp_gpr().unwrap();
|
||||
|
||||
let src1 = match src1 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src1, Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src1, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp1));
|
||||
tmp1
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let src2 = match src2 {
|
||||
Location::XMM(x) => x,
|
||||
Location::GPR(_) | Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, src2, Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm32(_) => {
|
||||
a.emit_mov(Size::S32, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S32, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
Location::Imm64(_) => {
|
||||
a.emit_mov(Size::S64, src2, Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp2));
|
||||
tmp2
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let tmp_xmm1 = XMM::XMM8;
|
||||
let tmp_xmm2 = XMM::XMM9;
|
||||
let tmp_xmm3 = XMM::XMM10;
|
||||
|
||||
static NEG_ZERO: u128 = 0x8000_0000_0000_0000;
|
||||
static CANONICAL_NAN: u128 = 0x7FF8_0000_0000_0000;
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1));
|
||||
a.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2));
|
||||
a.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1));
|
||||
a.emit_vminsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1);
|
||||
let label1 = a.get_label();
|
||||
let label2 = a.get_label();
|
||||
a.emit_jmp(Condition::NotEqual, label1);
|
||||
a.emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2));
|
||||
a.emit_jmp(Condition::None, label2);
|
||||
a.emit_label(label1);
|
||||
// load float -0.0
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&NEG_ZERO as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Memory(tmpg1, 0),
|
||||
Location::XMM(tmp_xmm2),
|
||||
);
|
||||
a.emit_label(label2);
|
||||
a.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3);
|
||||
a.emit_vblendvpd(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1);
|
||||
a.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1);
|
||||
// load float canonical nan
|
||||
a.emit_mov(
|
||||
Size::S64,
|
||||
Location::Imm64((&CANONICAL_NAN as *const u128) as u64),
|
||||
Location::GPR(tmpg1),
|
||||
);
|
||||
a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2));
|
||||
a.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1);
|
||||
match ret {
|
||||
Location::XMM(x) => {
|
||||
a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x));
|
||||
}
|
||||
Location::Memory(_, _) | Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, Location::XMM(src1), ret);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.machine.release_temp_gpr(tmpg2);
|
||||
self.machine.release_temp_gpr(tmpg1);
|
||||
self.machine.release_temp_xmm(tmp2);
|
||||
self.machine.release_temp_xmm(tmp1);
|
||||
}
|
||||
Operator::F64Eq => Self::emit_fp_cmpop_avx(
|
||||
a,
|
||||
&mut self.machine,
|
||||
@ -4155,7 +4715,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
|a| {
|
||||
a.emit_call_location(Location::GPR(GPR::RAX));
|
||||
},
|
||||
::std::iter::once(Location::Imm32(memory_index.index() as u32)),
|
||||
iter::once(Location::Imm32(memory_index.index() as u32)),
|
||||
None,
|
||||
);
|
||||
let ret = self.machine.acquire_locations(
|
||||
@ -4194,8 +4754,8 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
|a| {
|
||||
a.emit_call_location(Location::GPR(GPR::RAX));
|
||||
},
|
||||
::std::iter::once(Location::Imm32(memory_index.index() as u32))
|
||||
.chain(::std::iter::once(param_pages)),
|
||||
iter::once(Location::Imm32(memory_index.index() as u32))
|
||||
.chain(iter::once(param_pages)),
|
||||
None,
|
||||
);
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub enum Size {
|
||||
S64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum XMMOrMemory {
|
||||
XMM(XMM),
|
||||
@ -90,8 +90,8 @@ pub trait Emitter {
|
||||
fn emit_ror(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_and(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_or(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_bsr(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location);
|
||||
fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location);
|
||||
fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location);
|
||||
@ -105,6 +105,11 @@ pub trait Emitter {
|
||||
fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR);
|
||||
fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR);
|
||||
|
||||
fn emit_vmovaps(&mut self, src: XMMOrMemory, dst: XMMOrMemory);
|
||||
fn emit_vmovapd(&mut self, src: XMMOrMemory, dst: XMMOrMemory);
|
||||
fn emit_vxorps(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vxorpd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
|
||||
fn emit_vaddss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vaddsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vsubss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
@ -136,6 +141,12 @@ pub trait Emitter {
|
||||
fn emit_vcmpgess(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vcmpgesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
|
||||
fn emit_vcmpunordss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vcmpunordsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
|
||||
fn emit_vcmpordss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vcmpordsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
|
||||
fn emit_vsqrtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
fn emit_vsqrtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM);
|
||||
|
||||
@ -164,6 +175,9 @@ pub trait Emitter {
|
||||
fn emit_vcvtsi2sd_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM);
|
||||
fn emit_vcvtsi2sd_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM);
|
||||
|
||||
fn emit_vblendvps(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM);
|
||||
fn emit_vblendvpd(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM);
|
||||
|
||||
fn emit_test_gpr_64(&mut self, reg: GPR);
|
||||
|
||||
fn emit_ud2(&mut self);
|
||||
@ -369,6 +383,14 @@ macro_rules! avx_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rx((x as u8))),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rx((x as u8))),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rx((x as u8))),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rx((x as u8))),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rx((x as u8))),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rx((x as u8))),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rx((x as u8))),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rx((x as u8))),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rx((x as u8))),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rx((x as u8))),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rx((x as u8))),
|
||||
},
|
||||
XMMOrMemory::Memory(base, disp) => match src1 {
|
||||
XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, [Rq((base as u8)) + disp]),
|
||||
@ -379,6 +401,14 @@ macro_rules! avx_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, [Rq((base as u8)) + disp]),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, [Rq((base as u8)) + disp]),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -398,6 +428,14 @@ macro_rules! avx_i2f_64_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rq((x as u8))),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rq((x as u8))),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rq((x as u8))),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rq((x as u8))),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rq((x as u8))),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rq((x as u8))),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rq((x as u8))),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rq((x as u8))),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rq((x as u8))),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rq((x as u8))),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rq((x as u8))),
|
||||
},
|
||||
GPROrMemory::Memory(base, disp) => match src1 {
|
||||
XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, QWORD [Rq((base as u8)) + disp]),
|
||||
@ -408,6 +446,14 @@ macro_rules! avx_i2f_64_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, QWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, QWORD [Rq((base as u8)) + disp]),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -427,6 +473,14 @@ macro_rules! avx_i2f_32_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rd((x as u8))),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rd((x as u8))),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rd((x as u8))),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rd((x as u8))),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rd((x as u8))),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rd((x as u8))),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rd((x as u8))),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rd((x as u8))),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rd((x as u8))),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rd((x as u8))),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rd((x as u8))),
|
||||
},
|
||||
GPROrMemory::Memory(base, disp) => match src1 {
|
||||
XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, DWORD [Rq((base as u8)) + disp]),
|
||||
@ -437,6 +491,14 @@ macro_rules! avx_i2f_32_fn {
|
||||
XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, DWORD [Rq((base as u8)) + disp]),
|
||||
XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, DWORD [Rq((base as u8)) + disp]),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -753,17 +815,17 @@ impl Emitter for Assembler {
|
||||
panic!("singlepass can't emit OR {:?} {:?} {:?}", sz, src, dst)
|
||||
});
|
||||
}
|
||||
fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) {
|
||||
binop_gpr_gpr!(lzcnt, self, sz, src, dst, {
|
||||
binop_mem_gpr!(lzcnt, self, sz, src, dst, {
|
||||
panic!("singlepass can't emit LZCNT {:?} {:?} {:?}", sz, src, dst)
|
||||
fn emit_bsr(&mut self, sz: Size, src: Location, dst: Location) {
|
||||
binop_gpr_gpr!(bsr, self, sz, src, dst, {
|
||||
binop_mem_gpr!(bsr, self, sz, src, dst, {
|
||||
panic!("singlepass can't emit BSR {:?} {:?} {:?}", sz, src, dst)
|
||||
})
|
||||
});
|
||||
}
|
||||
fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location) {
|
||||
binop_gpr_gpr!(tzcnt, self, sz, src, dst, {
|
||||
binop_mem_gpr!(tzcnt, self, sz, src, dst, {
|
||||
panic!("singlepass can't emit TZCNT {:?} {:?} {:?}", sz, src, dst)
|
||||
fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location) {
|
||||
binop_gpr_gpr!(bsf, self, sz, src, dst, {
|
||||
binop_mem_gpr!(bsf, self, sz, src, dst, {
|
||||
panic!("singlepass can't emit BSF {:?} {:?} {:?}", sz, src, dst)
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -945,6 +1007,39 @@ impl Emitter for Assembler {
|
||||
dynasm!(self ; cmovae Rq(dst as u8), Rq(src as u8));
|
||||
}
|
||||
|
||||
fn emit_vmovaps(&mut self, src: XMMOrMemory, dst: XMMOrMemory) {
|
||||
match (src, dst) {
|
||||
(XMMOrMemory::XMM(src), XMMOrMemory::XMM(dst)) => {
|
||||
dynasm!(self ; movaps Rx(dst as u8), Rx(src as u8))
|
||||
}
|
||||
(XMMOrMemory::Memory(base, disp), XMMOrMemory::XMM(dst)) => {
|
||||
dynasm!(self ; movaps Rx(dst as u8), [Rq(base as u8) + disp])
|
||||
}
|
||||
(XMMOrMemory::XMM(src), XMMOrMemory::Memory(base, disp)) => {
|
||||
dynasm!(self ; movaps [Rq(base as u8) + disp], Rx(src as u8))
|
||||
}
|
||||
_ => panic!("singlepass can't emit VMOVAPS {:?} {:?}", src, dst),
|
||||
};
|
||||
}
|
||||
|
||||
fn emit_vmovapd(&mut self, src: XMMOrMemory, dst: XMMOrMemory) {
|
||||
match (src, dst) {
|
||||
(XMMOrMemory::XMM(src), XMMOrMemory::XMM(dst)) => {
|
||||
dynasm!(self ; movapd Rx(dst as u8), Rx(src as u8))
|
||||
}
|
||||
(XMMOrMemory::Memory(base, disp), XMMOrMemory::XMM(dst)) => {
|
||||
dynasm!(self ; movapd Rx(dst as u8), [Rq(base as u8) + disp])
|
||||
}
|
||||
(XMMOrMemory::XMM(src), XMMOrMemory::Memory(base, disp)) => {
|
||||
dynasm!(self ; movapd [Rq(base as u8) + disp], Rx(src as u8))
|
||||
}
|
||||
_ => panic!("singlepass can't emit VMOVAPD {:?} {:?}", src, dst),
|
||||
};
|
||||
}
|
||||
|
||||
avx_fn!(vxorps, emit_vxorps);
|
||||
avx_fn!(vxorpd, emit_vxorpd);
|
||||
|
||||
avx_fn!(vaddss, emit_vaddss);
|
||||
avx_fn!(vaddsd, emit_vaddsd);
|
||||
|
||||
@ -981,6 +1076,12 @@ impl Emitter for Assembler {
|
||||
avx_fn!(vcmpgess, emit_vcmpgess);
|
||||
avx_fn!(vcmpgesd, emit_vcmpgesd);
|
||||
|
||||
avx_fn!(vcmpunordss, emit_vcmpunordss);
|
||||
avx_fn!(vcmpunordsd, emit_vcmpunordsd);
|
||||
|
||||
avx_fn!(vcmpordss, emit_vcmpordss);
|
||||
avx_fn!(vcmpordsd, emit_vcmpordsd);
|
||||
|
||||
avx_fn!(vsqrtss, emit_vsqrtss);
|
||||
avx_fn!(vsqrtsd, emit_vsqrtsd);
|
||||
|
||||
@ -1001,6 +1102,28 @@ impl Emitter for Assembler {
|
||||
avx_i2f_64_fn!(vcvtsi2ss, emit_vcvtsi2ss_64);
|
||||
avx_i2f_64_fn!(vcvtsi2sd, emit_vcvtsi2sd_64);
|
||||
|
||||
fn emit_vblendvps(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM) {
|
||||
match src2 {
|
||||
XMMOrMemory::XMM(src2) => {
|
||||
dynasm!(self ; vblendvps Rx(dst as u8), Rx(mask as u8), Rx(src2 as u8), Rx(src1 as u8))
|
||||
}
|
||||
XMMOrMemory::Memory(base, disp) => {
|
||||
dynasm!(self ; vblendvps Rx(dst as u8), Rx(mask as u8), [Rq(base as u8) + disp], Rx(src1 as u8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_vblendvpd(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM) {
|
||||
match src2 {
|
||||
XMMOrMemory::XMM(src2) => {
|
||||
dynasm!(self ; vblendvpd Rx(dst as u8), Rx(mask as u8), Rx(src2 as u8), Rx(src1 as u8))
|
||||
}
|
||||
XMMOrMemory::Memory(base, disp) => {
|
||||
dynasm!(self ; vblendvpd Rx(dst as u8), Rx(mask as u8), [Rq(base as u8) + disp], Rx(src1 as u8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM) {
|
||||
match src {
|
||||
XMMOrMemory::XMM(x) => dynasm!(self ; ucomiss Rx(dst as u8), Rx(x as u8)),
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::emitter_x64::*;
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::HashSet;
|
||||
use wasmer_runtime_core::state::x64::X64Register;
|
||||
use wasmer_runtime_core::state::*;
|
||||
use wasmparser::Type as WpType;
|
||||
use wasmer_runtime_core::{
|
||||
state::{x64::X64Register, *},
|
||||
wasmparser::Type as WpType,
|
||||
};
|
||||
|
||||
struct MachineStackOffset(usize);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-spectests"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
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-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.8.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.8.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8.0", optional = true }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
wabt = "0.9.1"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -445,8 +445,9 @@ mod tests {
|
||||
"AssertReturnCanonicalNan"
|
||||
),
|
||||
message: format!(
|
||||
"value is not canonical nan {:?}",
|
||||
v
|
||||
"value is not canonical nan {:?} ({:?})",
|
||||
v,
|
||||
value_to_hex(v.clone()),
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
@ -512,8 +513,9 @@ mod tests {
|
||||
"AssertReturnArithmeticNan"
|
||||
),
|
||||
message: format!(
|
||||
"value is not arithmetic nan {:?}",
|
||||
v
|
||||
"value is not arithmetic nan {:?} ({:?})",
|
||||
v,
|
||||
value_to_hex(v.clone()),
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
@ -718,10 +720,44 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable {
|
||||
module: _,
|
||||
message: _,
|
||||
} => println!("AssertUninstantiable not yet implmented "),
|
||||
CommandKind::AssertUninstantiable { module, message: _ } => {
|
||||
let spectest_import_object = get_spectest_import_object(®istered_modules);
|
||||
let config = CompilerConfig {
|
||||
features: Features {
|
||||
simd: true,
|
||||
threads: true,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let module = wasmer_runtime_core::compile_with_config(
|
||||
&module.into_vec(),
|
||||
&get_compiler(),
|
||||
config,
|
||||
)
|
||||
.expect("WASM can't be compiled");
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
module
|
||||
.instantiate(&spectest_import_object)
|
||||
.expect("WASM can't be instantiated");
|
||||
}));
|
||||
match result {
|
||||
Err(_) => test_report.count_passed(),
|
||||
Ok(_) => {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line: line,
|
||||
kind: format!("{}", "AssertUninstantiable"),
|
||||
message: format!(
|
||||
"instantiate successful, expected uninstantiable"
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
CommandKind::AssertExhaustion { action, message: _ } => {
|
||||
match action {
|
||||
Action::Invoke {
|
||||
@ -945,6 +981,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_hex(val: wasmer_runtime_core::types::Value) -> String {
|
||||
match val {
|
||||
wasmer_runtime_core::types::Value::I32(x) => format!("{:#x}", x),
|
||||
wasmer_runtime_core::types::Value::I64(x) => format!("{:#x}", x),
|
||||
wasmer_runtime_core::types::Value::F32(x) => format!("{:#x}", x.to_bits()),
|
||||
wasmer_runtime_core::types::Value::F64(x) => format!("{:#x}", x.to_bits()),
|
||||
wasmer_runtime_core::types::Value::V128(x) => format!("{:#x}", x),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpectestValue {
|
||||
I32(i32),
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-wasi-tests"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Tests for our WASI implementation"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,20 +9,20 @@ publish = false
|
||||
build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.8.0" }
|
||||
wasmer-wasi = { path = "../wasi", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmer-wasi = { path = "../wasi", version = "0.9.0" }
|
||||
# hack to get tests to work
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.8.0", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.8.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
glob = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.8.0" }
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.8.0"}
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" }
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.9.0"}
|
||||
|
||||
[features]
|
||||
clif = []
|
||||
|
18
lib/wasi-tests/tests/wasitests/fd_close.rs
Normal file
18
lib/wasi-tests/tests/wasitests/fd_close.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// !!! THIS IS A GENERATED FILE !!!
|
||||
// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME
|
||||
// Files autogenerated with cargo build (build/wasitests.rs).
|
||||
|
||||
#[test]
|
||||
fn test_fd_close() {
|
||||
assert_wasi_output!(
|
||||
"../../wasitests/fd_close.wasm",
|
||||
"fd_close",
|
||||
vec![],
|
||||
vec![(
|
||||
".".to_string(),
|
||||
::std::path::PathBuf::from("wasitests/test_fs/hamlet")
|
||||
),],
|
||||
vec![],
|
||||
"../../wasitests/fd_close.out"
|
||||
);
|
||||
}
|
@ -9,6 +9,7 @@ mod close_preopen_fd;
|
||||
mod create_dir;
|
||||
mod envvar;
|
||||
mod fd_allocate;
|
||||
mod fd_close;
|
||||
mod fd_pread;
|
||||
mod fd_read;
|
||||
mod fd_sync;
|
||||
|
3
lib/wasi-tests/wasitests/fd_close.out
Normal file
3
lib/wasi-tests/wasitests/fd_close.out
Normal file
@ -0,0 +1,3 @@
|
||||
Successfully closed file!
|
||||
Successfully closed stderr!
|
||||
Successfully closed stdin!
|
63
lib/wasi-tests/wasitests/fd_close.rs
Normal file
63
lib/wasi-tests/wasitests/fd_close.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// Args:
|
||||
// mapdir: .:wasitests/test_fs/hamlet
|
||||
|
||||
use std::fs;
|
||||
#[cfg(target_os = "wasi")]
|
||||
use std::os::wasi::prelude::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
#[link(wasm_import_module = "wasi_unstable")]
|
||||
extern "C" {
|
||||
fn fd_close(fd: u32) -> u16;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
let mut base = PathBuf::from("wasitests/test_fs/hamlet");
|
||||
#[cfg(target_os = "wasi")]
|
||||
let mut base = PathBuf::from(".");
|
||||
|
||||
base.push("act3/scene3.txt");
|
||||
let file = fs::File::open(&base).expect("could not open file");
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
{
|
||||
let file_fd = file.as_raw_fd();
|
||||
let stdout_fd = std::io::stdout().as_raw_fd();
|
||||
let stderr_fd = std::io::stderr().as_raw_fd();
|
||||
let stdin_fd = std::io::stdin().as_raw_fd();
|
||||
|
||||
let result = unsafe { fd_close(file_fd) };
|
||||
if result == 0 {
|
||||
println!("Successfully closed file!")
|
||||
} else {
|
||||
println!("Could not close file");
|
||||
}
|
||||
|
||||
let result = unsafe { fd_close(stderr_fd) };
|
||||
if result == 0 {
|
||||
println!("Successfully closed stderr!")
|
||||
} else {
|
||||
println!("Could not close stderr");
|
||||
}
|
||||
let result = unsafe { fd_close(stdin_fd) };
|
||||
if result == 0 {
|
||||
println!("Successfully closed stdin!")
|
||||
} else {
|
||||
println!("Could not close stdin");
|
||||
}
|
||||
let result = unsafe { fd_close(stdout_fd) };
|
||||
if result == 0 {
|
||||
println!("Successfully closed stdout!")
|
||||
} else {
|
||||
println!("Could not close stdout");
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
{
|
||||
println!("Successfully closed file!");
|
||||
println!("Successfully closed stderr!");
|
||||
println!("Successfully closed stdin!");
|
||||
}
|
||||
}
|
BIN
lib/wasi-tests/wasitests/fd_close.wasm
Executable file
BIN
lib/wasi-tests/wasitests/fd_close.wasm
Executable file
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-wasi"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime WASI implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -13,11 +13,11 @@ byteorder = "1.3"
|
||||
generational-arena = { version = "0.2", features = ["serde"] }
|
||||
libc = "0.2.60"
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
getrandom = "0.1"
|
||||
time = "0.1"
|
||||
typetag = "0.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.3"
|
||||
|
@ -37,6 +37,19 @@ use wasmer_runtime_core::{debug, vm::Ctx};
|
||||
pub const VIRTUAL_ROOT_FD: __wasi_fd_t = 3;
|
||||
/// all the rights enabled
|
||||
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFFFFFF;
|
||||
const STDIN_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
|
||||
| __WASI_RIGHT_FD_READ
|
||||
| __WASI_RIGHT_FD_SYNC
|
||||
| __WASI_RIGHT_FD_ADVISE
|
||||
| __WASI_RIGHT_FD_FILESTAT_GET
|
||||
| __WASI_RIGHT_POLL_FD_READWRITE;
|
||||
const STDOUT_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
|
||||
| __WASI_RIGHT_FD_WRITE
|
||||
| __WASI_RIGHT_FD_SYNC
|
||||
| __WASI_RIGHT_FD_ADVISE
|
||||
| __WASI_RIGHT_FD_FILESTAT_GET
|
||||
| __WASI_RIGHT_POLL_FD_READWRITE;
|
||||
const STDERR_DEFAULT_RIGHTS: __wasi_rights_t = STDOUT_DEFAULT_RIGHTS;
|
||||
|
||||
/// Get WasiState from a Ctx
|
||||
/// This function is unsafe because it must be called on a WASI Ctx
|
||||
@ -109,6 +122,7 @@ pub struct Fd {
|
||||
pub rights_inheriting: __wasi_rights_t,
|
||||
pub flags: __wasi_fdflags_t,
|
||||
pub offset: u64,
|
||||
/// Used when reopening the file on the host system
|
||||
pub open_flags: u16,
|
||||
pub inode: Inode,
|
||||
}
|
||||
@ -134,10 +148,6 @@ pub struct WasiFs {
|
||||
inode_counter: Cell<u64>,
|
||||
/// for fds still open after the file has been deleted
|
||||
pub orphan_fds: HashMap<Inode, InodeVal>,
|
||||
|
||||
pub stdout: Box<dyn WasiFile>,
|
||||
pub stderr: Box<dyn WasiFile>,
|
||||
pub stdin: Box<dyn WasiFile>,
|
||||
}
|
||||
|
||||
impl WasiFs {
|
||||
@ -155,11 +165,11 @@ impl WasiFs {
|
||||
next_fd: Cell::new(3),
|
||||
inode_counter: Cell::new(1024),
|
||||
orphan_fds: HashMap::new(),
|
||||
|
||||
stdin: Box::new(Stdin),
|
||||
stdout: Box::new(Stdout),
|
||||
stderr: Box::new(Stderr),
|
||||
};
|
||||
wasi_fs.create_stdin();
|
||||
wasi_fs.create_stdout();
|
||||
wasi_fs.create_stderr();
|
||||
|
||||
// create virtual root
|
||||
let root_inode = {
|
||||
let all_rights = 0x1FFFFFFF;
|
||||
@ -291,6 +301,67 @@ impl WasiFs {
|
||||
Ok(wasi_fs)
|
||||
}
|
||||
|
||||
/// Get the `WasiFile` object at stdout
|
||||
pub fn stdout(&self) -> Result<&Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get(__WASI_STDOUT_FILENO)
|
||||
}
|
||||
/// Get the `WasiFile` object at stdout mutably
|
||||
pub fn stdout_mut(&mut self) -> Result<&mut Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get_mut(__WASI_STDOUT_FILENO)
|
||||
}
|
||||
|
||||
/// Get the `WasiFile` object at stderr
|
||||
pub fn stderr(&self) -> Result<&Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get(__WASI_STDERR_FILENO)
|
||||
}
|
||||
/// Get the `WasiFile` object at stderr mutably
|
||||
pub fn stderr_mut(&mut self) -> Result<&mut Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get_mut(__WASI_STDERR_FILENO)
|
||||
}
|
||||
|
||||
/// Get the `WasiFile` object at stdin
|
||||
pub fn stdin(&self) -> Result<&Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get(__WASI_STDIN_FILENO)
|
||||
}
|
||||
/// Get the `WasiFile` object at stdin mutably
|
||||
pub fn stdin_mut(&mut self) -> Result<&mut Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
self.std_dev_get_mut(__WASI_STDIN_FILENO)
|
||||
}
|
||||
|
||||
/// Internal helper function to get a standard device handle.
|
||||
/// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`.
|
||||
fn std_dev_get(&self, fd: __wasi_fd_t) -> Result<&Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
if let Some(fd) = self.fd_map.get(&fd) {
|
||||
if let Kind::File { ref handle, .. } = self.inodes[fd.inode].kind {
|
||||
Ok(handle)
|
||||
} else {
|
||||
// Our public API should ensure that this is not possible
|
||||
unreachable!("Non-file found in standard device location")
|
||||
}
|
||||
} else {
|
||||
// this should only trigger if we made a mistake in this crate
|
||||
Err(WasiFsError::NoDevice)
|
||||
}
|
||||
}
|
||||
/// Internal helper function to mutably get a standard device handle.
|
||||
/// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`.
|
||||
fn std_dev_get_mut(
|
||||
&mut self,
|
||||
fd: __wasi_fd_t,
|
||||
) -> Result<&mut Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
if let Some(fd) = self.fd_map.get_mut(&fd) {
|
||||
if let Kind::File { ref mut handle, .. } = self.inodes[fd.inode].kind {
|
||||
Ok(handle)
|
||||
} else {
|
||||
// Our public API should ensure that this is not possible
|
||||
unreachable!("Non-file found in standard device location")
|
||||
}
|
||||
} else {
|
||||
// this should only trigger if we made a mistake in this crate
|
||||
Err(WasiFsError::NoDevice)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_inode_index(&mut self) -> u64 {
|
||||
let next = self.inode_counter.get();
|
||||
self.inode_counter.set(next + 1);
|
||||
@ -358,21 +429,16 @@ impl WasiFs {
|
||||
fd: __wasi_fd_t,
|
||||
file: Box<dyn WasiFile>,
|
||||
) -> Result<Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||
let mut ret = Some(file);
|
||||
match fd {
|
||||
__WASI_STDIN_FILENO => {
|
||||
let mut ret = file;
|
||||
std::mem::swap(&mut self.stdin, &mut ret);
|
||||
Ok(Some(ret))
|
||||
std::mem::swap(self.stdin_mut()?, &mut ret);
|
||||
}
|
||||
__WASI_STDOUT_FILENO => {
|
||||
let mut ret = file;
|
||||
std::mem::swap(&mut self.stdout, &mut ret);
|
||||
Ok(Some(ret))
|
||||
std::mem::swap(self.stdout_mut()?, &mut ret);
|
||||
}
|
||||
__WASI_STDERR_FILENO => {
|
||||
let mut ret = file;
|
||||
std::mem::swap(&mut self.stderr, &mut ret);
|
||||
Ok(Some(ret))
|
||||
std::mem::swap(self.stderr_mut()?, &mut ret);
|
||||
}
|
||||
_ => {
|
||||
let base_fd = self.get_fd(fd).map_err(WasiFsError::from_wasi_err)?;
|
||||
@ -380,14 +446,14 @@ impl WasiFs {
|
||||
|
||||
match &mut self.inodes[base_inode].kind {
|
||||
Kind::File { ref mut handle, .. } => {
|
||||
let mut ret = Some(file);
|
||||
std::mem::swap(handle, &mut ret);
|
||||
Ok(ret)
|
||||
}
|
||||
_ => return Err(WasiFsError::NotAFile),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// refresh size from filesystem
|
||||
@ -733,6 +799,17 @@ impl WasiFs {
|
||||
}
|
||||
|
||||
pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> {
|
||||
match fd {
|
||||
__WASI_STDOUT_FILENO => {
|
||||
return Ok(__wasi_fdstat_t {
|
||||
fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
fs_flags: 0,
|
||||
fs_rights_base: ALL_RIGHTS,
|
||||
fs_rights_inheriting: ALL_RIGHTS,
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
let fd = self.get_fd(fd)?;
|
||||
|
||||
debug!("fdstat: {:?}", fd);
|
||||
@ -773,8 +850,18 @@ impl WasiFs {
|
||||
pub fn flush(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> {
|
||||
match fd {
|
||||
__WASI_STDIN_FILENO => (),
|
||||
__WASI_STDOUT_FILENO => self.stdout.flush().map_err(|_| __WASI_EIO)?,
|
||||
__WASI_STDERR_FILENO => self.stderr.flush().map_err(|_| __WASI_EIO)?,
|
||||
__WASI_STDOUT_FILENO => self
|
||||
.stdout_mut()
|
||||
.map_err(WasiFsError::into_wasi_err)?
|
||||
.as_mut()
|
||||
.and_then(|f| f.flush().ok())
|
||||
.ok_or(__WASI_EIO)?,
|
||||
__WASI_STDERR_FILENO => self
|
||||
.stderr_mut()
|
||||
.map_err(WasiFsError::into_wasi_err)?
|
||||
.as_mut()
|
||||
.and_then(|f| f.flush().ok())
|
||||
.ok_or(__WASI_EIO)?,
|
||||
_ => {
|
||||
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
|
||||
if fd.rights & __WASI_RIGHT_FD_DATASYNC == 0 {
|
||||
@ -793,7 +880,7 @@ impl WasiFs {
|
||||
}
|
||||
// TODO: verify this behavior
|
||||
Kind::Dir { .. } => return Err(__WASI_EISDIR),
|
||||
Kind::Symlink { .. } => unimplemented!(),
|
||||
Kind::Symlink { .. } => unimplemented!("WasiFs::flush Kind::Symlink"),
|
||||
Kind::Buffer { .. } => (),
|
||||
_ => return Err(__WASI_EIO),
|
||||
}
|
||||
@ -881,13 +968,78 @@ impl WasiFs {
|
||||
};
|
||||
|
||||
self.inodes.insert(InodeVal {
|
||||
stat: stat,
|
||||
stat,
|
||||
is_preopened: true,
|
||||
name: "/".to_string(),
|
||||
kind: root_kind,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_stdout(&mut self) {
|
||||
self.create_std_dev_inner(
|
||||
Box::new(Stdout),
|
||||
"stdout",
|
||||
__WASI_STDOUT_FILENO,
|
||||
STDOUT_DEFAULT_RIGHTS,
|
||||
__WASI_FDFLAG_APPEND,
|
||||
);
|
||||
}
|
||||
fn create_stdin(&mut self) {
|
||||
self.create_std_dev_inner(
|
||||
Box::new(Stdin),
|
||||
"stdin",
|
||||
__WASI_STDIN_FILENO,
|
||||
STDIN_DEFAULT_RIGHTS,
|
||||
0,
|
||||
);
|
||||
}
|
||||
fn create_stderr(&mut self) {
|
||||
self.create_std_dev_inner(
|
||||
Box::new(Stderr),
|
||||
"stderr",
|
||||
__WASI_STDERR_FILENO,
|
||||
STDERR_DEFAULT_RIGHTS,
|
||||
__WASI_FDFLAG_APPEND,
|
||||
);
|
||||
}
|
||||
|
||||
fn create_std_dev_inner(
|
||||
&mut self,
|
||||
handle: Box<dyn WasiFile>,
|
||||
name: &'static str,
|
||||
raw_fd: __wasi_fd_t,
|
||||
rights: __wasi_rights_t,
|
||||
fd_flags: __wasi_fdflags_t,
|
||||
) {
|
||||
let stat = __wasi_filestat_t {
|
||||
st_filetype: __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
st_ino: self.get_next_inode_index(),
|
||||
..__wasi_filestat_t::default()
|
||||
};
|
||||
let kind = Kind::File {
|
||||
handle: Some(handle),
|
||||
path: "".into(),
|
||||
};
|
||||
let inode = self.inodes.insert(InodeVal {
|
||||
stat,
|
||||
is_preopened: true,
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
});
|
||||
self.fd_map.insert(
|
||||
raw_fd,
|
||||
Fd {
|
||||
rights,
|
||||
rights_inheriting: 0,
|
||||
flags: fd_flags,
|
||||
// since we're not calling open on this, we don't need open flags
|
||||
open_flags: 0,
|
||||
offset: 0,
|
||||
inode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_stat_for_kind(&self, kind: &Kind) -> Option<__wasi_filestat_t> {
|
||||
let md = match kind {
|
||||
Kind::File { handle, path } => match handle {
|
||||
|
@ -49,6 +49,8 @@ pub enum WasiFsError {
|
||||
NotConnected,
|
||||
/// The requested file or directory could not be found
|
||||
EntityNotFound,
|
||||
/// The requested device couldn't be accessed
|
||||
NoDevice,
|
||||
/// Caller was not allowed to perform this operation
|
||||
PermissionDenied,
|
||||
/// The operation did not complete within the given amount of time
|
||||
@ -80,6 +82,7 @@ impl WasiFsError {
|
||||
__WASI_EINTR => WasiFsError::Interrupted,
|
||||
__WASI_EINVAL => WasiFsError::InvalidInput,
|
||||
__WASI_ENOTCONN => WasiFsError::NotConnected,
|
||||
__WASI_ENODEV => WasiFsError::NoDevice,
|
||||
__WASI_ENOENT => WasiFsError::EntityNotFound,
|
||||
__WASI_EPERM => WasiFsError::PermissionDenied,
|
||||
__WASI_ETIMEDOUT => WasiFsError::TimedOut,
|
||||
@ -105,6 +108,7 @@ impl WasiFsError {
|
||||
WasiFsError::InvalidFd => __WASI_EBADF,
|
||||
WasiFsError::InvalidInput => __WASI_EINVAL,
|
||||
WasiFsError::IOError => __WASI_EIO,
|
||||
WasiFsError::NoDevice => __WASI_ENODEV,
|
||||
WasiFsError::NotAFile => __WASI_EINVAL,
|
||||
WasiFsError::NotConnected => __WASI_ENOTCONN,
|
||||
WasiFsError::EntityNotFound => __WASI_ENOENT,
|
||||
|
@ -15,7 +15,6 @@ use crate::{
|
||||
},
|
||||
ExitCode,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::Cell;
|
||||
use std::convert::{Infallible, TryInto};
|
||||
@ -374,6 +373,7 @@ pub fn fd_allocate(
|
||||
/// If `fd` is invalid or not open
|
||||
pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
||||
debug!("wasi::fd_close");
|
||||
debug!("=> fd={}", fd);
|
||||
let state = get_wasi_state(ctx);
|
||||
|
||||
let fd_entry = wasi_try!(state.fs.get_fd(fd)).clone();
|
||||
@ -662,7 +662,15 @@ pub fn fd_pread(
|
||||
let state = get_wasi_state(ctx);
|
||||
|
||||
let bytes_read = match fd {
|
||||
__WASI_STDIN_FILENO => wasi_try!(read_bytes(&mut state.fs.stdin, memory, iov_cells)),
|
||||
__WASI_STDIN_FILENO => {
|
||||
if let Some(ref mut stdin) =
|
||||
wasi_try!(state.fs.stdin_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(read_bytes(stdin, memory, iov_cells))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
__WASI_STDOUT_FILENO => return __WASI_EINVAL,
|
||||
__WASI_STDERR_FILENO => return __WASI_EINVAL,
|
||||
_ => {
|
||||
@ -802,8 +810,24 @@ pub fn fd_pwrite(
|
||||
|
||||
let bytes_written = match fd {
|
||||
__WASI_STDIN_FILENO => return __WASI_EINVAL,
|
||||
__WASI_STDOUT_FILENO => wasi_try!(write_bytes(&mut state.fs.stdout, memory, iovs_arr_cell)),
|
||||
__WASI_STDERR_FILENO => wasi_try!(write_bytes(&mut state.fs.stderr, memory, iovs_arr_cell)),
|
||||
__WASI_STDOUT_FILENO => {
|
||||
if let Some(ref mut stdout) =
|
||||
wasi_try!(state.fs.stdout_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(write_bytes(stdout, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
__WASI_STDERR_FILENO => {
|
||||
if let Some(ref mut stderr) =
|
||||
wasi_try!(state.fs.stderr_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(write_bytes(stderr, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
|
||||
|
||||
@ -872,7 +896,15 @@ pub fn fd_read(
|
||||
let state = get_wasi_state(ctx);
|
||||
|
||||
let bytes_read = match fd {
|
||||
__WASI_STDIN_FILENO => wasi_try!(read_bytes(&mut state.fs.stdin, memory, iovs_arr_cell)),
|
||||
__WASI_STDIN_FILENO => {
|
||||
if let Some(ref mut stdin) =
|
||||
wasi_try!(state.fs.stdin_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(read_bytes(stdin, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
__WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return __WASI_EINVAL,
|
||||
_ => {
|
||||
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
|
||||
@ -1211,8 +1243,24 @@ pub fn fd_write(
|
||||
|
||||
let bytes_written = match fd {
|
||||
__WASI_STDIN_FILENO => return __WASI_EINVAL,
|
||||
__WASI_STDOUT_FILENO => wasi_try!(write_bytes(&mut state.fs.stdout, memory, iovs_arr_cell)),
|
||||
__WASI_STDERR_FILENO => wasi_try!(write_bytes(&mut state.fs.stderr, memory, iovs_arr_cell)),
|
||||
__WASI_STDOUT_FILENO => {
|
||||
if let Some(ref mut stdout) =
|
||||
wasi_try!(state.fs.stdout_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(write_bytes(stdout, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
__WASI_STDERR_FILENO => {
|
||||
if let Some(ref mut stderr) =
|
||||
wasi_try!(state.fs.stderr_mut().map_err(WasiFsError::into_wasi_err))
|
||||
{
|
||||
wasi_try!(write_bytes(stderr, memory, iovs_arr_cell))
|
||||
} else {
|
||||
return __WASI_EBADF;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let state = get_wasi_state(ctx);
|
||||
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
|
||||
@ -2292,9 +2340,21 @@ pub fn poll_oneoff(
|
||||
|
||||
if let Some(fd) = fd {
|
||||
let wasi_file_ref: &dyn WasiFile = match fd {
|
||||
__WASI_STDERR_FILENO => state.fs.stderr.as_ref(),
|
||||
__WASI_STDIN_FILENO => state.fs.stdin.as_ref(),
|
||||
__WASI_STDOUT_FILENO => state.fs.stdout.as_ref(),
|
||||
__WASI_STDERR_FILENO => wasi_try!(
|
||||
wasi_try!(state.fs.stderr().map_err(WasiFsError::into_wasi_err)).as_ref(),
|
||||
__WASI_EBADF
|
||||
)
|
||||
.as_ref(),
|
||||
__WASI_STDIN_FILENO => wasi_try!(
|
||||
wasi_try!(state.fs.stdin().map_err(WasiFsError::into_wasi_err)).as_ref(),
|
||||
__WASI_EBADF
|
||||
)
|
||||
.as_ref(),
|
||||
__WASI_STDOUT_FILENO => wasi_try!(
|
||||
wasi_try!(state.fs.stdout().map_err(WasiFsError::into_wasi_err)).as_ref(),
|
||||
__WASI_EBADF
|
||||
)
|
||||
.as_ref(),
|
||||
_ => {
|
||||
let fd_entry = wasi_try!(state.fs.get_fd(fd));
|
||||
let inode = fd_entry.inode;
|
||||
@ -2392,17 +2452,18 @@ pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t {
|
||||
/// The number of bytes that will be written
|
||||
pub fn random_get(ctx: &mut Ctx, buf: WasmPtr<u8, Array>, buf_len: u32) -> __wasi_errno_t {
|
||||
debug!("wasi::random_get buf_len: {}", buf_len);
|
||||
let mut rng = thread_rng();
|
||||
let memory = ctx.memory(0);
|
||||
|
||||
let buf = wasi_try!(buf.deref(memory, 0, buf_len));
|
||||
|
||||
unsafe {
|
||||
let res = unsafe {
|
||||
let u8_buffer = &mut *(buf as *const [_] as *mut [_] as *mut [u8]);
|
||||
thread_rng().fill(u8_buffer);
|
||||
getrandom::getrandom(u8_buffer)
|
||||
};
|
||||
match res {
|
||||
Ok(()) => __WASI_ESUCCESS,
|
||||
Err(_) => __WASI_EIO,
|
||||
}
|
||||
|
||||
__WASI_ESUCCESS
|
||||
}
|
||||
|
||||
/// ### `sched_yield()`
|
||||
|
@ -8,12 +8,12 @@ use std::mem;
|
||||
use wasmer_runtime_core::types::ValueType;
|
||||
|
||||
pub type __wasi_advice_t = u8;
|
||||
pub const __WASI_ADVICE_DONTNEED: u8 = 0;
|
||||
pub const __WASI_ADVICE_NOREUSE: u8 = 1;
|
||||
pub const __WASI_ADVICE_NORMAL: u8 = 2;
|
||||
pub const __WASI_ADVICE_RANDOM: u8 = 3;
|
||||
pub const __WASI_ADVICE_SEQUENTIAL: u8 = 4;
|
||||
pub const __WASI_ADVICE_WILLNEED: u8 = 5;
|
||||
pub const __WASI_ADVICE_NORMAL: u8 = 0;
|
||||
pub const __WASI_ADVICE_SEQUENTIAL: u8 = 1;
|
||||
pub const __WASI_ADVICE_RANDOM: u8 = 2;
|
||||
pub const __WASI_ADVICE_WILLNEED: u8 = 3;
|
||||
pub const __WASI_ADVICE_DONTNEED: u8 = 4;
|
||||
pub const __WASI_ADVICE_NOREUSE: u8 = 5;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-win-exception-handler"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "Wasmer runtime exception handling for Windows"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,11 +8,9 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" }
|
||||
winapi = { version = "0.3.8", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
libc = "0.2.60"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1"
|
||||
bindgen = "0.51"
|
||||
regex = "1.2"
|
||||
|
@ -1,7 +1,9 @@
|
||||
use std::ptr::NonNull;
|
||||
use wasmer_runtime_core::vm::{Ctx, Func};
|
||||
use wasmer_runtime_core::{
|
||||
typed_func::Trampoline,
|
||||
vm::{Ctx, Func},
|
||||
};
|
||||
|
||||
type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull<Func>, *const u64, *mut u64);
|
||||
type CallProtectedResult = Result<(), CallProtectedData>;
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
PREVIOUS_VERSION='0.7.0'
|
||||
NEXT_VERSION='0.8.0'
|
||||
PREVIOUS_VERSION='0.8.0'
|
||||
NEXT_VERSION='0.9.0'
|
||||
|
||||
# quick hack
|
||||
fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/"
|
||||
|
@ -23,7 +23,7 @@ use structopt::StructOpt;
|
||||
use wasmer::*;
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
use wasmer_llvm_backend::{LLVMCompiler, LLVMOptions};
|
||||
use wasmer_runtime::{
|
||||
cache::{Cache as BaseCache, FileSystemCache, WasmHash},
|
||||
Func, Value, VERSION,
|
||||
@ -109,7 +109,7 @@ pub struct LLVMCLIOptions {
|
||||
post_opt_ir: Option<PathBuf>,
|
||||
|
||||
/// Emit LLVM generated native code object file.
|
||||
#[structopt(long = "backend-llvm-object-file", parse(from_os_str))]
|
||||
#[structopt(long = "llvm-object-file", parse(from_os_str))]
|
||||
obj_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
@ -177,8 +177,8 @@ struct Run {
|
||||
|
||||
/// Whether or not state tracking should be disabled during compilation.
|
||||
/// State tracking is necessary for tier switching and backtracing.
|
||||
#[structopt(long = "no-track-state")]
|
||||
no_track_state: bool,
|
||||
#[structopt(long = "track-state")]
|
||||
track_state: bool,
|
||||
|
||||
/// The command name is a string that will override the first argument passed
|
||||
/// to the wasm program. This is used in wapm to provide nicer output in
|
||||
@ -405,7 +405,21 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
None => return Err("the requested backend is not enabled".into()),
|
||||
};
|
||||
|
||||
let track_state = !options.no_track_state;
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
{
|
||||
if options.backend == Backend::LLVM {
|
||||
let options = options.backend_llvm_options.clone();
|
||||
unsafe {
|
||||
wasmer_llvm_backend::GLOBAL_OPTIONS = LLVMOptions {
|
||||
pre_opt_ir: options.pre_opt_ir,
|
||||
post_opt_ir: options.post_opt_ir,
|
||||
obj_file: options.obj_file,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let track_state = options.track_state;
|
||||
|
||||
#[cfg(feature = "loader-kernel")]
|
||||
let is_kernel_loader = if let Some(LoaderName::Kernel) = options.loader {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[Setup]
|
||||
AppName=Wasmer
|
||||
AppVersion=0.8.0
|
||||
AppVersion=0.9.0
|
||||
DefaultDirName={pf}\Wasmer
|
||||
DefaultGroupName=Wasmer
|
||||
Compression=lzma2
|
||||
|
Loading…
x
Reference in New Issue
Block a user