diff --git a/.dockerignore b/.dockerignore index e861ea7b6..29fd6ce6b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,5 +2,6 @@ ** !lib/** !src/** +!examples/** !Cargo.toml !Cargo.lock \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..4dbbd44d8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +arch: + - arm64 + +language: rust +rust: + - nightly-2019-08-15 + +cache: + directories: + - /home/travis/.sccache/ + - /home/travis/.cargo/bin/ + +script: + # Sccache + # - curl -L https://github.com/mozilla/sccache/releases/download/0.2.10/sccache-0.2.10-x86_64-unknown-linux-musl.tar.gz | tar xzf - + # - export RUSTC_WRAPPER=`pwd`/sccache-0.2.10-x86_64-unknown-linux-musl/sccache + - test -f /home/travis/.cargo/bin/sccache || cargo install sccache + - export RUSTC_WRAPPER=/home/travis/.cargo/bin/sccache + - mkdir -p /home/travis/.sccache/ + - export SCCACHE_DIR="/home/travis/.sccache/" + - SCCACHE_ERROR_LOG=`pwd`/sccache.log RUST_LOG=debug $RUSTC_WRAPPER --start-server + - $RUSTC_WRAPPER -s + + # Tests + - make spectests-singlepass + +before_deploy: + # Release + - make release-singlepass + - make wapm + - make build-install + - mkdir -p artifacts + - cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh) + +# before_deploy: +# # Set up git user name and tag this commit +# - git config --local user.name "Syrus Akbary" +# - git config --local user.email "syrus@wasmer.io" +# - export TRAVIS_TAG="0.10.2" +# # - git tag $TRAVIS_TAG + +deploy: + provider: releases + file_glob: true + file: artifacts/* + api_key: $GITHUB_OAUTH_TOKEN + # This is set to the previous artifacts are not deleted by travis + skip_cleanup: true + on: + tags: true + # branch: feature/singlepass-aarch64 + +addons: + apt: + packages: + - cmake + +branches: + only: + - master + - staging + - trying + # Making sure Travis runs on new Tags + - /^\d+\.\d+(\.\d+)?(-\S*)?$/ + - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d90aaae..4b5b8a80a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,57 @@ # Changelog -All PRs to the Wasmer repository must add to this file. - -Blocks of changes will separated by version increments. - ## **[Unreleased]** +- [#1029](https://github.com/wasmerio/wasmer/pull/1029) Add the “floating” `WasiVersion::Latest` version. +- [#1006](https://github.com/wasmerio/wasmer/pull/1006) Fix minor panic issue when `wasmer::compile_with` called with llvm backend +- [#1009](https://github.com/wasmerio/wasmer/pull/1009) Enable LLVM verifier for all tests, add new llvm-backend-tests crate. +- [#1022](https://github.com/wasmerio/wasmer/pull/1022) Add caching support for Singlepass backend. +- [#1004](https://github.com/wasmerio/wasmer/pull/1004) Add the Auto backend to enable to adapt backend usage depending on wasm file executed. + +## 0.11.0 - 2019-11-22 + +- [#713](https://github.com/wasmerio/wasmer/pull/713) Add AArch64 support for singlepass. +- [#995](https://github.com/wasmerio/wasmer/pull/995) Detect when a global is read without being initialized (emit a proper error instead of panicking) +- [#996](https://github.com/wasmerio/wasmer/pull/997) Refactored spectests, emtests and wasitests to use default compiler logic +- [#992](https://github.com/wasmerio/wasmer/pull/992) Updates WAPM version to 0.4.1, fix arguments issue introduced in #990 +- [#990](https://github.com/wasmerio/wasmer/pull/990) Default wasmer CLI to `run`. Wasmer will now attempt to parse unrecognized command line options as if they were applied to the run command: `wasmer mywasm.wasm --dir=.` now works! +- [#987](https://github.com/wasmerio/wasmer/pull/987) Fix `runtime-c-api` header files when compiled by gnuc. +- [#957](https://github.com/wasmerio/wasmer/pull/957) Change the meaning of `wasmer_wasi::is_wasi_module` to detect any type of WASI module, add support for new wasi snapshot_preview1 +- [#934](https://github.com/wasmerio/wasmer/pull/934) Simplify float expressions in the LLVM backend. + +## 0.10.2 - 2019-11-18 + +- [#968](https://github.com/wasmerio/wasmer/pull/968) Added `--invoke` option to the command +- [#964](https://github.com/wasmerio/wasmer/pull/964) Enable cross-compilation for specific target +- [#971](https://github.com/wasmerio/wasmer/pull/971) In LLVM backend, use unaligned loads and stores for non-atomic accesses to wasmer memory. +- [#960](https://github.com/wasmerio/wasmer/pull/960) Fix `runtime-c-api` header files when compiled by clang. +- [#925](https://github.com/wasmerio/wasmer/pull/925) Host functions can be closures with a captured environment. +- [#917](https://github.com/wasmerio/wasmer/pull/917) Host functions (aka imported functions) may not have `&mut vm::Ctx` as first argument, i.e. the presence of the `&mut vm::Ctx` argument is optional. +- [#915](https://github.com/wasmerio/wasmer/pull/915) All backends share the same definition of `Trampoline` (defined in `wasmer-runtime-core`). + +## 0.10.1 - 2019-11-11 + +- [#952](https://github.com/wasmerio/wasmer/pull/952) Use C preprocessor to properly hide trampoline functions on Windows and non-x86_64 targets. + +## 0.10.0 - 2019-11-11 + +Special thanks to [@newpavlov](https://github.com/newpavlov) and [@Maxgy](https://github.com/Maxgy) for their contributions! + +- [#942](https://github.com/wasmerio/wasmer/pull/942) Deny missing docs in runtime core and add missing docs +- [#939](https://github.com/wasmerio/wasmer/pull/939) Fix bug causing attempts to append to files with WASI to delete the contents of the file +- [#940](https://github.com/wasmerio/wasmer/pull/940) Update supported Rust version to 1.38+ +- [#923](https://github.com/wasmerio/wasmer/pull/923) Fix memory leak in the C API caused by an incorrect cast in `wasmer_trampoline_buffer_destroy` +- [#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. ## 0.8.0 - 2019-10-02 diff --git a/Cargo.lock b/Cargo.lock index 9c0d362ed..eb081c3db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" version = "0.7.6" @@ -23,70 +28,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "bincode" -version = "1.1.4" +name = "backtrace" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "bindgen" -version = "0.51.1" +name = "backtrace-sys" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bincode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "blake2b_simd" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -98,7 +106,7 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -106,45 +114,41 @@ name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "c2-chacha" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "capstone" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "capstone-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "capstone-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cargo_toml" -version = "0.6.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cast" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cbindgen" @@ -153,43 +157,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "cexpr" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "clang-sys" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "clap" version = "2.33.0" @@ -197,7 +183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -209,7 +195,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -217,7 +203,7 @@ name = "cmake" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -225,6 +211,46 @@ name = "constant_time_eq" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cookie" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cookie_store" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cranelift-bforest" version = "0.44.0" @@ -242,10 +268,10 @@ dependencies = [ "cranelift-codegen-meta 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-codegen-shared 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -278,28 +304,36 @@ dependencies = [ "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "criterion" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -310,29 +344,29 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-deque" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -344,12 +378,30 @@ dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -361,8 +413,8 @@ dependencies = [ "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -375,11 +427,11 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -390,54 +442,45 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dtoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dynasm" -version = "0.3.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "dynasmrt" -version = "0.3.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "enum-methods" -version = "0.0.8" +name = "encoding_rs" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "env_logger" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -445,7 +488,7 @@ name = "erased-serde" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -454,7 +497,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -464,30 +507,63 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "field-offset" +name = "flate2" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -496,6 +572,34 @@ name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.55" @@ -503,11 +607,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "generational-arena" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -520,11 +624,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -533,9 +637,9 @@ name = "ghost" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -558,12 +662,37 @@ dependencies = [ "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "h2" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -572,43 +701,124 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "humantime" -version = "1.3.0" +name = "http" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "http-body" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.12.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "indexmap" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#8f480124663b812ee76cd4370f3ee170135b9d0e" +source = "git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520#781620e9fa30e51a6e03bd0d49b5f5bb7a782520" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell_internals 0.1.0 (git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "inkwell_internal_macros" +name = "inkwell_internals" version = "0.1.0" -source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#8f480124663b812ee76cd4370f3ee170135b9d0e" +source = "git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520#781620e9fa30e51a6e03bd0d49b5f5bb7a782520" dependencies = [ - "cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_toml 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -617,7 +827,7 @@ name = "inventory" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "ghost 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -627,17 +837,25 @@ name = "inventory-impl" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itertools" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -665,33 +883,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.62" +version = "0.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "libloading" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "llvm-sys" version = "80.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lock_api" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -702,9 +911,19 @@ name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "md5" version = "0.6.1" @@ -715,16 +934,7 @@ name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memmap" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -732,63 +942,162 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memoffset" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mime" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime_guess" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nix" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "num-traits" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "once_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl" +version = "0.10.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "owning_ref" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -800,7 +1109,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -808,10 +1117,10 @@ dependencies = [ name = "parallel" version = "0.1.0" dependencies = [ - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.8.0", - "wasmer-runtime-core 0.8.0", + "wasmer-runtime 0.11.0", + "wasmer-runtime-core 0.11.0", ] [[package]] @@ -827,7 +1136,7 @@ name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -837,18 +1146,28 @@ name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -862,7 +1181,7 @@ version = "0.1.0" [[package]] name = "ppv-lite86" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -870,9 +1189,9 @@ name = "proc-macro-error" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -885,21 +1204,23 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "quick-error" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.3.15" +name = "publicsuffix" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "quote" @@ -914,7 +1235,25 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -922,19 +1261,28 @@ name = "rand" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -956,7 +1304,15 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -967,6 +1323,24 @@ dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_os" version = "0.1.3" @@ -974,12 +1348,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_xoshiro" version = "0.1.0" @@ -994,31 +1385,31 @@ name = "raw-cpuid" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1067,13 +1458,43 @@ dependencies = [ ] [[package]] -name = "rustc-hash" -version = "1.0.1" +name = "reqwest" +version = "0.9.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc_version" version = "0.2.3" @@ -1084,7 +1505,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1095,6 +1516,15 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schannel" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "1.0.0" @@ -1119,6 +1549,25 @@ dependencies = [ "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "security-framework" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -1134,10 +1583,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1146,7 +1595,7 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1154,37 +1603,56 @@ name = "serde_bytes" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "shlex" -version = "0.1.1" +name = "serde_urlencoded" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.10" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1192,6 +1660,14 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "string" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1199,33 +1675,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1240,46 +1706,33 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "synstructure" -version = "0.10.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "target-lexicon" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1287,22 +1740,14 @@ name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "termcolor" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -1324,7 +1769,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1334,24 +1779,152 @@ name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-buf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-sync" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "toml" -version = "0.4.10" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "toml" -version = "0.5.3" +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "try_from" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1367,7 +1940,7 @@ dependencies = [ "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "typetag-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1376,14 +1949,38 @@ name = "typetag-impl" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-segmentation" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1391,11 +1988,6 @@ name = "unicode-width" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.1.0" @@ -1406,6 +1998,39 @@ name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uuid" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcpkg" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.1" @@ -1416,6 +2041,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -1426,9 +2056,9 @@ name = "wabt" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1437,7 +2067,7 @@ name = "wabt-sys" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1452,6 +2082,16 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "want" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wasi" version = "0.7.0" @@ -1459,52 +2099,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmer" -version = "0.8.0" +version = "0.11.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-dev-utils 0.8.0", - "wasmer-emscripten 0.8.0", - "wasmer-emscripten-tests 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-dev-utils 0.11.0", + "wasmer-emscripten 0.11.0", + "wasmer-emscripten-tests 0.11.0", "wasmer-kernel-loader 0.1.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-middleware-common 0.8.0", - "wasmer-middleware-common-tests 0.8.0", - "wasmer-runtime 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", - "wasmer-wasi 0.8.0", - "wasmer-wasi-tests 0.8.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-middleware-common 0.11.0", + "wasmer-middleware-common-tests 0.11.0", + "wasmer-runtime 0.11.0", + "wasmer-runtime-core 0.11.0", + "wasmer-singlepass-backend 0.11.0", + "wasmer-wasi 0.11.0", + "wasmer-wasi-tests 0.11.0", ] [[package]] name = "wasmer-clif-backend" -version = "0.8.0" +version = "0.11.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-native 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-wasm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", - "wasmer-win-exception-handler 0.8.0", - "wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", + "wasmer-win-exception-handler 0.11.0", + "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1515,7 +2155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1526,231 +2166,242 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-dev-utils" -version = "0.8.0" +version = "0.11.0" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.8.0" +version = "0.11.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-emscripten-tests" -version = "0.8.0" +version = "0.11.0" dependencies = [ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-dev-utils 0.8.0", - "wasmer-emscripten 0.8.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-dev-utils 0.11.0", + "wasmer-emscripten 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime 0.11.0", + "wasmer-singlepass-backend 0.11.0", ] [[package]] name = "wasmer-kernel-loader" version = "0.1.0" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-llvm-backend" -version = "0.8.0" +version = "0.11.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "capstone 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)", + "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", - "wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", + "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "wasmer-middleware-common" -version = "0.8.0" +name = "wasmer-llvm-backend-tests" +version = "0.10.2" dependencies = [ - "wasmer-runtime-core 0.8.0", + "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime 0.11.0", + "wasmer-runtime-core 0.11.0", +] + +[[package]] +name = "wasmer-middleware-common" +version = "0.11.0" +dependencies = [ + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.8.0" +version = "0.11.0" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-middleware-common 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-middleware-common 0.11.0", + "wasmer-runtime-core 0.11.0", + "wasmer-singlepass-backend 0.11.0", ] [[package]] name = "wasmer-runtime" -version = "0.8.0" +version = "0.11.0" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime-core 0.11.0", + "wasmer-singlepass-backend 0.11.0", ] [[package]] name = "wasmer-runtime-c-api" -version = "0.8.0" +version = "0.11.0" dependencies = [ "cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.8.0", - "wasmer-runtime-core 0.8.0", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.11.0", + "wasmer-runtime-core 0.11.0", + "wasmer-wasi 0.11.0", ] [[package]] name = "wasmer-runtime-core" -version = "0.8.0" +version = "0.11.0" dependencies = [ - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "wasmer-singlepass-backend" -version = "0.8.0" +name = "wasmer-runtime-core-tests" +version = "0.11.0" dependencies = [ + "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime-core 0.11.0", + "wasmer-singlepass-backend 0.11.0", +] + +[[package]] +name = "wasmer-singlepass-backend" +version = "0.11.0" +dependencies = [ + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dynasm 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dynasmrt 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", - "wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-spectests" -version = "0.8.0" +version = "0.11.0" dependencies = [ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime 0.11.0", + "wasmer-singlepass-backend 0.11.0", ] [[package]] name = "wasmer-wasi" -version = "0.8.0" +version = "0.11.0" dependencies = [ - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generational-arena 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "generational-arena 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", + "wasmer-runtime-core 0.11.0", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-wasi-tests" -version = "0.8.0" +version = "0.11.0" dependencies = [ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.8.0", - "wasmer-dev-utils 0.8.0", - "wasmer-llvm-backend 0.8.0", - "wasmer-runtime 0.8.0", - "wasmer-runtime-core 0.8.0", - "wasmer-singlepass-backend 0.8.0", - "wasmer-wasi 0.8.0", + "wasmer-clif-backend 0.11.0", + "wasmer-dev-utils 0.11.0", + "wasmer-llvm-backend 0.11.0", + "wasmer-runtime 0.11.0", + "wasmer-singlepass-backend 0.11.0", + "wasmer-wasi 0.11.0", ] [[package]] name = "wasmer-win-exception-handler" -version = "0.8.0" +version = "0.11.0" dependencies = [ - "bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.8.0", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmparser" -version = "0.39.1" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "which" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "winapi" version = "0.2.8" @@ -1789,193 +2440,264 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "wincolor" -version = "1.0.2" +name = "winreg" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" -"checksum bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebd71393f1ec0509b553aa012b9b58e81dadbdff7130bd3b8cba576e69b32f75" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b83b7baab1e671718d78204225800d6b170e648188ac7dc992e9d6bddf87d0c0" "checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum capstone 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "031ba51c39151a1d6336ec859646153187204b0147c7b3f6fe2de636f1b8dbb3" -"checksum capstone-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fae25eddcb80e24f98c35952c37a91ff7f8d0f60dbbdafb9763e8d5cc566b8d7" -"checksum cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "097f5ce64ba566a83d9d914fd005de1e5937fdd57d8c5d99a7593040955d75a9" -"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cargo_toml 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7877b00aaf997d7ed66a81281d3a8b9f9da5361df05b72785b985349979a0f3" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" "checksum cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd" -"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" +"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" +"checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" +"checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum cranelift-bforest 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fff04f4ad82c9704a22e753c6268cc6a89add76f094b837cefbba1c665411451" "checksum cranelift-codegen 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff4a221ec1b95df4b1d20a99fec4fe92a28bebf3a815f2eca72b26f9a627485" "checksum cranelift-codegen-meta 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd47f665e2ee8f177b97d1f5ce2bd70f54d3b793abb26d92942bfaa4a381fe9f" "checksum cranelift-codegen-shared 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05bb95945fd940bd5fc2616b063ce69e55de3d9449a32fa40f6bb99a927085bf" "checksum cranelift-entity 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8753f15d9bde04988834705d437b6f6e4b4da0527968b8d40d7342262d43052" "checksum cranelift-native 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd16b58e95af9ee837218cf41e70306becc1fc7d7dada55dac42df5130a4a4ba" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" "checksum criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" -"checksum ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5b6b2f4752cc29efbfd03474c532ce8f916f2d44ec5bb8c21f93bc76e5365528" +"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f36d49ab6f8ecc642d2c6ee10fda04ba68003ef0277300866745cdde160e6b40" -"checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" -"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" +"checksum dynasm 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "42a814e1edeb85dd2a3c6fc0d6bf76d02ca5695d438c70ecee3d90774f3259c5" +"checksum dynasmrt 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a393aaeb4441a48bcf47b5b6155971f82cc1eb77e22855403ccc0415ac8328d" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" "checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" "checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" -"checksum field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68" +"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generational-arena 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "921c3803adaeb9f9639de5149d9f0f9f4b79f00c423915b701db2e02ed80b9ce" +"checksum generational-arena 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "675c9623fbcdb4b402176db720bf5d95883a36303703ed1bd3a03482382f735a" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum ghost 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" +"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" -"checksum inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)" = "" -"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)" = "" +"checksum http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d7e06e336150b178206af098a055e3621e8336027e2b4d126bda0bc64824baaf" +"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" +"checksum inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520)" = "" +"checksum inkwell_internals 0.1.0 (git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520)" = "" "checksum inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299" "checksum inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9" -"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" "checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" -"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" +"checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" +"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" +"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" +"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" +"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" "checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" -"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" -"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" +"checksum rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43739f8831493b276363637423d3622d4bd6394ab6f0a9c4a552e208aeb7fddd" +"checksum rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8bf17de6f23b05473c437eb958b9c850bfc8af0961fe17b4cc92d5a627b4791" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" +"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" +"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" +"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" "checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" "checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc" -"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" +"checksum serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "1a3351dcbc1f067e2c92ab7c3c1f288ad1a4cffc470b5aaddb4c2e0a3ae80043" +"checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe8d3289b63ef2f196d89e7701f986583c0895e764b78f052a55b9b5d34d84a" -"checksum structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3add731f5b4fb85931d362a3c92deb1ad7113649a8d51701fb257673705f122" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3a3e93f5ad553c38b3301c8a0a0cec829a36783f6a0c467fc4bf553a5f5bf" +"checksum structopt-derive 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7975cb2c6f37d77b190bc5004a2bb015971464756fde9514651a525ada2a741a" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" +"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" +"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" +"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ebb2c484029d695fb68a06d80e1536c68d491b3e0cf874c66abed255e831cfe" "checksum typetag-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b63fd4799e4d0ec5cf0b055ebb8e2c3a657bbf76a84f6edc77ca60780e000204" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" +"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" +"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" +"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" "checksum wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum wasmer-clif-fork-frontend 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0cf2f552a9c1fda0555087170424bd8fedc63a079a97bb5638a4ef9b0d9656aa" "checksum wasmer-clif-fork-wasm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0073b512e1af5948d34be7944b74c747bbe735ccff2e2f28c26ed4c90725de8e" -"checksum wasmparser 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a026c1436af49d5537c7561c7474f81f7a754e36445fe52e6e88795a9747291c" -"checksum which 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "240a31163872f7e8e49f35b42b58485e35355b07eb009d9f3686733541339a69" +"checksum wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c702914acda5feeeffbc29e4d953e5b9ce79d8b98da4dbf18a77086e116c5470" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index 4d619059e..557fade49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "wasmer" -version = "0.8.0" +version = "0.11.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" publish = true description = "High-Performance WebAssembly JIT interpreter" license = "MIT" +default-run = "wasmer" include = [ "examples/**/*", "src/**/*", @@ -23,7 +24,7 @@ byteorder = "1.3" errno = "0.2" structopt = "0.3" wabt = "0.9.1" -wasmer-clif-backend = { path = "lib/clif-backend" } +wasmer-clif-backend = { path = "lib/clif-backend", optional = true } wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } wasmer-middleware-common = { path = "lib/middleware-common" } wasmer-runtime = { path = "lib/runtime" } @@ -43,11 +44,13 @@ members = [ "lib/singlepass-backend", "lib/runtime", "lib/runtime-core", + "lib/runtime-core-tests", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", + "lib/llvm-backend-tests", "lib/wasi", "lib/middleware-common", "lib/kernel-loader", @@ -56,8 +59,8 @@ members = [ "lib/wasi-tests", "lib/emscripten-tests", "lib/middleware-common-tests", - "examples/plugin-for-example", "examples/parallel", + "examples/plugin-for-example", "examples/parallel-guest", ] @@ -71,32 +74,31 @@ serde = { version = "1", features = ["derive"] } # used by the plugin example typetag = "0.1" # used by the plugin example [features] -default = ["fast-tests", "wasi", "backend-cranelift"] +default = ["fast-tests", "wasi"] "loader-kernel" = ["wasmer-kernel-loader"] debug = ["wasmer-runtime-core/debug"] trace = ["wasmer-runtime-core/trace"] +docs = ["wasmer-runtime/docs"] extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] backend-cranelift = [ + "wasmer-clif-backend", "wasmer-runtime-core/backend-cranelift", "wasmer-runtime/cranelift", "wasmer-middleware-common-tests/clif", - "wasmer-wasi-tests/clif" ] backend-llvm = [ "wasmer-llvm-backend", "wasmer-runtime-core/backend-llvm", "wasmer-runtime/llvm", "wasmer-middleware-common-tests/llvm", - "wasmer-wasi-tests/llvm" ] backend-singlepass = [ "wasmer-singlepass-backend", "wasmer-runtime-core/backend-singlepass", "wasmer-runtime/singlepass", "wasmer-middleware-common-tests/singlepass", - "wasmer-wasi-tests/singlepass" ] wasi = ["wasmer-wasi"] managed = ["backend-singlepass", "wasmer-runtime-core/managed"] diff --git a/Dockerfile b/Dockerfile index 43fcda359..73507be30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM circleci/rust:1.33.0-stretch as wasmer-build-env +FROM circleci/rust:1.38.0-stretch as wasmer-build-env RUN sudo apt-get update && \ sudo apt-get install -y --no-install-recommends \ cmake \ @@ -17,9 +17,9 @@ FROM wasmer-build-env AS wasmer-build WORKDIR /home/circleci/wasmer COPY . /home/circleci/wasmer RUN sudo chmod -R 777 . -RUN cargo build --release +RUN cargo build --release --features backend-cranelift FROM debian:stretch AS wasmer WORKDIR /root/ COPY --from=wasmer-build /home/circleci/wasmer/target/release/wasmer . -ENTRYPOINT ["./wasmer"] \ No newline at end of file +ENTRYPOINT ["./wasmer"] diff --git a/Makefile b/Makefile index 58b85dbb1..4acde7925 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ generate: generate-spectests generate-emtests generate-wasitests # Spectests spectests-singlepass: - cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture + cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture --test-threads 1 spectests-cranelift: cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture @@ -89,24 +89,43 @@ 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-llvm-backend-tests --release + cargo test -p wasmer-runtime-core-tests --release --no-default-features --features backend-llvm # All tests capi: - cargo build --release + cargo build --release --features backend-cranelift cargo build -p wasmer-runtime-c-api --release 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; @@ -133,37 +152,82 @@ lint: precommit: lint test debug: - cargo build --release --features backend-singlepass,debug,trace + cargo build --release --features backend-cranelift,backend-singlepass,debug,trace install: cargo install --path . # 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: - cargo build --release --features backend-singlepass,backend-llvm,loader-kernel + cargo build --release --features backend-singlepass,backend-cranelift,backend-llvm,loader-kernel # Only one backend (cranelift) release-clif: # If you are on macOS, you will need mingw-w64 for cross compiling to Windows # brew install mingw-w64 - cargo build --release + cargo build --release --features backend-cranelift release-singlepass: cargo build --release --features backend-singlepass @@ -202,4 +266,7 @@ dep-graph: cargo deps --optional-deps --filter wasmer-wasi wasmer-wasi-tests wasmer-kernel-loader wasmer-dev-utils wasmer-llvm-backend wasmer-emscripten wasmer-emscripten-tests wasmer-runtime-core wasmer-runtime wasmer-middleware-common wasmer-middleware-common-tests wasmer-singlepass-backend wasmer-clif-backend wasmer --manifest-path Cargo.toml | dot -Tpng > wasmer_depgraph.png docs: - cargo doc --features=backend-singlepass,backend-llvm,wasi,managed + cargo doc --features=backend-singlepass,backend-cranelift,backend-llvm,docs,wasi,managed + +wapm: + cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" diff --git a/README.md b/README.md index e69fe2d1c..0391bbd9b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ Build Status + + Documentation + License @@ -21,9 +24,9 @@ ## Introduction -[Wasmer](https://wasmer.io/) is a standalone WebAssembly runtime for running WebAssembly [outside of the browser](https://webassembly.org/docs/non-web/), supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/). +[Wasmer](https://wasmer.io/) is a standalone WebAssembly runtime for running WebAssembly [outside of the browser](https://webassembly.org/docs/non-web/), supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/). Wasmer can be used standalone (via the CLI) and embedded in different languages, running in x86 and [ARM devices](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c). -Install the Wasmer CLI with: +Install the Wasmer and [WAPM](https://wapm.io) cli with: ```sh curl https://get.wasmer.io -sSfL | sh @@ -44,6 +47,7 @@ Wasmer runtime can be used as a library embedded in different languages, so you | ![PHP logo](./docs/assets/languages/php.svg) | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed | ![last release](https://img.shields.io/github/v/release/wasmerio/php-ext-wasm?style=flat-square) | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/php-ext-wasm?style=flat-square) | | ![Ruby logo](./docs/assets/languages/ruby.svg) | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed | ![last release](https://img.shields.io/gem/v/wasmer?style=flat-square) | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/ruby-ext-wasm?style=flat-square) | | ![Postgres logo](./docs/assets/languages/postgres.svg) | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed | ![last release](https://img.shields.io/github/v/release/wasmerio/postgres-ext-wasm?style=flat-square) | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/postgres-ext-wasm?style=flat-square) | +| ![JS Logo](./docs/assets/languages/js.svg) | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | Wasmer | actively developed | ![last release](https://img.shields.io/npm/v/@wasmer/wasi?style=flat-square) | ![number of Github stars](https://img.shields.io/github/stars/wasmerio/wasmer-js?style=flat-square) | | ![C# logo](./docs/assets/languages/csharp.svg) | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | ![last release](https://img.shields.io/nuget/v/WasmerSharp?style=flat-square) | ![number of Github stars](https://img.shields.io/github/stars/migueldeicaza/WasmerSharp?style=flat-square) | | ![R logo](./docs/assets/languages/r.svg) | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | | ![number of Github stars](https://img.shields.io/github/stars/dirkschumacher/wasmr?style=flat-square) | | ![Swift logo](./docs/assets/languages/swift.svg) | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintained | | ![number of Github stars](https://img.shields.io/github/stars/markmals/swift-ext-wasm?style=flat-square) | @@ -58,28 +62,15 @@ Once installed, you will be able to run any WebAssembly files (_including Lua, P ```sh # Run Lua -wasmer run examples/lua.wasm +wasmer examples/lua.wasm ``` *You can find more `wasm/wat` examples in the [examples](./examples) directory.* -#### With wapm +### Docs -Installing Wasmer through `wasmer.io` includes -[`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/). +Wasmer documentation lives on [docs.wasmer.io](https://docs.wasmer.io). -wapm allows you to easily download, run, and distribute WebAssembly binaries. - -```sh -# Install cowsay globally -wapm install -g cowsay - -# Run cowsay -wapm run cowsay "Hello, world!" -``` - -For more information about wapm, check out the [website](https://www.wapm.io) -and this [example program](https://github.com/wapm-packages/rust-wasi-example). ## Code Structure @@ -166,21 +157,18 @@ nginx and Lua do not work on Windows - you can track the progress on [this issue 2. Install [Rust for Windows](https://win.rustup.rs) -3. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine. - Make sure to enable "Add python.exe to Path" during installation. - -4. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default +3. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default settings for the installer are fine). -5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. +4. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. -6. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe) +5. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe)

## Building -[![Rustc Version 1.37+](https://img.shields.io/badge/rustc-1.37+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html) +[![Rustc Version 1.38+](https://img.shields.io/badge/rustc-1.37+-red.svg?style=flat-square)](https://blog.rust-lang.org/2019/09/26/Rust-1.38.0.html) Wasmer is built with [Cargo](https://crates.io/), the Rust package manager. @@ -237,7 +225,7 @@ Each integration can be tested separately: * Spec tests: `make spectests` * Emscripten: `make emtests` -* WASI: `make wasi` +* WASI: `make wasitests` * Middleware: `make middleware` * C API: `make capi` @@ -261,8 +249,8 @@ Below are some of the goals of this project (in order of priority): - [x] It should be 100% compatible with the [WebAssembly spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests) - [x] It should be fast _(partially achieved)_ - [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0) -- [x] Support Emscripten calls _(in the works)_ -- [ ] Support Go JS ABI calls +- [x] Support Emscripten calls +- [ ] Support Go JS ABI calls _(in the works)_ ## Architecture diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c0c350d54..2409dadda 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,7 +22,7 @@ jobs: - script: cargo fmt --all -- --check displayName: Lint variables: - rust_toolchain: '1.37.0' + rust_toolchain: '1.38.0' - job: Test strategy: @@ -39,7 +39,7 @@ jobs: CARGO_HTTP_CHECK_REVOKE: false windows: imageName: "vs2017-win2016" - rust_toolchain: '1.37.0' + rust_toolchain: '1.38.0' pool: vmImage: $(imageName) condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying') @@ -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'))) @@ -96,11 +100,15 @@ jobs: MACOSX_DEPLOYMENT_TARGET: 10.10 windows: imageName: "vs2017-win2016" - rust_toolchain: '1.37.0' + rust_toolchain: '1.38.0' # RUSTFLAGS: -Ctarget-feature=+crt-static pool: vmImage: $(imageName) - condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying') + condition: | + or( + in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying'), + startsWith(variables['Build.SourceBranch'], 'refs/tags') + ) steps: - checkout: self submodules: true @@ -119,10 +127,10 @@ jobs: displayName: Build (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - bash: | - cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" + make wapm displayName: Build WAPM condition: | - eq(variables['Build.SourceBranch'], 'refs/heads/master') + startsWith(variables['Build.SourceBranch'], 'refs/tags') - bash: | make build-install cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh) @@ -130,7 +138,7 @@ jobs: condition: | and( succeeded(), - eq(variables['Build.SourceBranch'], 'refs/heads/master'), + startsWith(variables['Build.SourceBranch'], 'refs/tags'), not(eq(variables['Agent.OS'], 'Windows_NT')) ) - bash: | @@ -141,7 +149,7 @@ jobs: condition: | and( succeeded(), - eq(variables['Build.SourceBranch'], 'refs/heads/master'), + startsWith(variables['Build.SourceBranch'], 'refs/tags'), eq(variables['Agent.OS'], 'Windows_NT') ) - publish: $(System.DefaultWorkingDirectory)/artifacts @@ -159,11 +167,15 @@ jobs: MACOSX_DEPLOYMENT_TARGET: 10.10 windows: imageName: "vs2017-win2016" - rust_toolchain: '1.37.0' + rust_toolchain: '1.38.0' # RUSTFLAGS: -Ctarget-feature=+crt-static pool: vmImage: $(imageName) - condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying') + condition: | + or( + in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying'), + startsWith(variables['Build.SourceBranch'], 'refs/tags') + ) steps: - checkout: self submodules: true @@ -201,7 +213,8 @@ jobs: dependsOn: - Build_CLI - Build_Library - condition: eq(variables['Build.SourceBranch'], 'refs/heads/master') + condition: | + startsWith(variables['Build.SourceBranch'], 'refs/tags') steps: # - download: current - task: DownloadPipelineArtifact@1 @@ -212,14 +225,23 @@ jobs: displayName: List Artifacts env: ARTIFACT_STAGING_DIRECTORY: $(Build.ArtifactStagingDirectory) + - script: VERSION_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=VERSION_TAG]$VERSION_TAG" + displayName: Set the tag name as an environment variable - task: GithubRelease@0 displayName: "Create GitHub Release" - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags')) inputs: - gitHubConnection: wasmer - repositoryName: wasmerio/wasmer - tag: dev - assets: $(Build.ArtifactStagingDirectory) + gitHubConnection: 'wasmer' + repositoryName: 'wasmerio/wasmer' + action: 'create' + target: '$(Build.SourceVersion)' + title: '$(VERSION_TAG)' + addChangeLog: false + tagSource: 'auto' + # TODO: automate it all by getting the release notes from somewhere else and using the `releaseNotesFile` key + isDraft: false + isPreRelease: false + assets: '$(Build.ArtifactStagingDirectory)/**' - job: Docs pool: @@ -246,3 +268,4 @@ trigger: - master - staging - trying + - refs/tags/* diff --git a/docs/assets/languages/js.svg b/docs/assets/languages/js.svg new file mode 100755 index 000000000..6a186f383 --- /dev/null +++ b/docs/assets/languages/js.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/feature_matrix.md b/docs/feature_matrix.md index cdb6ff8ad..18d303270 100644 --- a/docs/feature_matrix.md +++ b/docs/feature_matrix.md @@ -4,13 +4,24 @@ |   | Singlepass | Cranelift | LLVM | | - | :-: | :-: | :-: | -| Caching | ⬜ | ✅ | ✅ | +| Caching | ✅ | ✅ | ✅ | | Emscripten | ✅ | ✅ | ✅ | | Metering | ✅ | ⬜ | ✅ | | Multi-value return | ⬜ | ⬜ | ⬜ | -| OSR | 🔄 | ❓ | ❓ | +| 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 | ✅ | ⬜ | ⬜ | ⬜ | ⬜ | diff --git a/examples/fib.wat b/examples/fib.wat new file mode 100644 index 000000000..a797fdae4 --- /dev/null +++ b/examples/fib.wat @@ -0,0 +1,20 @@ +(module + (func $main (result i32) + (call $fib (i32.const 40)) + ) + + (func $fib (param $n i32) (result i32) + (if (i32.eq (get_local $n) (i32.const 0)) + (then (return (i32.const 1))) + ) + (if (i32.eq (get_local $n) (i32.const 1)) + (then (return (i32.const 1))) + ) + (i32.add + (call $fib (i32.sub (get_local $n) (i32.const 1))) + (call $fib (i32.sub (get_local $n) (i32.const 2))) + ) + ) + + (export "main" (func $main)) +) diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml new file mode 100644 index 000000000..a71d719ed --- /dev/null +++ b/examples/hello_world/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "hello_world" +version = "0.1.0" +authors = ["losfair "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs new file mode 100644 index 000000000..ca24b9cea --- /dev/null +++ b/examples/hello_world/src/main.rs @@ -0,0 +1,7 @@ +fn main() { + for i in 0..8 { + let s = format!("Hello, {}", i); + println!("{}", s); + } + panic!("OK"); +} diff --git a/examples/iterative_hash/src/main.rs b/examples/iterative_hash/src/main.rs index 043bc02a1..66ed10405 100644 --- a/examples/iterative_hash/src/main.rs +++ b/examples/iterative_hash/src/main.rs @@ -20,7 +20,7 @@ fn main() { let diff = millis - last_millis; if diff >= 100 { record_count += 1; - println!("{}", (i - round_count) as f64 / diff as f64); + println!("{}", ((i - round_count) as u128) * 1000000 / diff ); last_millis = millis; round_count = i; } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 713443355..8d6a38140 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,6 +12,8 @@ cargo-fuzz = true wasmer-runtime = { path = "../lib/runtime" } wasmer-runtime-core = { path = "../lib/runtime-core" } wasmer = { path = "../" } +wasmer-llvm-backend = { path = "../lib/llvm-backend" } +wasmer-singlepass-backend = { path = "../lib/singlepass-backend" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } # Prevent this from interfering with workspaces diff --git a/fuzz/README.md b/fuzz/README.md index 00939ee5a..36f7e0c4c 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -47,7 +47,7 @@ The fuzzer works best when it has examples of small Wasm files to start with. Us ```sh mkdir spec-test-corpus -for i in lib/spectests/spectests/*.wast; do wast2json $i -o spec-test-corpus/$(basename $i).json; done +for i in lib/spectests/spectests/*.wast; do wast2json --enable-all $i -o spec-test-corpus/$(basename $i).json; done mv spec-test-corpus/*.wasm fuzz/corpus/simple_instantiate/ rm -r spec-test-corpus ``` diff --git a/fuzz/fuzz_targets/compile_wasm.rs b/fuzz/fuzz_targets/compile_wasm.rs index 4abe062f5..e36d8c396 100644 --- a/fuzz/fuzz_targets/compile_wasm.rs +++ b/fuzz/fuzz_targets/compile_wasm.rs @@ -2,9 +2,24 @@ #[macro_use] extern crate libfuzzer_sys; extern crate wasmer_runtime; +extern crate wasmer_runtime_core; +extern crate wasmer_llvm_backend; +extern crate wasmer_singlepass_backend; -use wasmer_runtime::compile; +use wasmer_runtime::{compile, compile_with}; +use wasmer_runtime_core::backend::Compiler; + +fn get_llvm_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} +fn get_singlepass_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} fuzz_target!(|data: &[u8]| { + let _ = compile_with(data, &get_llvm_compiler()); let _ = compile(data); + let _ = compile_with(data, &get_singlepass_compiler()); }); diff --git a/install.sh b/install.sh index a4fc2b841..e6c874e2a 100755 --- a/install.sh +++ b/install.sh @@ -208,9 +208,11 @@ initArch() { printf "$cyan> Using WASMER_ARCH ($WASMER_ARCH).$reset\n" ARCH="$WASMER_ARCH" fi + # If you modify this list, please also modify scripts/binary-name.sh case $ARCH in amd64) ARCH="amd64";; x86_64) ARCH="amd64";; + aarch64) ARCH="arm64";; # i386) ARCH="386";; *) printf "$red> The system architecture (${ARCH}) is not supported by this installation script.$reset\n"; exit 1;; esac diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index c940b1b6e..1dea62a24 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,15 +1,17 @@ [package] name = "wasmer-clif-backend" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "compiler", "JIT", "AOT"] +categories = ["wasm"] edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } cranelift-native = "0.44.0" cranelift-codegen = "0.44.0" cranelift-entity = "0.44.0" @@ -35,7 +37,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.11.0" } [features] debug = ["wasmer-runtime-core/debug"] diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index b2ccc6652..d86d19b4f 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -54,6 +54,10 @@ impl ModuleCodeGenerator } } + fn new_with_target(_: Option, _: Option, _: Option) -> Self { + unimplemented!("cross compilation is not available for clif backend") + } + fn backend_id() -> Backend { Backend::Cranelift } @@ -128,166 +132,6 @@ impl ModuleCodeGenerator .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()) } @@ -851,7 +695,9 @@ impl FuncEnvironment for FunctionEnvironment { } /// Generates a call IR with `callee` and `call_args` and inserts it at `pos` - /// TODO: add support for imported functions + /// + /// It's about generating code that calls a local or imported function; in + /// WebAssembly: `(call $foo)`. fn translate_call( &mut self, mut pos: FuncCursor, @@ -923,20 +769,31 @@ impl FuncEnvironment for FunctionEnvironment { readonly: true, }); - let imported_vmctx_addr = pos.func.create_global_value(ir::GlobalValueData::Load { - base: imported_func_struct_addr, - offset: (vm::ImportedFunc::offset_vmctx() as i32).into(), - global_type: ptr_type, - readonly: true, - }); + let imported_func_ctx_addr = + pos.func.create_global_value(ir::GlobalValueData::Load { + base: imported_func_struct_addr, + offset: (vm::ImportedFunc::offset_func_ctx() as i32).into(), + global_type: ptr_type, + readonly: true, + }); + + let imported_func_ctx_vmctx_addr = + pos.func.create_global_value(ir::GlobalValueData::Load { + base: imported_func_ctx_addr, + offset: (vm::FuncCtx::offset_vmctx() as i32).into(), + global_type: ptr_type, + readonly: true, + }); let imported_func_addr = pos.ins().global_value(ptr_type, imported_func_addr); - let imported_vmctx_addr = pos.ins().global_value(ptr_type, imported_vmctx_addr); + let imported_func_ctx_vmctx_addr = pos + .ins() + .global_value(ptr_type, imported_func_ctx_vmctx_addr); let sig_ref = pos.func.dfg.ext_funcs[callee].signature; let mut args = Vec::with_capacity(call_args.len() + 1); - args.push(imported_vmctx_addr); + args.push(imported_func_ctx_vmctx_addr); args.extend(call_args.iter().cloned()); Ok(pos diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index ddecdbaf0..06bffc791 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -1,5 +1,10 @@ +//! The Wasmer Cranelift Backend crate is used to compile wasm binary code via parse events from the +//! Wasmer runtime common parser code into machine code. +//! + #![deny( dead_code, + missing_docs, nonstandard_style, unused_imports, unused_mut, @@ -53,6 +58,8 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; +/// Streaming compiler implementation for the Cranelift backed. Compiles web assembly binary into +/// machine code. pub type CraneliftCompiler = SimpleStreamingCompilerGen< code::CraneliftModuleCodeGenerator, code::CraneliftFunctionCodeGenerator, diff --git a/lib/clif-backend/src/relocation.rs b/lib/clif-backend/src/relocation.rs index bb07c7c59..a86bdeec9 100644 --- a/lib/clif-backend/src/relocation.rs +++ b/lib/clif-backend/src/relocation.rs @@ -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"); } } diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 7a868f35e..efc038d58 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -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!(" ]"); -} diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 3facce2ad..116da3f56 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -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 { unsafe extern "C" fn invoke( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), + trampoline: Trampoline, ctx: *mut vm::Ctx, func: NonNull, args: *const u64, diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 39f3aa893..79b6619d3 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -98,7 +98,10 @@ pub fn call_protected( }, 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) { diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index d755cd575..363119c6e 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -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"); } diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index a6fc6572b..fcd1ff83d 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -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, *const u64, *mut u64); - pub struct Trampolines { memory: Memory, offsets: HashMap, diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index bb6957b3a..81fb49757 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/emscripten-tests/Cargo.toml b/lib/emscripten-tests/Cargo.toml index 53bf8d2c2..2439b4295 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.8.0" +version = "0.11.0" description = "Tests for our Emscripten implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,20 +9,20 @@ 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.11.0" } +wasmer-runtime = { path = "../runtime", version = "0.11.0", default-features = false } +wasmer-clif-backend = { path = "../clif-backend", version = "0.11.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", optional = true, features = ["test"] } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.11.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.11.0"} [build-dependencies] glob = "0.3" [features] -clif = [] -llvm = ["wasmer-llvm-backend"] -singlepass = ["wasmer-singlepass-backend"] +clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] +singlepass = ["wasmer-singlepass-backend", "wasmer-runtime/default-backend-singlepass"] +llvm = ["wasmer-llvm-backend", "wasmer-runtime/default-backend-llvm"] diff --git a/lib/emscripten-tests/src/lib.rs b/lib/emscripten-tests/src/lib.rs index 1da0abc73..59bcf90eb 100644 --- a/lib/emscripten-tests/src/lib.rs +++ b/lib/emscripten-tests/src/lib.rs @@ -3,40 +3,13 @@ mod tests { use std::sync::Arc; use wabt::wat2wasm; use wasmer_emscripten::is_emscripten_module; - use wasmer_runtime_core::backend::Compiler; - use wasmer_runtime_core::compile_with; - - #[cfg(feature = "clif")] - fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - - #[cfg(feature = "llvm")] - fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() - } - - #[cfg(feature = "singlepass")] - fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() - } - - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] - fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } + use wasmer_runtime::compile; #[test] fn should_detect_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_true.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = - compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); + let module = compile(&wasm_binary[..]).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(is_emscripten_module(&module)); } @@ -45,8 +18,7 @@ mod tests { fn should_detect_non_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_false.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = - compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); + let module = compile(&wasm_binary[..]).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(!is_emscripten_module(&module)); } diff --git a/lib/emscripten-tests/tests/emtests/_common.rs b/lib/emscripten-tests/tests/emtests/_common.rs index 1e5dfba6e..2e6c590d9 100644 --- a/lib/emscripten-tests/tests/emtests/_common.rs +++ b/lib/emscripten-tests/tests/emtests/_common.rs @@ -5,39 +5,12 @@ macro_rules! assert_emscripten_output { EmscriptenGlobals, generate_emscripten_env, }; - use wasmer_runtime_core::{ - backend::Compiler, - }; + use wasmer_runtime::compile; use wasmer_dev_utils::stdio::StdioCapturer; - #[cfg(feature = "clif")] - fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - - #[cfg(feature = "llvm")] - fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() - } - - #[cfg(feature = "singlepass")] - fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() - } - - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] - fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - let wasm_bytes = include_bytes!($file); - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler()) + let module = compile(&wasm_bytes[..]) .expect("WASM can't be compiled"); // let module = compile(&wasm_bytes[..]) diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index e55fb221a..dffbe0942 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-emscripten" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "ABI", "emscripten", "posix"] +categories = ["wasm"] edition = "2018" [dependencies] @@ -12,10 +14,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.11.0" } [target.'cfg(windows)'.dependencies] -rand = "0.7" +getrandom = "0.1" [features] debug = ["wasmer-runtime-core/debug"] diff --git a/lib/emscripten/src/exception.rs b/lib/emscripten/src/exception.rs index 09f04a798..09ae286f8 100644 --- a/lib/emscripten/src/exception.rs +++ b/lib/emscripten/src/exception.rs @@ -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 diff --git a/lib/emscripten/src/io/mod.rs b/lib/emscripten/src/io/mod.rs index 6666cd5af..bad5935fe 100644 --- a/lib/emscripten/src/io/mod.rs +++ b/lib/emscripten/src/io/mod.rs @@ -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") } diff --git a/lib/emscripten/src/io/windows.rs b/lib/emscripten/src/io/windows.rs index a3c6f70aa..a7d6dc60b 100644 --- a/lib/emscripten/src/io/windows.rs +++ b/lib/emscripten/src/io/windows.rs @@ -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") } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 7dc808bde..1c9b8a1f7 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -111,7 +111,7 @@ pub struct EmscriptenData<'a> { pub dyn_call_iii: Option>, pub dyn_call_iiii: Option>, pub dyn_call_iifi: Option>, - pub dyn_call_v: Option>, + pub dyn_call_v: Option>, pub dyn_call_vi: Option>, pub dyn_call_vii: Option>, pub dyn_call_viii: Option>, @@ -168,7 +168,7 @@ pub struct EmscriptenData<'a> { pub temp_ret_0: i32, pub stack_save: Option>, - pub stack_restore: Option>, + pub stack_restore: Option>, pub set_threw: Option>, pub mapped_dirs: HashMap, } diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index efcff0a5e..62d807c02 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -7,6 +7,7 @@ use libc::{ accept, access, bind, + c_char, c_int, c_ulong, c_void, @@ -259,7 +260,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 @@ -1063,14 +1064,14 @@ pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { let upper_bound = std::cmp::min((*dirent).d_reclen, 255) as usize; let mut i = 0; while i < upper_bound { - *(dirp.add(pos + 11 + i) as *mut i8) = (*dirent).d_name[i] as _; + *(dirp.add(pos + 11 + i) as *mut c_char) = (*dirent).d_name[i] as c_char; i += 1; } // We set the termination string char - *(dirp.add(pos + 11 + i) as *mut i8) = 0 as i8; + *(dirp.add(pos + 11 + i) as *mut c_char) = 0 as c_char; debug!( " => file {}", - CStr::from_ptr(dirp.add(pos + 11) as *const i8) + CStr::from_ptr(dirp.add(pos + 11) as *const c_char) .to_str() .unwrap() ); @@ -1111,6 +1112,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) } } diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index f6a867356..b5ed6cc83 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -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) } diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml new file mode 100644 index 000000000..bb6b24b51 --- /dev/null +++ b/lib/llvm-backend-tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wasmer-llvm-backend-tests" +version = "0.10.2" +authors = ["Nick Lewycky "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wabt = "0.9.1" +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } +wasmer-runtime = { path = "../runtime", version = "0.11.0" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", features = ["test"] } + +[features] diff --git a/lib/llvm-backend-tests/src/lib.rs b/lib/llvm-backend-tests/src/lib.rs new file mode 100644 index 000000000..ea79b7872 --- /dev/null +++ b/lib/llvm-backend-tests/src/lib.rs @@ -0,0 +1,7 @@ +pub use wabt::wat2wasm; +use wasmer_llvm_backend::LLVMCompiler; +use wasmer_runtime_core::backend::Compiler; + +pub fn get_compiler() -> impl Compiler { + LLVMCompiler::new() +} diff --git a/lib/llvm-backend-tests/tests/compile.rs b/lib/llvm-backend-tests/tests/compile.rs new file mode 100644 index 000000000..17071a44a --- /dev/null +++ b/lib/llvm-backend-tests/tests/compile.rs @@ -0,0 +1,40 @@ +use wasmer_llvm_backend_tests::{get_compiler, wat2wasm}; +use wasmer_runtime::imports; +use wasmer_runtime_core::compile_with; + +#[test] +fn crash_return_with_float_on_stack() { + const MODULE: &str = r#" +(module + (type (;0;) (func)) + (type (;1;) (func (param f64) (result f64))) + (func $_start (type 0)) + (func $fmod (type 1) (param f64) (result f64) + local.get 0 + f64.const 0x0p+0 (;=0;) + f64.mul + return) +) +"#; + let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); + let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); + module.instantiate(&imports! {}).unwrap(); +} + +#[test] +fn crash_select_with_mismatched_pending() { + const MODULE: &str = r#" + (module + (func (param f64) + f64.const 0x0p+0 (;=0;) + local.get 0 + f64.add + f64.const 0x0p+0 (;=0;) + i32.const 0 + select + drop)) +"#; + let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); + let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); + module.instantiate(&imports! {}).unwrap(); +} diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 1cc15ec89..b9fa7e6a7 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,22 +1,25 @@ [package] name = "wasmer-llvm-backend" -version = "0.8.0" +version = "0.11.0" +license = "MIT" authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "compiler", "JIT", "llvm"] +categories = ["wasm"] edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.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] -git = "https://github.com/wasmerio/inkwell" -branch = "llvm8-0" +git = "https://github.com/TheDan64/inkwell" +rev = "781620e9fa30e51a6e03bd0d49b5f5bb7a782520" default-features = false features = ["llvm8-0", "target-x86"] @@ -38,4 +41,4 @@ wabt = "0.9.1" [features] debug = ["wasmer-runtime-core/debug"] -disasm = ["capstone"] +test = [] diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 682bd7278..a5d262035 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -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, *const u64, *mut u64), + trampoline: Trampoline, vmctx_ptr: *mut vm::Ctx, func_ptr: NonNull, params: *const u64, @@ -168,34 +171,14 @@ pub struct LLVMBackend { impl LLVMBackend { pub fn new( - module: Module, + module: Rc>, _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 { - let trampoline: unsafe extern "C" fn( - *mut vm::Ctx, - NonNull, - *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("") - ); - } -} diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 9ccb0c990..c0f6e07dd 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -1,21 +1,34 @@ +use crate::{ + backend::LLVMBackend, + intrinsics::{tbaa_label, CtxType, GlobalCache, Intrinsics, MemoryCache}, + read_info::{blocktype_to_type, type_to_type}, + stackmap::{StackmapEntry, StackmapEntryKind, StackmapRegistry, ValueSemantic}, + state::{ControlFrame, ExtraInfo, IfElseState, State}, + trampolines::generate_trampolines, +}; use inkwell::{ builder::Builder, context::Context, module::{Linkage, Module}, passes::PassManager, + targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine}, types::{BasicType, BasicTypeEnum, FunctionType, PointerType, VectorType}, values::{ BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue, VectorValue, }, - AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate, + AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate, OptimizationLevel, }; use smallvec::SmallVec; -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::{Arc, RwLock}; +use std::{ + cell::RefCell, + collections::HashMap, + mem::ManuallyDrop, + rc::Rc, + sync::{Arc, RwLock}, +}; use wasmer_runtime_core::{ - backend::{Backend, CacheGen, Token}, + backend::{Backend, CacheGen, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, codegen::*, memory::MemoryType, @@ -27,19 +40,12 @@ use wasmer_runtime_core::{ }; use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; -use crate::backend::LLVMBackend; -use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; -use crate::read_info::{blocktype_to_type, type_to_type}; -use crate::stackmap::{StackmapEntry, StackmapEntryKind, StackmapRegistry, ValueSemantic}; -use crate::state::{ControlFrame, IfElseState, State}; -use crate::trampolines::generate_trampolines; - -fn func_sig_to_llvm( - context: &Context, - intrinsics: &Intrinsics, +fn func_sig_to_llvm<'ctx>( + context: &'ctx Context, + intrinsics: &Intrinsics<'ctx>, sig: &FuncSig, - type_to_llvm: fn(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum, -) -> FunctionType { + type_to_llvm: fn(intrinsics: &Intrinsics<'ctx>, ty: Type) -> BasicTypeEnum<'ctx>, +) -> FunctionType<'ctx> { let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty)); let param_types: Vec<_> = std::iter::once(intrinsics.ctx_ptr_ty.as_basic_type_enum()) @@ -62,7 +68,7 @@ fn func_sig_to_llvm( } } -fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { +fn type_to_llvm<'ctx>(intrinsics: &Intrinsics<'ctx>, ty: Type) -> BasicTypeEnum<'ctx> { match ty { Type::I32 => intrinsics.i32_ty.as_basic_type_enum(), Type::I64 => intrinsics.i64_ty.as_basic_type_enum(), @@ -72,7 +78,7 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } } -fn type_to_llvm_int_only(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { +fn type_to_llvm_int_only<'ctx>(intrinsics: &Intrinsics<'ctx>, ty: Type) -> BasicTypeEnum<'ctx> { match ty { Type::I32 | Type::F32 => intrinsics.i32_ty.as_basic_type_enum(), Type::I64 | Type::F64 => intrinsics.i64_ty.as_basic_type_enum(), @@ -81,13 +87,13 @@ fn type_to_llvm_int_only(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } // Create a vector where each lane contains the same value. -fn splat_vector( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, - vec_ty: VectorType, +fn splat_vector<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + vec_ty: VectorType<'ctx>, name: &str, -) -> VectorValue { +) -> VectorValue<'ctx> { // Use insert_element to insert the element into an undef vector, then use // shuffle vector to copy that lane to all lanes. builder.build_shuffle_vector( @@ -101,18 +107,18 @@ fn splat_vector( // Convert floating point vector to integer and saturate when out of range. // TODO: generalize to non-vectors using FloatMathType, IntMathType, etc. for // https://github.com/WebAssembly/nontrapping-float-to-int-conversions/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md -fn trunc_sat( - builder: &Builder, - intrinsics: &Intrinsics, - fvec_ty: VectorType, - ivec_ty: VectorType, +fn trunc_sat<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + fvec_ty: VectorType<'ctx>, + ivec_ty: VectorType<'ctx>, lower_bound: u64, // Exclusive (lowest representable value) upper_bound: u64, // Exclusive (greatest representable value) int_min_value: u64, int_max_value: u64, - value: IntValue, + value: IntValue<'ctx>, name: &str, -) -> IntValue { +) -> IntValue<'ctx> { // a) Compare vector with itself to identify NaN lanes. // b) Compare vector with splat of inttofp(upper_bound) to identify // lanes that need to saturate to max. @@ -220,11 +226,11 @@ fn trunc_sat( .into_int_value() } -fn trap_if_not_representable_as_int( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, +fn trap_if_not_representable_as_int<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + context: &'ctx Context, + function: &FunctionValue<'ctx>, lower_bound: u64, // Inclusive (not a trapping value) upper_bound: u64, // Inclusive (not a trapping value) value: FloatValue, @@ -256,8 +262,8 @@ fn trap_if_not_representable_as_int( "out_of_bounds", ); - let failure_block = context.append_basic_block(function, "conversion_failure_block"); - let continue_block = context.append_basic_block(function, "conversion_success_block"); + let failure_block = context.append_basic_block(*function, "conversion_failure_block"); + let continue_block = context.append_basic_block(*function, "conversion_success_block"); builder.build_conditional_branch(out_of_bounds, &failure_block, &continue_block); builder.position_at_end(&failure_block); @@ -270,11 +276,11 @@ fn trap_if_not_representable_as_int( builder.position_at_end(&continue_block); } -fn trap_if_zero_or_overflow( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, +fn trap_if_zero_or_overflow<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + context: &'ctx Context, + function: &FunctionValue<'ctx>, left: IntValue, right: IntValue, ) { @@ -321,8 +327,8 @@ fn trap_if_zero_or_overflow( .unwrap() .into_int_value(); - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); + let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(*function, "should_trap_block"); builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); builder.position_at_end(&should_trap_block); builder.build_call( @@ -334,11 +340,11 @@ fn trap_if_zero_or_overflow( builder.position_at_end(&shouldnt_trap_block); } -fn trap_if_zero( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, +fn trap_if_zero<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + context: &'ctx Context, + function: &FunctionValue<'ctx>, value: IntValue, ) { let int_type = value.get_type(); @@ -363,8 +369,8 @@ fn trap_if_zero( .unwrap() .into_int_value(); - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); + let shouldnt_trap_block = context.append_basic_block(*function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(*function, "should_trap_block"); builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); builder.position_at_end(&should_trap_block); builder.build_call( @@ -376,12 +382,161 @@ fn trap_if_zero( builder.position_at_end(&shouldnt_trap_block); } +fn v128_into_int_vec<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, + int_vec_ty: VectorType<'ctx>, +) -> (VectorValue<'ctx>, ExtraInfo) { + let (value, info) = if info.has_pending_f32_nan() { + let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) + } else if info.has_pending_f64_nan() { + let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) + } else { + (value, info) + }; + ( + builder + .build_bitcast(value, int_vec_ty, "") + .into_vector_value(), + info, + ) +} + +fn v128_into_i8x16<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i8x16_ty) +} + +fn v128_into_i16x8<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i16x8_ty) +} + +fn v128_into_i32x4<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i32x4_ty) +} + +fn v128_into_i64x2<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i64x2_ty) +} + +// If the value is pending a 64-bit canonicalization, do it now. +// Return a f32x4 vector. +fn v128_into_f32x4<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + let (value, info) = if info.has_pending_f64_nan() { + let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) + } else { + (value, info) + }; + ( + builder + .build_bitcast(value, intrinsics.f32x4_ty, "") + .into_vector_value(), + info, + ) +} + +// If the value is pending a 32-bit canonicalization, do it now. +// Return a f64x2 vector. +fn v128_into_f64x2<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> (VectorValue<'ctx>, ExtraInfo) { + let (value, info) = if info.has_pending_f32_nan() { + let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) + } else { + (value, info) + }; + ( + builder + .build_bitcast(value, intrinsics.f64x2_ty, "") + .into_vector_value(), + info, + ) +} + +fn apply_pending_canonicalization<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, + info: ExtraInfo, +) -> BasicValueEnum<'ctx> { + if info.has_pending_f32_nan() { + if value.get_type().is_vector_type() + || value.get_type() == intrinsics.i128_ty.as_basic_type_enum() + { + let ty = value.get_type(); + let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); + let value = canonicalize_nans(builder, intrinsics, value); + builder.build_bitcast(value, ty, "") + } else { + canonicalize_nans(builder, intrinsics, value) + } + } else if info.has_pending_f64_nan() { + if value.get_type().is_vector_type() + || value.get_type() == intrinsics.i128_ty.as_basic_type_enum() + { + let ty = value.get_type(); + let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); + let value = canonicalize_nans(builder, intrinsics, value); + builder.build_bitcast(value, ty, "") + } else { + canonicalize_nans(builder, intrinsics, value) + } + } else { + value + } +} + // Replaces any NaN with the canonical QNaN, otherwise leaves the value alone. -fn canonicalize_nans( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, -) -> BasicValueEnum { +fn canonicalize_nans<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, +) -> BasicValueEnum<'ctx> { let f_ty = value.get_type(); let canonicalized = if f_ty.is_vector_type() { let value = value.into_vector_value(); @@ -415,62 +570,110 @@ fn canonicalize_nans( canonicalized } -fn resolve_memory_ptr( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - state: &mut State, - ctx: &mut CtxType, +fn resolve_memory_ptr<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + context: &'ctx Context, + module: Rc>>, + function: &FunctionValue<'ctx>, + state: &mut State<'ctx>, + ctx: &mut CtxType<'static, 'ctx>, memarg: &MemoryImmediate, - ptr_ty: PointerType, + ptr_ty: PointerType<'ctx>, value_size: usize, -) -> Result { - // Ignore alignment hint for the time being. - let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); - let value_size_v = intrinsics.i64_ty.const_int(value_size as u64, false); - let var_offset_i32 = state.pop1()?.into_int_value(); - let var_offset = - builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); - let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); - let end_offset = builder.build_int_add(effective_offset, value_size_v, &state.var_name()); - let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics); - - let mem_base_int = match memory_cache { +) -> Result, BinaryReaderError> { + // Look up the memory base (as pointer) and bounds (as unsigned integer). + let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics, module.clone()); + let (mem_base, mem_bound, minimum, _maximum) = match memory_cache { MemoryCache::Dynamic { ptr_to_base_ptr, ptr_to_bounds, + minimum, + maximum, } => { let base = builder .build_load(ptr_to_base_ptr, "base") .into_pointer_value(); let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); + tbaa_label( + &module, + intrinsics, + "dynamic_memory_base", + base.as_instruction_value().unwrap(), + Some(0), + ); + tbaa_label( + &module, + intrinsics, + "dynamic_memory_bounds", + bounds.as_instruction_value().unwrap(), + Some(0), + ); + (base, bounds, minimum, maximum) + } + MemoryCache::Static { + base_ptr, + bounds, + minimum, + maximum, + } => (base_ptr, bounds, minimum, maximum), + }; + let mem_base = builder + .build_bitcast(mem_base, intrinsics.i8_ptr_ty, &state.var_name()) + .into_pointer_value(); - let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); + // Compute the offset over the memory_base. + let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); + let var_offset_i32 = state.pop1()?.into_int_value(); + let var_offset = + builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); + let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); - let base_in_bounds_1 = builder.build_int_compare( + if let MemoryCache::Dynamic { .. } = memory_cache { + // If the memory is dynamic, do a bounds check. For static we rely on + // the size being a multiple of the page size and hitting a guard page. + let value_size_v = intrinsics.i64_ty.const_int(value_size as u64, false); + let ptr_in_bounds = if effective_offset.is_const() { + let load_offset_end = effective_offset.const_add(value_size_v); + let ptr_in_bounds = load_offset_end.const_int_compare( IntPredicate::ULE, - end_offset, - bounds, - "base_in_bounds_1", + intrinsics.i64_ty.const_int(minimum.bytes().0 as u64, false), ); - let base_in_bounds_2 = builder.build_int_compare( - IntPredicate::ULT, - effective_offset, - end_offset, - "base_in_bounds_2", - ); - let base_in_bounds = - builder.build_and(base_in_bounds_1, base_in_bounds_2, "base_in_bounds"); + if ptr_in_bounds.get_zero_extended_constant() == Some(1) { + Some(ptr_in_bounds) + } else { + None + } + } else { + None + } + .unwrap_or_else(|| { + let load_offset_end = + builder.build_int_add(effective_offset, value_size_v, &state.var_name()); - let base_in_bounds = builder + builder.build_int_compare( + IntPredicate::ULE, + load_offset_end, + mem_bound, + &state.var_name(), + ) + }); + if !ptr_in_bounds.is_constant_int() + || ptr_in_bounds.get_zero_extended_constant().unwrap() != 1 + { + // LLVM may have folded this into 'i1 true' in which case we know + // the pointer is in bounds. LLVM may also have folded it into a + // constant expression, not known to be either true or false yet. + // If it's false, unknown-but-constant, or not-a-constant, emit a + // runtime bounds check. LLVM may yet succeed at optimizing it away. + let ptr_in_bounds = builder .build_call( intrinsics.expect_i1, &[ - base_in_bounds.as_basic_value_enum(), + ptr_in_bounds.as_basic_value_enum(), intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), ], - "base_in_bounds_expect", + "ptr_in_bounds_expect", ) .try_as_basic_value() .left() @@ -478,10 +681,10 @@ fn resolve_memory_ptr( .into_int_value(); let in_bounds_continue_block = - context.append_basic_block(function, "in_bounds_continue_block"); - let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); + context.append_basic_block(*function, "in_bounds_continue_block"); + let not_in_bounds_block = context.append_basic_block(*function, "not_in_bounds_block"); builder.build_conditional_branch( - base_in_bounds, + ptr_in_bounds, &in_bounds_continue_block, ¬_in_bounds_block, ); @@ -493,30 +696,25 @@ fn resolve_memory_ptr( ); builder.build_unreachable(); builder.position_at_end(&in_bounds_continue_block); - - base_as_int } - MemoryCache::Static { - base_ptr, - bounds: _, - } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), - }; + } - let effective_address_int = - builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); - Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) + let ptr = unsafe { builder.build_gep(mem_base, &[effective_offset], &state.var_name()) }; + Ok(builder + .build_bitcast(ptr, ptr_ty, &state.var_name()) + .into_pointer_value()) } -fn emit_stack_map( +fn emit_stack_map<'ctx>( _module_info: &ModuleInfo, - intrinsics: &Intrinsics, - builder: &Builder, + intrinsics: &Intrinsics<'ctx>, + builder: &Builder<'ctx>, local_function_id: usize, target: &mut StackmapRegistry, kind: StackmapEntryKind, locals: &[PointerValue], - state: &State, - _ctx: &mut CtxType, + state: &State<'ctx>, + _ctx: &mut CtxType<'_, 'ctx>, opcode_offset: usize, ) { let stackmap_id = target.entries.len(); @@ -538,7 +736,7 @@ fn emit_stack_map( params.extend_from_slice(&locals); value_semantics.extend((0..locals.len()).map(ValueSemantic::WasmLocal)); - params.extend_from_slice(&state.stack); + params.extend(state.stack.iter().map(|x| x.0)); value_semantics.extend((0..state.stack.len()).map(ValueSemantic::WasmStack)); // FIXME: Information needed for Abstract -> Runtime state transform is not fully preserved @@ -560,9 +758,9 @@ fn emit_stack_map( }); } -fn finalize_opcode_stack_map( - intrinsics: &Intrinsics, - builder: &Builder, +fn finalize_opcode_stack_map<'ctx>( + intrinsics: &Intrinsics<'ctx>, + builder: &Builder<'ctx>, local_function_id: usize, target: &mut StackmapRegistry, kind: StackmapEntryKind, @@ -591,13 +789,13 @@ fn finalize_opcode_stack_map( }); } -fn trap_if_misaligned( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, +fn trap_if_misaligned<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + context: &'ctx Context, + function: &FunctionValue<'ctx>, memarg: &MemoryImmediate, - ptr: PointerValue, + ptr: PointerValue<'ctx>, ) { let align = match memarg.flags & 3 { 0 => { @@ -629,8 +827,8 @@ fn trap_if_misaligned( .unwrap() .into_int_value(); - let continue_block = context.append_basic_block(function, "aligned_access_continue_block"); - let not_aligned_block = context.append_basic_block(function, "misaligned_trap_block"); + let continue_block = context.append_basic_block(*function, "aligned_access_continue_block"); + let not_aligned_block = context.append_basic_block(*function, "misaligned_trap_block"); builder.build_conditional_branch(aligned, &continue_block, ¬_aligned_block); builder.position_at_end(¬_aligned_block); @@ -664,38 +862,45 @@ pub unsafe extern "C" fn callback_trampoline( } } -pub struct LLVMModuleCodeGenerator { - context: Option, - builder: Option, - intrinsics: Option, - functions: Vec, - signatures: Map, +pub struct LLVMModuleCodeGenerator<'ctx> { + context: Option<&'ctx Context>, + builder: Option>, + intrinsics: Option>, + functions: Vec>, + signatures: Map>, signatures_raw: Map, function_signatures: Option>>, + llvm_functions: Rc>>>, func_import_count: usize, - personality_func: FunctionValue, - module: Module, + personality_func: ManuallyDrop>, + module: ManuallyDrop>>>, stackmaps: Rc>, + track_state: bool, + target_machine: TargetMachine, } -pub struct LLVMFunctionCodeGenerator { - context: Option, - builder: Option, - intrinsics: Option, - state: State, - function: FunctionValue, +pub struct LLVMFunctionCodeGenerator<'ctx> { + context: Option<&'ctx Context>, + builder: Option>, + alloca_builder: Option>, + intrinsics: Option>, + state: State<'ctx>, + llvm_functions: Rc>>>, + function: FunctionValue<'ctx>, func_sig: FuncSig, - signatures: Map, - locals: Vec, // Contains params and locals + signatures: Map>, + locals: Vec>, // Contains params and locals num_params: usize, - ctx: Option>, + ctx: Option>, unreachable_depth: usize, stackmaps: Rc>, index: usize, opcode_offset: usize, + track_state: bool, + module: Rc>>, } -impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { +impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ctx> { fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { Ok(()) } @@ -704,12 +909,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { Ok(()) } - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + fn feed_local(&mut self, ty: WpType, count: usize) -> Result<(), CodegenError> { let param_len = self.num_params; - let mut local_idx = 0; - // let (count, ty) = local?; - let count = n; let wasmer_ty = type_to_type(ty)?; let intrinsics = self.intrinsics.as_ref().unwrap(); @@ -724,14 +926,22 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { }; let builder = self.builder.as_ref().unwrap(); + let alloca_builder = self.alloca_builder.as_ref().unwrap(); - for _ in 0..count { - let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); - + for local_idx in 0..count { + let alloca = + alloca_builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); builder.build_store(alloca, default_value); - + if local_idx == 0 { + alloca_builder.position_before( + &alloca + .as_instruction() + .unwrap() + .get_next_instruction() + .unwrap(), + ); + } self.locals.push(alloca); - local_idx += 1; } Ok(()) } @@ -741,7 +951,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .context .as_ref() .unwrap() - .append_basic_block(&self.function, "start_of_code"); + .append_basic_block(self.function, "start_of_code"); let entry_end_inst = self .builder .as_ref() @@ -756,10 +966,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { cache_builder.position_before(&entry_end_inst); let module_info = unsafe { ::std::mem::transmute::<&ModuleInfo, &'static ModuleInfo>(module_info) }; - let function = unsafe { - ::std::mem::transmute::<&FunctionValue, &'static FunctionValue>(&self.function) - }; - let ctx = CtxType::new(module_info, function, cache_builder); + let ctx = CtxType::new(module_info, &self.function, cache_builder); self.ctx = Some(ctx); @@ -768,27 +975,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let builder = self.builder.as_ref().unwrap(); let intrinsics = self.intrinsics.as_ref().unwrap(); - let mut stackmaps = self.stackmaps.borrow_mut(); - emit_stack_map( - &module_info, - &intrinsics, - &builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::FunctionHeader, - &self.locals, - &state, - self.ctx.as_mut().unwrap(), - ::std::usize::MAX, - ); - finalize_opcode_stack_map( - &intrinsics, - &builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::FunctionHeader, - ::std::usize::MAX, - ); + if self.track_state { + let mut stackmaps = self.stackmaps.borrow_mut(); + emit_stack_map( + &module_info, + &intrinsics, + &builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::FunctionHeader, + &self.locals, + &state, + self.ctx.as_mut().unwrap(), + ::std::usize::MAX, + ); + finalize_opcode_stack_map( + &intrinsics, + &builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::FunctionHeader, + ::std::usize::MAX, + ); + } } Ok(()) @@ -830,17 +1039,33 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { InternalEvent::GetInternal(idx) => { if state.reachable { let idx = idx as usize; - let field_ptr = ctx.internal_field(idx, intrinsics, builder); + let field_ptr = + ctx.internal_field(idx, intrinsics, self.module.clone(), builder); let result = builder.build_load(field_ptr, "get_internal"); + tbaa_label( + &self.module, + intrinsics, + "internal", + result.as_instruction_value().unwrap(), + Some(idx as u32), + ); state.push1(result); } } InternalEvent::SetInternal(idx) => { if state.reachable { let idx = idx as usize; - let field_ptr = ctx.internal_field(idx, intrinsics, builder); + let field_ptr = + ctx.internal_field(idx, intrinsics, self.module.clone(), builder); let v = state.pop1()?; - builder.build_store(field_ptr, v); + let store = builder.build_store(field_ptr, v); + tbaa_label( + &self.module, + intrinsics, + "internal", + store, + Some(idx as u32), + ); } } } @@ -883,7 +1108,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { offset: -1isize as usize, })?; - let end_block = context.append_basic_block(&function, "end"); + let end_block = context.append_basic_block(function, "end"); builder.position_at_end(&end_block); let phis = if let Ok(wasmer_ty) = blocktype_to_type(ty) { @@ -900,8 +1125,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.position_at_end(¤t_block); } Operator::Loop { ty } => { - let loop_body = context.append_basic_block(&function, "loop_body"); - let loop_next = context.append_basic_block(&function, "loop_outer"); + let loop_body = context.append_basic_block(function, "loop_body"); + let loop_next = context.append_basic_block(function, "loop_outer"); builder.build_unconditional_branch(&loop_body); @@ -918,33 +1143,35 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.position_at_end(&loop_body); - if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - emit_stack_map( - &info, - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Loop, - &self.locals, - state, - ctx, - offset, - ); - let signal_mem = ctx.signal_mem(); - let iv = builder - .build_store(signal_mem, context.i8_type().const_int(0 as u64, false)); - // Any 'store' can be made volatile. - iv.set_volatile(true).unwrap(); - finalize_opcode_stack_map( - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Loop, - offset, - ); + if self.track_state { + if let Some(offset) = opcode_offset { + let mut stackmaps = self.stackmaps.borrow_mut(); + emit_stack_map( + &info, + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Loop, + &self.locals, + state, + ctx, + offset, + ); + let signal_mem = ctx.signal_mem(); + let iv = builder + .build_store(signal_mem, context.i8_type().const_int(0 as u64, false)); + // Any 'store' can be made volatile. + iv.set_volatile(true).unwrap(); + finalize_opcode_stack_map( + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Loop, + offset, + ); + } } state.push_loop(loop_body, loop_next, phis); @@ -963,13 +1190,16 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { frame.phis().len() }; - let values = state.peekn(value_len)?; + let values = state.peekn_extra(value_len)?; + let values = values.iter().map(|(v, info)| { + apply_pending_canonicalization(builder, intrinsics, *v, *info) + }); // For each result of the block we're branching to, // pop a value off the value stack and load it into // the corresponding phi. - for (phi, value) in frame.phis().iter().zip(values.iter()) { - phi.add_incoming(&[(value, ¤t_block)]); + for (phi, value) in frame.phis().iter().zip(values) { + phi.add_incoming(&[(&value, ¤t_block)]); } builder.build_unconditional_branch(frame.br_dest()); @@ -992,13 +1222,16 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { frame.phis().len() }; - let param_stack = state.peekn(value_len)?; + let param_stack = state.peekn_extra(value_len)?; + let param_stack = param_stack.iter().map(|(v, info)| { + apply_pending_canonicalization(builder, intrinsics, *v, *info) + }); - for (phi, value) in frame.phis().iter().zip(param_stack.iter()) { - phi.add_incoming(&[(value, ¤t_block)]); + for (phi, value) in frame.phis().iter().zip(param_stack) { + phi.add_incoming(&[(&value, ¤t_block)]); } - let else_block = context.append_basic_block(&function, "else"); + let else_block = context.append_basic_block(function, "else"); let cond_value = builder.build_int_compare( IntPredicate::NE, @@ -1022,7 +1255,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let default_frame = state.frame_at_depth(default_depth)?; let args = if default_frame.is_loop() { - &[] + Vec::new() } else { let res_len = default_frame.phis().len(); state.peekn(res_len)? @@ -1064,9 +1297,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { message: "not currently in a block", offset: -1isize as usize, })?; - let if_then_block = context.append_basic_block(&function, "if_then"); - let if_else_block = context.append_basic_block(&function, "if_else"); - let end_block = context.append_basic_block(&function, "if_end"); + let if_then_block = context.append_basic_block(function, "if_then"); + let if_else_block = context.append_basic_block(function, "if_else"); + let end_block = context.append_basic_block(function, "if_end"); let end_phis = { builder.position_at_end(&end_block); @@ -1101,16 +1334,19 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { Operator::Else => { if state.reachable { let frame = state.frame_at_depth(0)?; - builder.build_unconditional_branch(frame.code_after()); let current_block = builder.get_insert_block().ok_or(BinaryReaderError { message: "not currently in a block", offset: -1isize as usize, })?; for phi in frame.phis().to_vec().iter().rev() { - let value = state.pop1()?; + let (value, info) = state.pop1_extra()?; + let value = + apply_pending_canonicalization(builder, intrinsics, value, info); phi.add_incoming(&[(&value, ¤t_block)]) } + let frame = state.frame_at_depth(0)?; + builder.build_unconditional_branch(frame.code_after()); } let (if_else_block, if_else_state) = if let ControlFrame::IfElse { @@ -1138,12 +1374,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { })?; if state.reachable { - builder.build_unconditional_branch(frame.code_after()); - for phi in frame.phis().iter().rev() { - let value = state.pop1()?; + let (value, info) = state.pop1_extra()?; + let value = + apply_pending_canonicalization(builder, intrinsics, value, info); phi.add_incoming(&[(&value, ¤t_block)]); } + + builder.build_unconditional_branch(frame.code_after()); } if let ControlFrame::IfElse { @@ -1189,54 +1427,56 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } } Operator::Return => { - let frame = state.outermost_frame()?; let current_block = builder.get_insert_block().ok_or(BinaryReaderError { message: "not currently in a block", offset: -1isize as usize, })?; - builder.build_unconditional_branch(frame.br_dest()); - - let phis = frame.phis().to_vec(); - - for phi in phis.iter() { - let arg = state.pop1()?; + let frame = state.outermost_frame()?; + for phi in frame.phis().to_vec().iter() { + let (arg, info) = state.pop1_extra()?; + let arg = apply_pending_canonicalization(builder, intrinsics, arg, info); phi.add_incoming(&[(&arg, ¤t_block)]); } + let frame = state.outermost_frame()?; + builder.build_unconditional_branch(frame.br_dest()); + state.reachable = false; } Operator::Unreachable => { // Emit an unreachable instruction. - // If llvm cannot prove that this is never touched, + // If llvm cannot prove that this is never reached, // it will emit a `ud2` instruction on x86_64 arches. // Comment out this `if` block to allow spectests to pass. // TODO: fix this if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - emit_stack_map( - &info, - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Trappable, - &self.locals, - state, - ctx, - offset, - ); - builder.build_call(intrinsics.trap, &[], "trap"); - finalize_opcode_stack_map( - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Trappable, - offset, - ); + if self.track_state { + let mut stackmaps = self.stackmaps.borrow_mut(); + emit_stack_map( + &info, + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Trappable, + &self.locals, + state, + ctx, + offset, + ); + builder.build_call(intrinsics.trap, &[], "trap"); + finalize_opcode_stack_map( + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Trappable, + offset, + ); + } } builder.build_call( @@ -1263,21 +1503,41 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { // Generate const values. Operator::I32Const { value } => { let i = intrinsics.i32_ty.const_int(value as u64, false); - state.push1(i); + let info = if is_f32_arithmetic(value as u32) { + ExtraInfo::arithmetic_f32() + } else { + Default::default() + }; + state.push1_extra(i, info); } Operator::I64Const { value } => { let i = intrinsics.i64_ty.const_int(value as u64, false); - state.push1(i); + let info = if is_f64_arithmetic(value as u64) { + ExtraInfo::arithmetic_f64() + } else { + Default::default() + }; + state.push1_extra(i, info); } Operator::F32Const { value } => { let bits = intrinsics.i32_ty.const_int(value.bits() as u64, false); + let info = if is_f32_arithmetic(value.bits()) { + ExtraInfo::arithmetic_f32() + } else { + Default::default() + }; let f = builder.build_bitcast(bits, intrinsics.f32_ty, "f"); - state.push1(f); + state.push1_extra(f, info); } Operator::F64Const { value } => { let bits = intrinsics.i64_ty.const_int(value.bits(), false); + let info = if is_f64_arithmetic(value.bits()) { + ExtraInfo::arithmetic_f64() + } else { + Default::default() + }; let f = builder.build_bitcast(bits, intrinsics.f64_ty, "f"); - state.push1(f); + state.push1_extra(f, info); } Operator::V128Const { value } => { let mut hi: [u8; 8] = Default::default(); @@ -1286,11 +1546,31 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { lo.copy_from_slice(&value.bytes()[8..16]); let packed = [u64::from_le_bytes(hi), u64::from_le_bytes(lo)]; let i = intrinsics.i128_ty.const_int_arbitrary_precision(&packed); - state.push1(i); + let mut quad1: [u8; 4] = Default::default(); + let mut quad2: [u8; 4] = Default::default(); + let mut quad3: [u8; 4] = Default::default(); + let mut quad4: [u8; 4] = Default::default(); + quad1.copy_from_slice(&value.bytes()[0..4]); + quad2.copy_from_slice(&value.bytes()[4..8]); + quad3.copy_from_slice(&value.bytes()[8..12]); + quad4.copy_from_slice(&value.bytes()[12..16]); + let mut info: ExtraInfo = Default::default(); + if is_f32_arithmetic(u32::from_le_bytes(quad1)) + && is_f32_arithmetic(u32::from_le_bytes(quad2)) + && is_f32_arithmetic(u32::from_le_bytes(quad3)) + && is_f32_arithmetic(u32::from_le_bytes(quad4)) + { + info |= ExtraInfo::arithmetic_f32(); + } + if is_f64_arithmetic(packed[0]) && is_f64_arithmetic(packed[1]) { + info |= ExtraInfo::arithmetic_f64(); + } + state.push1_extra(i, info); } Operator::I8x16Splat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = v.into_int_value(); let v = builder.build_int_truncate(v, intrinsics.i8_ty, ""); let res = splat_vector( builder, @@ -1300,10 +1580,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i); } Operator::I16x8Splat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = v.into_int_value(); let v = builder.build_int_truncate(v, intrinsics.i16_ty, ""); let res = splat_vector( builder, @@ -1313,10 +1594,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i); } Operator::I32x4Splat => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = splat_vector( builder, intrinsics, @@ -1325,10 +1606,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i); } Operator::I64x2Splat => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = splat_vector( builder, intrinsics, @@ -1337,10 +1618,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i); } Operator::F32x4Splat => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = splat_vector( builder, intrinsics, @@ -1349,10 +1630,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The spec is unclear, we interpret splat as preserving NaN + // payload bits. + state.push1_extra(res, i); } Operator::F64x2Splat => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = splat_vector( builder, intrinsics, @@ -1361,46 +1644,74 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The spec is unclear, we interpret splat as preserving NaN + // payload bits. + state.push1_extra(res, i); } // Operate on locals. Operator::GetLocal { local_index } => { let pointer_value = locals[local_index as usize]; let v = builder.build_load(pointer_value, &state.var_name()); + tbaa_label( + &self.module, + intrinsics, + "local", + v.as_instruction_value().unwrap(), + Some(local_index), + ); state.push1(v); } Operator::SetLocal { local_index } => { let pointer_value = locals[local_index as usize]; - let v = state.pop1()?; - builder.build_store(pointer_value, v); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let store = builder.build_store(pointer_value, v); + tbaa_label(&self.module, intrinsics, "local", store, Some(local_index)); } Operator::TeeLocal { local_index } => { let pointer_value = locals[local_index as usize]; - let v = state.peek1()?; - builder.build_store(pointer_value, v); + let (v, i) = state.peek1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let store = builder.build_store(pointer_value, v); + tbaa_label(&self.module, intrinsics, "local", store, Some(local_index)); } Operator::GetGlobal { global_index } => { let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index, intrinsics); + let global_cache = ctx.global_cache(index, intrinsics, self.module.clone()); match global_cache { GlobalCache::Const { value } => { state.push1(value); } GlobalCache::Mut { ptr_to_value } => { let value = builder.build_load(ptr_to_value, "global_value"); + tbaa_label( + &self.module, + intrinsics, + "global", + value.as_instruction_value().unwrap(), + Some(global_index), + ); state.push1(value); } } } Operator::SetGlobal { global_index } => { - let value = state.pop1()?; + let (value, info) = state.pop1_extra()?; + let value = apply_pending_canonicalization(builder, intrinsics, value, info); let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index, intrinsics); + let global_cache = ctx.global_cache(index, intrinsics, self.module.clone()); match global_cache { GlobalCache::Mut { ptr_to_value } => { - builder.build_store(ptr_to_value, value); + let store = builder.build_store(ptr_to_value, value); + tbaa_label( + &self.module, + intrinsics, + "global", + store, + Some(global_index), + ); } GlobalCache::Const { value: _ } => { return Err(CodegenError { @@ -1411,7 +1722,26 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::Select => { - let (v1, v2, cond) = state.pop3()?; + let ((v1, i1), (v2, i2), (cond, _)) = state.pop3_extra()?; + // We don't bother canonicalizing 'cond' here because we only + // compare it to zero, and that's invariant under + // canonicalization. + + // If the pending bits of v1 and v2 are the same, we can pass + // them along to the result. Otherwise, apply pending + // canonicalizations now. + let (v1, i1, v2, i2) = if i1.has_pending_f32_nan() != i2.has_pending_f32_nan() + || i1.has_pending_f64_nan() != i2.has_pending_f64_nan() + { + ( + apply_pending_canonicalization(builder, intrinsics, v1, i1), + i1.strip_pending(), + apply_pending_canonicalization(builder, intrinsics, v2, i2), + i2.strip_pending(), + ) + } else { + (v1, i1, v2, i2) + }; let cond_value = builder.build_int_compare( IntPredicate::NE, cond.into_int_value(), @@ -1419,7 +1749,19 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_select(cond_value, v1, v2, &state.var_name()); - state.push1(res); + let info = { + let mut info = i1.strip_pending() & i2.strip_pending(); + if i1.has_pending_f32_nan() { + debug_assert!(i2.has_pending_f32_nan()); + info |= ExtraInfo::pending_f32_nan(); + } + if i1.has_pending_f64_nan() { + debug_assert!(i2.has_pending_f64_nan()); + info |= ExtraInfo::pending_f64_nan(); + } + info + }; + state.push1_extra(res, info); } Operator::Call { function_index } => { let func_index = FuncIndex::new(function_index as usize); @@ -1428,62 +1770,74 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let func_sig = &info.signatures[sigindex]; let (params, func_ptr) = match func_index.local_or_import(info) { - LocalOrImport::Local(local_func_index) => { + LocalOrImport::Local(_) => { let params: Vec<_> = std::iter::once(ctx.basic()) .chain( state - .peekn(func_sig.params().len())? + .peekn_extra(func_sig.params().len())? .iter() .enumerate() - .map(|(i, &v)| match func_sig.params()[i] { + .map(|(i, (v, info))| match func_sig.params()[i] { Type::F32 => builder.build_bitcast( - v, + apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), intrinsics.i32_ty, &state.var_name(), ), Type::F64 => builder.build_bitcast( - v, + apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), intrinsics.i64_ty, &state.var_name(), ), - _ => v, + Type::V128 => apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), + _ => *v, }), ) .collect(); - let func_ptr = - ctx.local_func(local_func_index, llvm_sig, intrinsics, builder); + let func_ptr = self.llvm_functions.borrow_mut()[&func_index]; - (params, func_ptr) + (params, func_ptr.as_global_value().as_pointer_value()) } LocalOrImport::Import(import_func_index) => { let (func_ptr_untyped, ctx_ptr) = - ctx.imported_func(import_func_index, intrinsics); + ctx.imported_func(import_func_index, intrinsics, self.module.clone()); let params: Vec<_> = std::iter::once(ctx_ptr.as_basic_value_enum()) .chain( state - .peekn(func_sig.params().len())? + .peekn_extra(func_sig.params().len())? .iter() .enumerate() - .map(|(i, &v)| match func_sig.params()[i] { + .map(|(i, (v, info))| match func_sig.params()[i] { Type::F32 => builder.build_bitcast( - v, + apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), intrinsics.i32_ty, &state.var_name(), ), Type::F64 => builder.build_bitcast( - v, + apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), intrinsics.i64_ty, &state.var_name(), ), - _ => v, + Type::V128 => apply_pending_canonicalization( + builder, intrinsics, *v, *info, + ), + _ => *v, }), ) .collect(); let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic); - let func_ptr = builder.build_pointer_cast( func_ptr_untyped, func_ptr_ty, @@ -1495,32 +1849,36 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { }; state.popn(func_sig.params().len())?; - if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - emit_stack_map( - &info, - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Call, - &self.locals, - state, - ctx, - offset, - ) + if self.track_state { + if let Some(offset) = opcode_offset { + let mut stackmaps = self.stackmaps.borrow_mut(); + emit_stack_map( + &info, + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Call, + &self.locals, + state, + ctx, + offset, + ) + } } let call_site = builder.build_call(func_ptr, ¶ms, &state.var_name()); - if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - finalize_opcode_stack_map( - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Call, - offset, - ) + if self.track_state { + if let Some(offset) = opcode_offset { + let mut stackmaps = self.stackmaps.borrow_mut(); + finalize_opcode_stack_map( + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Call, + offset, + ) + } } if let Some(basic_value) = call_site.try_as_basic_value().left() { @@ -1550,8 +1908,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { Operator::CallIndirect { index, table_index } => { let sig_index = SigIndex::new(index as usize); let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index, intrinsics); - let (table_base, table_bound) = - ctx.table(TableIndex::new(table_index as usize), intrinsics, builder); + let (table_base, table_bound) = ctx.table( + TableIndex::new(table_index as usize), + intrinsics, + self.module.clone(), + builder, + ); let func_index = state.pop1()?.into_int_value(); // We assume the table has the `anyfunc` element type. @@ -1620,9 +1982,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .into_int_value(); let in_bounds_continue_block = - context.append_basic_block(&function, "in_bounds_continue_block"); + context.append_basic_block(function, "in_bounds_continue_block"); let not_in_bounds_block = - context.append_basic_block(&function, "not_in_bounds_block"); + context.append_basic_block(function, "not_in_bounds_block"); builder.build_conditional_branch( index_in_bounds, &in_bounds_continue_block, @@ -1661,9 +2023,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .unwrap() .into_int_value(); - let continue_block = context.append_basic_block(&function, "continue_block"); + let continue_block = context.append_basic_block(function, "continue_block"); let sigindices_notequal_block = - context.append_basic_block(&function, "sigindices_notequal_block"); + context.append_basic_block(function, "sigindices_notequal_block"); builder.build_conditional_branch( sigindices_equal, &continue_block, @@ -1682,16 +2044,23 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let wasmer_fn_sig = &info.signatures[sig_index]; let fn_ty = signatures[sig_index]; - let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?; + let pushed_args = state.popn_save_extra(wasmer_fn_sig.params().len())?; let args: Vec<_> = std::iter::once(ctx_ptr) - .chain(pushed_args.into_iter().enumerate().map(|(i, v)| { + .chain(pushed_args.into_iter().enumerate().map(|(i, (v, info))| { match wasmer_fn_sig.params()[i] { - Type::F32 => { - builder.build_bitcast(v, intrinsics.i32_ty, &state.var_name()) - } - Type::F64 => { - builder.build_bitcast(v, intrinsics.i64_ty, &state.var_name()) + Type::F32 => builder.build_bitcast( + apply_pending_canonicalization(builder, intrinsics, v, info), + intrinsics.i32_ty, + &state.var_name(), + ), + Type::F64 => builder.build_bitcast( + apply_pending_canonicalization(builder, intrinsics, v, info), + intrinsics.i64_ty, + &state.var_name(), + ), + Type::V128 => { + apply_pending_canonicalization(builder, intrinsics, v, info) } _ => v, } @@ -1704,32 +2073,36 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { "typed_func_ptr", ); - if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - emit_stack_map( - &info, - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Call, - &self.locals, - state, - ctx, - offset, - ) + if self.track_state { + if let Some(offset) = opcode_offset { + let mut stackmaps = self.stackmaps.borrow_mut(); + emit_stack_map( + &info, + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Call, + &self.locals, + state, + ctx, + offset, + ) + } } let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call"); - if let Some(offset) = opcode_offset { - let mut stackmaps = self.stackmaps.borrow_mut(); - finalize_opcode_stack_map( - intrinsics, - builder, - self.index, - &mut *stackmaps, - StackmapEntryKind::Call, - offset, - ) + if self.track_state { + if let Some(offset) = opcode_offset { + let mut stackmaps = self.stackmaps.borrow_mut(); + finalize_opcode_stack_map( + intrinsics, + builder, + self.index, + &mut *stackmaps, + StackmapEntryKind::Call, + offset, + ) + } } match wasmer_fn_sig.returns() { @@ -1760,67 +2133,55 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions ***************************/ Operator::I32Add | Operator::I64Add => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_int_add(v1, v2, &state.var_name()); state.push1(res); } Operator::I8x16Add => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Add => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Add => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I64x2Add => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i64x2(builder, intrinsics, v2, i2); let res = builder.build_int_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I8x16AddSaturateS => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i8x16_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i8x16_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.sadd_sat_i8x16, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.sadd_sat_i8x16, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1828,12 +2189,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8AddSaturateS => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i16x8_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i16x8_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.sadd_sat_i16x8, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.sadd_sat_i16x8, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1841,12 +2205,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I8x16AddSaturateU => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i8x16_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i8x16_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.uadd_sat_i8x16, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.uadd_sat_i8x16, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1854,12 +2221,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8AddSaturateU => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i16x8_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i16x8_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.uadd_sat_i16x8, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.uadd_sat_i16x8, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1867,70 +2237,55 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Sub | Operator::I64Sub => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_int_sub(v1, v2, &state.var_name()); state.push1(res); } Operator::I8x16Sub => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Sub => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Sub => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I64x2Sub => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i64x2(builder, intrinsics, v2, i2); let res = builder.build_int_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I8x16SubSaturateS => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i8x16_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i8x16_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.ssub_sat_i8x16, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.ssub_sat_i8x16, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1938,12 +2293,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8SubSaturateS => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i16x8_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i16x8_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.ssub_sat_i16x8, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.ssub_sat_i16x8, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1951,12 +2309,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I8x16SubSaturateU => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i8x16_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i8x16_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.usub_sat_i8x16, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.usub_sat_i8x16, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1964,12 +2325,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8SubSaturateU => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder.build_bitcast(v1, intrinsics.i16x8_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.i16x8_ty, ""); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder - .build_call(intrinsics.usub_sat_i16x8, &[v1, v2], &state.var_name()) + .build_call( + intrinsics.usub_sat_i16x8, + &[v1.as_basic_value_enum(), v2.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -1977,52 +2341,41 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Mul | Operator::I64Mul => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_int_mul(v1, v2, &state.var_name()); state.push1(res); } Operator::I8x16Mul => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_mul(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Mul => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_mul(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Mul => { - let (v1, v2) = state.pop2()?; - let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_mul(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32DivS | Operator::I64DivS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); trap_if_zero_or_overflow(builder, intrinsics, context, &function, v1, v2); @@ -2031,7 +2384,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32DivU | Operator::I64DivU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); trap_if_zero(builder, intrinsics, context, &function, v2); @@ -2040,7 +2395,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32RemS | Operator::I64RemS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let int_type = v1.get_type(); let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { @@ -2085,7 +2442,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32RemU | Operator::I64RemU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); trap_if_zero(builder, intrinsics, context, &function, v2); @@ -2094,25 +2453,34 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32And | Operator::I64And | Operator::V128And => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_and(v1, v2, &state.var_name()); state.push1(res); } Operator::I32Or | Operator::I64Or | Operator::V128Or => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_or(v1, v2, &state.var_name()); state.push1(res); } Operator::I32Xor | Operator::I64Xor | Operator::V128Xor => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_xor(v1, v2, &state.var_name()); state.push1(res); } Operator::V128Bitselect => { - let (v1, v2, cond) = state.pop3()?; + let ((v1, i1), (v2, i2), (cond, cond_info)) = state.pop3_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); + let cond = apply_pending_canonicalization(builder, intrinsics, cond, cond_info); let v1 = builder .build_bitcast(v1, intrinsics.i1x128_ty, "") .into_vector_value(); @@ -2127,17 +2495,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Shl | Operator::I64Shl => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); // TODO: missing 'and' of v2? let res = builder.build_left_shift(v1, v2, &state.var_name()); state.push1(res); } Operator::I8x16Shl => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(7, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i8_ty, ""); @@ -2153,10 +2522,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8Shl => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(15, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i16_ty, ""); @@ -2172,10 +2540,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4Shl => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(31, false), ""); let v2 = splat_vector( @@ -2190,10 +2557,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2Shl => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i64x2(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(63, false), ""); let v2 = builder.build_int_z_extend(v2, intrinsics.i64_ty, ""); @@ -2209,17 +2575,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32ShrS | Operator::I64ShrS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); // TODO: check wasm spec, is this missing v2 mod LaneBits? let res = builder.build_right_shift(v1, v2, true, &state.var_name()); state.push1(res); } Operator::I8x16ShrS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(7, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i8_ty, ""); @@ -2235,10 +2602,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ShrS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(15, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i16_ty, ""); @@ -2254,10 +2620,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ShrS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(31, false), ""); let v2 = splat_vector( @@ -2272,10 +2637,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2ShrS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i64x2(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(63, false), ""); let v2 = builder.build_int_z_extend(v2, intrinsics.i64_ty, ""); @@ -2291,16 +2655,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32ShrU | Operator::I64ShrU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let res = builder.build_right_shift(v1, v2, false, &state.var_name()); state.push1(res); } Operator::I8x16ShrU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(7, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i8_ty, ""); @@ -2316,10 +2681,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ShrU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(15, false), ""); let v2 = builder.build_int_truncate(v2, intrinsics.i16_ty, ""); @@ -2335,10 +2699,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ShrU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(31, false), ""); let v2 = splat_vector( @@ -2353,10 +2716,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2ShrU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i64x2(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); let v2 = builder.build_and(v2, intrinsics.i32_ty.const_int(63, false), ""); let v2 = builder.build_int_z_extend(v2, intrinsics.i64_ty, ""); @@ -2372,7 +2734,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Rotl => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let lhs = builder.build_left_shift(v1, v2, &state.var_name()); let rhs = { @@ -2384,7 +2748,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64Rotl => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let lhs = builder.build_left_shift(v1, v2, &state.var_name()); let rhs = { @@ -2396,7 +2762,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Rotr => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); let rhs = { @@ -2408,7 +2776,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64Rotr => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); let rhs = { @@ -2420,7 +2790,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32Clz => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let is_zero_undef = intrinsics.i1_zero.as_basic_value_enum(); let res = builder .build_call( @@ -2431,10 +2802,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I64Clz => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let is_zero_undef = intrinsics.i1_zero.as_basic_value_enum(); let res = builder .build_call( @@ -2445,10 +2817,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::I32Ctz => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let is_zero_undef = intrinsics.i1_zero.as_basic_value_enum(); let res = builder .build_call( @@ -2459,10 +2832,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I64Ctz => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let is_zero_undef = intrinsics.i1_zero.as_basic_value_enum(); let res = builder .build_call( @@ -2473,25 +2847,27 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::I32Popcnt => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let res = builder .build_call(intrinsics.ctpop_i32, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I64Popcnt => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; + let input = apply_pending_canonicalization(builder, intrinsics, input, info); let res = builder .build_call(intrinsics.ctpop_i64, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::I32Eqz => { let input = state.pop1()?.into_int_value(); @@ -2502,7 +2878,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I64Eqz => { let input = state.pop1()?.into_int_value(); @@ -2513,132 +2889,160 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } /*************************** * Floating-Point Arithmetic instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions ***************************/ - Operator::F32Add | Operator::F64Add => { - let (v1, v2) = state.pop2()?; - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + Operator::F32Add => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let res = builder.build_float_add(v1, v2, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); + } + Operator::F64Add => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_add(v1, v2, &state.var_name()); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Add => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Add => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } - Operator::F32Sub | Operator::F64Sub => { - let (v1, v2) = state.pop2()?; - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + Operator::F32Sub => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let res = builder.build_float_sub(v1, v2, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); + } + Operator::F64Sub => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_sub(v1, v2, &state.var_name()); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Sub => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Sub => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } - Operator::F32Mul | Operator::F64Mul => { - let (v1, v2) = state.pop2()?; - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + Operator::F32Mul => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let res = builder.build_float_mul(v1, v2, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); + } + Operator::F64Mul => { + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_mul(v1, v2, &state.var_name()); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Mul => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Mul => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } - Operator::F32Div | Operator::F64Div => { + Operator::F32Div => { let (v1, v2) = state.pop2()?; - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let res = builder.build_float_div(v1, v2, &state.var_name()); - state.push1(res); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); + } + Operator::F64Div => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_div(v1, v2, &state.var_name()); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32x4Div => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64x2Div => { - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); - let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32Sqrt => { let input = state.pop1()?; @@ -2647,7 +3051,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64Sqrt => { let input = state.pop1()?; @@ -2656,24 +3060,32 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32x4Sqrt => { - let input = state.pop1()?.into_int_value(); - let float = builder.build_bitcast(input, intrinsics.f32x4_ty, "float"); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_f32x4(builder, intrinsics, v, i); let res = builder - .build_call(intrinsics.sqrt_f32x4, &[float], &state.var_name()) + .build_call( + intrinsics.sqrt_f32x4, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); let bits = builder.build_bitcast(res, intrinsics.i128_ty, "bits"); - state.push1(bits); + state.push1_extra(bits, ExtraInfo::pending_f32_nan()); } Operator::F64x2Sqrt => { - let input = state.pop1()?.into_int_value(); - let float = builder.build_bitcast(input, intrinsics.f64x2_ty, "float"); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_f64x2(builder, intrinsics, v, i); let res = builder - .build_call(intrinsics.sqrt_f64x2, &[float], &state.var_name()) + .build_call( + intrinsics.sqrt_f64x2, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); @@ -2682,11 +3094,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F32Min => { // This implements the same logic as LLVM's @llvm.minimum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. let (v1, v2) = state.pop2()?; + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. let v1 = canonicalize_nans(builder, intrinsics, v1); let v2 = canonicalize_nans(builder, intrinsics, v2); + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -2724,15 +3142,23 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .into_float_value(); let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64Min => { // This implements the same logic as LLVM's @llvm.minimum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. let (v1, v2) = state.pop2()?; + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. let v1 = canonicalize_nans(builder, intrinsics, v1); let v2 = canonicalize_nans(builder, intrinsics, v2); + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -2770,18 +3196,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .into_float_value(); let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::F32x4Min => { // This implements the same logic as LLVM's @llvm.minimum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. Note that this is a different + // canonicalization from that which may be performed in the + // v128_into_f32x4 function. That may canonicalize as F64x2 if + // previous computations may have emitted F64x2 NaNs. + let v1 = canonicalize_nans(builder, intrinsics, v1.as_basic_value_enum()); + let v2 = canonicalize_nans(builder, intrinsics, v2.as_basic_value_enum()); let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, v1, @@ -2825,18 +3262,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64x2Min => { // This implements the same logic as LLVM's @llvm.minimum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. Note that this is a different + // canonicalization from that which may be performed in the + // v128_into_f32x4 function. That may canonicalize as F64x2 if + // previous computations may have emitted F64x2 NaNs. + let v1 = canonicalize_nans(builder, intrinsics, v1.as_basic_value_enum()); + let v2 = canonicalize_nans(builder, intrinsics, v2.as_basic_value_enum()); let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); + let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, v1, @@ -2880,15 +3328,23 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::F32Max => { // This implements the same logic as LLVM's @llvm.maximum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. let (v1, v2) = state.pop2()?; + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. let v1 = canonicalize_nans(builder, intrinsics, v1); let v2 = canonicalize_nans(builder, intrinsics, v2); + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -2925,15 +3381,23 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .into_float_value(); let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64Max => { // This implements the same logic as LLVM's @llvm.maximum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. let (v1, v2) = state.pop2()?; + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. let v1 = canonicalize_nans(builder, intrinsics, v1); let v2 = canonicalize_nans(builder, intrinsics, v2); + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -2970,17 +3434,27 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .into_float_value(); let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::F32x4Max => { // This implements the same logic as LLVM's @llvm.maximum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f32x4_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f32x4_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. Note that this is a different + // canonicalization from that which may be performed in the + // v128_into_f32x4 function. That may canonicalize as F64x2 if + // previous computations may have emitted F64x2 NaNs. + let v1 = canonicalize_nans(builder, intrinsics, v1.as_basic_value_enum()); + let v2 = canonicalize_nans(builder, intrinsics, v2.as_basic_value_enum()); let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -3025,17 +3499,27 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64x2Max => { // This implements the same logic as LLVM's @llvm.maximum - // intrinsic would, but x86 lowering of that intrinsics + // intrinsic would, but x86 lowering of that intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. - let (v1, v2) = state.pop2()?; - let v1 = builder.build_bitcast(v1, intrinsics.f64x2_ty, ""); - let v2 = builder.build_bitcast(v2, intrinsics.f64x2_ty, ""); - let v1 = canonicalize_nans(builder, intrinsics, v1); - let v2 = canonicalize_nans(builder, intrinsics, v2); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); + + // To detect min(-0.0, 0.0), we check whether the integer + // representations are equal. There's one other case where that + // can happen: non-canonical NaNs. Here we unconditionally + // canonicalize the NaNs. Note that this is a different + // canonicalization from that which may be performed in the + // v128_into_f32x4 function. That may canonicalize as F64x2 if + // previous computations may have emitted F64x2 NaNs. + let v1 = canonicalize_nans(builder, intrinsics, v1.as_basic_value_enum()); + let v2 = canonicalize_nans(builder, intrinsics, v2.as_basic_value_enum()); let (v1, v2) = (v1.into_vector_value(), v2.into_vector_value()); let v1_is_nan = builder.build_float_compare( FloatPredicate::UNO, @@ -3080,160 +3564,218 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // Because inputs were canonicalized, we always produce + // canonical NaN outputs. No pending NaN cleanup. + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::F32Ceil => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; let res = builder .build_call(intrinsics.ceil_f32, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, info | ExtraInfo::pending_f32_nan()); } Operator::F64Ceil => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; let res = builder .build_call(intrinsics.ceil_f64, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, info | ExtraInfo::pending_f64_nan()); } Operator::F32Floor => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; let res = builder .build_call(intrinsics.floor_f32, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, info | ExtraInfo::pending_f32_nan()); } Operator::F64Floor => { - let input = state.pop1()?; + let (input, info) = state.pop1_extra()?; let res = builder .build_call(intrinsics.floor_f64, &[input], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, info | ExtraInfo::pending_f64_nan()); } Operator::F32Trunc => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = builder - .build_call(intrinsics.trunc_f32, &[input], &state.var_name()) + .build_call( + intrinsics.trunc_f32, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, i); } Operator::F64Trunc => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = builder - .build_call(intrinsics.trunc_f64, &[input], &state.var_name()) + .build_call( + intrinsics.trunc_f64, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, i); } Operator::F32Nearest => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = builder - .build_call(intrinsics.nearbyint_f32, &[input], &state.var_name()) + .build_call( + intrinsics.nearbyint_f32, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, i); } Operator::F64Nearest => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; let res = builder - .build_call(intrinsics.nearbyint_f64, &[input], &state.var_name()) + .build_call( + intrinsics.nearbyint_f64, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + state.push1_extra(res, i); } Operator::F32Abs => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let res = builder - .build_call(intrinsics.fabs_f32, &[input], &state.var_name()) + .build_call( + intrinsics.fabs_f32, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + // The exact NaN returned by F32Abs is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F64Abs => { - let input = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let res = builder - .build_call(intrinsics.fabs_f64, &[input], &state.var_name()) + .build_call( + intrinsics.fabs_f64, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + // The exact NaN returned by F64Abs is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F32x4Abs => { - let v = state.pop1()?.into_int_value(); - let v = builder.build_bitcast(v, intrinsics.f32x4_ty, ""); + let (v, i) = state.pop1_extra()?; + let v = builder.build_bitcast(v.into_int_value(), intrinsics.f32x4_ty, ""); + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let res = builder - .build_call(intrinsics.fabs_f32x4, &[v], &state.var_name()) + .build_call( + intrinsics.fabs_f32x4, + &[v.as_basic_value_enum()], + &state.var_name(), + ) .try_as_basic_value() .left() .unwrap(); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The exact NaN returned by F32x4Abs is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F64x2Abs => { - let v = state.pop1()?.into_int_value(); - let v = builder.build_bitcast(v, intrinsics.f64x2_ty, ""); + let (v, i) = state.pop1_extra()?; + let v = builder.build_bitcast(v.into_int_value(), intrinsics.f64x2_ty, ""); + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let res = builder .build_call(intrinsics.fabs_f64x2, &[v], &state.var_name()) .try_as_basic_value() .left() .unwrap(); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The exact NaN returned by F32x4Abs is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F32x4Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.f32x4_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let v = builder.build_bitcast(v.into_int_value(), intrinsics.f32x4_ty, ""); + let v = + apply_pending_canonicalization(builder, intrinsics, v, i).into_vector_value(); let res = builder.build_float_neg(v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The exact NaN returned by F32x4Neg is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F64x2Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.f64x2_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let v = builder.build_bitcast(v.into_int_value(), intrinsics.f64x2_ty, ""); + let v = + apply_pending_canonicalization(builder, intrinsics, v, i).into_vector_value(); let res = builder.build_float_neg(v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + // The exact NaN returned by F64x2Neg is fully defined. Do not + // adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F32Neg | Operator::F64Neg => { - let input = state.pop1()?.into_float_value(); - let res = builder.build_float_neg(input, &state.var_name()); - state.push1(res); + let (v, i) = state.pop1_extra()?; + let v = + apply_pending_canonicalization(builder, intrinsics, v, i).into_float_value(); + let res = builder.build_float_neg(v, &state.var_name()); + // The exact NaN returned by F32Neg and F64Neg are fully defined. + // Do not adjust. + state.push1_extra(res, i.strip_pending()); } Operator::F32Copysign => { - let (mag, sgn) = state.pop2()?; + let ((mag, mag_info), (sgn, sgn_info)) = state.pop2_extra()?; + let mag = apply_pending_canonicalization(builder, intrinsics, mag, mag_info); + let sgn = apply_pending_canonicalization(builder, intrinsics, sgn, sgn_info); let res = builder .build_call(intrinsics.copysign_f32, &[mag, sgn], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + // The exact NaN returned by F32Copysign is fully defined. + // Do not adjust. + state.push1_extra(res, mag_info.strip_pending()); } Operator::F64Copysign => { - let (msg, sgn) = state.pop2()?; + let ((mag, mag_info), (sgn, sgn_info)) = state.pop2_extra()?; + let mag = apply_pending_canonicalization(builder, intrinsics, mag, mag_info); + let sgn = apply_pending_canonicalization(builder, intrinsics, sgn, sgn_info); let res = builder - .build_call(intrinsics.copysign_f64, &[msg, sgn], &state.var_name()) + .build_call(intrinsics.copysign_f64, &[mag, sgn], &state.var_name()) .try_as_basic_value() .left() .unwrap(); - state.push1(res); + // The exact NaN returned by F32Copysign is fully defined. + // Do not adjust. + state.push1_extra(res, mag_info.strip_pending()); } /*************************** @@ -3241,460 +3783,384 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions ***************************/ Operator::I32Eq | Operator::I64Eq => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16Eq => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Eq => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Eq => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32Ne | Operator::I64Ne => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16Ne => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::NE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Ne => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::NE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Ne => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::NE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32LtS | Operator::I64LtS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16LtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8LtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4LtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32LtU | Operator::I64LtU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); state.push1(res); } Operator::I8x16LtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8LtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4LtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32LeS | Operator::I64LeS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16LeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8LeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4LeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32LeU | Operator::I64LeU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16LeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8LeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4LeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32GtS | Operator::I64GtS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16GtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8GtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4GtS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32GtU | Operator::I64GtU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16GtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8GtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4GtU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32GeS | Operator::I64GeS => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); state.push1(res); } Operator::I8x16GeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8GeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4GeS => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32GeU | Operator::I64GeU => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16GeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i8x16(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i8x16_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8GeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i16x8(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i16x8_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4GeU => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_i32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_i32x4(builder, intrinsics, v2, i2); let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3711,29 +4177,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Eq => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OEQ, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Eq => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OEQ, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3745,29 +4206,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Ne => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::UNE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Ne => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::UNE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3779,29 +4235,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Lt => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OLT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Lt => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OLT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3813,29 +4264,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Le => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OLE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Le => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OLE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3847,29 +4293,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Gt => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Gt => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OGT, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3881,29 +4322,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let cond = builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name()); let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::F32x4Ge => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f32x4(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f32x4(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i32x4_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::F64x2Ge => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = builder - .build_bitcast(v2, intrinsics.f64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, _) = v128_into_f64x2(builder, intrinsics, v1, i1); + let (v2, _) = v128_into_f64x2(builder, intrinsics, v2, i2); let res = builder.build_float_compare(FloatPredicate::OGE, v1, v2, ""); let res = builder.build_int_s_extend(res, intrinsics.i64x2_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); @@ -3915,22 +4351,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#conversion-instructions ***************************/ Operator::I32WrapI64 => { - let v1 = state.pop1()?.into_int_value(); - let res = builder.build_int_truncate(v1, intrinsics.i32_ty, &state.var_name()); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); + let res = builder.build_int_truncate(v, intrinsics.i32_ty, &state.var_name()); state.push1(res); } Operator::I64ExtendSI32 => { - let v1 = state.pop1()?.into_int_value(); - let res = builder.build_int_s_extend(v1, intrinsics.i64_ty, &state.var_name()); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); + let res = builder.build_int_s_extend(v, intrinsics.i64_ty, &state.var_name()); state.push1(res); } Operator::I64ExtendUI32 => { - let v1 = state.pop1()?.into_int_value(); - let res = builder.build_int_z_extend(v1, intrinsics.i64_ty, &state.var_name()); - state.push1(res); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); + let res = builder.build_int_z_extend(v, intrinsics.i64_ty, &state.var_name()); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::I32x4TruncSF32x4Sat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = trunc_sat( builder, intrinsics, @@ -3946,7 +4390,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4TruncUF32x4Sat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = trunc_sat( builder, intrinsics, @@ -3962,7 +4408,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2TruncSF64x2Sat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = trunc_sat( builder, intrinsics, @@ -3978,7 +4426,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2TruncUF64x2Sat => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = trunc_sat( builder, intrinsics, @@ -4123,39 +4573,47 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::F32DemoteF64 => { - let v1 = state.pop1()?; - let v1 = canonicalize_nans(builder, intrinsics, v1).into_float_value(); - let res = builder.build_float_trunc(v1, intrinsics.f32_ty, &state.var_name()); - state.push1(res); + let v = state.pop1()?; + let v = v.into_float_value(); + let res = builder.build_float_trunc(v, intrinsics.f32_ty, &state.var_name()); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64PromoteF32 => { - let v1 = state.pop1()?; - let v1 = canonicalize_nans(builder, intrinsics, v1).into_float_value(); - let res = builder.build_float_ext(v1, intrinsics.f64_ty, &state.var_name()); - state.push1(res); + let v = state.pop1()?; + let v = v.into_float_value(); + let res = builder.build_float_ext(v, intrinsics.f64_ty, &state.var_name()); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => { - let v1 = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = - builder.build_signed_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + builder.build_signed_int_to_float(v, intrinsics.f32_ty, &state.var_name()); state.push1(res); } Operator::F64ConvertSI32 | Operator::F64ConvertSI64 => { - let v1 = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = - builder.build_signed_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + builder.build_signed_int_to_float(v, intrinsics.f64_ty, &state.var_name()); state.push1(res); } Operator::F32ConvertUI32 | Operator::F32ConvertUI64 => { - let v1 = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = - builder.build_unsigned_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + builder.build_unsigned_int_to_float(v, intrinsics.f32_ty, &state.var_name()); state.push1(res); } Operator::F64ConvertUI32 | Operator::F64ConvertUI64 => { - let v1 = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); + let v = v.into_int_value(); let res = - builder.build_unsigned_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + builder.build_unsigned_int_to_float(v, intrinsics.f64_ty, &state.var_name()); state.push1(res); } Operator::F32x4ConvertSI32x4 => { @@ -4199,24 +4657,26 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32ReinterpretF32 => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let ret = builder.build_bitcast(v, intrinsics.i32_ty, &state.var_name()); - state.push1(ret); + state.push1_extra(ret, ExtraInfo::arithmetic_f32()); } Operator::I64ReinterpretF64 => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let ret = builder.build_bitcast(v, intrinsics.i64_ty, &state.var_name()); - state.push1(ret); + state.push1_extra(ret, ExtraInfo::arithmetic_f64()); } Operator::F32ReinterpretI32 => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let ret = builder.build_bitcast(v, intrinsics.f32_ty, &state.var_name()); - state.push1(ret); + state.push1_extra(ret, i); } Operator::F64ReinterpretI64 => { - let v = state.pop1()?; + let (v, i) = state.pop1_extra()?; let ret = builder.build_bitcast(v, intrinsics.f64_ty, &state.var_name()); - state.push1(ret); + state.push1_extra(ret, i); } /*************************** @@ -4273,6 +4733,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4281,6 +4742,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 4, )?; let result = builder.build_load(effective_address, &state.var_name()); + result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + result.as_instruction_value().unwrap(), + Some(0), + ); state.push1(result); } Operator::I64Load { ref memarg } => { @@ -4288,6 +4761,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4296,6 +4770,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 8, )?; let result = builder.build_load(effective_address, &state.var_name()); + result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + result.as_instruction_value().unwrap(), + Some(0), + ); state.push1(result); } Operator::F32Load { ref memarg } => { @@ -4303,6 +4789,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4311,6 +4798,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 4, )?; let result = builder.build_load(effective_address, &state.var_name()); + result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + result.as_instruction_value().unwrap(), + Some(0), + ); state.push1(result); } Operator::F64Load { ref memarg } => { @@ -4318,6 +4817,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4326,6 +4826,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 8, )?; let result = builder.build_load(effective_address, &state.var_name()); + result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + result.as_instruction_value().unwrap(), + Some(0), + ); state.push1(result); } Operator::V128Load { ref memarg } => { @@ -4333,6 +4845,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4341,6 +4854,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 16, )?; let result = builder.build_load(effective_address, &state.var_name()); + result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + result.as_instruction_value().unwrap(), + Some(0), + ); state.push1(result); } @@ -4350,6 +4875,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4357,7 +4883,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i32_ptr_ty, 4, )?; - builder.build_store(effective_address, value); + let store = builder.build_store(effective_address, value); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64Store { ref memarg } => { let value = state.pop1()?; @@ -4365,6 +4893,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4372,14 +4901,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i64_ptr_ty, 8, )?; - builder.build_store(effective_address, value); + let store = builder.build_store(effective_address, value); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::F32Store { ref memarg } => { - let value = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4387,14 +4920,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.f32_ptr_ty, 4, )?; - builder.build_store(effective_address, value); + let store = builder.build_store(effective_address, v); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::F64Store { ref memarg } => { - let value = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4402,14 +4939,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.f64_ptr_ty, 8, )?; - builder.build_store(effective_address, value); + let store = builder.build_store(effective_address, v); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::V128Store { ref memarg } => { - let value = state.pop1()?; + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4417,14 +4958,16 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i128_ptr_ty, 16, )?; - builder.build_store(effective_address, value); + let store = builder.build_store(effective_address, v); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } - Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4432,11 +4975,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i8_ptr_ty, 1, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_s_extend( + narrow_result.into_int_value(), + intrinsics.i32_ty, + &state.var_name(), + ); state.push1(result); } Operator::I32Load16S { ref memarg } => { @@ -4444,6 +5000,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4451,11 +5008,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_s_extend( + narrow_result.into_int_value(), + intrinsics.i32_ty, + &state.var_name(), + ); state.push1(result); } Operator::I64Load8S { ref memarg } => { @@ -4463,6 +5033,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4473,6 +5044,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let narrow_result = builder .build_load(effective_address, &state.var_name()) .into_int_value(); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); let result = builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); @@ -4482,6 +5065,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4492,6 +5076,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let narrow_result = builder .build_load(effective_address, &state.var_name()) .into_int_value(); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); let result = builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); @@ -4501,6 +5097,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4508,11 +5105,24 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i32_ptr_ty, 4, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_s_extend( + narrow_result.into_int_value(), + intrinsics.i64_ty, + &state.var_name(), + ); state.push1(result); } @@ -4521,6 +5131,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4528,18 +5139,32 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i8_ptr_ty, 1, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_z_extend( + narrow_result.into_int_value(), + intrinsics.i32_ty, + &state.var_name(), + ); + state.push1_extra(result, ExtraInfo::arithmetic_f32()); } Operator::I32Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4547,18 +5172,32 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_z_extend( + narrow_result.into_int_value(), + intrinsics.i32_ty, + &state.var_name(), + ); + state.push1_extra(result, ExtraInfo::arithmetic_f32()); } Operator::I64Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4566,18 +5205,32 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i8_ptr_ty, 1, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_z_extend( + narrow_result.into_int_value(), + intrinsics.i64_ty, + &state.var_name(), + ); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I64Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4585,18 +5238,32 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_z_extend( + narrow_result.into_int_value(), + intrinsics.i64_ty, + &state.var_name(), + ); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I64Load32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4604,12 +5271,25 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i32_ptr_ty, 4, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + let narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + narrow_result.as_instruction_value().unwrap(), + Some(0), + ); + let result = builder.build_int_z_extend( + narrow_result.into_int_value(), + intrinsics.i64_ty, + &state.var_name(), + ); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I32Store8 { ref memarg } | Operator::I64Store8 { ref memarg } => { @@ -4618,6 +5298,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4627,7 +5308,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { )?; let narrow_value = builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); - builder.build_store(effective_address, narrow_value); + let store = builder.build_store(effective_address, narrow_value); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32Store16 { ref memarg } | Operator::I64Store16 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -4635,6 +5318,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4644,7 +5328,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { )?; let narrow_value = builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); - builder.build_store(effective_address, narrow_value); + let store = builder.build_store(effective_address, narrow_value); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64Store32 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -4652,6 +5338,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4661,46 +5348,41 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { )?; let narrow_value = builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); - builder.build_store(effective_address, narrow_value); + let store = builder.build_store(effective_address, narrow_value); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I8x16Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i8x16_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i8x16(builder, intrinsics, v, i); let res = builder.build_int_sub(v.get_type().const_zero(), v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I16x8Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i16x8_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i16x8(builder, intrinsics, v, i); let res = builder.build_int_sub(v.get_type().const_zero(), v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I32x4Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i32x4_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i32x4(builder, intrinsics, v, i); let res = builder.build_int_sub(v.get_type().const_zero(), v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::I64x2Neg => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i64x2_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i64x2(builder, intrinsics, v, i); let res = builder.build_int_sub(v.get_type().const_zero(), v, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); state.push1(res); } Operator::V128Not => { - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i).into_int_value(); let res = builder.build_not(v, &state.var_name()); state.push1(res); } @@ -4708,6 +5390,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { | Operator::I16x8AnyTrue | Operator::I32x4AnyTrue | Operator::I64x2AnyTrue => { + // Skip canonicalization, it never changes non-zero values to zero or vice versa. let v = state.pop1()?.into_int_value(); let res = builder.build_int_compare( IntPredicate::NE, @@ -4716,7 +5399,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_int_z_extend(res, intrinsics.i32_ty, ""); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16AllTrue | Operator::I16x8AllTrue @@ -4729,7 +5415,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { Operator::I64x2AllTrue => intrinsics.i64x2_ty, _ => unreachable!(), }; - let v = state.pop1()?.into_int_value(); + let (v, i) = state.pop1_extra()?; + let v = apply_pending_canonicalization(builder, intrinsics, v, i).into_int_value(); let lane_int_ty = context.custom_width_int_type(vec_ty.get_size()); let vec = builder.build_bitcast(v, vec_ty, "vec").into_vector_value(); let mask = @@ -4744,13 +5431,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { &state.var_name(), ); let res = builder.build_int_z_extend(res, intrinsics.i32_ty, ""); - state.push1(res); + state.push1_extra( + res, + ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + ); } Operator::I8x16ExtractLaneS { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i8x16_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i8x16(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder .build_extract_element(v, idx, &state.var_name()) @@ -4759,22 +5447,18 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I8x16ExtractLaneU { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i8x16_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i8x16(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder .build_extract_element(v, idx, &state.var_name()) .into_int_value(); let res = builder.build_int_z_extend(res, intrinsics.i32_ty, ""); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I16x8ExtractLaneS { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i16x8_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i16x8(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder .build_extract_element(v, idx, &state.var_name()) @@ -4783,58 +5467,46 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ExtractLaneU { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i16x8_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, _) = v128_into_i16x8(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder .build_extract_element(v, idx, &state.var_name()) .into_int_value(); let res = builder.build_int_z_extend(res, intrinsics.i32_ty, ""); - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::I32x4ExtractLane { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i32x4_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, i) = v128_into_i32x4(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_extract_element(v, idx, &state.var_name()); - state.push1(res); + state.push1_extra(res, i); } Operator::I64x2ExtractLane { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.i64x2_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, i) = v128_into_i64x2(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_extract_element(v, idx, &state.var_name()); - state.push1(res); + state.push1_extra(res, i); } Operator::F32x4ExtractLane { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.f32x4_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, i) = v128_into_f32x4(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_extract_element(v, idx, &state.var_name()); - state.push1(res); + state.push1_extra(res, i); } Operator::F64x2ExtractLane { lane } => { - let v = state.pop1()?.into_int_value(); - let v = builder - .build_bitcast(v, intrinsics.f64x2_ty, "") - .into_vector_value(); + let (v, i) = state.pop1_extra()?; + let (v, i) = v128_into_f64x2(builder, intrinsics, v, i); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_extract_element(v, idx, &state.var_name()); - state.push1(res); + state.push1_extra(res, i); } Operator::I8x16ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i8x16_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, _)) = state.pop2_extra()?; + let (v1, _) = v128_into_i8x16(builder, intrinsics, v1, i1); let v2 = v2.into_int_value(); let v2 = builder.build_int_cast(v2, intrinsics.i8_ty, ""); let idx = intrinsics.i32_ty.const_int(lane.into(), false); @@ -4843,10 +5515,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i16x8_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, _)) = state.pop2_extra()?; + let (v1, _) = v128_into_i16x8(builder, intrinsics, v1, i1); let v2 = v2.into_int_value(); let v2 = builder.build_int_cast(v2, intrinsics.i16_ty, ""); let idx = intrinsics.i32_ty.const_int(lane.into(), false); @@ -4855,54 +5525,104 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i32x4_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_i32x4(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); + let i2 = i2.strip_pending(); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_insert_element(v1, v2, idx, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i1 & i2 & ExtraInfo::arithmetic_f32()); } Operator::I64x2ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.i64x2_ty, "") - .into_vector_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_i64x2(builder, intrinsics, v1, i1); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = v2.into_int_value(); + let i2 = i2.strip_pending(); let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_insert_element(v1, v2, idx, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + state.push1_extra(res, i1 & i2 & ExtraInfo::arithmetic_f64()); } Operator::F32x4ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f32x4_ty, "") - .into_vector_value(); - let v2 = v2.into_float_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1); + let push_pending_f32_nan_to_result = + i1.has_pending_f32_nan() && i2.has_pending_f32_nan(); + let (v1, v2) = if !push_pending_f32_nan_to_result { + ( + apply_pending_canonicalization( + builder, + intrinsics, + v1.as_basic_value_enum(), + i1, + ) + .into_vector_value(), + apply_pending_canonicalization( + builder, + intrinsics, + v2.as_basic_value_enum(), + i2, + ) + .into_float_value(), + ) + } else { + (v1, v2.into_float_value()) + }; let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_insert_element(v1, v2, idx, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + let info = if push_pending_f32_nan_to_result { + ExtraInfo::pending_f32_nan() + } else { + i1.strip_pending() & i2.strip_pending() + }; + state.push1_extra(res, info); } Operator::F64x2ReplaceLane { lane } => { - let (v1, v2) = state.pop2()?; - let v1 = builder - .build_bitcast(v1, intrinsics.f64x2_ty, "") - .into_vector_value(); - let v2 = v2.into_float_value(); + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1); + let push_pending_f64_nan_to_result = + i1.has_pending_f64_nan() && i2.has_pending_f64_nan(); + let (v1, v2) = if !push_pending_f64_nan_to_result { + ( + apply_pending_canonicalization( + builder, + intrinsics, + v1.as_basic_value_enum(), + i1, + ) + .into_vector_value(), + apply_pending_canonicalization( + builder, + intrinsics, + v2.as_basic_value_enum(), + i2, + ) + .into_float_value(), + ) + } else { + (v1, v2.into_float_value()) + }; let idx = intrinsics.i32_ty.const_int(lane.into(), false); let res = builder.build_insert_element(v1, v2, idx, &state.var_name()); let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); - state.push1(res); + let info = if push_pending_f64_nan_to_result { + ExtraInfo::pending_f64_nan() + } else { + i1.strip_pending() & i2.strip_pending() + }; + state.push1_extra(res, info); } Operator::V8x16Swizzle => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); let v1 = builder .build_bitcast(v1, intrinsics.i8x16_ty, "") .into_vector_value(); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = builder .build_bitcast(v2, intrinsics.i8x16_ty, "") .into_vector_value(); @@ -4960,10 +5680,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::V8x16Shuffle { lanes } => { - let (v1, v2) = state.pop2()?; + let ((v1, i1), (v2, i2)) = state.pop2_extra()?; + let v1 = apply_pending_canonicalization(builder, intrinsics, v1, i1); let v1 = builder .build_bitcast(v1, intrinsics.i8x16_ty, "") .into_vector_value(); + let v2 = apply_pending_canonicalization(builder, intrinsics, v2, i2); let v2 = builder .build_bitcast(v2, intrinsics.i8x16_ty, "") .into_vector_value(); @@ -4983,6 +5705,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -4990,11 +5713,22 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i8_ptr_ty, 1, )?; - let elem = builder.build_load(effective_address, "").into_int_value(); + let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + elem.as_instruction_value().unwrap(), + Some(0), + ); let res = splat_vector( builder, intrinsics, - elem.as_basic_value_enum(), + elem, intrinsics.i8x16_ty, &state.var_name(), ); @@ -5006,6 +5740,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5013,11 +5748,22 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let elem = builder.build_load(effective_address, "").into_int_value(); + let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + elem.as_instruction_value().unwrap(), + Some(0), + ); let res = splat_vector( builder, intrinsics, - elem.as_basic_value_enum(), + elem, intrinsics.i16x8_ty, &state.var_name(), ); @@ -5029,6 +5775,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5036,11 +5783,22 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i32_ptr_ty, 4, )?; - let elem = builder.build_load(effective_address, "").into_int_value(); + let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + elem.as_instruction_value().unwrap(), + Some(0), + ); let res = splat_vector( builder, intrinsics, - elem.as_basic_value_enum(), + elem, intrinsics.i32x4_ty, &state.var_name(), ); @@ -5052,6 +5810,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5059,11 +5818,22 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i64_ptr_ty, 8, )?; - let elem = builder.build_load(effective_address, "").into_int_value(); + let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + elem.as_instruction_value().unwrap(), + Some(0), + ); let res = splat_vector( builder, intrinsics, - elem.as_basic_value_enum(), + elem, intrinsics.i64x2_ty, &state.var_name(), ); @@ -5084,6 +5854,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5104,6 +5875,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(4).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); state.push1(result); } Operator::I64AtomicLoad { ref memarg } => { @@ -5111,6 +5883,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5131,6 +5904,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(8).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); state.push1(result); } Operator::I32AtomicLoad8U { ref memarg } => { @@ -5138,6 +5912,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5160,15 +5935,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(1).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); let result = builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + state.push1_extra(result, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicLoad16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5191,15 +5968,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(2).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); let result = builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + state.push1_extra(result, ExtraInfo::arithmetic_f32()); } Operator::I64AtomicLoad8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5222,15 +6001,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(1).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); let result = builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicLoad16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5253,15 +6034,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(2).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); let result = builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicLoad32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5284,9 +6067,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(4).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); let result = builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + state.push1_extra(result, ExtraInfo::arithmetic_f64()); } Operator::I32AtomicStore { ref memarg } => { let value = state.pop1()?; @@ -5294,6 +6078,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5314,6 +6099,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64AtomicStore { ref memarg } => { let value = state.pop1()?; @@ -5321,6 +6107,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5341,6 +6128,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicStore8 { ref memarg } | Operator::I64AtomicStore8 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5348,6 +6136,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5370,6 +6159,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicStore16 { ref memarg } | Operator::I64AtomicStore16 { ref memarg } => { @@ -5378,6 +6168,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5400,6 +6191,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64AtomicStore32 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5407,6 +6199,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5429,6 +6222,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicRmw8UAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5436,6 +6230,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5461,8 +6256,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5470,6 +6272,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5495,8 +6298,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5504,6 +6314,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5527,6 +6338,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I64AtomicRmw8UAdd { ref memarg } => { @@ -5535,6 +6353,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5560,8 +6379,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5569,6 +6395,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5594,8 +6421,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5603,6 +6437,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5628,8 +6463,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5637,6 +6479,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5660,6 +6503,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8USub { ref memarg } => { @@ -5668,6 +6518,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5693,8 +6544,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16USub { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5702,6 +6560,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5727,8 +6586,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwSub { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5736,6 +6602,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5759,6 +6626,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I64AtomicRmw8USub { ref memarg } => { @@ -5767,6 +6641,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5792,8 +6667,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I64AtomicRmw16USub { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5801,6 +6683,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5826,8 +6709,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32USub { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5835,6 +6725,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5860,8 +6751,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwSub { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5869,6 +6767,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5892,6 +6791,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8UAnd { ref memarg } => { @@ -5900,6 +6806,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5925,8 +6832,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UAnd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5934,6 +6848,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5959,8 +6874,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwAnd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5968,6 +6890,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -5991,6 +6914,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I64AtomicRmw8UAnd { ref memarg } => { @@ -5999,6 +6929,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6024,8 +6955,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UAnd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6033,6 +6971,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6058,8 +6997,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UAnd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6067,6 +7013,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6092,8 +7039,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwAnd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6101,6 +7055,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6124,6 +7079,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8UOr { ref memarg } => { @@ -6132,6 +7094,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6157,8 +7120,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6166,6 +7136,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6191,8 +7162,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6200,6 +7178,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6223,8 +7202,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I64AtomicRmw8UOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6232,6 +7218,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6257,8 +7244,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6266,6 +7260,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6291,8 +7286,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6300,6 +7302,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6325,8 +7328,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwOr { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6334,6 +7344,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6357,6 +7368,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8UXor { ref memarg } => { @@ -6365,6 +7383,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6390,8 +7409,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UXor { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6399,6 +7425,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6424,8 +7451,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwXor { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6433,6 +7467,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6456,6 +7491,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I64AtomicRmw8UXor { ref memarg } => { @@ -6464,6 +7506,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6489,8 +7532,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UXor { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6498,6 +7548,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6523,8 +7574,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UXor { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6532,6 +7590,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6557,8 +7616,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwXor { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6566,6 +7632,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6589,6 +7656,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8UXchg { ref memarg } => { @@ -6597,6 +7671,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6622,8 +7697,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UXchg { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6631,6 +7713,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6656,8 +7739,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwXchg { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6665,6 +7755,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6688,6 +7779,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I64AtomicRmw8UXchg { ref memarg } => { @@ -6696,6 +7794,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6721,8 +7820,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UXchg { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6730,6 +7836,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6755,8 +7862,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UXchg { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6764,6 +7878,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6789,8 +7904,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwXchg { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -6798,6 +7920,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6821,15 +7944,25 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); state.push1(old); } Operator::I32AtomicRmw8UCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6858,20 +7991,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder .build_extract_value(old, 0, "") .unwrap() .into_int_value(); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmw16UCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6900,20 +8043,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder .build_extract_value(old, 0, "") .unwrap() .into_int_value(); let old = builder.build_int_z_extend(old, intrinsics.i32_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f32()); } Operator::I32AtomicRmwCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6938,16 +8091,26 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_extract_value(old, 0, "").unwrap(); state.push1(old); } Operator::I64AtomicRmw8UCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -6976,20 +8139,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder .build_extract_value(old, 0, "") .unwrap() .into_int_value(); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw16UCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -7018,20 +8191,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder .build_extract_value(old, 0, "") .unwrap() .into_int_value(); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmw32UCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -7060,20 +8243,30 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder .build_extract_value(old, 0, "") .unwrap() .into_int_value(); let old = builder.build_int_z_extend(old, intrinsics.i64_ty, &state.var_name()); - state.push1(old); + state.push1_extra(old, ExtraInfo::arithmetic_f64()); } Operator::I64AtomicRmwCmpxchg { ref memarg } => { - let (cmp, new) = state.pop2()?; + let ((cmp, cmp_info), (new, new_info)) = state.pop2_extra()?; + let cmp = apply_pending_canonicalization(builder, intrinsics, cmp, cmp_info); + let new = apply_pending_canonicalization(builder, intrinsics, new, new_info); let (cmp, new) = (cmp.into_int_value(), new.into_int_value()); let effective_address = resolve_memory_ptr( builder, intrinsics, context, + self.module.clone(), &function, &mut state, &mut ctx, @@ -7098,6 +8291,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { AtomicOrdering::SequentiallyConsistent, ) .unwrap(); + tbaa_label( + &self.module, + intrinsics, + "memory", + old.as_instruction_value().unwrap(), + Some(0), + ); let old = builder.build_extract_value(old, 0, "").unwrap(); state.push1(old); } @@ -7179,15 +8379,21 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } fn finalize(&mut self) -> Result<(), CodegenError> { - let results = self.state.popn_save(self.func_sig.returns().len())?; + let results = self.state.popn_save_extra(self.func_sig.returns().len())?; match results.as_slice() { [] => { self.builder.as_ref().unwrap().build_return(None); } - [one_value] => { + [(one_value, one_value_info)] => { let builder = self.builder.as_ref().unwrap(); let intrinsics = self.intrinsics.as_ref().unwrap(); + let one_value = apply_pending_canonicalization( + builder, + intrinsics, + *one_value, + *one_value_info, + ); builder.build_return(Some(&builder.build_bitcast( one_value.as_basic_value_enum(), type_to_llvm_int_only(intrinsics, self.func_sig.returns()[0]), @@ -7212,12 +8418,76 @@ impl From for CodegenError { } } -impl ModuleCodeGenerator - for LLVMModuleCodeGenerator +impl Drop for LLVMModuleCodeGenerator<'_> { + fn drop(&mut self) { + // Ensure that all members of the context are dropped before we drop the context. + drop(self.builder.take()); + drop(self.intrinsics.take()); + self.functions.clear(); + self.signatures.clear(); + assert!( + Rc::strong_count(&*self.module) == 1, + "references to module live while dropping LLVMModuleCodeGenerator" + ); + unsafe { + ManuallyDrop::drop(&mut self.personality_func); + ManuallyDrop::drop(&mut self.module); + }; + let context = self.context.take(); + match context { + None => {} + Some(context_ref) => unsafe { + Box::from_raw(context_ref as *const Context as *mut Context); + }, + } + } +} + +impl<'ctx> ModuleCodeGenerator, LLVMBackend, CodegenError> + for LLVMModuleCodeGenerator<'ctx> { - fn new() -> LLVMModuleCodeGenerator { - let context = Context::create(); + fn new() -> LLVMModuleCodeGenerator<'ctx> { + Self::new_with_target(None, None, None) + } + + fn new_with_target( + triple: Option, + cpu_name: Option, + cpu_features: Option, + ) -> LLVMModuleCodeGenerator<'ctx> { + let context_ptr = Box::into_raw(Box::new(Context::create())); + let context = unsafe { &*context_ptr }; let module = context.create_module("module"); + + let triple = triple.unwrap_or(TargetMachine::get_default_triple().to_string()); + + match triple { + _ if triple.starts_with("x86") => Target::initialize_x86(&InitializationConfig { + asm_parser: true, + asm_printer: true, + base: true, + disassembler: true, + info: true, + machine_code: true, + }), + _ => unimplemented!("compile to target other than x86-64 is not supported"), + } + + let target = Target::from_triple(&triple).unwrap(); + let target_machine = target + .create_target_machine( + &triple, + &cpu_name.unwrap_or(TargetMachine::get_host_cpu_name().to_string()), + &cpu_features.unwrap_or(TargetMachine::get_host_cpu_features().to_string()), + OptimizationLevel::Aggressive, + RelocMode::Static, + CodeModel::Large, + ) + .unwrap(); + + module.set_target(&target); + module.set_data_layout(&target_machine.get_target_data().get_data_layout()); + let builder = context.create_builder(); let intrinsics = Intrinsics::declare(&module, &context); @@ -7228,20 +8498,21 @@ impl ModuleCodeGenerator Some(Linkage::External), ); - let signatures = Map::new(); - LLVMModuleCodeGenerator { context: Some(context), builder: Some(builder), intrinsics: Some(intrinsics), - module, + module: ManuallyDrop::new(Rc::new(RefCell::new(module))), functions: vec![], - signatures, + signatures: Map::new(), signatures_raw: Map::new(), function_signatures: None, + llvm_functions: Rc::new(RefCell::new(HashMap::new())), func_import_count: 0, - personality_func, + personality_func: ManuallyDrop::new(personality_func), stackmaps: Rc::new(RefCell::new(StackmapRegistry::default())), + track_state: false, + target_machine, } } @@ -7256,7 +8527,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, _module_info: Arc>, - ) -> Result<&mut LLVMFunctionCodeGenerator, CodegenError> { + ) -> Result<&mut LLVMFunctionCodeGenerator<'ctx>, CodegenError> { // Creates a new function and returns the function-scope code generator for it. let (context, builder, intrinsics) = match self.functions.last_mut() { Some(x) => ( @@ -7271,21 +8542,19 @@ impl ModuleCodeGenerator ), }; - let sig_id = self.function_signatures.as_ref().unwrap() - [FuncIndex::new(self.func_import_count + self.functions.len())]; + let func_index = FuncIndex::new(self.func_import_count + self.functions.len()); + let sig_id = self.function_signatures.as_ref().unwrap()[func_index]; let func_sig = self.signatures_raw[sig_id].clone(); - let function = self.module.add_function( - &format!("fn{}", self.func_import_count + self.functions.len()), - self.signatures[sig_id], - Some(Linkage::External), - ); - function.set_personality_function(self.personality_func); + let function = &self.llvm_functions.borrow_mut()[&func_index]; + function.set_personality_function(*self.personality_func); - let mut state = State::new(); - let entry_block = context.append_basic_block(&function, "entry"); + let mut state: State<'ctx> = State::new(); + let entry_block = context.append_basic_block(*function, "entry"); + let alloca_builder = context.create_builder(); + alloca_builder.position_at_end(&entry_block); - let return_block = context.append_basic_block(&function, "return"); + let return_block = context.append_basic_block(*function, "return"); builder.position_at_end(&return_block); let phis: SmallVec<[PhiValue; 1]> = func_sig @@ -7305,20 +8574,23 @@ impl ModuleCodeGenerator .skip(1) .enumerate() .map(|(index, param)| { - //let ty = param.get_type(); let real_ty = func_sig.params()[index]; let real_ty_llvm = type_to_llvm(&intrinsics, real_ty); - - let alloca = builder.build_alloca(real_ty_llvm, &format!("local{}", index)); - - //if real_ty_llvm != ty { + let alloca = + alloca_builder.build_alloca(real_ty_llvm, &format!("local{}", index)); builder.build_store( alloca, builder.build_bitcast(param, real_ty_llvm, &state.var_name()), ); - /*} else { - builder.build_store(alloca, param); - }*/ + if index == 0 { + alloca_builder.position_before( + &alloca + .as_instruction() + .unwrap() + .get_next_instruction() + .unwrap(), + ); + } alloca }), ); @@ -7330,8 +8602,10 @@ impl ModuleCodeGenerator state, context: Some(context), builder: Some(builder), + alloca_builder: Some(alloca_builder), intrinsics: Some(intrinsics), - function, + llvm_functions: self.llvm_functions.clone(), + function: *function, func_sig: func_sig, locals, signatures: self.signatures.clone(), @@ -7341,6 +8615,8 @@ impl ModuleCodeGenerator stackmaps: self.stackmaps.clone(), index: local_func_index, opcode_offset: 0, + track_state: self.track_state, + module: (*self.module).clone(), }; self.functions.push(code); Ok(self.functions.last_mut().unwrap()) @@ -7369,7 +8645,7 @@ impl ModuleCodeGenerator generate_trampolines( module_info, &self.signatures, - &self.module, + &self.module.borrow_mut(), self.context.as_ref().unwrap(), self.builder.as_ref().unwrap(), self.intrinsics.as_ref().unwrap(), @@ -7379,43 +8655,67 @@ impl ModuleCodeGenerator })?; if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.pre_opt_ir } { - self.module.print_to_file(path).unwrap(); + self.module.borrow_mut().print_to_file(path).unwrap(); } let pass_manager = PassManager::create(()); - if cfg!(test) { - pass_manager.add_verifier_pass(); - } + + #[cfg(feature = "test")] + pass_manager.add_verifier_pass(); + + pass_manager.add_type_based_alias_analysis_pass(); + pass_manager.add_ipsccp_pass(); + pass_manager.add_prune_eh_pass(); + pass_manager.add_dead_arg_elimination_pass(); + pass_manager.add_function_inlining_pass(); pass_manager.add_lower_expect_intrinsic_pass(); pass_manager.add_scalar_repl_aggregates_pass(); pass_manager.add_instruction_combining_pass(); - pass_manager.add_cfg_simplification_pass(); - pass_manager.add_gvn_pass(); pass_manager.add_jump_threading_pass(); pass_manager.add_correlated_value_propagation_pass(); - pass_manager.add_sccp_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_reassociate_pass(); + pass_manager.add_loop_rotate_pass(); + pass_manager.add_loop_unswitch_pass(); + pass_manager.add_ind_var_simplify_pass(); + pass_manager.add_licm_pass(); + pass_manager.add_loop_vectorize_pass(); + pass_manager.add_instruction_combining_pass(); + pass_manager.add_ipsccp_pass(); + pass_manager.add_reassociate_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_gvn_pass(); + pass_manager.add_memcpy_optimize_pass(); + pass_manager.add_dead_store_elimination_pass(); + pass_manager.add_bit_tracking_dce_pass(); pass_manager.add_instruction_combining_pass(); pass_manager.add_reassociate_pass(); pass_manager.add_cfg_simplification_pass(); - pass_manager.add_bit_tracking_dce_pass(); pass_manager.add_slp_vectorize_pass(); - pass_manager.run_on(&self.module); + pass_manager.add_early_cse_pass(); + pass_manager.run_on(&*self.module.borrow_mut()); if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.post_opt_ir } { - self.module.print_to_file(path).unwrap(); + self.module.borrow_mut().print_to_file(path).unwrap(); } let stackmaps = self.stackmaps.borrow(); let (backend, cache_gen) = LLVMBackend::new( - self.module, + (*self.module).clone(), self.intrinsics.take().unwrap(), &*stackmaps, module_info, + &self.target_machine, ); Ok((backend, Box::new(cache_gen))) } + fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> { + self.track_state = config.track_state; + Ok(()) + } + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { self.signatures = signatures .iter() @@ -7436,6 +8736,16 @@ impl ModuleCodeGenerator &mut self, assoc: Map, ) -> Result<(), CodegenError> { + for (index, sig_id) in &assoc { + if index.index() >= self.func_import_count { + let function = self.module.borrow_mut().add_function( + &format!("fn{}", index.index()), + self.signatures[*sig_id], + Some(Linkage::External), + ); + self.llvm_functions.borrow_mut().insert(index, function); + } + } self.function_signatures = Some(Arc::new(assoc)); Ok(()) } @@ -7458,3 +8768,15 @@ impl ModuleCodeGenerator }) } } + +fn is_f32_arithmetic(bits: u32) -> bool { + // Mask off sign bit. + let bits = bits & 0x7FFF_FFFF; + bits < 0x7FC0_0000 +} + +fn is_f64_arithmetic(bits: u64) -> bool { + // Mask off sign bit. + let bits = bits & 0x7FFF_FFFF_FFFF_FFFF; + bits < 0x7FF8_0000_0000_0000 +} diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index afd3bf8ad..c983e4175 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -3,28 +3,28 @@ use inkwell::{ builder::Builder, context::Context, module::Module, - types::{ - BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType, - }, + types::{BasicType, FloatType, IntType, PointerType, StructType, VectorType, VoidType}, values::{ - BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue, VectorValue, + 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, structures::TypedIndex, types::{ - GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, + GlobalIndex, ImportedFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, + units::Pages, vm::{Ctx, INTERNALS_SIZE}, }; -fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { +fn type_to_llvm_ptr<'ctx>(intrinsics: &Intrinsics<'ctx>, ty: Type) -> PointerType<'ctx> { match ty { Type::I32 => intrinsics.i32_ptr_ty, Type::I64 => intrinsics.i64_ptr_ty, @@ -34,134 +34,124 @@ fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { } } -pub struct Intrinsics { - pub ctlz_i32: FunctionValue, - pub ctlz_i64: FunctionValue, +pub struct Intrinsics<'ctx> { + pub ctlz_i32: FunctionValue<'ctx>, + pub ctlz_i64: FunctionValue<'ctx>, - pub cttz_i32: FunctionValue, - pub cttz_i64: FunctionValue, + pub cttz_i32: FunctionValue<'ctx>, + pub cttz_i64: FunctionValue<'ctx>, - pub ctpop_i32: FunctionValue, - pub ctpop_i64: FunctionValue, + pub ctpop_i32: FunctionValue<'ctx>, + pub ctpop_i64: FunctionValue<'ctx>, - pub sqrt_f32: FunctionValue, - pub sqrt_f64: FunctionValue, - pub sqrt_f32x4: FunctionValue, - pub sqrt_f64x2: FunctionValue, + pub sqrt_f32: FunctionValue<'ctx>, + pub sqrt_f64: FunctionValue<'ctx>, + pub sqrt_f32x4: FunctionValue<'ctx>, + pub sqrt_f64x2: FunctionValue<'ctx>, - pub minimum_f32: FunctionValue, - pub minimum_f64: FunctionValue, - pub minimum_f32x4: FunctionValue, - pub minimum_f64x2: FunctionValue, + pub ceil_f32: FunctionValue<'ctx>, + pub ceil_f64: FunctionValue<'ctx>, - pub maximum_f32: FunctionValue, - pub maximum_f64: FunctionValue, - pub maximum_f32x4: FunctionValue, - pub maximum_f64x2: FunctionValue, + pub floor_f32: FunctionValue<'ctx>, + pub floor_f64: FunctionValue<'ctx>, - pub ceil_f32: FunctionValue, - pub ceil_f64: FunctionValue, + pub trunc_f32: FunctionValue<'ctx>, + pub trunc_f64: FunctionValue<'ctx>, - pub floor_f32: FunctionValue, - pub floor_f64: FunctionValue, + pub nearbyint_f32: FunctionValue<'ctx>, + pub nearbyint_f64: FunctionValue<'ctx>, - pub trunc_f32: FunctionValue, - pub trunc_f64: FunctionValue, + pub fabs_f32: FunctionValue<'ctx>, + pub fabs_f64: FunctionValue<'ctx>, + pub fabs_f32x4: FunctionValue<'ctx>, + pub fabs_f64x2: FunctionValue<'ctx>, - pub nearbyint_f32: FunctionValue, - pub nearbyint_f64: FunctionValue, + pub copysign_f32: FunctionValue<'ctx>, + pub copysign_f64: FunctionValue<'ctx>, - pub fabs_f32: FunctionValue, - pub fabs_f64: FunctionValue, - pub fabs_f32x4: FunctionValue, - pub fabs_f64x2: FunctionValue, + pub sadd_sat_i8x16: FunctionValue<'ctx>, + pub sadd_sat_i16x8: FunctionValue<'ctx>, + pub uadd_sat_i8x16: FunctionValue<'ctx>, + pub uadd_sat_i16x8: FunctionValue<'ctx>, - pub copysign_f32: FunctionValue, - pub copysign_f64: FunctionValue, + pub ssub_sat_i8x16: FunctionValue<'ctx>, + pub ssub_sat_i16x8: FunctionValue<'ctx>, + pub usub_sat_i8x16: FunctionValue<'ctx>, + pub usub_sat_i16x8: FunctionValue<'ctx>, - pub sadd_sat_i8x16: FunctionValue, - pub sadd_sat_i16x8: FunctionValue, - pub uadd_sat_i8x16: FunctionValue, - pub uadd_sat_i16x8: FunctionValue, + pub expect_i1: FunctionValue<'ctx>, + pub trap: FunctionValue<'ctx>, - pub ssub_sat_i8x16: FunctionValue, - pub ssub_sat_i16x8: FunctionValue, - pub usub_sat_i8x16: FunctionValue, - pub usub_sat_i16x8: FunctionValue, + pub void_ty: VoidType<'ctx>, + pub i1_ty: IntType<'ctx>, + pub i8_ty: IntType<'ctx>, + pub i16_ty: IntType<'ctx>, + pub i32_ty: IntType<'ctx>, + pub i64_ty: IntType<'ctx>, + pub i128_ty: IntType<'ctx>, + pub f32_ty: FloatType<'ctx>, + pub f64_ty: FloatType<'ctx>, - pub expect_i1: FunctionValue, - pub trap: FunctionValue, + pub i1x128_ty: VectorType<'ctx>, + pub i8x16_ty: VectorType<'ctx>, + pub i16x8_ty: VectorType<'ctx>, + pub i32x4_ty: VectorType<'ctx>, + pub i64x2_ty: VectorType<'ctx>, + pub f32x4_ty: VectorType<'ctx>, + pub f64x2_ty: VectorType<'ctx>, - pub void_ty: VoidType, - pub i1_ty: IntType, - pub i8_ty: IntType, - pub i16_ty: IntType, - pub i32_ty: IntType, - pub i64_ty: IntType, - pub i128_ty: IntType, - pub f32_ty: FloatType, - pub f64_ty: FloatType, + pub i8_ptr_ty: PointerType<'ctx>, + pub i16_ptr_ty: PointerType<'ctx>, + pub i32_ptr_ty: PointerType<'ctx>, + pub i64_ptr_ty: PointerType<'ctx>, + pub i128_ptr_ty: PointerType<'ctx>, + pub f32_ptr_ty: PointerType<'ctx>, + pub f64_ptr_ty: PointerType<'ctx>, - pub i1x128_ty: VectorType, - pub i8x16_ty: VectorType, - pub i16x8_ty: VectorType, - pub i32x4_ty: VectorType, - pub i64x2_ty: VectorType, - pub f32x4_ty: VectorType, - pub f64x2_ty: VectorType, + pub anyfunc_ty: StructType<'ctx>, - pub i8_ptr_ty: PointerType, - pub i16_ptr_ty: PointerType, - pub i32_ptr_ty: PointerType, - pub i64_ptr_ty: PointerType, - pub i128_ptr_ty: PointerType, - pub f32_ptr_ty: PointerType, - pub f64_ptr_ty: PointerType, + pub i1_zero: IntValue<'ctx>, + pub i8_zero: IntValue<'ctx>, + pub i32_zero: IntValue<'ctx>, + pub i64_zero: IntValue<'ctx>, + pub i128_zero: IntValue<'ctx>, + pub f32_zero: FloatValue<'ctx>, + pub f64_zero: FloatValue<'ctx>, + pub f32x4_zero: VectorValue<'ctx>, + pub f64x2_zero: VectorValue<'ctx>, - pub anyfunc_ty: StructType, - - pub i1_zero: IntValue, - pub i8_zero: IntValue, - pub i32_zero: IntValue, - pub i64_zero: IntValue, - 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, - pub trap_call_indirect_oob: BasicValueEnum, - pub trap_memory_oob: BasicValueEnum, - pub trap_illegal_arithmetic: BasicValueEnum, - pub trap_misaligned_atomic: BasicValueEnum, + pub trap_unreachable: BasicValueEnum<'ctx>, + pub trap_call_indirect_sig: BasicValueEnum<'ctx>, + pub trap_call_indirect_oob: BasicValueEnum<'ctx>, + pub trap_memory_oob: BasicValueEnum<'ctx>, + pub trap_illegal_arithmetic: BasicValueEnum<'ctx>, + pub trap_misaligned_atomic: BasicValueEnum<'ctx>, // VM intrinsics. - pub memory_grow_dynamic_local: FunctionValue, - pub memory_grow_static_local: FunctionValue, - pub memory_grow_shared_local: FunctionValue, - pub memory_grow_dynamic_import: FunctionValue, - pub memory_grow_static_import: FunctionValue, - pub memory_grow_shared_import: FunctionValue, + pub memory_grow_dynamic_local: FunctionValue<'ctx>, + pub memory_grow_static_local: FunctionValue<'ctx>, + pub memory_grow_shared_local: FunctionValue<'ctx>, + pub memory_grow_dynamic_import: FunctionValue<'ctx>, + pub memory_grow_static_import: FunctionValue<'ctx>, + pub memory_grow_shared_import: FunctionValue<'ctx>, - pub memory_size_dynamic_local: FunctionValue, - pub memory_size_static_local: FunctionValue, - pub memory_size_shared_local: FunctionValue, - pub memory_size_dynamic_import: FunctionValue, - pub memory_size_static_import: FunctionValue, - pub memory_size_shared_import: FunctionValue, + pub memory_size_dynamic_local: FunctionValue<'ctx>, + pub memory_size_static_local: FunctionValue<'ctx>, + pub memory_size_shared_local: FunctionValue<'ctx>, + pub memory_size_dynamic_import: FunctionValue<'ctx>, + pub memory_size_static_import: FunctionValue<'ctx>, + pub memory_size_shared_import: FunctionValue<'ctx>, - pub throw_trap: FunctionValue, - pub throw_breakpoint: FunctionValue, + pub throw_trap: FunctionValue<'ctx>, + pub throw_breakpoint: FunctionValue<'ctx>, - pub experimental_stackmap: FunctionValue, + pub experimental_stackmap: FunctionValue<'ctx>, - pub ctx_ptr_ty: PointerType, + pub ctx_ptr_ty: PointerType<'ctx>, } -impl Intrinsics { - pub fn declare(module: &Module, context: &Context) -> Self { +impl<'ctx> Intrinsics<'ctx> { + pub fn declare(module: &Module<'ctx>, context: &'ctx Context) -> Self { let void_ty = context.void_type(); let i1_ty = context.bool_type(); let i8_ty = context.i8_type(); @@ -216,8 +206,13 @@ impl Intrinsics { context.struct_type(&[i8_ptr_ty_basic, i64_ty_basic, i8_ptr_ty_basic], false); let local_table_ty = local_memory_ty; let local_global_ty = i64_ty; - let imported_func_ty = - context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false); + let func_ctx_ty = + context.struct_type(&[ctx_ptr_ty.as_basic_type_enum(), i8_ptr_ty_basic], false); + let func_ctx_ptr_ty = func_ctx_ty.ptr_type(AddressSpace::Generic); + let imported_func_ty = context.struct_type( + &[i8_ptr_ty_basic, func_ctx_ptr_ty.as_basic_type_enum()], + false, + ); let sigindex_ty = i32_ty; let rt_intrinsics_ty = i8_ty; let stack_lower_bound_ty = i8_ty; @@ -309,8 +304,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], @@ -335,32 +328,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), @@ -592,62 +559,64 @@ impl Intrinsics { } #[derive(Clone, Copy)] -pub enum MemoryCache { +pub enum MemoryCache<'ctx> { /// The memory moves around. Dynamic { - ptr_to_base_ptr: PointerValue, - ptr_to_bounds: PointerValue, + ptr_to_base_ptr: PointerValue<'ctx>, + ptr_to_bounds: PointerValue<'ctx>, + minimum: Pages, + maximum: Option, }, /// The memory is always in the same place. Static { - base_ptr: PointerValue, - bounds: IntValue, + base_ptr: PointerValue<'ctx>, + bounds: IntValue<'ctx>, + minimum: Pages, + maximum: Option, }, } -struct TableCache { - ptr_to_base_ptr: PointerValue, - ptr_to_bounds: PointerValue, +struct TableCache<'ctx> { + ptr_to_base_ptr: PointerValue<'ctx>, + ptr_to_bounds: PointerValue<'ctx>, } #[derive(Clone, Copy)] -pub enum GlobalCache { - Mut { ptr_to_value: PointerValue }, - Const { value: BasicValueEnum }, +pub enum GlobalCache<'ctx> { + Mut { ptr_to_value: PointerValue<'ctx> }, + Const { value: BasicValueEnum<'ctx> }, } -struct ImportedFuncCache { - func_ptr: PointerValue, - ctx_ptr: PointerValue, +struct ImportedFuncCache<'ctx> { + func_ptr: PointerValue<'ctx>, + ctx_ptr: PointerValue<'ctx>, } -pub struct CtxType<'a> { - ctx_ptr_value: PointerValue, +pub struct CtxType<'a, 'ctx> { + ctx_ptr_value: PointerValue<'ctx>, info: &'a ModuleInfo, - cache_builder: Builder, + cache_builder: Builder<'ctx>, - cached_signal_mem: Option, + cached_signal_mem: Option>, - cached_memories: HashMap, - cached_tables: HashMap, - cached_sigindices: HashMap, - cached_globals: HashMap, - cached_imported_functions: HashMap, - - _phantom: PhantomData<&'a FunctionValue>, + cached_memories: HashMap>, + cached_tables: HashMap>, + cached_sigindices: HashMap>, + cached_globals: HashMap>, + cached_imported_functions: HashMap>, } fn offset_to_index(offset: u8) -> u32 { (offset as usize / ::std::mem::size_of::()) as u32 } -impl<'a> CtxType<'a> { +impl<'a, 'ctx> CtxType<'a, 'ctx> { pub fn new( info: &'a ModuleInfo, - func_value: &'a FunctionValue, - cache_builder: Builder, - ) -> CtxType<'a> { + func_value: &FunctionValue<'ctx>, + cache_builder: Builder<'ctx>, + ) -> CtxType<'a, 'ctx> { CtxType { ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), @@ -661,16 +630,14 @@ impl<'a> CtxType<'a> { cached_sigindices: HashMap::new(), cached_globals: HashMap::new(), cached_imported_functions: HashMap::new(), - - _phantom: PhantomData, } } - pub fn basic(&self) -> BasicValueEnum { + pub fn basic(&self) -> BasicValueEnum<'ctx> { self.ctx_ptr_value.as_basic_value_enum() } - pub fn signal_mem(&mut self) -> PointerValue { + pub fn signal_mem(&mut self) -> PointerValue<'ctx> { if let Some(x) = self.cached_signal_mem { return x; } @@ -691,7 +658,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<'ctx>, + module: Rc>>, + ) -> MemoryCache<'ctx> { let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, self.info, @@ -700,34 +672,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, + 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( @@ -739,6 +725,13 @@ impl<'a> CtxType<'a> { let memory_ptr = cache_builder .build_load(memory_ptr_ptr, "memory_ptr") .into_pointer_value(); + tbaa_label( + &module, + intrinsics, + "memory_ptr", + memory_ptr.as_instruction_value().unwrap(), + Some(index as u32), + ); let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { ( @@ -751,15 +744,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, + intrinsics, + "static_memory_base", + base_ptr.as_instruction_value().unwrap(), + Some(index as u32), + ); + tbaa_label( + &module, + intrinsics, + "static_memory_bounds", + bounds.as_instruction_value().unwrap(), + Some(index as u32), + ); + MemoryCache::Static { + base_ptr, + bounds, + minimum, + maximum, + } + } } }) } @@ -767,8 +782,9 @@ impl<'a> CtxType<'a> { pub fn table_prepare( &mut self, index: TableIndex, - intrinsics: &Intrinsics, - ) -> (PointerValue, PointerValue) { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + ) -> (PointerValue<'ctx>, PointerValue<'ctx>) { let (cached_tables, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_tables, self.info, @@ -780,7 +796,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( @@ -790,6 +806,7 @@ impl<'a> CtxType<'a> { ) }, local_table_index.index() as u64, + "context_field_ptr_to_local_table", ), LocalOrImport::Import(import_table_index) => ( unsafe { @@ -800,12 +817,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, + 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") @@ -813,6 +838,13 @@ impl<'a> CtxType<'a> { let table_ptr = cache_builder .build_load(table_ptr_ptr, "table_ptr") .into_pointer_value(); + tbaa_label( + &module, + intrinsics, + "table_ptr", + table_array_ptr.as_instruction_value().unwrap(), + Some(index as u32), + ); let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { ( @@ -833,53 +865,38 @@ impl<'a> CtxType<'a> { pub fn table( &mut self, index: TableIndex, - intrinsics: &Intrinsics, - 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(), - ) + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + builder: &Builder<'ctx>, + ) -> (PointerValue<'ctx>, IntValue<'ctx>) { + 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, + intrinsics, + "table_base_ptr", + base_ptr.as_instruction_value().unwrap(), + Some(index.index() as u32), + ); + tbaa_label( + &module, + intrinsics, + "table_bounds", + bounds.as_instruction_value().unwrap(), + Some(index.index() as u32), + ); + (base_ptr, bounds) } - pub fn local_func( + pub fn dynamic_sigindex( &mut self, - index: LocalFuncIndex, - fn_ty: FunctionType, - intrinsics: &Intrinsics, - builder: &Builder, - ) -> PointerValue { - let local_func_array_ptr_ptr = unsafe { - builder.build_struct_gep( - self.ctx_ptr_value, - offset_to_index(Ctx::offset_local_functions()), - "local_func_array_ptr_ptr", - ) - }; - let local_func_array_ptr = builder - .build_load(local_func_array_ptr_ptr, "local_func_array_ptr") - .into_pointer_value(); - let local_func_ptr_ptr = unsafe { - builder.build_in_bounds_gep( - local_func_array_ptr, - &[intrinsics.i32_ty.const_int(index.index() as u64, false)], - "local_func_ptr_ptr", - ) - }; - let local_func_ptr = builder - .build_load(local_func_ptr_ptr, "local_func_ptr") - .into_pointer_value(); - builder.build_pointer_cast( - local_func_ptr, - fn_ty.ptr_type(AddressSpace::Generic), - "local_func_ptr", - ) - } - - pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue { + index: SigIndex, + intrinsics: &Intrinsics<'ctx>, + ) -> IntValue<'ctx> { let (cached_sigindices, ctx_ptr_value, cache_builder) = ( &mut self.cached_sigindices, self.ctx_ptr_value, @@ -913,7 +930,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<'ctx>, + module: Rc>>, + ) -> GlobalCache<'ctx> { let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, self.ctx_ptr_value, @@ -922,7 +944,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; @@ -937,6 +959,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) => { @@ -952,6 +975,7 @@ impl<'a> CtxType<'a> { import_global_index.index() as u64, desc.mutable, desc.ty, + "context_field_ptr_to_imported_globals", ) } }; @@ -961,6 +985,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, + intrinsics, + field_name, + global_array_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( @@ -972,6 +1003,13 @@ impl<'a> CtxType<'a> { let global_ptr = cache_builder .build_load(global_ptr_ptr, "global_ptr") .into_pointer_value(); + tbaa_label( + &module, + intrinsics, + "global_ptr", + global_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"); @@ -981,9 +1019,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, + intrinsics, + "global", + value.as_instruction_value().unwrap(), + Some(index as u32), + ); + GlobalCache::Const { value } } }) } @@ -991,8 +1035,9 @@ impl<'a> CtxType<'a> { pub fn imported_func( &mut self, index: ImportedFuncIndex, - intrinsics: &Intrinsics, - ) -> (PointerValue, PointerValue) { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + ) -> (PointerValue<'ctx>, PointerValue<'ctx>) { let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( &mut self.cached_imported_functions, self.ctx_ptr_value, @@ -1010,6 +1055,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, + 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( @@ -1018,19 +1070,37 @@ impl<'a> CtxType<'a> { "imported_func_ptr", ) }; - let (func_ptr_ptr, ctx_ptr_ptr) = unsafe { + let (func_ptr_ptr, func_ctx_ptr_ptr) = unsafe { ( cache_builder.build_struct_gep(imported_func_ptr, 0, "func_ptr_ptr"), - cache_builder.build_struct_gep(imported_func_ptr, 1, "ctx_ptr_ptr"), + cache_builder.build_struct_gep(imported_func_ptr, 1, "func_ctx_ptr_ptr"), ) }; let func_ptr = cache_builder .build_load(func_ptr_ptr, "func_ptr") .into_pointer_value(); + let func_ctx_ptr = cache_builder + .build_load(func_ctx_ptr_ptr, "func_ctx_ptr") + .into_pointer_value(); + let ctx_ptr_ptr = unsafe { cache_builder.build_struct_gep(func_ctx_ptr, 0, "ctx_ptr") }; let ctx_ptr = cache_builder .build_load(ctx_ptr_ptr, "ctx_ptr") .into_pointer_value(); + tbaa_label( + &module, + intrinsics, + "imported_func_ptr", + func_ptr.as_instruction_value().unwrap(), + Some(index.index() as u32), + ); + tbaa_label( + &module, + intrinsics, + "imported_func_ctx_ptr", + ctx_ptr.as_instruction_value().unwrap(), + Some(index.index() as u32), + ); ImportedFuncCache { func_ptr, ctx_ptr } }); @@ -1041,9 +1111,10 @@ impl<'a> CtxType<'a> { pub fn internal_field( &mut self, index: usize, - intrinsics: &Intrinsics, - builder: &Builder, - ) -> PointerValue { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + builder: &Builder<'ctx>, + ) -> PointerValue<'ctx> { assert!(index < INTERNALS_SIZE); let local_internals_ptr_ptr = unsafe { @@ -1056,6 +1127,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, + intrinsics, + "context_field_ptr_to_internals", + local_internals_ptr.as_instruction_value().unwrap(), + None, + ); unsafe { builder.build_in_bounds_gep( local_internals_ptr, @@ -1065,3 +1143,89 @@ 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<'ctx>( + module: &Rc>>, + intrinsics: &Intrinsics<'ctx>, + label: &str, + instruction: InstructionValue<'ctx>, + index: Option, +) { + // 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(); + + // TODO: ContextRef can't return us the lifetime from module through Deref. + // This could be fixed once generic_associated_types is stable. + let context2 = &*context; + let context = unsafe { std::mem::transmute::<&Context, &'ctx Context>(context2) }; + std::mem::forget(context2); + + // `!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); +} diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 8194f797a..2b3f7d2e6 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -29,8 +29,8 @@ pub use code::LLVMModuleCodeGenerator as ModuleCodeGenerator; use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; pub type LLVMCompiler = SimpleStreamingCompilerGen< - code::LLVMModuleCodeGenerator, - code::LLVMFunctionCodeGenerator, + code::LLVMModuleCodeGenerator<'static>, + code::LLVMFunctionCodeGenerator<'static>, backend::LLVMBackend, code::CodegenError, >; diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs index a56c3c6a3..4a9dbf816 100644 --- a/lib/llvm-backend/src/stackmap.rs +++ b/lib/llvm-backend/src/stackmap.rs @@ -161,7 +161,7 @@ impl StackmapEntry { ValueSemantic::ImportedFuncCtx(idx) => MachineValue::VmctxDeref(vec![ Ctx::offset_imported_funcs() as usize, vm::ImportedFunc::size() as usize * idx - + vm::ImportedFunc::offset_vmctx() as usize, + + vm::ImportedFunc::offset_func_ctx() as usize, 0, ]), ValueSemantic::DynamicSigindice(idx) => { diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs index 20f915ebc..e870b6627 100644 --- a/lib/llvm-backend/src/state.rs +++ b/lib/llvm-backend/src/state.rs @@ -4,26 +4,27 @@ use inkwell::{ }; use smallvec::SmallVec; use std::cell::Cell; +use std::ops::{BitAnd, BitOr, BitOrAssign}; use wasmparser::BinaryReaderError; #[derive(Debug)] -pub enum ControlFrame { +pub enum ControlFrame<'ctx> { Block { next: BasicBlock, - phis: SmallVec<[PhiValue; 1]>, + phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, }, Loop { body: BasicBlock, next: BasicBlock, - phis: SmallVec<[PhiValue; 1]>, + phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, }, IfElse { if_then: BasicBlock, if_else: BasicBlock, next: BasicBlock, - phis: SmallVec<[PhiValue; 1]>, + phis: SmallVec<[PhiValue<'ctx>; 1]>, stack_size_snapshot: usize, if_else_state: IfElseState, }, @@ -35,7 +36,7 @@ pub enum IfElseState { Else, } -impl ControlFrame { +impl<'ctx> ControlFrame<'ctx> { pub fn code_after(&self) -> &BasicBlock { match self { ControlFrame::Block { ref next, .. } @@ -51,7 +52,7 @@ impl ControlFrame { } } - pub fn phis(&self) -> &[PhiValue] { + pub fn phis(&self) -> &[PhiValue<'ctx>] { match self { ControlFrame::Block { ref phis, .. } | ControlFrame::Loop { ref phis, .. } @@ -67,16 +68,140 @@ impl ControlFrame { } } +#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)] +pub struct ExtraInfo { + state: u8, +} +impl ExtraInfo { + // This value 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. + // + // When applied to a 64-bit value, this flag has no meaning and must be + // ignored. It may be set in such cases to allow for common handling of + // 32 and 64-bit operations. + pub const fn pending_f32_nan() -> ExtraInfo { + ExtraInfo { state: 1 } + } + + // This value 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. + // + // When applied to a 32-bit value, this flag has no meaning and must be + // ignored. It may be set in such cases to allow for common handling of + // 32 and 64-bit operations. + pub const fn pending_f64_nan() -> ExtraInfo { + ExtraInfo { state: 2 } + } + + // This value either does not contain a 32-bit NaN, or it contains an + // arithmetic NaN. In SIMD, applies to all 4 lanes. + pub const fn arithmetic_f32() -> ExtraInfo { + ExtraInfo { state: 4 } + } + + // This value either does not contain a 64-bit NaN, or it contains an + // arithmetic NaN. In SIMD, applies to both lanes. + pub const fn arithmetic_f64() -> ExtraInfo { + ExtraInfo { state: 8 } + } + + pub const fn has_pending_f32_nan(&self) -> bool { + self.state & ExtraInfo::pending_f32_nan().state != 0 + } + pub const fn has_pending_f64_nan(&self) -> bool { + self.state & ExtraInfo::pending_f64_nan().state != 0 + } + pub const fn is_arithmetic_f32(&self) -> bool { + self.state & ExtraInfo::arithmetic_f32().state != 0 + } + pub const fn is_arithmetic_f64(&self) -> bool { + self.state & ExtraInfo::arithmetic_f64().state != 0 + } + + pub const fn strip_pending(&self) -> ExtraInfo { + ExtraInfo { + state: self.state + & !(ExtraInfo::pending_f32_nan().state | ExtraInfo::pending_f64_nan().state), + } + } +} + +// Union two ExtraInfos. +impl BitOr for ExtraInfo { + type Output = Self; + + fn bitor(self, other: Self) -> Self { + debug_assert!(!(self.has_pending_f32_nan() && other.has_pending_f64_nan())); + debug_assert!(!(self.has_pending_f64_nan() && other.has_pending_f32_nan())); + ExtraInfo { + state: if self.is_arithmetic_f32() || other.is_arithmetic_f32() { + ExtraInfo::arithmetic_f32().state + } else if self.has_pending_f32_nan() || other.has_pending_f32_nan() { + ExtraInfo::pending_f32_nan().state + } else { + 0 + } + if self.is_arithmetic_f64() || other.is_arithmetic_f64() { + ExtraInfo::arithmetic_f64().state + } else if self.has_pending_f64_nan() || other.has_pending_f64_nan() { + ExtraInfo::pending_f64_nan().state + } else { + 0 + }, + } + } +} +impl BitOrAssign for ExtraInfo { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + +// Intersection for ExtraInfo. +impl BitAnd for ExtraInfo { + type Output = Self; + fn bitand(self, other: Self) -> Self { + // Pending canonicalizations are not safe to discard, or even reorder. + debug_assert!( + self.has_pending_f32_nan() == other.has_pending_f32_nan() + || self.is_arithmetic_f32() + || other.is_arithmetic_f32() + ); + debug_assert!( + self.has_pending_f64_nan() == other.has_pending_f64_nan() + || self.is_arithmetic_f64() + || other.is_arithmetic_f64() + ); + let info = match ( + self.is_arithmetic_f32() && other.is_arithmetic_f32(), + self.is_arithmetic_f64() && other.is_arithmetic_f64(), + ) { + (false, false) => Default::default(), + (true, false) => ExtraInfo::arithmetic_f32(), + (false, true) => ExtraInfo::arithmetic_f64(), + (true, true) => ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(), + }; + let info = match (self.has_pending_f32_nan(), self.has_pending_f64_nan()) { + (false, false) => info, + (true, false) => info | ExtraInfo::pending_f32_nan(), + (false, true) => info | ExtraInfo::pending_f64_nan(), + (true, true) => unreachable!("Can't form ExtraInfo with two pending canonicalizations"), + }; + info + } +} + #[derive(Debug)] -pub struct State { - pub stack: Vec, - control_stack: Vec, +pub struct State<'ctx> { + pub stack: Vec<(BasicValueEnum<'ctx>, ExtraInfo)>, + control_stack: Vec>, value_counter: Cell, pub reachable: bool, } -impl State { +impl<'ctx> State<'ctx> { pub fn new() -> Self { Self { stack: vec![], @@ -86,7 +211,7 @@ impl State { } } - pub fn reset_stack(&mut self, frame: &ControlFrame) { + pub fn reset_stack(&mut self, frame: &ControlFrame<'ctx>) { let stack_size_snapshot = match frame { ControlFrame::Block { stack_size_snapshot, @@ -104,14 +229,14 @@ impl State { self.stack.truncate(stack_size_snapshot); } - pub fn outermost_frame(&self) -> Result<&ControlFrame, BinaryReaderError> { + pub fn outermost_frame(&self) -> Result<&ControlFrame<'ctx>, BinaryReaderError> { self.control_stack.get(0).ok_or(BinaryReaderError { message: "invalid control stack depth", offset: -1isize as usize, }) } - pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame, BinaryReaderError> { + pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame<'ctx>, BinaryReaderError> { let index = self.control_stack.len() - 1 - (depth as usize); self.control_stack.get(index).ok_or(BinaryReaderError { message: "invalid control stack depth", @@ -122,7 +247,7 @@ impl State { pub fn frame_at_depth_mut( &mut self, depth: u32, - ) -> Result<&mut ControlFrame, BinaryReaderError> { + ) -> Result<&mut ControlFrame<'ctx>, BinaryReaderError> { let index = self.control_stack.len() - 1 - (depth as usize); self.control_stack.get_mut(index).ok_or(BinaryReaderError { message: "invalid control stack depth", @@ -130,7 +255,7 @@ impl State { }) } - pub fn pop_frame(&mut self) -> Result { + pub fn pop_frame(&mut self) -> Result, BinaryReaderError> { self.control_stack.pop().ok_or(BinaryReaderError { message: "cannot pop from control stack", offset: -1isize as usize, @@ -144,33 +269,64 @@ impl State { s } - pub fn push1(&mut self, value: T) { - self.stack.push(value.as_basic_value_enum()) + pub fn push1>(&mut self, value: T) { + self.push1_extra(value, Default::default()); } - pub fn pop1(&mut self) -> Result { + pub fn push1_extra>(&mut self, value: T, info: ExtraInfo) { + self.stack.push((value.as_basic_value_enum(), info)); + } + + pub fn pop1(&mut self) -> Result, BinaryReaderError> { + Ok(self.pop1_extra()?.0) + } + + pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), BinaryReaderError> { self.stack.pop().ok_or(BinaryReaderError { message: "invalid value stack", offset: -1isize as usize, }) } - pub fn pop2(&mut self) -> Result<(BasicValueEnum, BasicValueEnum), BinaryReaderError> { + pub fn pop2( + &mut self, + ) -> Result<(BasicValueEnum<'ctx>, BasicValueEnum<'ctx>), BinaryReaderError> { let v2 = self.pop1()?; let v1 = self.pop1()?; Ok((v1, v2)) } - pub fn pop3( + pub fn pop2_extra( &mut self, - ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { - let v3 = self.pop1()?; - let v2 = self.pop1()?; - let v1 = self.pop1()?; + ) -> Result< + ( + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + ), + BinaryReaderError, + > { + let v2 = self.pop1_extra()?; + let v1 = self.pop1_extra()?; + Ok((v1, v2)) + } + + pub fn pop3_extra( + &mut self, + ) -> Result< + ( + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + ), + BinaryReaderError, + > { + let v3 = self.pop1_extra()?; + let v2 = self.pop1_extra()?; + let v1 = self.pop1_extra()?; Ok((v1, v2, v3)) } - pub fn peek1(&self) -> Result { + pub fn peek1_extra(&self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), BinaryReaderError> { self.stack .get(self.stack.len() - 1) .ok_or(BinaryReaderError { @@ -180,17 +336,27 @@ impl State { .map(|v| *v) } - pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> { - self.stack - .get(self.stack.len() - n..) - .ok_or(BinaryReaderError { - message: "invalid value stack", - offset: -1isize as usize, - }) + pub fn peekn(&self, n: usize) -> Result>, BinaryReaderError> { + Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect()) } - pub fn popn_save(&mut self, n: usize) -> Result, BinaryReaderError> { - let v = self.peekn(n)?.to_vec(); + pub fn peekn_extra( + &self, + n: usize, + ) -> Result<&[(BasicValueEnum<'ctx>, ExtraInfo)], BinaryReaderError> { + let new_len = self.stack.len().checked_sub(n).ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + })?; + + Ok(&self.stack[new_len..]) + } + + pub fn popn_save_extra( + &mut self, + n: usize, + ) -> Result, ExtraInfo)>, BinaryReaderError> { + let v = self.peekn_extra(n)?.to_vec(); self.popn(n)?; Ok(v) } @@ -208,7 +374,7 @@ impl State { Ok(()) } - pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue<'ctx>; 1]>) { self.control_stack.push(ControlFrame::Block { next, phis, @@ -216,7 +382,12 @@ impl State { }); } - pub fn push_loop(&mut self, body: BasicBlock, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + pub fn push_loop( + &mut self, + body: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue<'ctx>; 1]>, + ) { self.control_stack.push(ControlFrame::Loop { body, next, @@ -230,7 +401,7 @@ impl State { if_then: BasicBlock, if_else: BasicBlock, next: BasicBlock, - phis: SmallVec<[PhiValue; 1]>, + phis: SmallVec<[PhiValue<'ctx>; 1]>, ) { self.control_stack.push(ControlFrame::IfElse { if_then, diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs index c9bd50e1b..c4894ef84 100644 --- a/lib/llvm-backend/src/trampolines.rs +++ b/lib/llvm-backend/src/trampolines.rs @@ -13,13 +13,13 @@ use wasmer_runtime_core::{ types::{FuncSig, SigIndex, Type}, }; -pub fn generate_trampolines( +pub fn generate_trampolines<'ctx>( info: &ModuleInfo, - signatures: &SliceMap, - module: &Module, - context: &Context, - builder: &Builder, - intrinsics: &Intrinsics, + signatures: &SliceMap>, + module: &Module<'ctx>, + context: &'ctx Context, + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, ) -> Result<(), String> { for (sig_index, sig) in info.signatures.iter() { let func_type = signatures[sig_index]; @@ -47,14 +47,14 @@ pub fn generate_trampolines( Ok(()) } -fn generate_trampoline( +fn generate_trampoline<'ctx>( trampoline_func: FunctionValue, func_sig: &FuncSig, - context: &Context, - builder: &Builder, - intrinsics: &Intrinsics, + context: &'ctx Context, + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, ) -> Result<(), String> { - let entry_block = context.append_basic_block(&trampoline_func, "entry"); + let entry_block = context.append_basic_block(trampoline_func, "entry"); builder.position_at_end(&entry_block); let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice() diff --git a/lib/middleware-common-tests/Cargo.toml b/lib/middleware-common-tests/Cargo.toml index 48a5c68bd..1541a096c 100644 --- a/lib/middleware-common-tests/Cargo.toml +++ b/lib/middleware-common-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common-tests" -version = "0.8.0" +version = "0.11.0" authors = ["The Wasmer Engineering Team "] 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.11.0" } +wasmer-middleware-common = { path = "../middleware-common", version = "0.11.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.11.0" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.11.0", optional = true } [features] clif = [] diff --git a/lib/middleware-common-tests/benches/metering_benchmark.rs b/lib/middleware-common-tests/benches/metering_benchmark.rs index 79796646a..7c425ce99 100644 --- a/lib/middleware-common-tests/benches/metering_benchmark.rs +++ b/lib/middleware-common-tests/benches/metering_benchmark.rs @@ -189,7 +189,7 @@ fn bench_metering(c: &mut Criterion) { let wasm_binary = wat2wasm(WAT).unwrap(); let module = compile_with(&wasm_binary, &compiler).unwrap(); let import_object = imports! {}; - let mut instance = module.instantiate(&import_object).unwrap(); + let instance = module.instantiate(&import_object).unwrap(); let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap(); b.iter(|| black_box(add_to.call(100, 4))) }) @@ -202,7 +202,7 @@ fn bench_metering(c: &mut Criterion) { "gas" => Func::new(gas), }, }; - let mut gas_instance = gas_module.instantiate(&gas_import_object).unwrap(); + let gas_instance = gas_module.instantiate(&gas_import_object).unwrap(); let gas_add_to: Func<(i32, i32), i32> = gas_instance.func("add_to").unwrap(); b.iter(|| black_box(gas_add_to.call(100, 4))) }) diff --git a/lib/middleware-common-tests/src/lib.rs b/lib/middleware-common-tests/src/lib.rs index 0b6754c5f..1515f5cd2 100644 --- a/lib/middleware-common-tests/src/lib.rs +++ b/lib/middleware-common-tests/src/lib.rs @@ -4,38 +4,43 @@ mod tests { use wasmer_middleware_common::metering::*; use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; - use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func}; + use wasmer_runtime_core::fault::{pop_code_version, push_code_version}; + use wasmer_runtime_core::state::CodeVersion; + use wasmer_runtime_core::{ + backend::{Backend, Compiler}, + compile_with, imports, Func, + }; #[cfg(feature = "llvm")] - fn get_compiler(limit: u64) -> impl Compiler { + fn get_compiler(limit: u64) -> (impl Compiler, Backend) { use wasmer_llvm_backend::ModuleCodeGenerator as LLVMMCG; let c: StreamingCompiler = StreamingCompiler::new(move || { let mut chain = MiddlewareChain::new(); chain.push(Metering::new(limit)); chain }); - c + (c, Backend::LLVM) } #[cfg(feature = "singlepass")] - fn get_compiler(limit: u64) -> impl Compiler { + fn get_compiler(limit: u64) -> (impl Compiler, Backend) { use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG; let c: StreamingCompiler = StreamingCompiler::new(move || { let mut chain = MiddlewareChain::new(); chain.push(Metering::new(limit)); chain }); - c + (c, Backend::Singlepass) } #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] compile_error!("compiler not specified, activate a compiler via features"); #[cfg(feature = "clif")] - fn get_compiler(_limit: u64) -> impl Compiler { + fn get_compiler(_limit: u64) -> (impl Compiler, Backend) { compile_error!("cranelift does not implement metering"); use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() + (CraneliftCompiler::new(), Backend::Cranelift) } // Assemblyscript @@ -103,7 +108,8 @@ mod tests { let limit = 100u64; - let module = compile_with(&wasm_binary, &get_compiler(limit)).unwrap(); + let (compiler, backend_id) = get_compiler(limit); + let module = compile_with(&wasm_binary, &compiler).unwrap(); let import_object = imports! {}; let mut instance = module.instantiate(&import_object).unwrap(); @@ -111,7 +117,23 @@ mod tests { set_points_used(&mut instance, 0u64); let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap(); + + let cv_pushed = if let Some(msm) = instance.module.runnable_module.get_module_state_map() { + push_code_version(CodeVersion { + baseline: true, + msm: msm, + base: instance.module.runnable_module.get_code().unwrap().as_ptr() as usize, + backend: backend_id, + }); + true + } else { + false + }; + let value = add_to.call(3, 4).unwrap(); + if cv_pushed { + pop_code_version().unwrap(); + } // verify it returns the correct value assert_eq!(value, 7); @@ -127,7 +149,8 @@ mod tests { let limit = 100u64; - let module = compile_with(&wasm_binary, &get_compiler(limit)).unwrap(); + let (compiler, backend_id) = get_compiler(limit); + let module = compile_with(&wasm_binary, &compiler).unwrap(); let import_object = imports! {}; let mut instance = module.instantiate(&import_object).unwrap(); @@ -135,7 +158,22 @@ mod tests { set_points_used(&mut instance, 0u64); let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap(); + + let cv_pushed = if let Some(msm) = instance.module.runnable_module.get_module_state_map() { + push_code_version(CodeVersion { + baseline: true, + msm: msm, + base: instance.module.runnable_module.get_code().unwrap().as_ptr() as usize, + backend: backend_id, + }); + true + } else { + false + }; let result = add_to.call(10_000_000, 4); + if cv_pushed { + pop_code_version().unwrap(); + } let err = result.unwrap_err(); match err { diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index 0da56b81d..3cddcefcc 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,11 +1,13 @@ [package] name = "wasmer-middleware-common" -version = "0.8.0" +version = "0.11.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" authors = ["The Wasmer Engineering Team "] +keywords = ["wasm", "webassembly", "middleware", "metering"] +categories = ["wasm"] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } diff --git a/lib/middleware-common/src/block_trace.rs b/lib/middleware-common/src/block_trace.rs new file mode 100644 index 000000000..f104ab086 --- /dev/null +++ b/lib/middleware-common/src/block_trace.rs @@ -0,0 +1,153 @@ +use wasmer_runtime_core::{ + codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, + module::ModuleInfo, + wasmparser::Operator, +}; + +pub struct BlockTrace { + func_idx: usize, + evt_idx: usize, +} + +impl BlockTrace { + pub fn new() -> BlockTrace { + BlockTrace { + func_idx: std::usize::MAX, + evt_idx: 0, + } + } +} + +impl FunctionMiddleware for BlockTrace { + type Error = String; + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + _module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), Self::Error> { + match op { + Event::Internal(InternalEvent::FunctionBegin(_)) => { + self.func_idx = self.func_idx.wrapping_add(1); + self.evt_idx = 0; + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> enter_func % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + Event::Wasm(Operator::Call { .. }) => { + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> leave_call % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + Event::Wasm(Operator::Block { .. }) => { + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> block % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + Event::Wasm(Operator::Loop { .. }) => { + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> loop % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + Event::Wasm(Operator::If { .. }) => { + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> if % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + Event::Wasm(Operator::Else { .. }) => { + let func_idx = self.func_idx; + let evt_idx = self.evt_idx; + sink.push(op); + sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new( + move |info| { + eprintln!( + "[BlockTrace] ({}, {}) -> else % {:?}", + func_idx, + evt_idx, + info.fault + .and_then(|x| unsafe { x.read_stack(Some(1)) }) + .unwrap() + .frames[0] + ); + Ok(()) + }, + )))) + } + _ => { + sink.push(op); + } + } + self.evt_idx += 1; + Ok(()) + } +} diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs index 04a763abc..5cb77534c 100644 --- a/lib/middleware-common/src/call_trace.rs +++ b/lib/middleware-common/src/call_trace.rs @@ -1,9 +1,23 @@ +use std::sync::{ + atomic::{AtomicU32, Ordering}, + Arc, +}; use wasmer_runtime_core::{ codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, module::ModuleInfo, }; -pub struct CallTrace; +pub struct CallTrace { + counter: Arc, +} + +impl CallTrace { + pub fn new() -> CallTrace { + CallTrace { + counter: Arc::new(AtomicU32::new(0)), + } + } +} impl FunctionMiddleware for CallTrace { type Error = String; @@ -13,10 +27,13 @@ impl FunctionMiddleware for CallTrace { _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, ) -> Result<(), Self::Error> { + let counter = self.counter.clone(); + match op { Event::Internal(InternalEvent::FunctionBegin(id)) => sink.push(Event::Internal( InternalEvent::Breakpoint(Box::new(move |_| { - eprintln!("func ({})", id); + let idx = counter.fetch_add(1, Ordering::SeqCst); + eprintln!("[{}] func ({})", idx, id); Ok(()) })), )), diff --git a/lib/middleware-common/src/lib.rs b/lib/middleware-common/src/lib.rs index c7900c83a..85885383f 100644 --- a/lib/middleware-common/src/lib.rs +++ b/lib/middleware-common/src/lib.rs @@ -10,5 +10,7 @@ #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#[cfg(unix)] +pub mod block_trace; pub mod call_trace; pub mod metering; diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 5b9bc2634..0a278b6ba 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-runtime-c-api" -version = "0.8.0" +version = "0.11.0" description = "Wasmer C API library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "runtime"] +categories = ["wasm"] edition = "2018" readme = "README.md" @@ -17,19 +19,26 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.8.0" +version = "0.11.0" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.8.0" +version = "0.11.0" + +[dependencies.wasmer-wasi] +default-features = false +path = "../wasi" +version = "0.11.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" diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md index c652948b8..06e545bf7 100644 --- a/lib/runtime-c-api/README.md +++ b/lib/runtime-c-api/README.md @@ -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: @@ -104,6 +105,10 @@ int main() # Testing +Tests are run using the release build of the library. If you make +changes or compile with non-default features, please ensure you +rebuild in release mode for the tests to see the changes. + The tests can be run via `cargo test`, such as: ```sh diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs index 1db19a197..5897001b0 100644 --- a/lib/runtime-c-api/build.rs +++ b/lib/runtime-c-api/build.rs @@ -12,12 +12,33 @@ fn main() { let mut out_wasmer_header_file = PathBuf::from(&out_dir); out_wasmer_header_file.push("wasmer"); + const WASMER_PRE_HEADER: &str = r#" +#if !defined(WASMER_H_MACROS) +#define WASMER_H_MACROS + +#if defined(MSVC) +#if defined(_M_AMD64) +#define ARCH_X86_64 +#endif +#endif + +#if defined(GCC) || defined(__GNUC__) || defined(__clang__) +#if defined(__x86_64__) +#define ARCH_X86_64 +#endif +#endif + +#endif // WASMER_H_MACROS +"#; // Generate the C bindings in the `OUT_DIR`. out_wasmer_header_file.set_extension("h"); Builder::new() .with_crate(crate_dir.clone()) .with_language(Language::C) .with_include_guard("WASMER_H") + .with_header(WASMER_PRE_HEADER) + .with_define("target_family", "windows", "_WIN32") + .with_define("target_arch", "x86_64", "ARCH_X86_64") .generate() .expect("Unable to generate C bindings") .write_to_file(out_wasmer_header_file.as_path()); @@ -28,6 +49,9 @@ fn main() { .with_crate(crate_dir) .with_language(Language::Cxx) .with_include_guard("WASMER_H") + .with_header(WASMER_PRE_HEADER) + .with_define("target_family", "windows", "_WIN32") + .with_define("target_arch", "x86_64", "ARCH_X86_64") .generate() .expect("Unable to generate C++ bindings") .write_to_file(out_wasmer_header_file.as_path()); diff --git a/lib/runtime-c-api/src/export.rs b/lib/runtime-c-api/src/export.rs index d6dfa5d97..40d370111 100644 --- a/lib/runtime-c-api/src/export.rs +++ b/lib/runtime-c-api/src/export.rs @@ -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 for wasmer_import_export_kind { + type Error = (); + + fn try_from(value: u32) -> Result { + 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 { diff --git a/lib/runtime-c-api/src/import.rs b/lib/runtime-c-api/src/import/mod.rs similarity index 56% rename from lib/runtime-c-api/src/import.rs rename to lib/runtime-c-api/src/import/mod.rs index 2d6f9e99d..5b44eb2b5 100644 --- a/lib/runtime-c-api/src/import.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -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::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::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 = 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 = 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 = Box::from_raw(import.value.func as *mut _); + } + wasmer_import_export_kind::WASM_GLOBAL => { + let _: Box = Box::from_raw(import.value.global as *mut _); + } + wasmer_import_export_kind::WASM_MEMORY => { + let _: Box = Box::from_raw(import.value.memory as *mut _); + } + wasmer_import_export_kind::WASM_TABLE => { + let _: Box = 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); diff --git a/lib/runtime-c-api/src/import/wasi.rs b/lib/runtime-c-api/src/import/wasi.rs new file mode 100644 index 000000000..3df3c7f70 --- /dev/null +++ b/lib/runtime-c-api/src/import/wasi.rs @@ -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::, _>>()?; + let mapped_dir_vec = mapped_dir_list + .iter() + .map(|entry| unsafe { entry.as_tuple() }) + .collect::, _>>()?; + + 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 +} diff --git a/lib/runtime-c-api/src/instance.rs b/lib/runtime-c-api/src/instance.rs index 69fa0131a..b39e52d7e 100644 --- a/lib/runtime-c-api/src/instance.rs +++ b/lib/runtime-c-api/src/instance.rs @@ -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; diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index 4dae18cd5..f04640754 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -101,7 +101,9 @@ pub mod instance; pub mod memory; pub mod module; pub mod table; -#[cfg(all(unix, target_arch = "x86_64"))] +// `not(target_family = "windows")` is simpler than `unix`. See build.rs +// if you want to change the meaning of these `cfg`s in the header file. +#[cfg(all(not(target_family = "windows"), target_arch = "x86_64"))] pub mod trampoline; pub mod value; @@ -129,3 +131,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 { + 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) + } +} diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index 923622aa5..ed7b8971b 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -61,7 +61,7 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build( #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(buffer: *mut wasmer_trampoline_buffer_t) { if !buffer.is_null() { - Box::from_raw(buffer); + Box::from_raw(buffer as *mut TrampolineBuffer); } } diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index 26a85acee..b64fa610c 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -25,3 +25,5 @@ test-tables test-validate test-context test-module-import-instantiate +test-wasi-import-object + diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 7db3cbcb0..e4a573e63 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -6,6 +6,7 @@ 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-instantiate test-instantiate.c) add_executable(test-memory test-memory.c) add_executable(test-module test-module.c) @@ -17,6 +18,10 @@ add_executable(test-validate test-validate.c) add_executable(test-context test-context.c) add_executable(test-module-import-instantiate test-module-import-instantiate.c) +if (DEFINED WASI_TESTS) + add_executable(test-wasi-import-object test-wasi-import-object.c) +endif() + find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/ @@ -58,6 +63,17 @@ 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) + + +if (DEFINED WASI_TESTS) + 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) +endif() + target_link_libraries(test-instantiate general ${WASMER_LIB}) target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) add_test(test-instantiate test-instantiate) diff --git a/lib/runtime-c-api/tests/assets/README.md b/lib/runtime-c-api/tests/assets/README.md new file mode 100644 index 000000000..52e92c052 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/README.md @@ -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. diff --git a/lib/runtime-c-api/tests/assets/extended_wasi.rs b/lib/runtime-c-api/tests/assets/extended_wasi.rs new file mode 100644 index 000000000..31c659f8e --- /dev/null +++ b/lib/runtime-c-api/tests/assets/extended_wasi.rs @@ -0,0 +1,31 @@ +extern "C" { + fn host_print(ptr: u32, len: u32); +} + +fn main() { + let args = std::env::args().collect::>(); + + println!("Found {} args on program {}", args.len(), args[0]); + + let env_vars = std::env::vars() + .map(|(arg, val)| format!("{}={}", arg, val)) + .collect::>(); + 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::, _>>() + .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) }; +} diff --git a/lib/runtime-c-api/tests/assets/extended_wasi.wasm b/lib/runtime-c-api/tests/assets/extended_wasi.wasm new file mode 100755 index 000000000..b8eedd722 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/extended_wasi.wasm differ diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs index 0e3257786..ed4146363 100644 --- a/lib/runtime-c-api/tests/runtime_c_api_tests.rs +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -4,7 +4,14 @@ use std::process::Command; fn test_c_api() { let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests"); - run_command("cmake", project_tests_dir, vec!["."]); + let cmake_args = vec![ + ".", + #[cfg(feature = "wasi")] + "-DWASI_TESTS=ON", + ]; + // we use -f so it doesn't fail if the fiel doesn't exist + run_command("rm", project_tests_dir, vec!["-f", "CMakeCache.txt"]); + run_command("cmake", project_tests_dir, cmake_args); run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]); run_command("make", project_tests_dir, vec!["test", "ARGS=\"-V\""]); } diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c index 893174908..e245414be 100644 --- a/lib/runtime-c-api/tests/test-exports.c +++ b/lib/runtime-c-api/tests/test-exports.c @@ -1,3 +1,4 @@ +#include #include #include "../wasmer.h" #include @@ -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); diff --git a/lib/runtime-c-api/tests/test-import-object b/lib/runtime-c-api/tests/test-import-object new file mode 100755 index 000000000..bb0a97db3 Binary files /dev/null and b/lib/runtime-c-api/tests/test-import-object differ diff --git a/lib/runtime-c-api/tests/test-import-object.c b/lib/runtime-c-api/tests/test-import-object.c new file mode 100644 index 000000000..640f16640 --- /dev/null +++ b/lib/runtime-c-api/tests/test-import-object.c @@ -0,0 +1,172 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +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; +} diff --git a/lib/runtime-c-api/tests/test-wasi-import-object.c b/lib/runtime-c-api/tests/test-wasi-import-object.c new file mode 100644 index 000000000..bd1478dc1 --- /dev/null +++ b/lib/runtime-c-api/tests/test-wasi-import-object.c @@ -0,0 +1,250 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +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; +} + diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 447d31075..40a265cff 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -1,3 +1,22 @@ + +#if !defined(WASMER_H_MACROS) +#define WASMER_H_MACROS + +#if defined(MSVC) +#if defined(_M_AMD64) +#define ARCH_X86_64 +#endif +#endif + +#if defined(GCC) || defined(__GNUC__) || defined(__clang__) +#if defined(__x86_64__) +#define ARCH_X86_64 +#endif +#endif + +#endif // WASMER_H_MACROS + + #ifndef WASMER_H #define WASMER_H @@ -10,10 +29,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 +157,10 @@ typedef struct { typedef struct { +} wasmer_import_object_iter_t; + +typedef struct { + } wasmer_instance_t; typedef struct { @@ -158,17 +181,38 @@ typedef struct { } wasmer_serialized_module_t; +#if (!defined(_WIN32) && defined(ARCH_X86_64)) typedef struct { } wasmer_trampoline_buffer_builder_t; +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) typedef struct { } wasmer_trampoline_callable_t; +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) typedef struct { } wasmer_trampoline_buffer_t; +#endif + +/** + * 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 +495,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` @@ -710,6 +805,7 @@ uint32_t wasmer_table_length(wasmer_table_t *table); */ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Adds a callinfo trampoline to the builder. */ @@ -717,43 +813,81 @@ uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampo const wasmer_trampoline_callable_t *func, const void *ctx, uint32_t num_params); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Adds a context trampoline to the builder. */ uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *builder, const wasmer_trampoline_callable_t *func, const void *ctx); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Finalizes the trampoline builder into an executable buffer. */ wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *builder); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Creates a new trampoline builder. */ wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(void); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Destroys the trampoline buffer if not null. */ void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Returns the callable pointer for the trampoline with index `idx`. */ const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *buffer, uintptr_t idx); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /** * Returns the context added by `add_context_trampoline`, from within the callee function. */ void *wasmer_trampoline_get_context(void); +#endif /** * 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(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 */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index d372d8eb3..b437edd4b 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -1,3 +1,22 @@ + +#if !defined(WASMER_H_MACROS) +#define WASMER_H_MACROS + +#if defined(MSVC) +#if defined(_M_AMD64) +#define ARCH_X86_64 +#endif +#endif + +#if defined(GCC) || defined(__GNUC__) || defined(__clang__) +#if defined(__x86_64__) +#define ARCH_X86_64 +#endif +#endif + +#endif // WASMER_H_MACROS + + #ifndef WASMER_H #define WASMER_H @@ -8,10 +27,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 +139,10 @@ struct wasmer_import_t { wasmer_import_export_value value; }; +struct wasmer_import_object_iter_t { + +}; + struct wasmer_instance_t { }; @@ -142,17 +165,32 @@ struct wasmer_serialized_module_t { }; +#if (!defined(_WIN32) && defined(ARCH_X86_64)) struct wasmer_trampoline_buffer_builder_t { }; +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) struct wasmer_trampoline_callable_t { }; +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) struct wasmer_trampoline_buffer_t { }; +#endif + +/// 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" { @@ -359,9 +397,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(); @@ -560,36 +637,71 @@ uint32_t wasmer_table_length(wasmer_table_t *table); /// and `wasmer_last_error_message` to get an error message. wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Adds a callinfo trampoline to the builder. uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder, const wasmer_trampoline_callable_t *func, const void *ctx, uint32_t num_params); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Adds a context trampoline to the builder. uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *builder, const wasmer_trampoline_callable_t *func, const void *ctx); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Finalizes the trampoline builder into an executable buffer. wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *builder); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Creates a new trampoline builder. wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Destroys the trampoline buffer if not null. void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Returns the callable pointer for the trampoline with index `idx`. const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *buffer, uintptr_t idx); +#endif +#if (!defined(_WIN32) && defined(ARCH_X86_64)) /// Returns the context added by `add_context_trampoline`, from within the callee function. void *wasmer_trampoline_get_context(); +#endif /// 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 diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml new file mode 100644 index 000000000..57e9e19f9 --- /dev/null +++ b/lib/runtime-core-tests/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "wasmer-runtime-core-tests" +version = "0.11.0" +description = "Tests for the Wasmer runtime core crate" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +publish = false + +[dependencies] +wabt = "0.9.1" +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.11.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.11.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", features = ["test"], optional = true } + +[features] +default = ["backend-cranelift"] +backend-cranelift = ["wasmer-clif-backend"] +backend-singlepass = ["wasmer-singlepass-backend"] +backend-llvm = ["wasmer-llvm-backend"] diff --git a/lib/runtime-core-tests/src/lib.rs b/lib/runtime-core-tests/src/lib.rs new file mode 100644 index 000000000..96f7e6265 --- /dev/null +++ b/lib/runtime-core-tests/src/lib.rs @@ -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() +} diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs new file mode 100644 index 000000000..6b7223c67 --- /dev/null +++ b/lib/runtime-core-tests/tests/imports.rs @@ -0,0 +1,294 @@ +use wasmer_runtime_core::{ + compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func, + types::MemoryDescriptor, units::Pages, vm, Instance, +}; +use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; + +macro_rules! call_and_assert { + ($instance:ident, $function:ident, $expected_value:expr) => { + let $function: Func = $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::(), + expected_data.downcast_ref::(), + ) { + 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 + ) + ), + } + }; +} + +/// The shift that is set in the instance memory. The value is part of +/// the result returned by the imported functions if the memory is +/// read properly. +const SHIFT: i32 = 10; + +/// The shift that is captured in the environment of a closure. The +/// value is part of the result returned by the imported function if +/// the closure captures its environment properly. +#[allow(non_upper_case_globals)] +const shift: i32 = 100; + +fn imported_functions_forms(test: &dyn Fn(&Instance)) { + 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_closure" (func $callback_closure (type $type))) + (import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type))) + (import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type))) + (import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type))) + (import "env" "callback_closure_with_vmctx_and_env" (func $callback_closure_with_vmctx_and_env (type $type))) + (import "env" "callback_fn_trap" (func $callback_fn_trap (type $type))) + (import "env" "callback_closure_trap" (func $callback_closure_trap (type $type))) + (import "env" "callback_fn_trap_with_vmctx" (func $callback_fn_trap_with_vmctx (type $type))) + (import "env" "callback_closure_trap_with_vmctx" (func $callback_closure_trap_with_vmctx (type $type))) + (import "env" "callback_closure_trap_with_vmctx_and_env" (func $callback_closure_trap_with_vmctx_and_env (type $type))) + + (func (export "function_fn") (type $type) + get_local 0 + call $callback_fn) + + (func (export "function_closure") (type $type) + get_local 0 + call $callback_closure) + + (func (export "function_closure_with_env") (type $type) + get_local 0 + call $callback_closure_with_env) + + (func (export "function_fn_with_vmctx") (type $type) + get_local 0 + call $callback_fn_with_vmctx) + + (func (export "function_closure_with_vmctx") (type $type) + get_local 0 + call $callback_closure_with_vmctx) + + (func (export "function_closure_with_vmctx_and_env") (type $type) + get_local 0 + call $callback_closure_with_vmctx_and_env) + + (func (export "function_fn_trap") (type $type) + get_local 0 + call $callback_fn_trap) + + (func (export "function_closure_trap") (type $type) + get_local 0 + call $callback_closure_trap) + + (func (export "function_fn_trap_with_vmctx") (type $type) + get_local 0 + call $callback_fn_trap_with_vmctx) + + (func (export "function_closure_trap_with_vmctx") (type $type) + get_local 0 + call $callback_closure_trap_with_vmctx) + + (func (export "function_closure_trap_with_vmctx_and_env") (type $type) + get_local 0 + call $callback_closure_trap_with_vmctx_and_env)) +"#; + + 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(); + + memory.view()[0].set(SHIFT); + + let import_object = imports! { + "env" => { + "memory" => memory.clone(), + + // Regular function. + "callback_fn" => Func::new(callback_fn), + + // Closure without a captured environment. + "callback_closure" => Func::new(|n: i32| -> Result { + Ok(n + 1) + }), + + // Closure with a captured environment (a single variable + an instance of `Memory`). + "callback_closure_with_env" => Func::new(move |n: i32| -> Result { + let shift_ = shift + memory.view::()[0].get(); + + Ok(shift_ + n + 1) + }), + + // Regular function with an explicit `vmctx`. + "callback_fn_with_vmctx" => Func::new(callback_fn_with_vmctx), + + // Closure without a captured environment but with an explicit `vmctx`. + "callback_closure_with_vmctx" => Func::new(|vmctx: &mut vm::Ctx, n: i32| -> Result { + let memory = vmctx.memory(0); + let shift_: i32 = memory.view()[0].get(); + + Ok(shift_ + n + 1) + }), + + // Closure with a captured environment (a single variable) and with an explicit `vmctx`. + "callback_closure_with_vmctx_and_env" => Func::new(move |vmctx: &mut vm::Ctx, n: i32| -> Result { + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + + Ok(shift_ + n + 1) + }), + + // Trap a regular function. + "callback_fn_trap" => Func::new(callback_fn_trap), + + // Trap a closure without a captured environment. + "callback_closure_trap" => Func::new(|n: i32| -> Result { + Err(format!("bar {}", n + 1)) + }), + + // Trap a regular function with an explicit `vmctx`. + "callback_fn_trap_with_vmctx" => Func::new(callback_fn_trap_with_vmctx), + + // Trap a closure without a captured environment but with an explicit `vmctx`. + "callback_closure_trap_with_vmctx" => Func::new(|vmctx: &mut vm::Ctx, n: i32| -> Result { + let memory = vmctx.memory(0); + let shift_: i32 = memory.view()[0].get(); + + Err(format!("qux {}", shift_ + n + 1)) + }), + + // Trap a closure with a captured environment (a single variable) and with an explicit `vmctx`. + "callback_closure_trap_with_vmctx_and_env" => Func::new(move |vmctx: &mut vm::Ctx, n: i32| -> Result { + let memory = vmctx.memory(0); + let shift_ = shift + memory.view::()[0].get(); + + Err(format!("! {}", shift_ + n + 1)) + }), + }, + }; + let instance = module.instantiate(&import_object).unwrap(); + + test(&instance); +} + +fn callback_fn(n: i32) -> Result { + Ok(n + 1) +} + +fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { + let memory = vmctx.memory(0); + let shift_: i32 = memory.view()[0].get(); + + Ok(shift_ + n + 1) +} + +fn callback_fn_trap(n: i32) -> Result { + Err(format!("foo {}", n + 1)) +} + +fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { + let memory = vmctx.memory(0); + let shift_: i32 = memory.view()[0].get(); + + Err(format!("baz {}", shift_ + n + 1)) +} + +macro_rules! test { + ($test_name:ident, $function:ident, $expected_value:expr) => { + #[test] + fn $test_name() { + imported_functions_forms(&|instance| { + call_and_assert!(instance, $function, $expected_value); + }); + } + }; +} + +test!(test_fn, function_fn, Ok(2)); +test!(test_closure, function_closure, Ok(2)); +test!( + test_closure_with_env, + function_closure_with_env, + Ok(2 + shift + SHIFT) +); +test!(test_fn_with_vmctx, function_fn_with_vmctx, Ok(2 + SHIFT)); +test!( + test_closure_with_vmctx, + function_closure_with_vmctx, + Ok(2 + SHIFT) +); +test!( + test_closure_with_vmctx_and_env, + function_closure_with_vmctx_and_env, + Ok(2 + shift + SHIFT) +); +test!( + test_fn_trap, + function_fn_trap, + Err(RuntimeError::Error { + data: Box::new(format!("foo {}", 2)) + }) +); +test!( + test_closure_trap, + function_closure_trap, + Err(RuntimeError::Error { + data: Box::new(format!("bar {}", 2)) + }) +); +test!( + test_fn_trap_with_vmctx, + function_fn_trap_with_vmctx, + Err(RuntimeError::Error { + data: Box::new(format!("baz {}", 2 + SHIFT)) + }) +); +test!( + test_closure_trap_with_vmctx, + function_closure_trap_with_vmctx, + Err(RuntimeError::Error { + data: Box::new(format!("qux {}", 2 + SHIFT)) + }) +); +test!( + test_closure_trap_with_vmctx_and_env, + function_closure_trap_with_vmctx_and_env, + Err(RuntimeError::Error { + data: Box::new(format!("! {}", 2 + shift + SHIFT)) + }) +); diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index f4f80d985..1e28133c9 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-runtime-core" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "runtime"] +categories = ["wasm"] edition = "2018" [dependencies] @@ -42,9 +44,6 @@ version = "0.8" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["memoryapi"] } -[dev-dependencies] -field-offset = "0.1" - [build-dependencies] blake2b_simd = "0.5" rustc_version = "0.2" diff --git a/lib/runtime-core/build.rs b/lib/runtime-core/build.rs index 81884f0e1..3a549e45f 100644 --- a/lib/runtime-core/build.rs +++ b/lib/runtime-core/build.rs @@ -29,14 +29,20 @@ fn main() { println!("cargo:rustc-cfg=nightly"); } - if cfg!(all(target_os = "linux", target_arch = "x86_64")) { - cc::Build::new() - .file("image-loading-linux-x86-64.s") - .compile("image-loading"); - } else if cfg!(all(target_os = "macos", target_arch = "x86_64")) { - cc::Build::new() - .file("image-loading-macos-x86-64.s") - .compile("image-loading"); - } else { + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + + match (target_os.as_str(), target_arch.as_str()) { + ("linux", "x86_64") => { + cc::Build::new() + .file("image-loading-linux-x86-64.s") + .compile("image-loading"); + } + ("macos", "x86_64") => { + cc::Build::new() + .file("image-loading-macos-x86-64.s") + .compile("image-loading"); + } + _ => {} } } diff --git a/lib/runtime-core/image-loading-linux-x86-64.s b/lib/runtime-core/image-loading-linux-x86-64.s index 859f010f8..c9d6edff2 100644 --- a/lib/runtime-core/image-loading-linux-x86-64.s +++ b/lib/runtime-core/image-loading-linux-x86-64.s @@ -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 diff --git a/lib/runtime-core/image-loading-macos-x86-64.s b/lib/runtime-core/image-loading-macos-x86-64.s index ef6f94510..5e19f7b4a 100644 --- a/lib/runtime-core/image-loading-macos-x86-64.s +++ b/lib/runtime-core/image-loading-macos-x86-64.s @@ -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 diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index f1086de0d..febfdda0e 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -28,6 +28,7 @@ pub enum Backend { Cranelift, Singlepass, LLVM, + Auto, } impl Backend { @@ -40,6 +41,7 @@ impl Backend { "singlepass", #[cfg(feature = "backend-llvm")] "llvm", + "auto", ] } @@ -50,6 +52,7 @@ impl Backend { Backend::Cranelift => "cranelift", Backend::Singlepass => "singlepass", Backend::LLVM => "llvm", + Backend::Auto => "auto", } } } @@ -67,11 +70,88 @@ impl std::str::FromStr for Backend { "singlepass" => Ok(Backend::Singlepass), "cranelift" => Ok(Backend::Cranelift), "llvm" => Ok(Backend::LLVM), + "auto" => Ok(Backend::Auto), _ => Err(format!("The backend {} doesn't exist", s)), } } } +#[derive(Copy, Clone, Debug)] +pub enum Architecture { + X64, + Aarch64, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum InlineBreakpointType { + Trace, + Middleware, + Unknown, +} + +#[derive(Clone, Debug)] +pub struct InlineBreakpoint { + pub size: usize, + pub ty: InlineBreakpointType, +} + +pub fn get_inline_breakpoint_size(arch: Architecture, backend: Backend) -> Option { + match (arch, backend) { + (Architecture::X64, Backend::Singlepass) => Some(7), + (Architecture::Aarch64, Backend::Singlepass) => Some(12), + _ => None, + } +} + +pub fn read_inline_breakpoint( + arch: Architecture, + backend: Backend, + code: &[u8], +) -> Option { + match arch { + Architecture::X64 => match backend { + Backend::Singlepass => { + if code.len() < 7 { + None + } else if &code[..6] == &[0x0f, 0x0b, 0x0f, 0xb9, 0xcd, 0xff] { + // ud2 ud (int 0xff) code + Some(InlineBreakpoint { + size: 7, + ty: match code[6] { + 0 => InlineBreakpointType::Trace, + 1 => InlineBreakpointType::Middleware, + _ => InlineBreakpointType::Unknown, + }, + }) + } else { + None + } + } + _ => None, + }, + Architecture::Aarch64 => match backend { + Backend::Singlepass => { + if code.len() < 12 { + None + } else if &code[..8] == &[0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff] { + Some(InlineBreakpoint { + size: 12, + ty: match code[8] { + 0 => InlineBreakpointType::Trace, + 1 => InlineBreakpointType::Middleware, + _ => InlineBreakpointType::Unknown, + }, + }) + } else { + None + } + } + _ => None, + }, + } +} + #[cfg(test)] mod backend_test { use super::*; @@ -129,6 +209,11 @@ pub struct CompilerConfig { pub enforce_stack_check: bool, pub track_state: bool, pub features: Features, + + // target info used by LLVM + pub triple: Option, + pub cpu_name: Option, + pub cpu_features: Option, } pub trait Compiler { diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index ff21e9195..1b50abaf6 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -15,8 +15,13 @@ use crate::{ }, vm, }; -use std::{fmt::Debug, slice}; +use std::{ + fmt::Debug, + ptr::{self, NonNull}, + slice, +}; +/// Size of the array for internal instance usage pub const INTERNALS_SIZE: usize = 256; pub(crate) struct Internals(pub(crate) [u64; INTERNALS_SIZE]); @@ -72,7 +77,7 @@ impl LocalBacking { } }; let mut tables = Self::generate_tables(module); - let mut globals = Self::generate_globals(module, imports); + let mut globals = Self::generate_globals(module, imports)?; // Ensure all initializers are valid before running finalizers Self::validate_memories(module, imports)?; @@ -382,9 +387,9 @@ impl LocalBacking { vmctx, ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, vmctx } = + let vm::ImportedFunc { func, func_ctx } = imports.vm_functions[imported_func_index]; - (func, vmctx) + (func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr()) } }; @@ -415,9 +420,9 @@ impl LocalBacking { vmctx, ), LocalOrImport::Import(imported_func_index) => { - let vm::ImportedFunc { func, vmctx } = + let vm::ImportedFunc { func, func_ctx } = imports.vm_functions[imported_func_index]; - (func, vmctx) + (func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr()) } }; @@ -438,13 +443,22 @@ impl LocalBacking { fn generate_globals( module: &ModuleInner, imports: &ImportBacking, - ) -> BoxedMap { + ) -> LinkResult> { let mut globals = Map::with_capacity(module.info.globals.len()); for (_, global_init) in module.info.globals.iter() { let value = match &global_init.init { Initializer::Const(value) => value.clone(), Initializer::GetGlobal(import_global_index) => { + if imports.globals.len() <= import_global_index.index() { + return Err(vec![LinkError::Generic { + message: format!( + "Trying to read the `{:?}` global that is not properly initialized.", + import_global_index.index() + ), + }]); + } + imports.globals[*import_global_index].get() } }; @@ -458,7 +472,7 @@ impl LocalBacking { globals.push(global); } - globals.into_boxed_map() + Ok(globals.into_boxed_map()) } fn finalize_globals( @@ -472,6 +486,8 @@ impl LocalBacking { } } +/// The `ImportBacking` stores references to the imported resources of an Instance. This includes +/// imported memories, tables, globals and functions. #[derive(Debug)] pub struct ImportBacking { pub(crate) memories: BoxedMap, @@ -488,6 +504,7 @@ pub struct ImportBacking { unsafe impl Send for ImportBacking {} impl ImportBacking { + /// Creates a new `ImportBacking` from the given `ModuleInner`, `ImportObject`, and `Ctx`. pub fn new( module: &ModuleInner, imports: &ImportObject, @@ -536,11 +553,21 @@ impl ImportBacking { } } + /// Gets a `ImportedFunc` from the given `ImportedFuncIndex`. pub fn imported_func(&self, index: ImportedFuncIndex) -> vm::ImportedFunc { self.vm_functions[index].clone() } } +impl Drop for ImportBacking { + fn drop(&mut self) { + // Properly drop the `vm::FuncCtx` in `vm::ImportedFunc`. + for (_imported_func_index, imported_func) in (*self.vm_functions).iter_mut() { + let _: Box = unsafe { Box::from_raw(imported_func.func_ctx.as_ptr()) }; + } + } +} + fn import_functions( module: &ModuleInner, imports: &ImportObject, @@ -564,6 +591,7 @@ fn import_functions( let import = imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name)); + match import { Some(Export::Function { func, @@ -573,10 +601,28 @@ fn import_functions( if *expected_sig == *signature { functions.push(vm::ImportedFunc { func: func.inner(), - vmctx: match ctx { - Context::External(ctx) => ctx, - Context::Internal => vmctx, - }, + func_ctx: NonNull::new(Box::into_raw(Box::new(vm::FuncCtx { + // ^^^^^^^^ `vm::FuncCtx` is purposely leaked. + // It is dropped by the specific `Drop` + // implementation of `ImportBacking`. + vmctx: NonNull::new(match ctx { + Context::External(vmctx) => vmctx, + Context::ExternalWithEnv(vmctx_, _) => { + if vmctx_.is_null() { + vmctx + } else { + vmctx_ + } + } + Context::Internal => vmctx, + }) + .expect("`vmctx` must not be null."), + func_env: match ctx { + Context::ExternalWithEnv(_, func_env) => func_env, + _ => None, + }, + }))) + .unwrap(), }); } else { link_errors.push(LinkError::IncorrectImportSignature { @@ -605,8 +651,8 @@ fn import_functions( None => { if imports.allow_missing_functions { functions.push(vm::ImportedFunc { - func: ::std::ptr::null(), - vmctx: ::std::ptr::null_mut(), + func: ptr::null(), + func_ctx: unsafe { NonNull::new_unchecked(ptr::null_mut()) }, // TODO: Non-sense… }); } else { link_errors.push(LinkError::ImportNotFound { diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 89bbaf796..e924cd9f5 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -1,3 +1,7 @@ +//! The cache module provides the common data structures used by compiler backends to allow +//! serializing compiled wasm code to a binary format. The binary format can be persisted, +//! and loaded to allow skipping compilation and fast startup. + use crate::{ backend::Backend, module::{Module, ModuleInfo}, @@ -6,20 +10,31 @@ use crate::{ use blake2b_simd::blake2bp; use std::{fmt, io, mem, slice}; +/// Indicates the invalid type of invalid cache file #[derive(Debug)] pub enum InvalidFileType { + /// Given cache header slice does not match the expected size of an `ArtifactHeader` InvalidSize, + /// Given cache header slice does not contain the expected magic bytes InvalidMagic, } +/// Kinds of caching errors #[derive(Debug)] pub enum Error { + /// An IO error while reading/writing a cache binary. IoError(io::Error), + /// An error deserializing bytes into a cache data structure. DeserializeError(String), + /// An error serializing bytes from a cache data structure. SerializeError(String), + /// An undefined caching error with a message. Unknown(String), + /// An invalid cache binary given. InvalidFile(InvalidFileType), + /// The cached binary has been invalidated. InvalidatedCache, + /// The current backend does not support caching. UnsupportedBackend(Backend), } @@ -164,6 +179,8 @@ struct ArtifactInner { compiled_code: Memory, } +/// Artifact are produced by caching, are serialized/deserialized to binaries, and contain +/// module info, backend metadata, and compiled code. pub struct Artifact { inner: ArtifactInner, } @@ -183,6 +200,7 @@ impl Artifact { } } + /// Deserializes an `Artifact` from the given byte slice. pub fn deserialize(bytes: &[u8]) -> Result { let (_, body_slice) = ArtifactHeader::read_from_slice(bytes)?; @@ -192,6 +210,7 @@ impl Artifact { Ok(Artifact { inner }) } + /// A reference to the `Artifact`'s stored `ModuleInfo` pub fn info(&self) -> &ModuleInfo { &self.inner.info } @@ -205,6 +224,7 @@ impl Artifact { ) } + /// Serializes the `Artifact` into a vector of bytes pub fn serialize(&self) -> Result, Error> { let cache_header = ArtifactHeader { magic: WASMER_CACHE_MAGIC, @@ -230,7 +250,9 @@ impl Artifact { /// /// The `wasmer-runtime` supplies a naive `FileSystemCache` api. pub trait Cache { + /// Error type to return when load error occurs type LoadError: fmt::Debug; + /// Error type to return when store error occurs type StoreError: fmt::Debug; /// loads a module using the default `Backend` @@ -238,6 +260,7 @@ pub trait Cache { /// loads a cached module using a specific `Backend` fn load_with_backend(&self, key: WasmHash, backend: Backend) -> Result; + /// Store a module into the cache with the given key fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>; } diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 8d98bb0c0..a3ed432e8 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -1,3 +1,7 @@ +//! The codegen module provides common functions and data structures used by multiple backends +//! during the code generation process. +#[cfg(unix)] +use crate::fault::FaultInfo; use crate::{ backend::RunnableModule, backend::{Backend, CacheGen, Compiler, CompilerConfig, Features, Token}, @@ -17,22 +21,35 @@ use std::sync::{Arc, RwLock}; use wasmparser::{self, WasmDecoder}; use wasmparser::{Operator, Type as WpType}; +/// A type that defines a function pointer, which is called when breakpoints occur. pub type BreakpointHandler = Box Result<(), Box> + Send + Sync + 'static>; + +/// Maps instruction pointers to their breakpoint handlers. pub type BreakpointMap = Arc>; +/// An event generated during parsing of a wasm binary #[derive(Debug)] pub enum Event<'a, 'b> { + /// An internal event created by the parser used to provide hooks during code generation. Internal(InternalEvent), + /// An event generated by parsing a wasm operator Wasm(&'b Operator<'a>), + /// An event generated by parsing a wasm operator that contains an owned `Operator` WasmOwned(Operator<'a>), } +/// Kinds of `InternalEvent`s created during parsing. pub enum InternalEvent { + /// A function parse is about to begin. FunctionBegin(u32), + /// A function parsing has just completed. FunctionEnd, + /// A breakpoint emitted during parsing. Breakpoint(BreakpointHandler), + /// Indicates setting an internal field. SetInternal(u32), + /// Indicates getting an internal field. GetInternal(u32), } @@ -48,14 +65,32 @@ impl fmt::Debug for InternalEvent { } } +/// Information for a breakpoint +#[cfg(unix)] pub struct BreakpointInfo<'a> { - pub fault: Option<&'a dyn Any>, + /// Fault. + pub fault: Option<&'a FaultInfo>, } +/// Information for a breakpoint +#[cfg(not(unix))] +pub struct BreakpointInfo { + /// Fault placeholder. + pub fault: Option<()>, +} + +/// A trait that represents the functions needed to be implemented to generate code for a module. pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { /// Creates a new module code generator. fn new() -> Self; + /// Creates a new module code generator for specified target. + fn new_with_target( + triple: Option, + cpu_name: Option, + cpu_features: Option, + ) -> Self; + /// Returns the backend id associated with this MCG. fn backend_id() -> Backend; @@ -65,7 +100,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, } /// Adds an import function. fn feed_import_function(&mut self) -> Result<(), E>; - + /// Sets the signatures. fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; /// Sets function signatures. fn feed_function_signatures(&mut self, assoc: Map) -> Result<(), E>; @@ -80,6 +115,8 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } +/// A streaming compiler which is designed to generated code for a module based on a stream +/// of wasm parser events. pub struct StreamingCompiler< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -94,6 +131,7 @@ pub struct StreamingCompiler< _phantom_e: PhantomData, } +/// A simple generator for a `StreamingCompiler`. pub struct SimpleStreamingCompilerGen< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -113,6 +151,7 @@ impl< E: Debug, > SimpleStreamingCompilerGen { + /// Create a new `StreamingCompiler`. pub fn new() -> StreamingCompiler MiddlewareChain> { StreamingCompiler::new(|| MiddlewareChain::new()) } @@ -126,6 +165,7 @@ impl< CGEN: Fn() -> MiddlewareChain, > StreamingCompiler { + /// Create a new `StreamingCompiler` with the given `MiddlewareChain`. pub fn new(chain_gen: CGEN) -> Self { Self { middleware_chain_generator: chain_gen, @@ -137,6 +177,7 @@ impl< } } +/// Create a new `ValidatingParserConfig` with the given features. pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingParserConfig { wasmparser::ValidatingParserConfig { operator_config: wasmparser::OperatorValidatorConfig { @@ -185,7 +226,14 @@ impl< validate_with_features(wasm, &compiler_config.features)?; } - let mut mcg = MCG::new(); + let mut mcg = match MCG::backend_id() { + Backend::LLVM => MCG::new_with_target( + compiler_config.triple.clone(), + compiler_config.cpu_name.clone(), + compiler_config.cpu_features.clone(), + ), + _ => MCG::new(), + }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module( wasm, @@ -218,34 +266,41 @@ impl< fn requires_pre_validation(backend: Backend) -> bool { match backend { Backend::Cranelift => true, - Backend::LLVM => false, + Backend::LLVM => true, Backend::Singlepass => false, + Backend::Auto => false, } } +/// A sink for parse events. pub struct EventSink<'a, 'b> { buffer: SmallVec<[Event<'a, 'b>; 2]>, } impl<'a, 'b> EventSink<'a, 'b> { + /// Push a new `Event` to this sink. pub fn push(&mut self, ev: Event<'a, 'b>) { self.buffer.push(ev); } } +/// A container for a chain of middlewares. pub struct MiddlewareChain { chain: Vec>, } impl MiddlewareChain { + /// Create a new empty `MiddlewareChain`. pub fn new() -> MiddlewareChain { MiddlewareChain { chain: vec![] } } + /// Push a new `FunctionMiddleware` to this `MiddlewareChain`. pub fn push(&mut self, m: M) { self.chain.push(Box::new(m)); } + /// Run this chain with the provided function code generator, event and module info. pub(crate) fn run>( &mut self, fcg: Option<&mut FCG>, @@ -273,8 +328,11 @@ impl MiddlewareChain { } } +/// A trait that represents the signature required to implement middleware for a function. pub trait FunctionMiddleware { + /// The error type for this middleware's functions. type Error: Debug; + /// Processes the given event, module info and sink. fn feed_event<'a, 'b: 'a>( &mut self, op: Event<'a, 'b>, diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 9aac82fab..f2fcf7ebd 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,13 +1,28 @@ +//! The error module contains the data structures and helper functions used to implement errors that +//! are produced and returned from the wasmer runtime core. use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; +/// Aliases the standard `Result` type as `Result` within this module. pub type Result = std::result::Result; +/// Result of an attempt to compile the provided WebAssembly module into a `Module`. +/// Aliases the standard `Result` with `CompileError` as the default error type. pub type CompileResult = std::result::Result; +/// Result of an attempt to link the provided WebAssembly instance. +/// Aliases the standard `Result` with `Vec` as the default error type. pub type LinkResult = std::result::Result>; +/// Result of an attempt to run the provided WebAssembly instance. +/// Aliases the standard `Result` with `RuntimeError` as the default error type. pub type RuntimeResult = std::result::Result; +/// Result of an attempt to call the provided WebAssembly instance. +/// Aliases the standard `Result` with `CallError` as the default error type. pub type CallResult = std::result::Result; +/// Result of an attempt to resolve a WebAssembly function by name. +/// Aliases the standard `Result` with `ResolveError` as the default error type. pub type ResolveResult = std::result::Result; +/// Result of an attempt to parse bytes into a WebAssembly module. +/// Aliases the standard `Result` with `ParseError` as the default error type. pub type ParseResult = std::result::Result; /// This is returned when the chosen compiler is unable to @@ -17,8 +32,16 @@ pub type ParseResult = std::result::Result; /// Comparing two `CompileError`s always evaluates to false. #[derive(Debug, Clone)] pub enum CompileError { - ValidationError { msg: String }, - InternalError { msg: String }, + /// A validation error containing an error message. + ValidationError { + /// An error message. + msg: String, + }, + /// A internal error containing an error message. + InternalError { + /// An error message. + msg: String, + }, } impl PartialEq for CompileError { @@ -46,41 +69,71 @@ impl std::error::Error for CompileError {} /// Comparing two `LinkError`s always evaluates to false. #[derive(Debug, Clone)] pub enum LinkError { + /// The type of the provided import does not match the expected type. IncorrectImportType { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: String, + /// Found. found: String, }, + /// The signature of the provided import does not match the expected signature. IncorrectImportSignature { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: FuncSig, + /// Found. found: FuncSig, }, + /// An expected import was not provided. ImportNotFound { + /// Namespace. namespace: String, + /// Name. name: String, }, + /// The memory descriptor provided does not match the expected descriptor. IncorrectMemoryDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: MemoryDescriptor, + /// Found. found: MemoryDescriptor, }, + /// The table descriptor provided does not match the expected descriptor. IncorrectTableDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: TableDescriptor, + /// Found. found: TableDescriptor, }, + /// The global descriptor provided does not match the expected descriptor. IncorrectGlobalDescriptor { + /// Namespace. namespace: String, + /// Name. name: String, + /// Expected. expected: GlobalDescriptor, + /// Found. found: GlobalDescriptor, }, + /// A generic error with a message. Generic { + /// Error message. message: String, }, } @@ -126,8 +179,16 @@ impl std::error::Error for LinkError {} /// /// Comparing two `RuntimeError`s always evaluates to false. pub enum RuntimeError { - Trap { msg: Box }, - Error { data: Box }, + /// Trap. + Trap { + /// Trap message. + msg: Box, + }, + /// Error. + Error { + /// Error data. + data: Box, + }, } impl PartialEq for RuntimeError { @@ -169,9 +230,23 @@ impl std::error::Error for RuntimeError {} /// Comparing two `ResolveError`s always evaluates to false. #[derive(Debug, Clone)] pub enum ResolveError { - Signature { expected: FuncSig, found: Vec }, - ExportNotFound { name: String }, - ExportWrongType { name: String }, + /// Found signature did not match expected signature. + Signature { + /// Expected `FuncSig`. + expected: FuncSig, + /// Found type. + found: Vec, + }, + /// Export not found. + ExportNotFound { + /// Name. + name: String, + }, + /// Export found with the wrong type. + ExportWrongType { + /// Name. + name: String, + }, } impl PartialEq for ResolveError { @@ -213,7 +288,9 @@ impl std::error::Error for ResolveError {} /// /// Comparing two `CallError`s always evaluates to false. pub enum CallError { + /// An error occured resolving the functions name or types. Resolve(ResolveError), + /// A runtime error occurred during the function call. Runtime(RuntimeError), } @@ -247,8 +324,11 @@ impl std::error::Error for CallError {} /// like a `Memory` or a `Table`. #[derive(Debug, Clone)] pub enum CreationError { + /// Unable to create memory error. UnableToCreateMemory, + /// Unable to create table error. UnableToCreateTable, + /// Invalid descriptor error with message. InvalidDescriptor(String), } @@ -281,11 +361,17 @@ impl std::error::Error for CreationError {} /// Comparing two `Error`s always evaluates to false. #[derive(Debug)] pub enum Error { + /// Compile error. CompileError(CompileError), + /// Link errors. LinkError(Vec), + /// Runtime error. RuntimeError(RuntimeError), + /// Resolve error. ResolveError(ResolveError), + /// Call error. CallError(CallError), + /// Creation error. CreationError(CreationError), } @@ -368,13 +454,20 @@ impl std::fmt::Display for Error { impl std::error::Error for Error {} +/// An error occurred while growing a memory or table. #[derive(Debug)] pub enum GrowError { + /// Error growing memory. MemoryGrowError, + /// Error growing table. TableGrowError, + /// Max pages were exceeded. ExceededMaxPages(PageError), + /// Max pages for memory were exceeded. ExceededMaxPagesForMemory(usize, usize), + /// Error protecting memory. CouldNotProtectMemory(MemoryProtectionError), + /// Error creating memory. CouldNotCreateMemory(MemoryCreationError), } @@ -393,9 +486,11 @@ impl std::fmt::Display for GrowError { impl std::error::Error for GrowError {} +/// A kind of page error. #[derive(Debug)] pub enum PageError { // left, right, added + /// Max pages were exceeded error. ExceededMaxPages(usize, usize, usize), } @@ -414,9 +509,12 @@ impl Into for PageError { } } +/// Error occured while creating memory. #[derive(Debug)] pub enum MemoryCreationError { + /// Allocation of virtual memory failed error. VirtualMemoryAllocationFailed(usize, String), + /// Error creating memory from file. CouldNotCreateMemoryFromFile(std::io::Error), } @@ -446,8 +544,10 @@ impl From for MemoryCreationError { } } +/// Error protecting memory. #[derive(Debug)] pub enum MemoryProtectionError { + /// Protection failed error. ProtectionFailed(usize, usize, String), } @@ -470,8 +570,10 @@ impl Into for MemoryProtectionError { } } +/// Parse Error. #[derive(Debug)] pub enum ParseError { + /// Error reading binary. BinaryReadError, } diff --git a/lib/runtime-core/src/export.rs b/lib/runtime-core/src/export.rs index 7960d76e6..8729d9797 100644 --- a/lib/runtime-core/src/export.rs +++ b/lib/runtime-core/src/export.rs @@ -1,31 +1,51 @@ +//! The export module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm module's exports including memories, tables, globals, and +//! functions. use crate::{ global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex, module::ModuleInner, table::Table, types::FuncSig, vm, }; use indexmap::map::Iter as IndexMapIter; -use std::sync::Arc; +use std::{ptr::NonNull, sync::Arc}; +/// A kind of Context. #[derive(Debug, Copy, Clone)] pub enum Context { + /// External context include a mutable pointer to `Ctx`. External(*mut vm::Ctx), + + /// External context with an environment include a mutable pointer + /// to `Ctx` and an optional non-null pointer to `FuncEnv`. + ExternalWithEnv(*mut vm::Ctx, Option>), + + /// Internal context. Internal, } // Manually implemented because context contains a raw pointer to Ctx unsafe impl Send for Context {} +/// Kind of WebAssembly export. #[derive(Debug, Clone)] pub enum Export { + /// Function export. Function { + /// A pointer to a function. func: FuncPointer, + /// A kind of context. ctx: Context, + /// The signature of the function. signature: Arc, }, + /// Memory export. Memory(Memory), + /// Table export. Table(Table), + /// Global export. Global(Global), } +/// Const pointer to a `Func`. #[derive(Debug, Clone)] pub struct FuncPointer(*const vm::Func); @@ -45,6 +65,7 @@ impl FuncPointer { } } +/// An iterator to an instance's exports. pub struct ExportIter<'a> { inner: &'a InstanceInner, iter: IndexMapIter<'a, String, ExportIndex>, diff --git a/lib/runtime-core/src/fault.rs b/lib/runtime-core/src/fault.rs index a1a1f58c2..bc64c15fa 100644 --- a/lib/runtime-core/src/fault.rs +++ b/lib/runtime-core/src/fault.rs @@ -1,17 +1,35 @@ +//! The fault module contains the implementation for handling breakpoints, traps, and signals +//! for wasm code. + pub mod raw { + //! The raw module contains required externed function interfaces for the fault module. use std::ffi::c_void; + #[cfg(target_arch = "x86_64")] extern "C" { + /// Load registers and return on the stack [stack_end..stack_begin]. pub fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64; + /// Internal routine for switching into a backend without information about where registers are preserved. pub fn register_preservation_trampoline(); // NOT safe to call directly + } + + /// Internal routine for switching into a backend without information about where registers are preserved. + #[cfg(not(target_arch = "x86_64"))] + pub extern "C" fn register_preservation_trampoline() { + unimplemented!("register_preservation_trampoline"); + } + + extern "C" { + /// libc setjmp pub fn setjmp(env: *mut c_void) -> i32; + /// libc longjmp pub fn longjmp(env: *mut c_void, val: i32) -> !; } } use crate::codegen::{BreakpointInfo, BreakpointMap}; -use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR, XMM}; -use crate::state::CodeVersion; +use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR}; +use crate::state::{CodeVersion, ExecutionStateImage}; use crate::vm; use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; use nix::sys::signal::{ @@ -25,13 +43,19 @@ use std::process; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; +#[cfg(target_arch = "x86_64")] pub(crate) unsafe fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64 { raw::run_on_alternative_stack(stack_end, stack_begin) } +#[cfg(not(target_arch = "x86_64"))] +pub(crate) unsafe fn run_on_alternative_stack(_stack_end: *mut u64, _stack_begin: *mut u64) -> u64 { + unimplemented!("run_on_alternative_stack"); +} + const TRAP_STACK_SIZE: usize = 1048576; // 1MB -const SETJMP_BUFFER_LEN: usize = 27; +const SETJMP_BUFFER_LEN: usize = 128; type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN]; struct UnwindInfo { @@ -40,13 +64,19 @@ struct UnwindInfo { payload: Option>, // out } +/// A store for boundary register preservation. #[repr(packed)] #[derive(Default, Copy, Clone)] pub struct BoundaryRegisterPreservation { + /// R15. pub r15: u64, + /// R14. pub r14: u64, + /// R13. pub r13: u64, + /// R12. pub r12: u64, + /// RBX. pub rbx: u64, } @@ -58,6 +88,7 @@ thread_local! { static BOUNDARY_REGISTER_PRESERVATION: UnsafeCell = UnsafeCell::new(BoundaryRegisterPreservation::default()); } +/// Gets a mutable pointer to the `BoundaryRegisterPreservation`. #[no_mangle] pub unsafe extern "C" fn get_boundary_register_preservation() -> *mut BoundaryRegisterPreservation { BOUNDARY_REGISTER_PRESERVATION.with(|x| x.get()) @@ -89,10 +120,12 @@ lazy_static! { } static INTERRUPT_SIGNAL_DELIVERED: AtomicBool = AtomicBool::new(false); +/// Returns a boolean indicating if SIGINT triggered the fault. pub fn was_sigint_triggered_fault() -> bool { WAS_SIGINT_TRIGGERED.with(|x| x.get()) } +/// Runs a callback function with the given `Ctx`. pub unsafe fn with_ctx R>(ctx: *mut vm::Ctx, cb: F) -> R { let addr = CURRENT_CTX.with(|x| x.get()); let old = *addr; @@ -102,18 +135,22 @@ pub unsafe fn with_ctx R>(ctx: *mut vm::Ctx, cb: F) -> R { ret } +/// Pushes a new `CodeVersion` to the current code versions. pub fn push_code_version(version: CodeVersion) { CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().push(version)); } +/// Pops a `CodeVersion` from the current code versions. pub fn pop_code_version() -> Option { CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().pop()) } +/// Gets the wasm interrupt signal mem. pub unsafe fn get_wasm_interrupt_signal_mem() -> *mut u8 { INTERRUPT_SIGNAL_MEM.0 } +/// Sets the wasm interrupt on the given `Ctx`. pub unsafe fn set_wasm_interrupt_on_ctx(ctx: *mut vm::Ctx) { if mprotect( (&*ctx).internal.interrupt_signal_mem as _, @@ -125,6 +162,7 @@ pub unsafe fn set_wasm_interrupt_on_ctx(ctx: *mut vm::Ctx) { } } +/// Sets a wasm interrupt. pub unsafe fn set_wasm_interrupt() { let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0; if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_NONE) < 0 { @@ -132,6 +170,7 @@ pub unsafe fn set_wasm_interrupt() { } } +/// Clears the wasm interrupt. pub unsafe fn clear_wasm_interrupt() { let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0; if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_READ | PROT_WRITE) < 0 { @@ -139,6 +178,7 @@ pub unsafe fn clear_wasm_interrupt() { } } +/// Catches an unsafe unwind with the given functions and breakpoints. pub unsafe fn catch_unsafe_unwind R>( f: F, breakpoints: Option, @@ -164,6 +204,7 @@ pub unsafe fn catch_unsafe_unwind R>( } } +/// Begins an unsafe unwind. pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { let unwind = UNWIND.with(|x| x.get()); let inner = (*unwind) @@ -181,6 +222,14 @@ unsafe fn with_breakpoint_map) -> R>(f: F) - f(inner.breakpoints.as_ref()) } +#[cfg(not(target_arch = "x86_64"))] +/// Allocates and runs with the given stack size and closure. +pub fn allocate_and_run R>(_size: usize, f: F) -> R { + f() +} + +#[cfg(target_arch = "x86_64")] +/// Allocates and runs with the given stack size and closure. pub fn allocate_and_run R>(size: usize, f: F) -> R { struct Context R, R> { f: Option, @@ -207,7 +256,7 @@ pub fn allocate_and_run R>(size: usize, f: F) -> R { // NOTE: Keep this consistent with `image-loading-*.s`. stack[end_offset - 4 - 10] = &mut ctx as *mut Context 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); @@ -223,12 +272,75 @@ extern "C" fn signal_trap_handler( siginfo: *mut siginfo_t, ucontext: *mut c_void, ) { + use crate::backend::{ + get_inline_breakpoint_size, read_inline_breakpoint, Architecture, InlineBreakpointType, + }; + + #[cfg(target_arch = "x86_64")] + static ARCH: Architecture = Architecture::X64; + + #[cfg(target_arch = "aarch64")] + static ARCH: Architecture = Architecture::Aarch64; + + let mut should_unwind = false; + let mut unwind_result: Box = Box::new(()); + unsafe { let fault = get_fault_info(siginfo as _, ucontext); + let early_return = allocate_and_run(TRAP_STACK_SIZE, || { + CURRENT_CODE_VERSIONS.with(|versions| { + let versions = versions.borrow(); + for v in versions.iter() { + let magic_size = if let Some(x) = get_inline_breakpoint_size(ARCH, v.backend) { + x + } else { + continue; + }; + let ip = fault.ip.get(); + let end = v.base + v.msm.total_size; + if ip >= v.base && ip < end && ip + magic_size <= end { + if let Some(ib) = read_inline_breakpoint( + ARCH, + v.backend, + std::slice::from_raw_parts(ip as *const u8, magic_size), + ) { + match ib.ty { + InlineBreakpointType::Trace => {} + InlineBreakpointType::Middleware => { + let out: Option>> = + with_breakpoint_map(|bkpt_map| { + bkpt_map.and_then(|x| x.get(&ip)).map(|x| { + x(BreakpointInfo { + fault: Some(&fault), + }) + }) + }); + if let Some(Ok(())) = out { + } else if let Some(Err(e)) = out { + should_unwind = true; + unwind_result = e; + } + } + _ => println!("Unknown breakpoint type: {:?}", ib.ty), + } - let mut unwind_result: Box = Box::new(()); + fault.ip.set(ip + magic_size); + return true; + } + break; + } + } + false + }) + }); + if should_unwind { + begin_unsafe_unwind(unwind_result); + } + if early_return { + return; + } - let should_unwind = allocate_and_run(TRAP_STACK_SIZE, || { + should_unwind = allocate_and_run(TRAP_STACK_SIZE, || { let mut is_suspend_signal = false; WAS_SIGINT_TRIGGERED.with(|x| x.set(false)); @@ -237,7 +349,7 @@ extern "C" fn signal_trap_handler( Ok(SIGTRAP) => { // breakpoint let out: Option>> = with_breakpoint_map(|bkpt_map| { - bkpt_map.and_then(|x| x.get(&(fault.ip as usize))).map(|x| { + bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| { x(BreakpointInfo { fault: Some(&fault), }) @@ -267,17 +379,9 @@ extern "C" fn signal_trap_handler( } let ctx: &mut vm::Ctx = &mut **CURRENT_CTX.with(|x| x.get()); - let rsp = fault.known_registers[X64Register::GPR(GPR::RSP).to_index().0].unwrap(); - - let es_image = CURRENT_CODE_VERSIONS.with(|versions| { - let versions = versions.borrow(); - read_stack( - || versions.iter(), - rsp as usize as *const u64, - fault.known_registers, - Some(fault.ip as usize as u64), - ) - }); + let es_image = fault + .read_stack(None) + .expect("fault.read_stack() failed. Broken invariants?"); if is_suspend_signal { let image = build_instance_image(ctx, es_image); @@ -290,7 +394,7 @@ extern "C" fn signal_trap_handler( ); es_image.print_backtrace_if_needed(); } - // Just let the error propagate otherrwise + // Just let the error propagate otherwise } true @@ -316,6 +420,7 @@ extern "C" fn sigint_handler( } } +/// Ensure the signal handler is installed. pub fn ensure_sighandler() { INSTALL_SIGHANDLER.call_once(|| unsafe { install_sighandler(); @@ -344,14 +449,109 @@ unsafe fn install_sighandler() { sigaction(SIGINT, &sa_interrupt).unwrap(); } +#[derive(Debug, Clone)] +/// Info about the fault pub struct FaultInfo { + /// Faulting address. pub faulting_addr: *const c_void, - pub ip: *const c_void, - pub known_registers: [Option; 24], + /// Instruction pointer. + pub ip: &'static Cell, + /// Values of known registers. + pub known_registers: [Option; 32], +} + +impl FaultInfo { + /// Parses the stack and builds an execution state image. + pub unsafe fn read_stack(&self, max_depth: Option) -> Option { + let rsp = match self.known_registers[X64Register::GPR(GPR::RSP).to_index().0] { + Some(x) => x, + None => return None, + }; + + Some(CURRENT_CODE_VERSIONS.with(|versions| { + let versions = versions.borrow(); + read_stack( + || versions.iter(), + rsp as usize as *const u64, + self.known_registers, + Some(self.ip.get() as u64), + max_depth, + ) + })) + } +} + +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +/// Get fault info from siginfo and ucontext. +pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo { + #[allow(dead_code)] + #[allow(non_camel_case_types)] + #[repr(packed)] + struct sigcontext { + fault_address: u64, + regs: [u64; 31], + sp: u64, + pc: u64, + pstate: u64, + reserved: [u8; 4096], + } + + #[allow(dead_code)] + #[allow(non_camel_case_types)] + #[repr(packed)] + struct ucontext { + unknown: [u8; 176], + uc_mcontext: sigcontext, + } + + #[allow(dead_code)] + #[allow(non_camel_case_types)] + #[repr(C)] + struct siginfo_t { + si_signo: i32, + si_errno: i32, + si_code: i32, + si_addr: u64, + // ... + } + + let siginfo = siginfo as *const siginfo_t; + let si_addr = (*siginfo).si_addr; + + let ucontext = ucontext as *mut ucontext; + let gregs = &(*ucontext).uc_mcontext.regs; + + let mut known_registers: [Option; 32] = [None; 32]; + + known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[15] as _); + known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[14] as _); + known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[13] as _); + known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs[12] as _); + known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs[11] as _); + known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs[10] as _); + known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs[9] as _); + known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs[8] as _); + known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs[6] as _); + known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs[7] as _); + known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs[2] as _); + known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs[1] as _); + known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs[3] as _); + known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs[0] as _); + + known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[5] as _); + known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[28] as _); + + FaultInfo { + faulting_addr: si_addr as usize as _, + ip: std::mem::transmute::<&mut u64, &'static Cell>(&mut (*ucontext).uc_mcontext.pc), + known_registers, + } } #[cfg(all(target_os = "linux", target_arch = "x86_64"))] -pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo { +/// Get fault info from siginfo and ucontext. +pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo { + use crate::state::x64::XMM; use libc::{ _libc_xmmreg, ucontext_t, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8, REG_R9, REG_RAX, REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP, @@ -374,11 +574,10 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> let siginfo = siginfo as *const siginfo_t; let si_addr = (*siginfo).si_addr; - let ucontext = ucontext as *const ucontext_t; - let gregs = &(*ucontext).uc_mcontext.gregs; - let fpregs = &*(*ucontext).uc_mcontext.fpregs; + let ucontext = ucontext as *mut ucontext_t; + let gregs = &mut (*ucontext).uc_mcontext.gregs; - let mut known_registers: [Option; 24] = [None; 24]; + let mut known_registers: [Option; 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 _); @@ -397,24 +596,43 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[REG_RBP as usize] as _); known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[REG_RSP as usize] as _); - known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(read_xmm(&fpregs._xmm[0])); - known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(read_xmm(&fpregs._xmm[1])); - known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(read_xmm(&fpregs._xmm[2])); - known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(read_xmm(&fpregs._xmm[3])); - known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(read_xmm(&fpregs._xmm[4])); - 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])); + if !(*ucontext).uc_mcontext.fpregs.is_null() { + let fpregs = &*(*ucontext).uc_mcontext.fpregs; + known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(read_xmm(&fpregs._xmm[0])); + known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(read_xmm(&fpregs._xmm[1])); + known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(read_xmm(&fpregs._xmm[2])); + known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(read_xmm(&fpregs._xmm[3])); + known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(read_xmm(&fpregs._xmm[4])); + 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 _, - ip: gregs[REG_RIP as usize] as _, + ip: std::mem::transmute::<&mut i64, &'static Cell>(&mut gregs[REG_RIP as usize]), known_registers, } } +/// Get fault info from siginfo and ucontext. #[cfg(all(target_os = "macos", target_arch = "x86_64"))] -pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo { +pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo { + use crate::state::x64::XMM; #[allow(dead_code)] #[repr(C)] struct ucontext_t { @@ -423,7 +641,7 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> uc_stack: libc::stack_t, uc_link: *const ucontext_t, uc_mcsize: u64, - uc_mcontext: *const mcontext_t, + uc_mcontext: *mut mcontext_t, } #[repr(C)] struct exception_state { @@ -458,8 +676,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)] @@ -472,11 +699,11 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> let siginfo = siginfo as *const siginfo_t; let si_addr = (*siginfo).si_addr; - let ucontext = ucontext as *const ucontext_t; - let ss = &(*(*ucontext).uc_mcontext).ss; + let ucontext = ucontext as *mut ucontext_t; + let ss = &mut (*(*ucontext).uc_mcontext).ss; let fs = &(*(*ucontext).uc_mcontext).fs; - let mut known_registers: [Option; 24] = [None; 24]; + let mut known_registers: [Option; 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,10 +731,18 @@ 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, - ip: ss.rip as _, + ip: std::mem::transmute::<&mut u64, &'static Cell>(&mut ss.rip), known_registers, } } diff --git a/lib/runtime-core/src/global.rs b/lib/runtime-core/src/global.rs index b59d1599b..be9eaf6ae 100644 --- a/lib/runtime-core/src/global.rs +++ b/lib/runtime-core/src/global.rs @@ -1,3 +1,5 @@ +//! The global module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm globals. use crate::{ export::Export, import::IsExport, @@ -9,6 +11,7 @@ use std::{ sync::{Arc, Mutex}, }; +/// Container with a descriptor and a reference to a global value. pub struct Global { desc: GlobalDescriptor, storage: Arc>, diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 9d86205b1..284ff1725 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -1,3 +1,6 @@ +//! The import module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm module's imports including memories, tables, globals, and +//! functions. use crate::export::Export; use std::collections::VecDeque; use std::collections::{hash_map::Entry, HashMap}; @@ -7,13 +10,20 @@ use std::{ sync::{Arc, Mutex}, }; +/// This trait represents objects that act as a namespace for imports. For example, an `Instance` +/// or `ImportObject` could be considered namespaces that could provide imports to an instance. pub trait LikeNamespace { + /// Gets an export by name. fn get_export(&self, name: &str) -> Option; + /// Gets all exports in the namespace. fn get_exports(&self) -> Vec<(String, Export)>; + /// Maybe insert an `Export` by name into the namespace. fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>; } +/// A trait that represents `Export` values. pub trait IsExport { + /// Gets self as `Export`. fn to_export(&self) -> Export; } @@ -48,6 +58,8 @@ pub struct ImportObject { map: Arc>>>, pub(crate) state_creator: Option (*mut c_void, fn(*mut c_void)) + Send + Sync + 'static>>, + /// Allow missing functions to be generated and instantiation to continue when required + /// functions are not provided. pub allow_missing_functions: bool, } @@ -61,6 +73,7 @@ impl ImportObject { } } + /// Create a new `ImportObject` which generates data from the provided state creator. pub fn new_with_data(state_creator: F) -> Self where F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static + Send + Sync, @@ -145,6 +158,7 @@ impl ImportObject { .and_then(|ns| f(ns)) } + /// Create a clone ref of this namespace. pub fn clone_ref(&self) -> Self { Self { map: Arc::clone(&self.map), @@ -166,6 +180,7 @@ impl ImportObject { } } +/// Iterator for an `ImportObject`'s exports. pub struct ImportObjectIterator { elements: VecDeque<(String, String, Export)>, } @@ -204,17 +219,20 @@ impl Extend<(String, String, Export)> for ImportObject { } } +/// The top-level container for the two-level wasm imports pub struct Namespace { map: HashMap>, } impl Namespace { + /// Create a new empty `Namespace`. pub fn new() -> Self { Self { map: HashMap::new(), } } + /// Insert a new `Export` into the namespace with the given name. pub fn insert(&mut self, name: S, export: E) -> Option> where S: Into, @@ -223,6 +241,7 @@ impl Namespace { self.map.insert(name.into(), Box::new(export)) } + /// Returns true if the `Namespace` contains the given name. pub fn contains_key(&mut self, key: S) -> bool where S: Into, diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 85e9f8f76..eb47c98e1 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,3 +1,5 @@ +//! The instance module contains the implementation data structures and helper functions used to +//! manipulate and access wasm instances. use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, @@ -48,6 +50,7 @@ impl Drop for InstanceInner { /// /// [`ImportObject`]: struct.ImportObject.html pub struct Instance { + /// Reference to the module used to instantiate this instance. pub module: Arc, inner: Pin>, #[allow(dead_code)] @@ -110,9 +113,13 @@ impl Instance { let ctx_ptr = match start_index.local_or_import(&instance.module.info) { LocalOrImport::Local(_) => instance.inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - instance.inner.import_backing.vm_functions[imported_func_index].vmctx + LocalOrImport::Import(imported_func_index) => unsafe { + instance.inner.import_backing.vm_functions[imported_func_index] + .func_ctx + .as_ref() } + .vmctx + .as_ptr(), }; let sig_index = *instance @@ -129,7 +136,7 @@ impl Instance { .expect("wasm trampoline"); let start_func: Func<(), (), Wasm> = - unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) }; + unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, None, ctx_ptr) }; start_func.call()?; } @@ -137,6 +144,7 @@ impl Instance { Ok(instance) } + /// Load an `Instance` using the given loader. pub fn load(&self, loader: T) -> ::std::result::Result { loader.load(&*self.module.runnable_module, &self.module.info, unsafe { &*self.inner.vmctx @@ -195,9 +203,13 @@ impl Instance { let ctx = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - self.inner.import_backing.vm_functions[imported_func_index].vmctx + LocalOrImport::Import(imported_func_index) => unsafe { + self.inner.import_backing.vm_functions[imported_func_index] + .func_ctx + .as_ref() } + .vmctx + .as_ptr(), }; let func_wasm_inner = self @@ -206,20 +218,26 @@ impl Instance { .get_trampoline(&self.module.info, sig_index) .unwrap(); - let func_ptr = match func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(local_func_index) => self - .module - .runnable_module - .get_func(&self.module.info, local_func_index) - .unwrap(), - LocalOrImport::Import(import_func_index) => NonNull::new( - self.inner.import_backing.vm_functions[import_func_index].func as *mut _, - ) - .unwrap(), + let (func_ptr, func_env) = match func_index.local_or_import(&self.module.info) { + LocalOrImport::Local(local_func_index) => ( + self.module + .runnable_module + .get_func(&self.module.info, local_func_index) + .unwrap(), + None, + ), + LocalOrImport::Import(import_func_index) => { + let imported_func = &self.inner.import_backing.vm_functions[import_func_index]; + + ( + NonNull::new(imported_func.func as *mut _).unwrap(), + unsafe { imported_func.func_ctx.as_ref() }.func_env, + ) + } }; let typed_func: Func = - unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) }; + unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, func_env, ctx) }; Ok(typed_func) } else { @@ -230,6 +248,7 @@ impl Instance { } } + /// Resolve a function by name. pub fn resolve_func(&self, name: &str) -> ResolveResult { let export_index = self.module @@ -381,10 +400,12 @@ impl Instance { Module::new(Arc::clone(&self.module)) } + /// Get the value of an internal field pub fn get_internal(&self, field: &InternalField) -> u64 { self.inner.backing.internals.0[field.index()] } + /// Set the value of an internal field. pub fn set_internal(&mut self, field: &InternalField, value: u64) { self.inner.backing.internals.0[field.index()] = value; } @@ -405,6 +426,7 @@ impl InstanceInner { ctx: match ctx { Context::Internal => Context::External(self.vmctx), ctx @ Context::External(_) => ctx, + ctx @ Context::ExternalWithEnv(_, _) => ctx, }, signature, } @@ -447,15 +469,16 @@ impl InstanceInner { ), LocalOrImport::Import(imported_func_index) => { let imported_func = &self.import_backing.vm_functions[imported_func_index]; + let func_ctx = unsafe { imported_func.func_ctx.as_ref() }; + ( imported_func.func as *const _, - Context::External(imported_func.vmctx), + Context::ExternalWithEnv(func_ctx.vmctx.as_ptr(), func_ctx.func_env), ) } }; let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]); - // let signature = &module.info.signatures[sig_index]; (unsafe { FuncPointer::new(func_ptr) }, ctx, signature) } @@ -574,9 +597,13 @@ fn call_func_with_index( let ctx_ptr = match func_index.local_or_import(info) { LocalOrImport::Local(_) => local_ctx, - LocalOrImport::Import(imported_func_index) => { - import_backing.vm_functions[imported_func_index].vmctx + LocalOrImport::Import(imported_func_index) => unsafe { + import_backing.vm_functions[imported_func_index] + .func_ctx + .as_ref() } + .vmctx + .as_ptr(), }; let wasm = runnable @@ -774,10 +801,12 @@ impl<'a> DynFunc<'a> { Ok(results) } + /// Gets the signature of this `Dynfunc`. pub fn signature(&self) -> &FuncSig { &*self.signature } + /// Gets a const pointer to the function represent by this `DynFunc`. pub fn raw(&self) -> *const vm::Func { match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index accea7641..5b46677a8 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,5 +1,20 @@ +//! Wasmer Runtime Core Library +//! +//! The runtime core library provides common data structures which are shared by compiler backends +//! to implement a Web Assembly runtime. +//! +//! The runtime core also provides an API for users who use wasmer as an embedded wasm runtime which +//! allows operations like compiling, instantiating, providing imports, access exports, memories, +//! and tables for example. +//! +//! The runtime core library is recommended to be used by only power users who wish to customize the +//! wasmer runtime. Most wasmer users should prefer the API which is re-exported by the wasmer +//! runtime library which provides common defaults and a friendly API. +//! + #![deny( dead_code, + missing_docs, nonstandard_style, unused_imports, unused_mut, @@ -11,10 +26,6 @@ #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] -#[cfg(test)] -#[macro_use] -extern crate field_offset; - #[macro_use] extern crate serde_derive; @@ -53,7 +64,7 @@ pub mod vm; pub mod vmcalls; #[cfg(all(unix, target_arch = "x86_64"))] pub use trampoline_x64 as trampoline; -#[cfg(all(unix, target_arch = "x86_64"))] +#[cfg(unix)] pub mod fault; pub mod state; #[cfg(feature = "managed")] @@ -77,6 +88,9 @@ pub use wasmparser; use self::cache::{Artifact, Error as CacheError}; pub mod prelude { + //! The prelude module is a helper module used to bring commonly used runtime core imports into + //! scope. + pub use crate::import::{ImportObject, Namespace}; pub use crate::types::{ FuncIndex, GlobalIndex, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, @@ -161,6 +175,7 @@ pub fn validate_and_report_errors_with_features( } } +/// Creates a new module from the given cache `Artifact` for the specified compiler backend pub unsafe fn load_cache_with( cache: Artifact, compiler: &dyn backend::Compiler, diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index 4f843a54d..84d2bbbfb 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -1,3 +1,4 @@ +//! The loader module functions are used to load an instance. use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx}; #[cfg(unix)] use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE}; @@ -6,10 +7,14 @@ use std::{ ops::{Deref, DerefMut}, }; +/// The loader trait represents the functions used to load an instance. pub trait Loader { + /// The type of `Instance` for the loader. type Instance: Instance; + /// The error type returned by the loader. type Error: Debug; + /// Loads the given module and context into an instance. fn load( &self, rm: &dyn RunnableModule, @@ -18,18 +23,23 @@ pub trait Loader { ) -> Result; } +/// This trait represents an instance used by the loader. pub trait Instance { + /// The error type returned by this instance. type Error: Debug; + /// Call a function by id with the given args. fn call(&mut self, id: usize, args: &[Value]) -> Result; + /// Read memory at the given offset and length. fn read_memory(&mut self, _offset: u32, _len: u32) -> Result, Self::Error> { - unimplemented!() + unimplemented!("Instance::read_memory") } - + /// Write memory at the given offset and length. fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> { - unimplemented!() + unimplemented!("Instance::write_memory") } } +/// A local implementation for `Loader`. pub struct LocalLoader; impl Loader for LocalLoader { @@ -54,6 +64,7 @@ impl Loader for LocalLoader { } } +/// A local instance. pub struct LocalInstance { code: CodeMemory, offsets: Vec, @@ -111,6 +122,7 @@ impl Instance for LocalInstance { } } +/// A pointer to code in memory. pub struct CodeMemory { ptr: *mut u8, size: usize, @@ -121,21 +133,25 @@ unsafe impl Sync for CodeMemory {} #[cfg(not(unix))] impl CodeMemory { + /// Creates a new code memory with the given size. pub fn new(_size: usize) -> CodeMemory { - unimplemented!(); + unimplemented!("CodeMemory::new"); } + /// Makes this code memory executable. pub fn make_executable(&self) { - unimplemented!(); + unimplemented!("CodeMemory::make_executable"); } + /// Makes this code memory writable. pub fn make_writable(&self) { - unimplemented!(); + unimplemented!("CodeMemory::make_writable"); } } #[cfg(unix)] impl CodeMemory { + /// Creates a new code memory with the given size. pub fn new(size: usize) -> CodeMemory { if size == 0 { return CodeMemory { @@ -167,12 +183,14 @@ impl CodeMemory { } } + /// Makes this code memory executable. pub fn make_executable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 { panic!("cannot set code memory to executable"); } } + /// Makes this code memory writable. pub fn make_writable(&self) { if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 { panic!("cannot set code memory to writable"); diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index 6dbb93ca0..71e8c2e2f 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -1,3 +1,5 @@ +/// Prints a log message with args, similar to println, when the debug feature is enabled. +/// If the debug feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(feature = "debug")] macro_rules! debug { @@ -11,6 +13,8 @@ macro_rules! debug { }, line!(), $($arg)*)); } +/// Prints a log message with args, similar to println, when the debug feature is enabled. +/// If the debug feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(not(feature = "debug"))] macro_rules! debug { @@ -18,6 +22,8 @@ macro_rules! debug { ($fmt:expr, $($arg:tt)*) => {}; } +/// Prints a log message with args, similar to println, when the trace feature is enabled. +/// If the trace feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(feature = "trace")] macro_rules! trace { @@ -29,6 +35,8 @@ macro_rules! trace { } } +/// Prints a log message with args, similar to println, when the trace feature is enabled. +/// If the trace feature is disabled, arguments are not evaluated or printed. #[macro_export] #[cfg(not(feature = "trace"))] macro_rules! trace { @@ -36,9 +44,48 @@ macro_rules! trace { ($fmt:expr, $($arg:tt)*) => {}; } +/// Helper macro to create a new `Func` object using the provided function pointer. +/// +/// # Usage +/// +/// Function pointers or closures are supported. Closures can capture +/// their environment (with `move). The first parameter is likely to +/// be of kind `vm::Ctx`, though it can be optional. +/// +/// ``` +/// # use wasmer_runtime_core::{imports, func}; +/// # use wasmer_runtime_core::vm; +/// +/// // A function that has access to `vm::Ctx`. +/// fn func_with_vmctx(_: &mut vm::Ctx, n: i32) -> i32 { +/// n +/// } +/// +/// // A function that cannot access `vm::Ctx`. +/// fn func(n: i32) -> i32 { +/// n +/// } +/// +/// let i = 7; +/// +/// let import_object = imports! { +/// "env" => { +/// "foo" => func!(func_with_vmctx), +/// "bar" => func!(func), +/// // A closure with a captured environment, and an access to `vm::Ctx`. +/// "baz" => func!(move |_: &mut vm::Ctx, n: i32| -> i32 { +/// n + i +/// }), +/// // A closure without a captured environment, and no access to `vm::Ctx`. +/// "qux" => func!(|n: i32| -> i32 { +/// n +/// }), +/// }, +/// }; +/// ``` #[macro_export] macro_rules! func { - ($func:path) => {{ + ($func:expr) => {{ $crate::Func::new($func) }}; } @@ -47,12 +94,12 @@ macro_rules! func { /// /// [`ImportObject`]: struct.ImportObject.html /// -/// # Note: +/// # Note /// The `import` macro currently only supports /// importing functions. /// /// -/// # Usage: +/// # Usage /// ``` /// # use wasmer_runtime_core::{imports, func}; /// # use wasmer_runtime_core::vm::Ctx; diff --git a/lib/runtime-core/src/memory/dynamic.rs b/lib/runtime-core/src/memory/dynamic.rs index 332a37acb..885c47dbe 100644 --- a/lib/runtime-core/src/memory/dynamic.rs +++ b/lib/runtime-core/src/memory/dynamic.rs @@ -62,10 +62,12 @@ impl DynamicMemory { Ok(storage) } + /// The size of this memory in `Pages`. pub fn size(&self) -> Pages { self.current } + /// Try to grow self by the given number of delta pages. pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { return Ok(self.current); @@ -104,10 +106,12 @@ impl DynamicMemory { Ok(old_pages) } + /// Get this memory represented as a slice of bytes. pub fn as_slice(&self) -> &[u8] { unsafe { &self.memory.as_slice()[0..self.current.bytes().0] } } + /// Get this memory represented as a mutable slice of bytes pub fn as_slice_mut(&mut self) -> &mut [u8] { unsafe { &mut self.memory.as_slice_mut()[0..self.current.bytes().0] } } diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 60fb87c4d..75a02e227 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -1,3 +1,5 @@ +//! The memory module contains the implementation data structures and helper functions used to +//! manipulate and access wasm memory. use crate::{ error::{CreationError, GrowError}, export::Export, @@ -170,10 +172,14 @@ impl fmt::Debug for Memory { } } +/// A kind a memory. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MemoryType { + /// A dynamic memory. Dynamic, + /// A static memory. Static, + /// A shared static memory. SharedStatic, } @@ -200,6 +206,7 @@ enum UnsharedMemoryStorage { Static(Box), } +/// A reference to an unshared memory. pub struct UnsharedMemory { internal: Arc, } @@ -214,6 +221,7 @@ struct UnsharedMemoryInternal { unsafe impl Sync for UnsharedMemoryInternal {} impl UnsharedMemory { + /// Create a new `UnsharedMemory` from the given memory descriptor. pub fn new(desc: MemoryDescriptor) -> Result { let mut local = vm::LocalMemory { base: std::ptr::null_mut(), @@ -243,6 +251,7 @@ impl UnsharedMemory { }) } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&self, delta: Pages) -> Result { let mut storage = self.internal.storage.lock().unwrap(); @@ -260,6 +269,7 @@ impl UnsharedMemory { pages } + /// Size of this memory in pages. pub fn size(&self) -> Pages { let storage = self.internal.storage.lock().unwrap(); @@ -282,10 +292,12 @@ impl Clone for UnsharedMemory { } } +/// A reference to a shared memory. pub struct SharedMemory { internal: Arc, } +/// Data structure for a shared internal memory. pub struct SharedMemoryInternal { memory: StdMutex>, local: Cell, @@ -315,6 +327,7 @@ impl SharedMemory { }) } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&self, delta: Pages) -> Result { let _guard = self.internal.lock.lock(); let mut local = self.internal.local.get(); @@ -323,12 +336,14 @@ impl SharedMemory { pages } + /// Size of this memory in pages. pub fn size(&self) -> Pages { let _guard = self.internal.lock.lock(); let memory = self.internal.memory.lock().unwrap(); memory.size() } + /// Gets a mutable pointer to the `LocalMemory`. // This function is scary, because the mutex is not locked here pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory { self.internal.local.as_ptr() diff --git a/lib/runtime-core/src/memory/ptr.rs b/lib/runtime-core/src/memory/ptr.rs index abed45bfb..5e31627c2 100644 --- a/lib/runtime-core/src/memory/ptr.rs +++ b/lib/runtime-core/src/memory/ptr.rs @@ -12,9 +12,12 @@ use crate::{ }; use std::{cell::Cell, fmt, marker::PhantomData, mem}; +/// Array. pub struct Array; +/// Item. pub struct Item; +/// A pointer to a Wasm item. #[repr(transparent)] pub struct WasmPtr { offset: u32, @@ -22,6 +25,7 @@ pub struct WasmPtr { } impl WasmPtr { + /// Create a new `WasmPtr` at the given offset. #[inline] pub fn new(offset: u32) -> Self { Self { @@ -30,6 +34,7 @@ impl WasmPtr { } } + /// Get the offset for this `WasmPtr`. #[inline] pub fn offset(self) -> u32 { self.offset @@ -44,6 +49,7 @@ fn align_pointer(ptr: usize, align: usize) -> usize { } impl WasmPtr { + /// Dereference this `WasmPtr`. #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -58,6 +64,7 @@ impl WasmPtr { } } + /// Mutable dereference this `WasmPtr`. #[inline] pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell> { if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { @@ -72,6 +79,7 @@ impl WasmPtr { } impl WasmPtr { + /// Dereference this `WasmPtr`. #[inline] pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell]> { // gets the size of the item in the array with padding added such that @@ -94,6 +102,7 @@ impl WasmPtr { } } + /// Mutable dereference this `WasmPtr`. #[inline] pub unsafe fn deref_mut<'a>( self, @@ -119,6 +128,7 @@ impl WasmPtr { Some(cell_ptrs) } + /// Get a UTF-8 string representation of this `WasmPtr` with the given length. pub fn get_utf8_string<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> { if self.offset as usize + str_len as usize > memory.size().bytes().0 { return None; diff --git a/lib/runtime-core/src/memory/static_.rs b/lib/runtime-core/src/memory/static_.rs index 957e53eaf..c6355b2a2 100644 --- a/lib/runtime-core/src/memory/static_.rs +++ b/lib/runtime-core/src/memory/static_.rs @@ -56,10 +56,12 @@ impl StaticMemory { Ok(storage) } + /// The size of this memory in `Pages`. pub fn size(&self) -> Pages { self.current } + /// Try to grow this memory by the given number of delta pages. pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { return Ok(self.current); @@ -94,10 +96,12 @@ impl StaticMemory { Ok(old_pages) } + /// Get this memory represented as a slice of bytes. pub fn as_slice(&self) -> &[u8] { unsafe { &self.memory.as_slice()[0..self.current.bytes().0] } } + /// Get this memory represented as a mutable slice of bytes. pub fn as_slice_mut(&mut self) -> &mut [u8] { unsafe { &mut self.memory.as_slice_mut()[0..self.current.bytes().0] } } diff --git a/lib/runtime-core/src/memory/view.rs b/lib/runtime-core/src/memory/view.rs index 4dbaa5bd5..762e1a614 100644 --- a/lib/runtime-core/src/memory/view.rs +++ b/lib/runtime-core/src/memory/view.rs @@ -39,12 +39,16 @@ impl Atomic for f64 { type Output = AtomicU64; } +/// A trait that represants an atomic type. pub trait Atomicity {} +/// Atomically. pub struct Atomically; impl Atomicity for Atomically {} +/// Non-atomically. pub struct NonAtomically; impl Atomicity for NonAtomically {} +/// A view into a memory. pub struct MemoryView<'a, T: 'a, A = NonAtomically> { ptr: *mut T, length: usize, @@ -65,6 +69,7 @@ where } impl<'a, T: Atomic> MemoryView<'a, T> { + /// Get atomic access to a memory view. pub fn atomically(&self) -> MemoryView<'a, T::Output, Atomically> { MemoryView { ptr: self.ptr as *mut T::Output, diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 6cc02343f..eb87b85d3 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,3 +1,5 @@ +//! The module module contains the implementation data structures and helper functions used to +//! manipulate and access wasm modules. use crate::{ backend::{Backend, RunnableModule}, cache::{Artifact, Error as CacheError}, @@ -27,40 +29,59 @@ pub struct ModuleInner { pub info: ModuleInfo, } +/// Container for module data including memories, globals, tables, imports, and exports. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ModuleInfo { - // This are strictly local and the typsystem ensures that. + /// Map of memory index to memory descriptors. + // This are strictly local and the typesystem ensures that. pub memories: Map, + /// Map of global index to global descriptors. pub globals: Map, + /// Map of table index to table descriptors. pub tables: Map, + /// Map of imported function index to import name. // These are strictly imported and the typesystem ensures that. pub imported_functions: Map, + /// Map of imported memory index to import name and memory descriptor. pub imported_memories: Map, + /// Map of imported table index to import name and table descriptor. pub imported_tables: Map, + /// Map of imported global index to import name and global descriptor. pub imported_globals: Map, + /// Map of string to export index. pub exports: IndexMap, + /// Vector of data initializers. pub data_initializers: Vec, + /// Vector of table initializers. pub elem_initializers: Vec, + /// Index of optional start function. pub start_func: Option, + /// Map function index to signature index. pub func_assoc: Map, + /// Map signature index to function signature. pub signatures: Map, + /// Backend. pub backend: Backend, + /// Table of namespace indexes. pub namespace_table: StringTable, + /// Table of name indexes. pub name_table: StringTable, - /// Symbol information from emscripten + /// Symbol information from emscripten. pub em_symbol_map: Option>, + /// Custom sections. pub custom_sections: HashMap>, } impl ModuleInfo { + /// Creates custom section info from the given wasm file. pub fn import_custom_sections(&mut self, wasm: &[u8]) -> crate::error::ParseResult<()> { let mut parser = wasmparser::ModuleReader::new(wasm)?; while !parser.eof() { @@ -120,6 +141,7 @@ impl Module { Instance::new(Arc::clone(&self.inner), import_object) } + /// Create a cache artifact from this module. pub fn cache(&self) -> Result { let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?; Ok(Artifact::from_parts( @@ -129,6 +151,7 @@ impl Module { )) } + /// Get the module data for this module. pub fn info(&self) -> &ModuleInfo { &self.inner.info } @@ -151,11 +174,16 @@ pub struct ImportName { pub name_index: NameIndex, } +/// Kinds of export indexes. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ExportIndex { + /// Function export index. Func(FuncIndex), + /// Memory export index. Memory(MemoryIndex), + /// Global export index. Global(GlobalIndex), + /// Table export index. Table(TableIndex), } @@ -182,6 +210,7 @@ pub struct TableInitializer { pub elements: Vec, } +/// String table builder. pub struct StringTableBuilder { map: IndexMap, buffer: String, @@ -189,6 +218,7 @@ pub struct StringTableBuilder { } impl StringTableBuilder { + /// Creates a new `StringTableBuilder`. pub fn new() -> Self { Self { map: IndexMap::new(), @@ -197,6 +227,7 @@ impl StringTableBuilder { } } + /// Register a new string into table. pub fn register(&mut self, s: S) -> K where S: Into + AsRef, @@ -219,6 +250,7 @@ impl StringTableBuilder { } } + /// Finish building the `StringTable`. pub fn finish(self) -> StringTable { let table = self .map @@ -233,6 +265,7 @@ impl StringTableBuilder { } } +/// A map of index to string. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct StringTable { table: Map, @@ -240,6 +273,7 @@ pub struct StringTable { } impl StringTable { + /// Creates a `StringTable`. pub fn new() -> Self { Self { table: Map::new(), @@ -247,6 +281,7 @@ impl StringTable { } } + /// Gets a reference to a string at the given index. pub fn get(&self, index: K) -> &str { let (offset, length) = self.table[index]; let offset = offset as usize; @@ -256,6 +291,7 @@ impl StringTable { } } +/// Namespace index. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NamespaceIndex(u32); @@ -271,6 +307,7 @@ impl TypedIndex for NamespaceIndex { } } +/// Name index. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NameIndex(u32); diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index e3e2c162e..ab6c79cbc 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -1,3 +1,6 @@ +//! The parse module contains common data structures and functions using to parse wasm files into +//! runtime data structures. + use crate::codegen::*; use crate::{ backend::{Backend, CompilerConfig, RunnableModule}, @@ -22,9 +25,12 @@ use wasmparser::{ WasmDecoder, }; +/// Kind of load error. #[derive(Debug)] pub enum LoadError { + /// Parse error. Parse(BinaryReaderError), + /// Code generation error. Codegen(String), } @@ -42,6 +48,8 @@ impl From for LoadError { } } +/// Read wasm binary into module data using the given backend, module code generator, middlewares, +/// and compiler configuration. pub fn read_module< MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, @@ -394,6 +402,7 @@ pub fn read_module< Ok(info) } +/// Convert given `WpType` to `Type`. pub fn wp_type_to_type(ty: WpType) -> Result { match ty { WpType::I32 => Ok(Type::I32), @@ -410,6 +419,7 @@ pub fn wp_type_to_type(ty: WpType) -> Result { } } +/// Convert given `Type` to `WpType`. pub fn type_to_wp_type(ty: Type) -> WpType { match ty { Type::I32 => WpType::I32, diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 7b8dcd2b1..a5a0a0036 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -1,119 +1,195 @@ +//! The state module is used to track state of a running web assembly instances so that +//! state could read or updated at runtime. Use cases include generating stack traces, switching +//! generated code from one tier to another, or serializing state of a running instace. + +use crate::backend::Backend; use std::collections::BTreeMap; use std::ops::Bound::{Included, Unbounded}; -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +/// An index to a register +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct RegisterIndex(pub usize); -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +/// A kind of wasm or constant value +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum WasmAbstractValue { + /// A wasm runtime value Runtime, + /// A wasm constant value Const(u64), } -#[derive(Clone, Debug)] +/// A container for the state of a running wasm instance. +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct MachineState { + /// Stack values. pub stack_values: Vec, + /// Register values. pub register_values: Vec, - + /// Previous frame. pub prev_frame: BTreeMap, - + /// Wasm stack. pub wasm_stack: Vec, + /// Private depth of the wasm stack. pub wasm_stack_private_depth: usize, - + /// Wasm instruction offset. pub wasm_inst_offset: usize, } -#[derive(Clone, Debug, Default)] +/// A diff of two `MachineState`s. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct MachineStateDiff { + /// Last. pub last: Option, + /// Stack push. pub stack_push: Vec, + /// Stack pop. pub stack_pop: usize, + + /// Register diff. pub reg_diff: Vec<(RegisterIndex, MachineValue)>, + /// Previous frame diff. pub prev_frame_diff: BTreeMap>, // None for removal + /// Wasm stack push. pub wasm_stack_push: Vec, + /// Wasm stack pop. pub wasm_stack_pop: usize, + /// Private depth of the wasm stack. pub wasm_stack_private_depth: usize, // absolute value; not a diff. - + /// Wasm instruction offset. pub wasm_inst_offset: usize, // absolute value; not a diff. } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +/// A kind of machine value. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum MachineValue { + /// Undefined. Undefined, + /// Vmctx. Vmctx, + /// Vmctx Deref. VmctxDeref(Vec), + /// Preserve Register. PreserveRegister(RegisterIndex), + /// Copy Stack BP Relative. CopyStackBPRelative(i32), // relative to Base Pointer, in byte offset - ExplicitShadow, // indicates that all values above this are above the shadow region + /// Explicit Shadow. + ExplicitShadow, // indicates that all values above this are above the shadow region + /// Wasm Stack. WasmStack(usize), + /// Wasm Local. WasmLocal(usize), + /// Two Halves. TwoHalves(Box<(MachineValue, MachineValue)>), // 32-bit values. TODO: optimize: add another type for inner "half" value to avoid boxing? } -#[derive(Clone, Debug)] +/// A map of function states. +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct FunctionStateMap { + /// Initial. pub initial: MachineState, + /// Local Function Id. pub local_function_id: usize, + /// Locals. pub locals: Vec, + /// Shadow size. pub shadow_size: usize, // for single-pass backend, 32 bytes on x86-64 + /// Diffs. pub diffs: Vec, + /// Wasm Function Header target offset. pub wasm_function_header_target_offset: Option, + /// Wasm offset to target offset pub wasm_offset_to_target_offset: BTreeMap, + /// Loop offsets. pub loop_offsets: BTreeMap, /* suspend_offset -> info */ + /// Call offsets. pub call_offsets: BTreeMap, /* suspend_offset -> info */ + /// Trappable offsets. pub trappable_offsets: BTreeMap, /* suspend_offset -> info */ } -#[derive(Clone, Copy, Debug)] +/// A kind of suspend offset. +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum SuspendOffset { + /// A loop. Loop(usize), + /// A call. Call(usize), + /// A trappable. Trappable(usize), } -#[derive(Clone, Debug)] +/// Info for an offset. +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct OffsetInfo { + /// End offset. pub end_offset: usize, // excluded bound + /// Diff Id. pub diff_id: usize, + /// Activate offset. pub activate_offset: usize, } -#[derive(Clone, Debug)] +/// A map of module state. +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct ModuleStateMap { + /// Local functions. pub local_functions: BTreeMap, + /// Total size. pub total_size: usize, } +/// State dump of a wasm function. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WasmFunctionStateDump { + /// Local function id. pub local_function_id: usize, + /// Wasm instruction offset. pub wasm_inst_offset: usize, + /// Stack. pub stack: Vec>, + /// Locals. pub locals: Vec>, } +/// An image of the execution state. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ExecutionStateImage { + /// Frames. pub frames: Vec, } +/// Represents an image of an `Instance` including its memory, globals, and execution state. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InstanceImage { + /// Memory for this `InstanceImage` pub memory: Option>, + /// Stored globals for this `InstanceImage` pub globals: Vec, + /// `ExecutionStateImage` for this `InstanceImage` pub execution_state: ExecutionStateImage, } +/// A `CodeVersion` is a container for a unit of generated code for a module. #[derive(Debug, Clone)] pub struct CodeVersion { + /// Indicates if this code version is the baseline version. pub baseline: bool, + + /// `ModuleStateMap` for this code version. pub msm: ModuleStateMap, + + /// A pointer to the machine code for this module. pub base: usize, + + /// The backend used to compile this module. + pub backend: Backend, } impl ModuleStateMap { + /// Looks up an ip from self using the given ip, base, and offset table provider. pub fn lookup_ip &BTreeMap>( &self, ip: usize, @@ -146,6 +222,7 @@ impl ModuleStateMap { } } } + /// Looks up a call ip from self using the given ip and base values. pub fn lookup_call_ip( &self, ip: usize, @@ -154,6 +231,7 @@ impl ModuleStateMap { self.lookup_ip(ip, base, |fsm| &fsm.call_offsets) } + /// Looks up a trappable ip from self using the given ip and base values. pub fn lookup_trappable_ip( &self, ip: usize, @@ -162,6 +240,7 @@ impl ModuleStateMap { self.lookup_ip(ip, base, |fsm| &fsm.trappable_offsets) } + /// Looks up a loop ip from self using the given ip and base values. pub fn lookup_loop_ip( &self, ip: usize, @@ -172,6 +251,7 @@ impl ModuleStateMap { } impl FunctionStateMap { + /// Creates a new `FunctionStateMap` with the given parameters. pub fn new( initial: MachineState, local_function_id: usize, @@ -194,6 +274,7 @@ impl FunctionStateMap { } impl MachineState { + /// Creates a `MachineStateDiff` from self and the given `&MachineState`. pub fn diff(&self, old: &MachineState) -> MachineStateDiff { let first_diff_stack_depth: usize = self .stack_values @@ -256,6 +337,7 @@ impl MachineState { } impl MachineStateDiff { + /// Creates a `MachineState` from the given `&FunctionStateMap`. pub fn build_state(&self, m: &FunctionStateMap) -> MachineState { let mut chain: Vec<&MachineStateDiff> = vec![]; chain.push(self); @@ -298,6 +380,7 @@ impl MachineStateDiff { } impl ExecutionStateImage { + /// Prints a backtrace if the `WASMER_BACKTRACE` environment variable is 1. pub fn print_backtrace_if_needed(&self) { use std::env; @@ -311,6 +394,7 @@ impl ExecutionStateImage { eprintln!("Run with `WASMER_BACKTRACE=1` environment variable to display a backtrace."); } + /// Converts self into a `String`, used for display purposes. pub fn output(&self) -> String { fn join_strings(x: impl Iterator, sep: &str) -> String { let mut ret = String::new(); @@ -376,6 +460,7 @@ impl ExecutionStateImage { } impl InstanceImage { + /// Converts a slice of bytes into an `Option` pub fn from_bytes(input: &[u8]) -> Option { use bincode::deserialize; match deserialize(input) { @@ -384,14 +469,150 @@ impl InstanceImage { } } + /// Converts self into a vector of bytes. pub fn to_bytes(&self) -> Vec { use bincode::serialize; serialize(self).unwrap() } } -#[cfg(all(unix, target_arch = "x86_64"))] +/// Declarations for x86-64 registers. +#[cfg(unix)] +pub mod x64_decl { + use super::*; + + /// General-purpose registers. + #[repr(u8)] + #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub enum GPR { + /// RAX register + RAX, + /// RCX register + RCX, + /// RDX register + RDX, + /// RBX register + RBX, + /// RSP register + RSP, + /// RBP register + RBP, + /// RSI register + RSI, + /// RDI register + RDI, + /// R8 register + R8, + /// R9 register + R9, + /// R10 register + R10, + /// R11 register + R11, + /// R12 register + R12, + /// R13 register + R13, + /// R14 register + R14, + /// R15 register + R15, + } + + /// XMM registers. + #[repr(u8)] + #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub enum XMM { + /// XMM register 0 + XMM0, + /// XMM register 1 + XMM1, + /// XMM register 2 + XMM2, + /// XMM register 3 + XMM3, + /// XMM register 4 + XMM4, + /// XMM register 5 + XMM5, + /// XMM register 6 + XMM6, + /// XMM register 7 + XMM7, + /// XMM register 8 + XMM8, + /// XMM register 9 + XMM9, + /// XMM register 10 + XMM10, + /// XMM register 11 + XMM11, + /// XMM register 12 + XMM12, + /// XMM register 13 + XMM13, + /// XMM register 14 + XMM14, + /// XMM register 15 + XMM15, + } + + /// A machine register under the x86-64 architecture. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum X64Register { + /// General-purpose registers. + GPR(GPR), + /// XMM (floating point/SIMD) registers. + XMM(XMM), + } + + impl X64Register { + /// Returns the index of the register. + pub fn to_index(&self) -> RegisterIndex { + match *self { + X64Register::GPR(x) => RegisterIndex(x as usize), + X64Register::XMM(x) => RegisterIndex(x as usize + 16), + } + } + + /// Converts a DWARD regnum to X64Register. + pub fn from_dwarf_regnum(x: u16) -> Option { + Some(match x { + 0 => X64Register::GPR(GPR::RAX), + 1 => X64Register::GPR(GPR::RDX), + 2 => X64Register::GPR(GPR::RCX), + 3 => X64Register::GPR(GPR::RBX), + 4 => X64Register::GPR(GPR::RSI), + 5 => X64Register::GPR(GPR::RDI), + 6 => X64Register::GPR(GPR::RBP), + 7 => X64Register::GPR(GPR::RSP), + 8 => X64Register::GPR(GPR::R8), + 9 => X64Register::GPR(GPR::R9), + 10 => X64Register::GPR(GPR::R10), + 11 => X64Register::GPR(GPR::R11), + 12 => X64Register::GPR(GPR::R12), + 13 => X64Register::GPR(GPR::R13), + 14 => X64Register::GPR(GPR::R14), + 15 => X64Register::GPR(GPR::R15), + + 17 => X64Register::XMM(XMM::XMM0), + 18 => X64Register::XMM(XMM::XMM1), + 19 => X64Register::XMM(XMM::XMM2), + 20 => X64Register::XMM(XMM::XMM3), + 21 => X64Register::XMM(XMM::XMM4), + 22 => X64Register::XMM(XMM::XMM5), + 23 => X64Register::XMM(XMM::XMM6), + 24 => X64Register::XMM(XMM::XMM7), + _ => return None, + }) + } + } +} + +#[cfg(unix)] pub mod x64 { + //! The x64 state module contains functions to generate state and code for x64 targets. + pub use super::x64_decl::*; use super::*; use crate::codegen::BreakpointMap; use crate::fault::{ @@ -410,6 +631,7 @@ pub mod x64 { ptr as usize as u64 } + /// Create a new `MachineState` with default values. pub fn new_machine_state() -> MachineState { MachineState { stack_values: vec![], @@ -421,6 +643,8 @@ pub mod x64 { } } + /// Invokes a call return on the stack for the given module state map, code base, instance + /// image and context. #[warn(unused_variables)] pub unsafe fn invoke_call_return_on_stack( msm: &ModuleStateMap, @@ -436,7 +660,7 @@ pub mod x64 { let mut last_stack_offset: u64 = 0; // rbp - let mut known_registers: [Option; 24] = [None; 24]; + let mut known_registers: [Option; 32] = [None; 32]; let local_functions_vec: Vec<&FunctionStateMap> = msm.local_functions.iter().map(|(_, v)| v).collect(); @@ -672,6 +896,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); @@ -741,6 +996,7 @@ pub mod x64 { ) } + /// Builds an `InstanceImage` for the given `Ctx` and `ExecutionStateImage`. pub fn build_instance_image( vmctx: &mut Ctx, execution_state: ExecutionStateImage, @@ -776,18 +1032,27 @@ pub mod x64 { } } + /// Returns a `ExecutionStateImage` for the given versions, stack, initial registers and + /// initial address. #[warn(unused_variables)] pub unsafe fn read_stack<'a, I: Iterator, F: Fn() -> I + 'a>( versions: F, mut stack: *const u64, - initially_known_registers: [Option; 24], + initially_known_registers: [Option; 32], mut initial_address: Option, + max_depth: Option, ) -> ExecutionStateImage { - let mut known_registers: [Option; 24] = initially_known_registers; + let mut known_registers: [Option; 32] = initially_known_registers; let mut results: Vec = vec![]; let mut was_baseline = true; - for _ in 0.. { + for depth in 0.. { + if let Some(max_depth) = max_depth { + if depth >= max_depth { + return ExecutionStateImage { frames: results }; + } + } + let ret_addr = initial_address.take().unwrap_or_else(|| { let x = *stack; stack = stack.offset(1); @@ -798,6 +1063,7 @@ pub mod x64 { let mut is_baseline: Option = None; for version in versions() { + //println!("Lookup IP: {:x}", ret_addr); match version .msm .lookup_call_ip(ret_addr as usize, version.base) @@ -985,89 +1251,10 @@ pub mod x64 { stack: wasm_stack, locals: wasm_locals, }; + //println!("WFS = {:?}", wfs); results.push(wfs); } unreachable!(); } - - #[repr(u8)] - #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] - pub enum GPR { - RAX, - RCX, - RDX, - RBX, - RSP, - RBP, - RSI, - RDI, - R8, - R9, - R10, - R11, - R12, - R13, - R14, - R15, - } - - #[repr(u8)] - #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] - pub enum XMM { - XMM0, - XMM1, - XMM2, - XMM3, - XMM4, - XMM5, - XMM6, - XMM7, - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub enum X64Register { - GPR(GPR), - XMM(XMM), - } - - impl X64Register { - pub fn to_index(&self) -> RegisterIndex { - match *self { - X64Register::GPR(x) => RegisterIndex(x as usize), - X64Register::XMM(x) => RegisterIndex(x as usize + 16), - } - } - - pub fn from_dwarf_regnum(x: u16) -> Option { - Some(match x { - 0 => X64Register::GPR(GPR::RAX), - 1 => X64Register::GPR(GPR::RDX), - 2 => X64Register::GPR(GPR::RCX), - 3 => X64Register::GPR(GPR::RBX), - 4 => X64Register::GPR(GPR::RSI), - 5 => X64Register::GPR(GPR::RDI), - 6 => X64Register::GPR(GPR::RBP), - 7 => X64Register::GPR(GPR::RSP), - 8 => X64Register::GPR(GPR::R8), - 9 => X64Register::GPR(GPR::R9), - 10 => X64Register::GPR(GPR::R10), - 11 => X64Register::GPR(GPR::R11), - 12 => X64Register::GPR(GPR::R12), - 13 => X64Register::GPR(GPR::R13), - 14 => X64Register::GPR(GPR::R14), - 15 => X64Register::GPR(GPR::R15), - - 17 => X64Register::XMM(XMM::XMM0), - 18 => X64Register::XMM(XMM::XMM1), - 19 => X64Register::XMM(XMM::XMM2), - 20 => X64Register::XMM(XMM::XMM3), - 21 => X64Register::XMM(XMM::XMM4), - 22 => X64Register::XMM(XMM::XMM5), - 23 => X64Register::XMM(XMM::XMM6), - 24 => X64Register::XMM(XMM::XMM7), - _ => return None, - }) - } - } } diff --git a/lib/runtime-core/src/structures/boxed.rs b/lib/runtime-core/src/structures/boxed.rs index 710dc70a6..439bad680 100644 --- a/lib/runtime-core/src/structures/boxed.rs +++ b/lib/runtime-core/src/structures/boxed.rs @@ -5,6 +5,7 @@ use std::{ ops::{Deref, DerefMut}, }; +/// Boxed map. #[derive(Debug, Clone)] pub struct BoxedMap where diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 2d4f3323e..08773b50a 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -21,6 +21,7 @@ impl Map where K: TypedIndex, { + /// Creates a new `Map`. pub fn new() -> Self { Self { elems: Vec::new(), @@ -28,6 +29,7 @@ where } } + /// Creates a new empty `Map` with the given capacity. pub fn with_capacity(capacity: usize) -> Self { Self { elems: Vec::with_capacity(capacity), @@ -35,32 +37,44 @@ where } } + /// Clears the map. Keeps the allocated memory for future use. + pub fn clear(&mut self) { + self.elems.clear(); + } + + /// Returns the size of this map. pub fn len(&self) -> usize { self.elems.len() } + /// Returns true if this map is empty. pub fn is_empty(&self) -> bool { self.elems.is_empty() } + /// Adds a new value to this map. pub fn push(&mut self, value: V) -> K { let len = self.len(); self.elems.push(value); K::new(len) } + /// Returns the next index into the map. pub fn next_index(&self) -> K { K::new(self.len()) } + /// Reserves the given size. pub fn reserve_exact(&mut self, size: usize) { self.elems.reserve_exact(size); } + /// Convert this into a `BoxedMap`. pub fn into_boxed_map(self) -> BoxedMap { BoxedMap::new(self.elems.into_boxed_slice()) } + /// Convert this into a `Vec`. pub fn into_vec(self) -> Vec { self.elems } @@ -71,6 +85,7 @@ where K: TypedIndex, V: Clone, { + /// Resize this map to the given new length and value. pub fn resize(&mut self, new_len: usize, value: V) { self.elems.resize(new_len, value); } @@ -184,6 +199,7 @@ where } } +/// Iterator for a `Map`. pub struct Iter<'a, K: TypedIndex, V: 'a> { enumerated: iter::Enumerate>, _marker: PhantomData, @@ -206,6 +222,7 @@ impl<'a, K: TypedIndex, V: 'a> Iterator for Iter<'a, K, V> { } } +/// Mutable iterator for a `Map`. pub struct IterMut<'a, K: TypedIndex, V: 'a> { enumerated: iter::Enumerate>, _marker: PhantomData, diff --git a/lib/runtime-core/src/structures/mod.rs b/lib/runtime-core/src/structures/mod.rs index ca7a449df..42af0d2c0 100644 --- a/lib/runtime-core/src/structures/mod.rs +++ b/lib/runtime-core/src/structures/mod.rs @@ -1,3 +1,4 @@ +//! The structures module contains commonly used data structures. mod boxed; mod map; mod slice; @@ -6,6 +7,7 @@ pub use self::boxed::BoxedMap; pub use self::map::{Iter, IterMut, Map}; pub use self::slice::SliceMap; +/// Represents a typed index. pub trait TypedIndex: Copy + Clone { #[doc(hidden)] fn new(index: usize) -> Self; diff --git a/lib/runtime-core/src/structures/slice.rs b/lib/runtime-core/src/structures/slice.rs index 8574b2d5b..5ac0d6190 100644 --- a/lib/runtime-core/src/structures/slice.rs +++ b/lib/runtime-core/src/structures/slice.rs @@ -20,30 +20,37 @@ impl SliceMap where K: TypedIndex, { + /// Gets a reference to the value at the given index. pub fn get(&self, index: K) -> Option<&V> { self.slice.get(index.index()) } + /// Gets a mutable reference to the value at the given index. pub fn get_mut(&mut self, index: K) -> Option<&mut V> { self.slice.get_mut(index.index()) } + /// Gets the length of this slice map. pub fn len(&self) -> usize { self.slice.len() } + /// Returns an iterator for this slice map. pub fn iter(&self) -> Iter { Iter::new(self.slice.iter()) } + /// Returns a mutable iterator for this slice map. pub fn iter_mut(&mut self) -> IterMut { IterMut::new(self.slice.iter_mut()) } + /// Gets a pointer to the `SliceMap`. pub fn as_ptr(&self) -> *const V { self as *const SliceMap as *const V } + /// Gets a mutable pointer to the `SliceMap`. pub fn as_mut_ptr(&mut self) -> *mut V { self as *mut SliceMap as *mut V } diff --git a/lib/runtime-core/src/sys/unix/memory.rs b/lib/runtime-core/src/sys/unix/memory.rs index fc60d732b..051dc1f0e 100644 --- a/lib/runtime-core/src/sys/unix/memory.rs +++ b/lib/runtime-core/src/sys/unix/memory.rs @@ -9,6 +9,7 @@ use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, slice, sync::Arc}; unsafe impl Send for Memory {} unsafe impl Sync for Memory {} +/// Data for a sized and protected region of memory. #[derive(Debug)] pub struct Memory { ptr: *mut u8, @@ -18,6 +19,7 @@ pub struct Memory { } impl Memory { + /// Create a new memory from the given path value and protection. pub fn from_file_path

(path: P, protection: Protect) -> Result where P: AsRef, @@ -54,6 +56,7 @@ impl Memory { } } + /// Create a new memory with the given size and protection. pub fn with_size_protect(size: usize, protection: Protect) -> Result { if size == 0 { return Ok(Self { @@ -89,6 +92,7 @@ impl Memory { } } + /// Create a new memory with the given size. pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { @@ -127,6 +131,7 @@ impl Memory { } } + /// Protect this memory with the given range bounds and protection. pub unsafe fn protect( &mut self, range: impl RangeBounds, @@ -166,6 +171,7 @@ impl Memory { } } + /// Split this memory into multiple memories by the given offset. pub fn split_at(mut self, offset: usize) -> (Memory, Memory) { let page_size = page_size::get(); if offset % page_size == 0 { @@ -187,22 +193,27 @@ impl Memory { } } + /// Gets the size of this memory. pub fn size(&self) -> usize { self.size } + /// Gets a slice for this memory. pub unsafe fn as_slice(&self) -> &[u8] { slice::from_raw_parts(self.ptr, self.size) } + /// Gets a mutable slice for this memory. pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] { slice::from_raw_parts_mut(self.ptr, self.size) } + /// Gets the protect kind of this memory. pub fn protection(&self) -> Protect { self.protection } + /// Gets mutable pointer to the memory. pub fn as_ptr(&self) -> *mut u8 { self.ptr } @@ -238,13 +249,19 @@ impl Clone for Memory { } } +/// Kinds of memory protection. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { + /// Read/write/exec allowed. None, + /// Read only. Read, + /// Read/write only. ReadWrite, + /// Read/exec only. ReadExec, + /// Read/write/exec only. ReadWriteExec, } @@ -259,6 +276,7 @@ impl Protect { } } + /// Returns true if this memory is readable. pub fn is_readable(self) -> bool { match self { Protect::Read | Protect::ReadWrite | Protect::ReadExec | Protect::ReadWriteExec => true, @@ -266,6 +284,7 @@ impl Protect { } } + /// Returns true if this memory is writable. pub fn is_writable(self) -> bool { match self { Protect::ReadWrite | Protect::ReadWriteExec => true, diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index 888aeff4d..411dee5b7 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -12,6 +12,7 @@ use winapi::um::winnt::{ unsafe impl Send for Memory {} unsafe impl Sync for Memory {} +/// Data for a sized and protected region of memory. #[derive(Debug)] pub struct Memory { ptr: *mut u8, @@ -20,6 +21,7 @@ pub struct Memory { } impl Memory { + /// Create a new memory from the given path value and protection. pub fn with_size_protect(size: usize, protection: Protect) -> Result { if size == 0 { return Ok(Self { @@ -52,6 +54,7 @@ impl Memory { } } + /// Create a new memory with the given size. pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { @@ -79,6 +82,7 @@ impl Memory { } } + /// Protect this memory with the given range bounds and protection. pub unsafe fn protect( &mut self, range: impl RangeBounds, @@ -120,6 +124,7 @@ impl Memory { } } + /// Split this memory into multiple memories by the given offset. pub fn split_at(mut self, offset: usize) -> (Memory, Memory) { let page_size = page_size::get(); if offset % page_size == 0 { @@ -140,22 +145,27 @@ impl Memory { } } + /// Gets the size of this memory. pub fn size(&self) -> usize { self.size } + /// Gets a slice for this memory. pub unsafe fn as_slice(&self) -> &[u8] { slice::from_raw_parts(self.ptr, self.size) } + /// Gets a mutable slice for this memory. pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] { slice::from_raw_parts_mut(self.ptr, self.size) } + /// Gets the protect kind of this memory. pub fn protection(&self) -> Protect { self.protection } + /// Gets mutable pointer to the memory. pub fn as_ptr(&self) -> *mut u8 { self.ptr } @@ -192,12 +202,17 @@ impl Clone for Memory { } } +/// Kinds of memory protection. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { + /// Read/write/exec allowed. None, + /// Read only. Read, + /// Read/write only. ReadWrite, + /// Read/exec only. ReadExec, } @@ -211,6 +226,7 @@ impl Protect { } } + /// Returns true if this memory is readable. pub fn is_readable(self) -> bool { match self { Protect::Read | Protect::ReadWrite | Protect::ReadExec => true, @@ -218,6 +234,7 @@ impl Protect { } } + /// Returns true if this memory is writable. pub fn is_writable(self) -> bool { match self { Protect::ReadWrite => true, diff --git a/lib/runtime-core/src/table/anyfunc.rs b/lib/runtime-core/src/table/anyfunc.rs index 4a63c8f62..38cb578f2 100644 --- a/lib/runtime-core/src/table/anyfunc.rs +++ b/lib/runtime-core/src/table/anyfunc.rs @@ -17,11 +17,13 @@ enum AnyfuncInner<'a> { Managed(DynFunc<'a>), } +/// Anyfunc data type. pub struct Anyfunc<'a> { inner: AnyfuncInner<'a>, } impl<'a> Anyfunc<'a> { + /// Create a new `Anyfunc`. pub unsafe fn new(func: *const vm::Func, signature: Sig) -> Self where Sig: Into>, diff --git a/lib/runtime-core/src/table/mod.rs b/lib/runtime-core/src/table/mod.rs index 8e5c0e033..997ce5eb1 100644 --- a/lib/runtime-core/src/table/mod.rs +++ b/lib/runtime-core/src/table/mod.rs @@ -1,3 +1,5 @@ +//! The runtime table module contains data structures and functions used to create and update wasm +//! tables. use crate::{ error::CreationError, export::Export, @@ -16,16 +18,20 @@ pub use self::anyfunc::Anyfunc; pub(crate) use self::anyfunc::AnyfuncTable; use crate::error::GrowError; +/// Kind of table element. pub enum Element<'a> { + /// Anyfunc. Anyfunc(Anyfunc<'a>), } +/// Kind of table storage. // #[derive(Debug)] pub enum TableStorage { /// This is intended to be a caller-checked Anyfunc. Anyfunc(Box), } +/// Container with a descriptor and a reference to a table storage. pub struct Table { desc: TableDescriptor, storage: Arc>, @@ -128,6 +134,7 @@ impl Table { } } + /// Get a mutable pointer to underlying table storage. pub fn vm_local_table(&mut self) -> *mut vm::LocalTable { let mut storage = self.storage.lock().unwrap(); &mut storage.1 diff --git a/lib/runtime-core/src/tiering.rs b/lib/runtime-core/src/tiering.rs index 21a9ea2d3..7d2a27d87 100644 --- a/lib/runtime-core/src/tiering.rs +++ b/lib/runtime-core/src/tiering.rs @@ -1,4 +1,6 @@ -use crate::backend::{Compiler, CompilerConfig}; +//! The tiering module supports switching between code compiled with different optimization levels +//! as runtime. +use crate::backend::{Backend, Compiler, CompilerConfig}; use crate::compile_with_config; use crate::fault::{ catch_unsafe_unwind, ensure_sighandler, pop_code_version, push_code_version, with_ctx, @@ -22,12 +24,17 @@ impl Drop for Defer { } } +/// Kind of shell exit operation. pub enum ShellExitOperation { + /// Operation to continue with an instance image. ContinueWith(InstanceImage), } +/// Context for an interactive shell. pub struct InteractiveShellContext { + /// Optional instance image. pub image: Option, + /// Flag to indicate patching. pub patched: bool, } @@ -36,6 +43,7 @@ struct OptimizationState { } struct OptimizationOutcome { + backend_id: Backend, module: Module, } @@ -46,6 +54,7 @@ unsafe impl Sync for CtxWrapper {} unsafe fn do_optimize( binary: &[u8], + backend_id: Backend, compiler: Box, ctx: &Mutex, state: &OptimizationState, @@ -65,11 +74,12 @@ unsafe fn do_optimize( let ctx_inner = ctx.lock().unwrap(); if !ctx_inner.0.is_null() { - *state.outcome.lock().unwrap() = Some(OptimizationOutcome { module }); + *state.outcome.lock().unwrap() = Some(OptimizationOutcome { backend_id, module }); set_wasm_interrupt_on_ctx(ctx_inner.0); } } +/// Runs an instance with tiering. pub unsafe fn run_tiering ShellExitOperation>( module_info: &ModuleInfo, wasm_binary: &[u8], @@ -77,7 +87,8 @@ pub unsafe fn run_tiering ShellExitOperation>( import_object: &ImportObject, start_raw: extern "C" fn(&mut Ctx), baseline: &mut Instance, - optimized_backends: Vec Box + Send>>, + baseline_backend: Backend, + optimized_backends: Vec<(Backend, Box Box + Send>)>, interactive_shell: F, ) -> Result<(), String> { ensure_sighandler(); @@ -99,9 +110,9 @@ pub unsafe fn run_tiering ShellExitOperation>( let ctx_box = ctx_box.clone(); let opt_state = opt_state.clone(); ::std::thread::spawn(move || { - for backend in optimized_backends { + for (backend_id, backend) in optimized_backends { if !ctx_box.lock().unwrap().0.is_null() { - do_optimize(&wasm_binary, backend(), &ctx_box, &opt_state); + do_optimize(&wasm_binary, backend_id, backend(), &ctx_box, &opt_state); } } }); @@ -117,6 +128,7 @@ pub unsafe fn run_tiering ShellExitOperation>( .get_module_state_map() .unwrap(), base: baseline.module.runnable_module.get_code().unwrap().as_ptr() as usize, + backend: baseline_backend, }); let n_versions: Cell = Cell::new(1); @@ -127,7 +139,7 @@ pub unsafe fn run_tiering ShellExitOperation>( })); loop { - let new_optimized: Option<&mut Instance> = { + let new_optimized: Option<(Backend, &mut Instance)> = { let mut outcome = opt_state.outcome.lock().unwrap(); if let Some(x) = outcome.take() { let instance = x @@ -136,12 +148,12 @@ pub unsafe fn run_tiering ShellExitOperation>( .map_err(|e| format!("Can't instantiate module: {:?}", e))?; // Keep the optimized code alive. optimized_instances.push(instance); - optimized_instances.last_mut() + optimized_instances.last_mut().map(|y| (x.backend_id, y)) } else { None } }; - if let Some(optimized) = new_optimized { + if let Some((backend_id, optimized)) = new_optimized { let base = module_info.imported_functions.len(); let code_ptr = optimized .module @@ -178,6 +190,7 @@ pub unsafe fn run_tiering ShellExitOperation>( .get_code() .unwrap() .as_ptr() as usize, + backend: backend_id, }); n_versions.set(n_versions.get() + 1); diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 80de3e0d6..3d07484c7 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -62,6 +62,7 @@ pub fn get_context() -> *const CallContext { } impl TrampolineBufferBuilder { + /// Creates a new empty `TrampolineBufferBuilder`. pub fn new() -> TrampolineBufferBuilder { TrampolineBufferBuilder { code: vec![], @@ -100,6 +101,7 @@ impl TrampolineBufferBuilder { idx } + /// Adds context RSP state preserving trampoline to the buffer. pub fn add_context_rsp_state_preserving_trampoline( &mut self, target: unsafe extern "C" fn(&mut Ctx, *const CallContext, *const u64), diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 0014e13cd..f1e62c238 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,9 +1,11 @@ +//! The typed func module implements a way of representing a wasm function +//! with the correct types from rust. Function calls using a typed func have a low overhead. use crate::{ error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, - vm::{self, Ctx}, + vm, }; use std::{ any::Any, @@ -16,14 +18,22 @@ use std::{ sync::Arc, }; +/// Wasm trap info. #[repr(C)] pub enum WasmTrapInfo { + /// Unreachable trap. Unreachable = 0, + /// Call indirect incorrect signature trap. IncorrectCallIndirectSignature = 1, + /// Memory out of bounds trap. MemoryOutOfBounds = 2, + /// Call indirect out of bounds trap. CallIndirectOOB = 3, + /// Illegal arithmetic trap. IllegalArithmetic = 4, + /// Misaligned atomic access trap. MisalignedAtomicAccess = 5, + /// Unknown trap. Unknown, } @@ -52,16 +62,24 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} -pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); +/// Aliases to an extern "C" type used as a trampoline to a function. +pub type Trampoline = unsafe extern "C" fn( + vmctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, +); + +/// Aliases to an extern "C" type used to invoke a function. pub type Invoke = unsafe extern "C" fn( - Trampoline, - *mut Ctx, - NonNull, - *const u64, - *mut u64, - *mut WasmTrapInfo, - *mut Option>, - Option>, + trampoline: Trampoline, + vmctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + extra: Option>, ) -> bool; /// TODO(lachlan): Naming TBD. @@ -75,6 +93,7 @@ pub struct Wasm { } impl Wasm { + /// Create new `Wasm` from given parts. pub unsafe fn from_raw_parts( trampoline: Trampoline, invoke: Invoke, @@ -91,40 +110,99 @@ 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 { + /// CStruct type. type CStruct; + + /// Array of return values. 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( self, f: NonNull, wasm: Wasm, - ctx: *mut Ctx, + ctx: *mut vm::Ctx, ) -> Result where Rets: WasmTypeList; } -pub trait ExternalFunction +/// 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 where + Kind: ExternalFunctionKind, Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(&self) -> NonNull; + /// Conver to function pointer. + fn to_raw(self) -> (NonNull, Option>); } +/// Represents a TrapEarly type. pub trait TrapEarly where Rets: WasmTypeList, { + /// The error type for this trait. type Error: 'static; + /// Get returns or error result. fn report(self) -> Result; } @@ -149,19 +227,12 @@ where } } -// pub fn Func<'a, Args, Rets, F>(f: F) -> Func<'a, Args, Rets, Unsafe> -// where -// Args: WasmTypeList, -// Rets: WasmTypeList, -// F: ExternalFunction -// { -// 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, - ctx: *mut Ctx, + func: NonNull, + func_env: Option>, + vmctx: *mut vm::Ctx, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -175,19 +246,22 @@ where { pub(crate) unsafe fn from_raw_parts( inner: Wasm, - f: NonNull, - ctx: *mut Ctx, + func: NonNull, + func_env: Option>, + vmctx: *mut vm::Ctx, ) -> Func<'a, Args, Rets, Wasm> { Func { inner, - f, - ctx, + func, + func_env, + vmctx, _phantom: PhantomData, } } + /// Get the underlying func pointer. pub fn get_vm_func(&self) -> NonNull { - self.f + self.func } } @@ -196,14 +270,19 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Host> + /// Creates a new `Func`. + pub fn new(func: F) -> Func<'a, Args, Rets, Host> where - F: ExternalFunction, + Kind: ExternalFunctionKind, + F: ExternalFunction, { + let (func, func_env) = func.to_raw(); + Func { inner: Host(()), - f: f.to_raw(), - ctx: ptr::null_mut(), + func, + func_env, + vmctx: ptr::null_mut(), _phantom: PhantomData, } } @@ -215,9 +294,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,139 +308,112 @@ 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( + unsafe fn call( self, _: NonNull, _: Wasm, - _: *mut Ctx, - ) -> Result { + _: *mut vm::Ctx, + ) -> Result + where + Rets: WasmTypeList, + { unreachable!() } } -impl WasmTypeList for (A,) { - type CStruct = S1; - 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( - self, - f: NonNull, - wasm: Wasm, - ctx: *mut Ctx, - ) -> Result { - let (a,) = self; - let args = [a.to_native().to_binary()]; - let mut rets = Rets::empty_ret_array(); - 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, - ) { - Ok(Rets::from_ret_array(rets)) - } else { - if let Some(data) = user_error { - Err(RuntimeError::Error { data }) - } else { - Err(RuntimeError::Trap { - msg: trap.to_string().into(), - }) - } - } - } -} - -impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> -where - Rets: WasmTypeList, -{ - pub fn call(&self, a: A) -> Result { - unsafe { ::call(a, self.f, self.inner, self.ctx) } - } -} - macro_rules! impl_traits { ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { + /// Struct for typed funcs. #[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) ),* ) } + + #[allow(unused_parens, non_snake_case)] 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(self, f: NonNull, wasm: Wasm, ctx: *mut Ctx) -> Result { - #[allow(unused_parens)] + + #[allow(unused_parens, non_snake_case)] + unsafe fn call( + self, + f: NonNull, + wasm: Wasm, + ctx: *mut vm::Ctx, + ) -> Result + where + Rets: WasmTypeList + { let ( $( $x ),* ) = self; let args = [ $( $x.to_native().to_binary()),* ]; let mut rets = Rets::empty_ret_array(); 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,49 +425,250 @@ macro_rules! impl_traits { } } - impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap + 'static, + { #[allow(non_snake_case)] - fn to_raw(&self) -> NonNull { - if mem::size_of::() == 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, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct { - let f: FN = unsafe { mem::transmute_copy(&()) }; + fn to_raw(self) -> (NonNull, Option>) { + // The `wrap` function is a wrapper around the + // imported function. It manages the argument passed + // to the imported function (in this case, the + // `vmctx` along with the regular WebAssembly + // arguments), and it manages the trapping. + // + // It is also 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: &vm::Ctx $( , $x: <$x as WasmExternType>::Native )* + ) -> Rets::CStruct + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap, + { + // Get the pointer to this `wrap` function. + let self_pointer = wrap::<$( $x, )* Rets, Trap, FN> as *const vm::Func; - let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| { - f( ctx $( ,WasmExternType::from_native($x) )* ).report() - })) { - Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => { - let b: Box<_> = err.into(); - b as Box - }, - Err(err) => err, - }; + // Get the collection of imported functions. + let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions }; - unsafe { - (&*ctx.module).runnable_module.do_early_trap(err) - } + // Retrieve the `vm::FuncCtx`. + let mut func_ctx: NonNull = vm_imported_functions + .iter() + .find_map(|(_, imported_func)| { + if imported_func.func == self_pointer { + Some(imported_func.func_ctx) + } else { + None + } + }) + .expect("Import backing is not well-formed, cannot find `func_ctx`."); + let func_ctx = unsafe { func_ctx.as_mut() }; + + // Extract `vm::Ctx` from `vm::FuncCtx`. The + // pointer is always non-null. + let vmctx = unsafe { func_ctx.vmctx.as_mut() }; + + // Extract `vm::FuncEnv` from `vm::FuncCtx`. + let func_env = func_ctx.func_env; + + let func: &FN = match func_env { + // The imported function is a regular + // function, a closure without a captured + // environment, or a closure with a captured + // environment. + Some(func_env) => unsafe { + let func: NonNull = func_env.cast(); + + &*func.as_ptr() + }, + + // This branch is supposed to be unreachable. + None => unreachable!() + }; + + // Catch unwind in case of errors. + let err = match panic::catch_unwind( + panic::AssertUnwindSafe( + || { + func(vmctx $( , WasmExternType::from_native($x) )* ).report() + // ^^^^^ The imported function + // expects `vm::Ctx` as first + // argument; provide it. + } + ) + ) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, + Err(err) => err, + }; + + // At this point, there is an error that needs to + // be trapped. + 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::(), mem::size_of::(), "you cannot use a closure that captures state for `Func`."); - NonNull::new(unsafe { - ::std::mem::transmute_copy::<_, *mut vm::Func>(self) - }).unwrap() } + + // Extract the captured environment of the imported + // function if any. + let func_env: Option> = + // `FN` is a function pointer, or a closure + // _without_ a captured environment. + if mem::size_of::() == 0 { + NonNull::new(&self as *const _ as *mut vm::FuncEnv) + } + // `FN` is a closure _with_ a captured + // environment. + else { + NonNull::new(Box::into_raw(Box::new(self))).map(NonNull::cast) + }; + + ( + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(), + func_env + ) } } - impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> + impl< $( $x, )* Rets, Trap, FN > ExternalFunction for FN where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn($( $x, )*) -> Trap + 'static, + { + #[allow(non_snake_case)] + fn to_raw(self) -> (NonNull, Option>) { + // The `wrap` function is a wrapper around the + // imported function. It manages the argument passed + // to the imported function (in this case, only the + // regular WebAssembly arguments), and it manages the + // trapping. + // + // It is also 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: &vm::Ctx $( , $x: <$x as WasmExternType>::Native )* + ) -> Rets::CStruct + where + $( $x: WasmExternType, )* + Rets: WasmTypeList, + Trap: TrapEarly, + FN: Fn($( $x, )*) -> Trap, + { + // Get the pointer to this `wrap` function. + let self_pointer = wrap::<$( $x, )* Rets, Trap, FN> as *const vm::Func; + + // Get the collection of imported functions. + let vm_imported_functions = unsafe { &(*vmctx.import_backing).vm_functions }; + + // Retrieve the `vm::FuncCtx`. + let mut func_ctx: NonNull = vm_imported_functions + .iter() + .find_map(|(_, imported_func)| { + if imported_func.func == self_pointer { + Some(imported_func.func_ctx) + } else { + None + } + }) + .expect("Import backing is not well-formed, cannot find `func_ctx`."); + let func_ctx = unsafe { func_ctx.as_mut() }; + + // Extract `vm::Ctx` from `vm::FuncCtx`. The + // pointer is always non-null. + let vmctx = unsafe { func_ctx.vmctx.as_mut() }; + + // Extract `vm::FuncEnv` from `vm::FuncCtx`. + let func_env = func_ctx.func_env; + + let func: &FN = match func_env { + // The imported function is a regular + // function, a closure without a captured + // environment, or a closure with a captured + // environment. + Some(func_env) => unsafe { + let func: NonNull = func_env.cast(); + + &*func.as_ptr() + }, + + // This branch is supposed to be unreachable. + None => unreachable!() + }; + + // Catch unwind in case of errors. + let err = match panic::catch_unwind( + panic::AssertUnwindSafe( + || { + func($( WasmExternType::from_native($x), )* ).report() + } + ) + ) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, + Err(err) => err, + }; + + // At this point, there is an error that needs to + // be trapped. + unsafe { + (&*vmctx.module).runnable_module.do_early_trap(err) + } + } + + // Extract the captured environment of the imported + // function if any. + let func_env: Option> = + // `FN` is a function pointer, or a closure + // _without_ a captured environment. + if mem::size_of::() == 0 { + NonNull::new(&self as *const _ as *mut vm::FuncEnv) + } + // `FN` is a closure _with_ a captured + // environment. + else { + NonNull::new(Box::into_raw(Box::new(self))).map(NonNull::cast) + }; + + ( + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap(), + func_env + ) + } + } + + impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> + where + $( $x: WasmExternType, )* Rets: WasmTypeList, { + /// Call the typed func and return results. #[allow(non_snake_case)] pub fn call(&self, $( $x: $x, )* ) -> Result { #[allow(unused_parens)] - unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) } + unsafe { + <( $( $x ),* ) as WasmTypeList>::call( + ( $( $x ),* ), + self.func, + self.inner, + self.vmctx + ) + } } } }; @@ -448,8 +704,11 @@ where Inner: Kind, { fn to_export(&self) -> Export { - let func = unsafe { FuncPointer::new(self.f.as_ptr()) }; - let ctx = Context::Internal; + let func = unsafe { FuncPointer::new(self.func.as_ptr()) }; + let ctx = match self.func_env { + func_env @ Some(_) => Context::ExternalWithEnv(self.vmctx, func_env), + None => Context::Internal, + }; let signature = Arc::new(FuncSig::new(Args::types(), Rets::types())); Export::Function { @@ -463,9 +722,65 @@ 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::new(with_vmctx); + let _ = Func::new(without_vmctx); + let _ = Func::new(|_: &mut vm::Ctx, $($x: i32),*| -> i32 { + vec![$($x),*].iter().sum() + }); + let _ = Func::new(|$($x: i32),*| -> i32 { + vec![$($x),*].iter().sum() + }); + } + } + } + + #[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); + let _ = Func::new(|_: &mut vm::Ctx| -> i32 { 0 }); + let _ = Func::new(|| -> i32 { 0 }); + } + + 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 +791,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 } diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index bea89ffe3..1b19bbc3d 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,3 +1,6 @@ +//! The runtime types modules represent type used within the wasm runtime and helper functions to +//! convert to other represenations. + use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages}; use std::borrow::Cow; @@ -41,6 +44,7 @@ pub enum Value { } impl Value { + /// The `Type` of this `Value`. pub fn ty(&self) -> Type { match self { Value::I32(_) => Type::I32, @@ -51,6 +55,7 @@ impl Value { } } + /// Convert this `Value` to a u128 binary representation. pub fn to_u128(&self) -> u128 { match *self { Value::I32(x) => x as u128, @@ -92,12 +97,16 @@ impl From for Value { } } +/// Represents a native wasm type. pub unsafe trait NativeWasmType: Copy + Into where Self: Sized, { + /// Type for this `NativeWasmType`. const TYPE: Type; + /// Convert from u64 bites to self. fn from_binary(bits: u64) -> Self; + /// Convert self to u64 binary representation. fn to_binary(self) -> u64; } @@ -138,12 +147,16 @@ unsafe impl NativeWasmType for f64 { } } +/// A trait to represent a wasm extern type. pub unsafe trait WasmExternType: Copy where Self: Sized, { + /// Native wasm type for this `WasmExternType`. type Native: NativeWasmType; + /// Convert from given `Native` type to self. fn from_native(native: Self::Native) -> Self; + /// Convert self to `Native` type. fn to_native(self) -> Self::Native; } @@ -255,6 +268,7 @@ unsafe impl WasmExternType for f64 { // fn swap(&self, other: Self::Primitive) -> Self::Primitive; // } +/// Trait for a Value type. pub unsafe trait ValueType: Copy where Self: Sized, @@ -274,12 +288,15 @@ macro_rules! convert_value_impl { convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); +/// Kinds of element types. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementType { /// Any wasm function. Anyfunc, } +/// Describes the properties of a table including the element types, minimum and optional maximum, +/// number of elements in the table. #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct TableDescriptor { /// Type of data stored in this table. @@ -315,14 +332,18 @@ pub enum Initializer { /// Describes the mutability and type of a Global #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct GlobalDescriptor { + /// Mutable flag. pub mutable: bool, + /// Wasm type. pub ty: Type, } /// A wasm global. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GlobalInit { + /// Global descriptor. pub desc: GlobalDescriptor, + /// Global initializer. pub init: Initializer, } @@ -340,6 +361,7 @@ pub struct MemoryDescriptor { } impl MemoryDescriptor { + /// Create a new memory descriptor with the given min/max pages and shared flag. pub fn new(minimum: Pages, maximum: Option, shared: bool) -> Result { let memory_type = match (maximum.is_some(), shared) { (true, true) => MemoryType::SharedStatic, @@ -357,6 +379,7 @@ impl MemoryDescriptor { }) } + /// Returns the `MemoryType` for this descriptor. pub fn memory_type(&self) -> MemoryType { self.memory_type } @@ -380,6 +403,7 @@ pub struct FuncSig { } impl FuncSig { + /// Creates a new function signatures with the given parameter and return types. pub fn new(params: Params, returns: Returns) -> Self where Params: Into>, @@ -391,14 +415,17 @@ impl FuncSig { } } + /// Parameter types. pub fn params(&self) -> &[Type] { &self.params } + /// Return types. pub fn returns(&self) -> &[Type] { &self.returns } + /// Returns true if parameter types match the function signature. pub fn check_param_value_types(&self, params: &[Value]) -> bool { self.params.len() == params.len() && self @@ -427,14 +454,18 @@ impl std::fmt::Display for FuncSig { } } +/// Trait that represents Local or Import. pub trait LocalImport { + /// Local type. type Local: TypedIndex; + /// Import type. type Import: TypedIndex; } #[rustfmt::skip] macro_rules! define_map_index { ($ty:ident) => { + /// Typed Index #[derive(Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); @@ -475,6 +506,7 @@ define_map_index![ macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { impl $ty { + /// Converts self into `LocalOrImport`. pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> { if self.index() < info.$imports.len() { LocalOrImport::Import(::Import::new(self.index())) @@ -485,12 +517,14 @@ macro_rules! define_local_or_import { } impl $local_ty { + /// Convert up. pub fn convert_up(self, info: &ModuleInfo) -> $ty { $ty ((self.index() + info.$imports.len()) as u32) } } impl $imported_ty { + /// Convert up. pub fn convert_up(self, _info: &ModuleInfo) -> $ty { $ty (self.index() as u32) } @@ -511,6 +545,7 @@ define_local_or_import![ (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals), ]; +/// Index for signature. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SigIndex(u32); impl TypedIndex for SigIndex { @@ -525,11 +560,14 @@ impl TypedIndex for SigIndex { } } +/// Kind of local or import type. pub enum LocalOrImport where T: LocalImport, { + /// Local. Local(T::Local), + /// Import. Import(T::Import), } @@ -537,6 +575,7 @@ impl LocalOrImport where T: LocalImport, { + /// Returns `Some` if self is local, `None` if self is an import. pub fn local(self) -> Option { match self { LocalOrImport::Local(local) => Some(local), @@ -544,6 +583,7 @@ where } } + /// Returns `Some` if self is an import, `None` if self is local. pub fn import(self) -> Option { match self { LocalOrImport::Import(import) => Some(import), diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index e8232efc8..2e8f2c0a5 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -1,12 +1,17 @@ +//! The units module provides common WebAssembly units like `Pages` and conversion functions into +//! other units. use crate::error::PageError; use std::{ fmt, ops::{Add, Sub}, }; +/// The page size in bytes of a wasm page. pub const WASM_PAGE_SIZE: usize = 65_536; +/// Tbe max number of wasm pages allowed. pub const WASM_MAX_PAGES: usize = 65_536; // From emscripten resize_heap implementation +/// The minimum number of wasm pages allowed. pub const WASM_MIN_PAGES: usize = 256; /// Units of WebAssembly pages (as specified to be 65,536 bytes). @@ -14,6 +19,7 @@ pub const WASM_MIN_PAGES: usize = 256; pub struct Pages(pub u32); impl Pages { + /// Checked add of Pages to Pages. pub fn checked_add(self, rhs: Pages) -> Result { let added = (self.0 as usize) + (rhs.0 as usize); if added <= WASM_MAX_PAGES { @@ -27,6 +33,7 @@ impl Pages { } } + /// Calculate number of bytes from pages. pub fn bytes(self) -> Bytes { self.into() } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index fd8cebbec..a976336a6 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -1,3 +1,5 @@ +//! The runtime vm module contains data structures and helper functions used during runtime to +//! execute wasm instance functions. pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE}; use crate::{ error::CallResult, @@ -36,6 +38,7 @@ use std::collections::HashMap; #[repr(C)] pub struct Ctx { // `internal` must be the first field of `Ctx`. + /// InternalCtx data field pub internal: InternalCtx, pub(crate) local_functions: *const *const Func, @@ -43,7 +46,9 @@ pub struct Ctx { /// These are pointers to things that are known to be owned /// by the owning `Instance`. pub local_backing: *mut LocalBacking, + /// Mutable pointer to import data pub import_backing: *mut ImportBacking, + /// Const pointer to module inner data pub module: *const ModuleInner, /// This is intended to be user-supplied, per-instance @@ -110,22 +115,31 @@ pub struct InternalCtx { /// modules safely. pub dynamic_sigindices: *const SigId, + /// Const pointer to Intrinsics. pub intrinsics: *const Intrinsics, + /// Stack lower bound. pub stack_lower_bound: *mut u8, + /// Mutable pointer to memory base. pub memory_base: *mut u8, + /// Memory bound. pub memory_bound: usize, + /// Mutable pointer to internal fields. pub internals: *mut [u64; INTERNALS_SIZE], // TODO: Make this dynamic? + /// Interrupt signal mem. pub interrupt_signal_mem: *mut u8, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); +/// An internal field. pub struct InternalField { + /// Init once field. init: Once, + /// Inner field. inner: UnsafeCell, } @@ -133,6 +147,7 @@ unsafe impl Send for InternalField {} unsafe impl Sync for InternalField {} impl InternalField { + /// Allocate and return an `InternalField`. pub const fn allocate() -> InternalField { InternalField { init: Once::new(), @@ -140,6 +155,7 @@ impl InternalField { } } + /// Get the index of this `InternalField`. pub fn index(&self) -> usize { let inner: *mut usize = self.inner.get(); self.init.call_once(|| { @@ -157,9 +173,12 @@ impl InternalField { } } +/// A container for VM instrinsic functions #[repr(C)] pub struct Intrinsics { + /// Const pointer to memory grow `Func`. pub memory_grow: *const Func, + /// Const pointer to memory size `Func`. pub memory_size: *const Func, /*pub memory_grow: unsafe extern "C" fn( ctx: &mut Ctx, @@ -176,27 +195,33 @@ unsafe impl Send for Intrinsics {} unsafe impl Sync for Intrinsics {} impl Intrinsics { + /// Memory grow offset #[allow(clippy::erasing_op)] pub fn offset_memory_grow() -> u8 { (0 * ::std::mem::size_of::()) as u8 } + /// Memory size offset pub fn offset_memory_size() -> u8 { (1 * ::std::mem::size_of::()) as u8 } } +/// Local static memory intrinsics pub static INTRINSICS_LOCAL_STATIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::local_static_memory_grow as _, memory_size: vmcalls::local_static_memory_size as _, }; +/// Local dynamic memory intrinsics pub static INTRINSICS_LOCAL_DYNAMIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::local_dynamic_memory_grow as _, memory_size: vmcalls::local_dynamic_memory_size as _, }; +/// Imported static memory intrinsics pub static INTRINSICS_IMPORTED_STATIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::imported_static_memory_grow as _, memory_size: vmcalls::imported_static_memory_size as _, }; +/// Imported dynamic memory intrinsics pub static INTRINSICS_IMPORTED_DYNAMIC_MEMORY: Intrinsics = Intrinsics { memory_grow: vmcalls::imported_dynamic_memory_grow as _, memory_size: vmcalls::imported_dynamic_memory_size as _, @@ -498,34 +523,80 @@ impl Ctx { } } -enum InnerFunc {} -/// Used to provide type safety (ish) for passing around function pointers. -/// The typesystem ensures this cannot be dereferenced since an -/// empty enum cannot actually exist. -#[repr(C)] -pub struct Func(InnerFunc); +/// Represents a function pointer. It is mostly used in the +/// `typed_func` module within the `wrap` functions, to wrap imported +/// functions. +#[repr(transparent)] +pub struct Func(pub(self) *mut c_void); -/// An imported function, which contains the vmctx that owns this function. +/// Represents a function environment pointer, like a captured +/// environment of a closure. It is mostly used in the `typed_func` +/// module within the `wrap` functions, to wrap imported functions. +#[repr(transparent)] +pub struct FuncEnv(pub(self) *mut c_void); + +/// Represents a function context. It is used by imported functions +/// only. +#[derive(Debug)] +#[repr(C)] +pub struct FuncCtx { + /// The `Ctx` pointer. + pub(crate) vmctx: NonNull, + + /// A pointer to the function environment. It is used by imported + /// functions only to store the pointer to the real host function, + /// whether it is a regular function, or a closure with or without + /// a captured environment. + pub(crate) func_env: Option>, +} + +impl FuncCtx { + /// Offset to `vmctx`. + pub fn offset_vmctx() -> u8 { + 0 * (mem::size_of::() as u8) + } + + /// Offset to `func_env`. + pub fn offset_func_env() -> u8 { + 1 * (mem::size_of::() as u8) + } + + /// Size of a `FuncCtx`. + pub fn size() -> u8 { + mem::size_of::() as u8 + } +} + +/// An imported function is a function pointer associated to a +/// function context. #[derive(Debug, Clone)] #[repr(C)] pub struct ImportedFunc { - pub func: *const Func, - pub vmctx: *mut Ctx, + /// Const pointer to `Func`. + pub(crate) func: *const Func, + + /// Mutable non-null pointer to `FuncCtx`. + pub(crate) func_ctx: NonNull, } -// manually implemented because ImportedFunc contains raw pointers directly; `Func` is marked Send (But `Ctx` actually isn't! (TODO: review this, shouldn't `Ctx` be Send?)) +// Manually implemented because ImportedFunc contains raw pointers +// directly; `Func` is marked Send (But `Ctx` actually isn't! (TODO: +// review this, shouldn't `Ctx` be Send?)) unsafe impl Send for ImportedFunc {} impl ImportedFunc { + /// Offset to func. #[allow(clippy::erasing_op)] // TODO pub fn offset_func() -> u8 { 0 * (mem::size_of::() as u8) } - pub fn offset_vmctx() -> u8 { + /// Offset to func_ctx. + pub fn offset_func_ctx() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of an `ImportedFunc`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -547,15 +618,18 @@ pub struct LocalTable { unsafe impl Send for LocalTable {} impl LocalTable { + /// Offset to base. #[allow(clippy::erasing_op)] // TODO pub fn offset_base() -> u8 { 0 * (mem::size_of::() as u8) } + /// Offset count. pub fn offset_count() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of a `LocalTable`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -579,15 +653,18 @@ pub struct LocalMemory { unsafe impl Send for LocalMemory {} impl LocalMemory { + /// Offset base. #[allow(clippy::erasing_op)] // TODO pub fn offset_base() -> u8 { 0 * (mem::size_of::() as u8) } + /// Offset bound. pub fn offset_bound() -> u8 { 1 * (mem::size_of::() as u8) } + /// Size of a `LocalMemory`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -597,24 +674,29 @@ impl LocalMemory { #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LocalGlobal { + /// Data. pub data: u128, } impl LocalGlobal { + /// Offset data. #[allow(clippy::erasing_op)] // TODO pub fn offset_data() -> u8 { 0 * (mem::size_of::() as u8) } + /// A null `LocalGlobal`. pub fn null() -> Self { Self { data: 0 } } + /// Size of a `LocalGlobal`. pub fn size() -> u8 { mem::size_of::() as u8 } } +/// Identifier for a function signature. #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct SigId(pub u32); @@ -623,8 +705,11 @@ pub struct SigId(pub u32); #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Anyfunc { + /// Const pointer to `Func`. pub func: *const Func, + /// Mutable pointer to `Ctx`. pub ctx: *mut Ctx, + /// Sig id of this function pub sig_id: SigId, } @@ -632,6 +717,7 @@ pub struct Anyfunc { unsafe impl Send for Anyfunc {} impl Anyfunc { + /// A null `Anyfunc` value. pub fn null() -> Self { Self { func: ptr::null(), @@ -640,19 +726,23 @@ impl Anyfunc { } } + /// The offset for this func. #[allow(clippy::erasing_op)] // TODO pub fn offset_func() -> u8 { 0 * (mem::size_of::() as u8) } + /// The offset of the vmctx. pub fn offset_vmctx() -> u8 { 1 * (mem::size_of::() as u8) } + /// The offset of the sig id. pub fn offset_sig_id() -> u8 { 2 * (mem::size_of::() as u8) } + /// The size of `Anyfunc`. pub fn size() -> u8 { mem::size_of::() as u8 } @@ -660,93 +750,160 @@ impl Anyfunc { #[cfg(test)] mod vm_offset_tests { - use super::{Anyfunc, Ctx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable}; + use super::{ + Anyfunc, Ctx, FuncCtx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable, + }; + + // Inspired by https://internals.rust-lang.org/t/discussion-on-offset-of/7440/2. + macro_rules! offset_of { + ($struct:path, $field:ident) => {{ + fn offset() -> usize { + use std::mem; + + let structure = mem::MaybeUninit::<$struct>::uninit(); + + let &$struct { + $field: ref field, .. + } = unsafe { &*structure.as_ptr() }; + + let offset = + (field as *const _ as usize).wrapping_sub(&structure as *const _ as usize); + + assert!((0..=mem::size_of_val(&structure)).contains(&offset)); + + offset + } + + offset() + }}; + } + + #[test] + fn offset_of() { + use std::{mem, ptr::NonNull}; + + struct S0; + + #[repr(C)] + struct S1 { + f1: u8, + f2: u16, + f3: u32, + f4: u64, + f5: u128, + f6: f32, + f7: f64, + f8: NonNull, + f9: Option>, + f10: *mut S0, + z: u8, + } + + assert_eq!(offset_of!(S1, f1), 0); + assert_eq!(offset_of!(S1, f2), 2); + assert_eq!(offset_of!(S1, f3), 4); + assert_eq!(offset_of!(S1, f4), 8); + assert_eq!(offset_of!(S1, f5), 16); + assert_eq!(offset_of!(S1, f6), 32); + assert_eq!(offset_of!(S1, f7), 40); + assert_eq!(offset_of!(S1, f8), 40 + mem::size_of::()); + assert_eq!(offset_of!(S1, f9), 48 + mem::size_of::()); + assert_eq!(offset_of!(S1, f10), 56 + mem::size_of::()); + assert_eq!(offset_of!(S1, z), 64 + mem::size_of::()); + } #[test] fn vmctx() { - assert_eq!(0usize, offset_of!(Ctx => internal).get_byte_offset(),); + assert_eq!(0usize, offset_of!(Ctx, internal)); assert_eq!( Ctx::offset_memories() as usize, - offset_of!(InternalCtx => memories).get_byte_offset(), + offset_of!(InternalCtx, memories), ); assert_eq!( Ctx::offset_tables() as usize, - offset_of!(InternalCtx => tables).get_byte_offset(), + offset_of!(InternalCtx, tables), ); assert_eq!( Ctx::offset_globals() as usize, - offset_of!(InternalCtx => globals).get_byte_offset(), + offset_of!(InternalCtx, globals), ); assert_eq!( Ctx::offset_imported_memories() as usize, - offset_of!(InternalCtx => imported_memories).get_byte_offset(), + offset_of!(InternalCtx, imported_memories), ); assert_eq!( Ctx::offset_imported_tables() as usize, - offset_of!(InternalCtx => imported_tables).get_byte_offset(), + offset_of!(InternalCtx, imported_tables), ); assert_eq!( Ctx::offset_imported_globals() as usize, - offset_of!(InternalCtx => imported_globals).get_byte_offset(), + offset_of!(InternalCtx, imported_globals), ); assert_eq!( Ctx::offset_imported_funcs() as usize, - offset_of!(InternalCtx => imported_funcs).get_byte_offset(), + offset_of!(InternalCtx, imported_funcs), ); assert_eq!( Ctx::offset_intrinsics() as usize, - offset_of!(InternalCtx => intrinsics).get_byte_offset(), + offset_of!(InternalCtx, intrinsics), ); assert_eq!( Ctx::offset_stack_lower_bound() as usize, - offset_of!(InternalCtx => stack_lower_bound).get_byte_offset(), + offset_of!(InternalCtx, stack_lower_bound), ); assert_eq!( Ctx::offset_memory_base() as usize, - offset_of!(InternalCtx => memory_base).get_byte_offset(), + offset_of!(InternalCtx, memory_base), ); assert_eq!( Ctx::offset_memory_bound() as usize, - offset_of!(InternalCtx => memory_bound).get_byte_offset(), + offset_of!(InternalCtx, memory_bound), ); assert_eq!( Ctx::offset_internals() as usize, - offset_of!(InternalCtx => internals).get_byte_offset(), + offset_of!(InternalCtx, internals), ); assert_eq!( Ctx::offset_interrupt_signal_mem() as usize, - offset_of!(InternalCtx => interrupt_signal_mem).get_byte_offset(), + offset_of!(InternalCtx, interrupt_signal_mem), ); assert_eq!( Ctx::offset_local_functions() as usize, - offset_of!(Ctx => local_functions).get_byte_offset(), + offset_of!(Ctx, local_functions), ); } + #[test] + fn func_ctx() { + assert_eq!(FuncCtx::offset_vmctx() as usize, 0,); + + assert_eq!(FuncCtx::offset_func_env() as usize, 8,); + } + #[test] fn imported_func() { assert_eq!( ImportedFunc::offset_func() as usize, - offset_of!(ImportedFunc => func).get_byte_offset(), + offset_of!(ImportedFunc, func), ); assert_eq!( - ImportedFunc::offset_vmctx() as usize, - offset_of!(ImportedFunc => vmctx).get_byte_offset(), + ImportedFunc::offset_func_ctx() as usize, + offset_of!(ImportedFunc, func_ctx), ); } @@ -754,12 +911,12 @@ mod vm_offset_tests { fn local_table() { assert_eq!( LocalTable::offset_base() as usize, - offset_of!(LocalTable => base).get_byte_offset(), + offset_of!(LocalTable, base), ); assert_eq!( LocalTable::offset_count() as usize, - offset_of!(LocalTable => count).get_byte_offset(), + offset_of!(LocalTable, count), ); } @@ -767,12 +924,12 @@ mod vm_offset_tests { fn local_memory() { assert_eq!( LocalMemory::offset_base() as usize, - offset_of!(LocalMemory => base).get_byte_offset(), + offset_of!(LocalMemory, base), ); assert_eq!( LocalMemory::offset_bound() as usize, - offset_of!(LocalMemory => bound).get_byte_offset(), + offset_of!(LocalMemory, bound), ); } @@ -780,25 +937,19 @@ mod vm_offset_tests { fn local_global() { assert_eq!( LocalGlobal::offset_data() as usize, - offset_of!(LocalGlobal => data).get_byte_offset(), + offset_of!(LocalGlobal, data), ); } #[test] fn cc_anyfunc() { - assert_eq!( - Anyfunc::offset_func() as usize, - offset_of!(Anyfunc => func).get_byte_offset(), - ); + assert_eq!(Anyfunc::offset_func() as usize, offset_of!(Anyfunc, func),); - assert_eq!( - Anyfunc::offset_vmctx() as usize, - offset_of!(Anyfunc => ctx).get_byte_offset(), - ); + assert_eq!(Anyfunc::offset_vmctx() as usize, offset_of!(Anyfunc, ctx),); assert_eq!( Anyfunc::offset_sig_id() as usize, - offset_of!(Anyfunc => sig_id).get_byte_offset(), + offset_of!(Anyfunc, sig_id), ); } } @@ -916,15 +1067,15 @@ mod vm_ctx_tests { } fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option { - unimplemented!() + unimplemented!("generate_module::get_trampoline") } unsafe fn do_early_trap(&self, _: Box) -> ! { - 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") } } diff --git a/lib/runtime-core/src/vmcalls.rs b/lib/runtime-core/src/vmcalls.rs index 5205a0d69..32aa9367f 100644 --- a/lib/runtime-core/src/vmcalls.rs +++ b/lib/runtime-core/src/vmcalls.rs @@ -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") } diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 8ed986c50..56430f051 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,25 +1,27 @@ [package] name = "wasmer-runtime" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "runtime", "sandbox", "secure"] +categories = ["wasm", "api-bindings"] 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.11.0", optional = true } lazy_static = "1.4" memmap = "0.7" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.8.0" +version = "0.11.0" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.8.0" +version = "0.11.0" optional = true [dev-dependencies] @@ -33,6 +35,7 @@ optional = true [features] default = ["cranelift", "default-backend-cranelift"] +docs = [] cranelift = ["wasmer-clif-backend"] cache = ["cranelift"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] diff --git a/lib/runtime/README.md b/lib/runtime/README.md index 769e4cdb6..ce18500bf 100644 --- a/lib/runtime/README.md +++ b/lib/runtime/README.md @@ -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 diff --git a/lib/runtime/benches/many_instances.rs b/lib/runtime/benches/many_instances.rs index e1c655c78..c10cf34e9 100644 --- a/lib/runtime/benches/many_instances.rs +++ b/lib/runtime/benches/many_instances.rs @@ -4,7 +4,7 @@ use criterion::Criterion; use tempfile::tempdir; use wasmer_runtime::{ cache::{Cache, FileSystemCache, WasmHash}, - compile, func, imports, instantiate, validate, ImportObject, + compile, func, imports, instantiate, validate, }; use wasmer_runtime_core::vm::Ctx; @@ -71,7 +71,7 @@ fn calling_fn_benchmark(c: &mut Criterion) { ); let instance = instantiate(SIMPLE_WASM, &imports).unwrap(); c.bench_function("calling fn", move |b| { - let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap(); + let entry_point = instance.func::("plugin_entrypoint").unwrap(); b.iter(|| entry_point.call(2).unwrap()) }); } diff --git a/lib/runtime/src/cache.rs b/lib/runtime/src/cache.rs index fe39b512a..714fd9f54 100644 --- a/lib/runtime/src/cache.rs +++ b/lib/runtime/src/cache.rs @@ -1,3 +1,7 @@ +//! The cache module provides the common data structures used by compiler backends to allow +//! serializing compiled wasm code to a binary format. The binary format can be persisted, +//! and loaded to allow skipping compilation and fast startup. + use crate::Module; use memmap::Mmap; use std::{ @@ -125,7 +129,7 @@ impl Cache for FileSystemCache { } } -#[cfg(all(test, not(feature = "singlepass")))] +#[cfg(test)] mod tests { use super::*; diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 903dc4783..3febd072c 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -1,5 +1,6 @@ #![deny( dead_code, + missing_docs, nonstandard_style, unused_imports, unused_mut, @@ -86,12 +87,13 @@ //! [`wasmer-clif-backend`]: https://crates.io/crates/wasmer-clif-backend //! [`compile_with`]: fn.compile_with.html -pub use wasmer_runtime_core::backend::Backend; +pub use wasmer_runtime_core::backend::{Backend, Features}; pub use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; 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::import::{ImportObject, LikeNamespace}; 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; @@ -103,6 +105,8 @@ pub use wasmer_runtime_core::{compile_with, validate}; pub use wasmer_runtime_core::{func, imports}; pub mod memory { + //! The memory module contains the implementation data structures and helper functions used to + //! manipulate and access wasm memory. pub use wasmer_runtime_core::memory::{Atomically, Memory, MemoryView}; } @@ -116,6 +120,8 @@ pub mod wasm { } pub mod error { + //! The error module contains the data structures and helper functions used to implement errors that + //! are produced and returned from the wasmer runtime. pub use wasmer_runtime_core::cache::Error as CacheError; pub use wasmer_runtime_core::error::*; } @@ -125,9 +131,14 @@ pub mod units { pub use wasmer_runtime_core::units::{Bytes, Pages}; } +pub mod types { + //! Various types. + pub use wasmer_runtime_core::types::*; +} + pub mod cache; -use wasmer_runtime_core::backend::{Compiler, CompilerConfig}; +pub use wasmer_runtime_core::backend::{Compiler, CompilerConfig}; /// Compile WebAssembly binary code into a [`Module`]. /// This function is useful if it is necessary to @@ -197,6 +208,7 @@ pub fn default_compiler() -> impl Compiler { #[cfg(any( all( feature = "default-backend-llvm", + not(feature = "docs"), any( feature = "default-backend-cranelift", feature = "default-backend-singlepass", @@ -204,6 +216,7 @@ pub fn default_compiler() -> impl Compiler { ) ), all( + not(feature = "docs"), feature = "default-backend-cranelift", any( feature = "default-backend-singlepass", @@ -219,16 +232,16 @@ pub fn default_compiler() -> impl Compiler { "The `default-backend-X` features are mutually exclusive. Please choose just one" ); - #[cfg(feature = "default-backend-llvm")] + #[cfg(all(feature = "default-backend-llvm", not(feature = "docs")))] use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; - #[cfg(any( + #[cfg(all(any( feature = "default-backend-singlepass", - feature = "deterministic-execution" - ))] + all(feature = "deterministic-execution", feature = "singlepass") + ), not(feature = "docs")))] use wasmer_singlepass_backend::SinglePassCompiler as DefaultCompiler; - #[cfg(feature = "default-backend-cranelift")] + #[cfg(any(feature = "default-backend-cranelift", feature = "docs"))] use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; DefaultCompiler::new() @@ -252,12 +265,11 @@ pub fn compiler_for_backend(backend: Backend) -> Option> { #[cfg(feature = "llvm")] Backend::LLVM => Some(Box::new(wasmer_llvm_backend::LLVMCompiler::new())), - #[cfg(any( - not(feature = "llvm"), - not(feature = "singlepass"), - not(feature = "cranelift"), - not(feature = "deterministic-execution"), - ))] + #[cfg(not(all( + feature = "llvm", + any(feature = "singlepass", feature = "deterministic-execution"), + feature = "cranelift", + )))] _ => None, } } diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index e3a40548e..eb292dbcb 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,24 +1,28 @@ [package] name = "wasmer-singlepass-backend" -version = "0.8.0" +version = "0.11.0" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] +keywords = ["wasm", "webassembly", "compiler", "JIT", "AOT"] +categories = ["wasm"] edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.8.0" } -wasmparser = "0.39.1" -dynasm = "0.3.2" -dynasmrt = "0.3.1" +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } +dynasm = "0.5" +dynasmrt = "0.5" lazy_static = "1.4" byteorder = "1.3" nix = "0.15" libc = "0.2.60" smallvec = "0.6" +serde = "1.0" +serde_derive = "1.0" +bincode = "1.2" [features] default = [] -deterministic-execution = ["wasmparser/deterministic", "wasmer-runtime-core/deterministic-execution"] +deterministic-execution = ["wasmparser/deterministic", "wasmer-runtime-core/deterministic-execution"] \ No newline at end of file diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 7e3a9ed5f..eee81e3b5 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -4,17 +4,28 @@ use crate::emitter_x64::*; use crate::machine::*; use crate::protect_unix; -use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; +#[cfg(target_arch = "aarch64")] +use dynasmrt::aarch64::Assembler; +#[cfg(target_arch = "x86_64")] +use dynasmrt::x64::Assembler; +use dynasmrt::{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::{ - sys::Memory, Backend, CacheGen, CompilerConfig, MemoryBoundCheckMode, RunnableModule, Token, + get_inline_breakpoint_size, + sys::{Memory, Protect}, + Architecture, Backend, CacheGen, CompilerConfig, MemoryBoundCheckMode, RunnableModule, + Token, }, cache::{Artifact, Error as CacheError}, codegen::*, @@ -27,15 +38,23 @@ 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}; +#[cfg(target_arch = "aarch64")] +#[allow(dead_code)] +static ARCH: Architecture = Architecture::Aarch64; +#[cfg(target_arch = "x86_64")] +#[allow(dead_code)] +static ARCH: Architecture = Architecture::X64; + +#[cfg(target_arch = "x86_64")] lazy_static! { /// Performs a System V call to `target` with [stack_top..stack_base] as the argument list, from right to left. static ref CONSTRUCT_STACK_AND_CALL_WASM: unsafe extern "C" fn (stack_top: *const u64, stack_base: *const u64, ctx: *mut vm::Ctx, target: *const vm::Func) -> u64 = { @@ -118,8 +137,47 @@ 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 + }; +} + +#[cfg(target_arch = "aarch64")] +#[repr(C)] +#[allow(dead_code)] +struct CallCtx { + ctx: *mut vm::Ctx, + stack: *mut u64, + target: *mut u8, +} + +#[cfg(target_arch = "aarch64")] +lazy_static! { + /// Switches stack and executes the provided callback. + static ref SWITCH_STACK: unsafe extern "C" fn (stack: *mut u64, cb: extern "C" fn (*mut u8) -> u64, userdata: *mut u8) -> u64 = { + let mut assembler = Assembler::new().unwrap(); + let offset = assembler.offset(); + dynasm!( + assembler + ; .arch aarch64 + ; sub x0, x0, 16 + ; mov x8, sp + ; str x8, [x0, 0] + ; str x30, [x0, 8] + ; adr x30, >done + ; mov sp, x0 + ; mov x0, x2 + ; br x1 + ; done: + ; ldr x30, [sp, 8] + ; ldr x8, [sp, 0] + ; mov sp, x8 + ; br x30 + ); + let buf = assembler.finalize().unwrap(); + let ret = unsafe { mem::transmute(buf.ptr(offset)) }; + mem::forget(buf); ret }; } @@ -173,8 +231,6 @@ unsafe impl Sync for FuncPtr {} pub struct X64ExecutionContext { #[allow(dead_code)] code: CodeMemory, - #[allow(dead_code)] - functions: Vec, function_pointers: Vec, function_offsets: Vec, signatures: Arc>, @@ -183,6 +239,28 @@ pub struct X64ExecutionContext { msm: ModuleStateMap, } +/// On-disk cache format. +/// Offsets are relative to the start of the executable image. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CacheImage { + /// The executable image. + code: Vec, + + /// Offsets to the start of each function. Including trampoline, if any. + /// Trampolines are only present on AArch64. + /// On x86-64, `function_pointers` are identical to `function_offsets`. + function_pointers: Vec, + + /// Offsets to the start of each function after trampoline. + function_offsets: Vec, + + /// Number of imported functions. + func_import_count: usize, + + /// Module state map. + msm: ModuleStateMap, +} + #[derive(Debug)] pub struct ControlFrame { pub label: DynamicLabel, @@ -201,6 +279,25 @@ pub enum IfElseState { Else, } +pub struct SinglepassCache { + buffer: Arc<[u8]>, +} + +impl CacheGen for SinglepassCache { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + let mut memory = Memory::with_size_protect(self.buffer.len(), Protect::ReadWrite) + .map_err(CacheError::SerializeError)?; + + let buffer = &*self.buffer; + + unsafe { + memory.as_slice_mut()[..buffer.len()].copy_from_slice(buffer); + } + + Ok(([].as_ref().into(), memory)) + } +} + impl RunnableModule for X64ExecutionContext { fn get_func( &self, @@ -227,7 +324,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 +335,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 +352,8 @@ impl RunnableModule for X64ExecutionContext { } fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { - 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, - *const u64, - *mut u64, - ), + _trampoline: Trampoline, ctx: *mut vm::Ctx, func: NonNull, args: *const u64, @@ -275,21 +364,137 @@ impl RunnableModule for X64ExecutionContext { ) -> bool { let rm: &Box = &(&*(*ctx).module).runnable_module; let execution_context = - ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + + let args = + slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1); - let args = std::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( || { - CONSTRUCT_STACK_AND_CALL_WASM( - args_reverse.as_ptr(), - args_reverse.as_ptr().offset(args_reverse.len() as isize), - ctx, - func.as_ptr(), - ) + #[cfg(target_arch = "x86_64")] + { + let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); + CONSTRUCT_STACK_AND_CALL_WASM( + args_reverse.as_ptr(), + args_reverse.as_ptr().offset(args_reverse.len() as isize), + ctx, + func.as_ptr(), + ) + } + #[cfg(target_arch = "aarch64")] + { + struct CallCtx<'a> { + args: &'a [u64], + ctx: *mut vm::Ctx, + callable: NonNull, + } + extern "C" fn call_fn(f: *mut u8) -> u64 { + unsafe { + let f = &*(f as *const CallCtx); + let callable: extern "C" fn( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) + -> u64 = std::mem::transmute(f.callable); + let mut args = f.args.iter(); + callable( + f.ctx as u64, + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + args.next().cloned().unwrap_or(0), + ) + } + } + let mut cctx = CallCtx { + args: &args, + ctx: ctx, + callable: func, + }; + use libc::{ + mmap, munmap, MAP_ANON, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, + PROT_WRITE, + }; + const STACK_SIZE: usize = 1048576 * 1024; // 1GB of virtual address space for stack. + let stack_ptr = mmap( + ::std::ptr::null_mut(), + STACK_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + -1, + 0, + ); + if stack_ptr as isize == -1 { + panic!("unable to allocate stack"); + } + // TODO: Mark specific regions in the stack as PROT_NONE. + let ret = SWITCH_STACK( + (stack_ptr as *mut u8).offset(STACK_SIZE as isize) as *mut u64, + call_fn, + &mut cctx as *mut CallCtx as *mut u8, + ); + munmap(stack_ptr, STACK_SIZE); + ret + } }, Some(execution_context.breakpoints.clone()), ) { @@ -367,17 +572,23 @@ impl ModuleCodeGenerator for X64ModuleCodeGenerator { fn new() -> X64ModuleCodeGenerator { + let a = Assembler::new().unwrap(); + X64ModuleCodeGenerator { functions: vec![], signatures: None, function_signatures: None, function_labels: Some(HashMap::new()), - assembler: Some(Assembler::new().unwrap()), + assembler: Some(a), func_import_count: 0, config: None, } } + fn new_with_target(_: Option, _: Option, _: Option) -> Self { + unimplemented!("cross compilation is not available for singlepass backend") + } + fn backend_id() -> Backend { Backend::Singlepass } @@ -409,15 +620,12 @@ impl ModuleCodeGenerator .or_insert_with(|| (assembler.new_dynamic_label(), None)); begin_label_info.1 = Some(begin_offset); + assembler.arch_emit_entry_trampoline(); let begin_label = begin_label_info.0; let mut machine = Machine::new(); machine.track_state = self.config.as_ref().unwrap().track_state; - dynasm!( - assembler - ; => begin_label - //; int 3 - ); + assembler.emit_label(begin_label); let code = X64FunctionCode { local_function_id: self.functions.len(), @@ -510,29 +718,41 @@ impl ModuleCodeGenerator .map(|x| (x.offset, x.fsm.clone())) .collect(); - struct Placeholder; - impl CacheGen for Placeholder { - fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { - Err(CacheError::Unknown( - "the singlepass backend doesn't support caching yet".to_string(), - )) - } - } + let msm = ModuleStateMap { + local_functions: local_function_maps, + total_size, + }; + + let cache_image = CacheImage { + code: output.to_vec(), + function_pointers: out_labels + .iter() + .map(|x| { + (x.0 as usize) + .checked_sub(output.as_ptr() as usize) + .unwrap() + }) + .collect(), + function_offsets: out_offsets.iter().map(|x| x.0 as usize).collect(), + func_import_count: self.func_import_count, + msm: msm.clone(), + }; + + let cache = SinglepassCache { + buffer: Arc::from(bincode::serialize(&cache_image).unwrap().into_boxed_slice()), + }; + Ok(( X64ExecutionContext { code: output, - functions: self.functions, signatures: self.signatures.as_ref().unwrap().clone(), breakpoints: breakpoints, func_import_count: self.func_import_count, function_pointers: out_labels, function_offsets: out_offsets, - msm: ModuleStateMap { - local_functions: local_function_maps, - total_size, - }, + msm: msm, }, - Box::new(Placeholder), + Box::new(cache), )) } @@ -555,6 +775,7 @@ impl ModuleCodeGenerator let a = self.assembler.as_mut().unwrap(); let offset = a.offset(); + a.arch_emit_entry_trampoline(); let label = a.get_label(); a.emit_label(label); labels.insert(id, (label, Some(offset))); @@ -562,21 +783,33 @@ impl ModuleCodeGenerator // Emits a tail call trampoline that loads the address of the target import function // from Ctx and jumps to it. + let imported_funcs_addr = vm::Ctx::offset_imported_funcs(); + let imported_func = vm::ImportedFunc::size() as usize * id; + let imported_func_addr = imported_func + vm::ImportedFunc::offset_func() as usize; + let imported_func_ctx_addr = imported_func + vm::ImportedFunc::offset_func_ctx() as usize; + let imported_func_ctx_vmctx_addr = vm::FuncCtx::offset_vmctx() as usize; + a.emit_mov( Size::S64, - Location::Memory(GPR::RDI, vm::Ctx::offset_imported_funcs() as i32), + Location::Memory(GPR::RDI, imported_funcs_addr as i32), Location::GPR(GPR::RAX), ); a.emit_mov( Size::S64, - Location::Memory( - GPR::RAX, - (vm::ImportedFunc::size() as usize * id + vm::ImportedFunc::offset_func() as usize) - as i32, - ), + Location::Memory(GPR::RAX, imported_func_ctx_addr as i32), + Location::GPR(GPR::RDI), + ); + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDI, imported_func_ctx_vmctx_addr as i32), + Location::GPR(GPR::RDI), + ); + a.emit_mov( + Size::S64, + Location::Memory(GPR::RAX, imported_func_addr as i32), Location::GPR(GPR::RAX), ); - a.emit_jmp_location(Location::GPR(GPR::RAX)); + a.emit_host_redirection(GPR::RAX); self.func_import_count += 1; @@ -591,10 +824,45 @@ impl ModuleCodeGenerator })); Ok(()) } - unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result { - Err(CacheError::Unknown( - "the singlepass compiler API doesn't support caching yet".to_string(), - )) + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + + let cache_image: CacheImage = bincode::deserialize(memory.as_slice()) + .map_err(|x| CacheError::DeserializeError(format!("{:?}", x)))?; + + let mut code_mem = CodeMemory::new(cache_image.code.len()); + code_mem[0..cache_image.code.len()].copy_from_slice(&cache_image.code); + code_mem.make_executable(); + + let function_pointers: Vec = cache_image + .function_pointers + .iter() + .map(|&x| FuncPtr(code_mem.as_ptr().offset(x as isize) as *const FuncPtrInner)) + .collect(); + let function_offsets: Vec = cache_image + .function_offsets + .iter() + .cloned() + .map(AssemblyOffset) + .collect(); + + let ec = X64ExecutionContext { + code: code_mem, + function_pointers, + function_offsets, + signatures: Arc::new(info.signatures.clone()), + breakpoints: Arc::new(HashMap::new()), + func_import_count: cache_image.func_import_count, + msm: cache_image.msm, + }; + Ok(ModuleInner { + runnable_module: Box::new(ec), + cache_gen: Box::new(SinglepassCache { + buffer: Arc::from(memory.as_slice().to_vec().into_boxed_slice()), + }), + + info, + }) } } @@ -619,6 +887,29 @@ impl X64FunctionCode { .insert(m.state.wasm_inst_offset, SuspendOffset::Trappable(offset)); } + #[allow(dead_code)] + fn mark_inline_breakpoint( + a: &mut Assembler, + m: &Machine, + fsm: &mut FunctionStateMap, + control_stack: &mut [ControlFrame], + ) { + let state_diff_id = Self::get_state_diff(m, fsm, control_stack); + let offset = a.get_offset().0; + fsm.trappable_offsets.insert( + offset, + OffsetInfo { + end_offset: offset + + get_inline_breakpoint_size(ARCH, Backend::Singlepass) + .expect("cannot get inline breakpoint size"), + activate_offset: offset, + diff_id: state_diff_id, + }, + ); + fsm.wasm_offset_to_target_offset + .insert(m.state.wasm_inst_offset, SuspendOffset::Trappable(offset)); + } + /// Moves `loc` to a valid location for `div`/`idiv`. fn emit_relaxed_xdiv( a: &mut Assembler, @@ -654,32 +945,34 @@ impl X64FunctionCode { sz_dst: Size, dst: Location, ) { - let tmp_src = m.acquire_temp_gpr().unwrap(); - let tmp_dst = m.acquire_temp_gpr().unwrap(); - - match src { - Location::Imm32(_) | Location::Imm64(_) => { - a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); - src = Location::GPR(tmp_src); - } - Location::Memory(_, _) | Location::GPR(_) => {} - _ => unreachable!(), - } - - match dst { + let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst { Location::Imm32(_) | Location::Imm64(_) => unreachable!(), Location::Memory(_, _) => { + let tmp_dst = m.acquire_temp_gpr().unwrap(); op(a, sz_src, src, sz_dst, Location::GPR(tmp_dst)); a.emit_mov(Size::S64, Location::GPR(tmp_dst), dst); + + m.release_temp_gpr(tmp_dst); } Location::GPR(_) => { op(a, sz_src, src, sz_dst, dst); } _ => unreachable!(), - } + }; - m.release_temp_gpr(tmp_dst); - m.release_temp_gpr(tmp_src); + match src { + Location::Imm32(_) | Location::Imm64(_) => { + let tmp_src = m.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); + src = Location::GPR(tmp_src); + + inner(m, a, src); + + m.release_temp_gpr(tmp_src); + } + Location::GPR(_) | Location::Memory(_, _) => inner(m, a, src), + _ => unreachable!(), + } } /// Moves `src` and `dst` to valid locations for generic instructions. @@ -1193,7 +1486,10 @@ impl X64FunctionCode { value_stack.push(ret); Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); - a.emit_and(Size::S32, Location::Imm32(1), ret); // FIXME: Why? + + // Workaround for behavior inconsistency among different backing implementations. + // (all bits or only the least significant bit are set to one?) + a.emit_and(Size::S32, Location::Imm32(1), ret); } /// Floating point (AVX) binary operation with both operands popped from the virtual stack. @@ -1737,7 +2033,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 +2149,7 @@ impl FunctionCodeGenerator 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(()) } @@ -1917,11 +2213,18 @@ impl FunctionCodeGenerator for X64FunctionCode { Event::Internal(x) => { match x { InternalEvent::Breakpoint(callback) => { - a.emit_bkpt(); + use wasmer_runtime_core::backend::InlineBreakpointType; self.breakpoints .as_mut() .unwrap() .insert(a.get_offset(), callback); + Self::mark_trappable( + a, + &self.machine, + &mut self.fsm, + &mut self.control_stack, + ); + a.emit_inline_breakpoint(InlineBreakpointType::Middleware); } InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} InternalEvent::GetInternal(idx) => { @@ -2341,18 +2644,117 @@ impl FunctionCodeGenerator 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!(), + }; + + if a.arch_has_xzcnt() { + a.arch_emit_lzcnt(Size::S32, Location::GPR(src), Location::GPR(dst)); + } else { + 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!(), + }; + + if a.arch_has_xzcnt() { + a.arch_emit_tzcnt(Size::S32, Location::GPR(src), Location::GPR(dst)); + } else { + 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 +3034,117 @@ impl FunctionCodeGenerator 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!(), + }; + + if a.arch_has_xzcnt() { + a.arch_emit_lzcnt(Size::S64, Location::GPR(src), Location::GPR(dst)); + } else { + 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!(), + }; + + if a.arch_has_xzcnt() { + a.arch_emit_tzcnt(Size::S64, Location::GPR(src), Location::GPR(dst)); + } else { + 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, @@ -2762,6 +3263,106 @@ impl FunctionCodeGenerator for X64FunctionCode { ret, ); } + Operator::I32Extend8S => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S8, + loc, + Size::S32, + ret, + ); + } + Operator::I32Extend16S => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S16, + loc, + Size::S32, + ret, + ); + } + Operator::I64Extend8S => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S8, + loc, + Size::S64, + ret, + ); + } + Operator::I64Extend16S => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S16, + loc, + Size::S64, + ret, + ); + } + Operator::I64Extend32S => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations( + a, + &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S32, + loc, + Size::S64, + ret, + ); + } Operator::I32WrapI64 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); @@ -2812,18 +3413,234 @@ impl FunctionCodeGenerator 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 => { + if !a.arch_supports_canonicalize_nan() { + Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxss, + ); + } else { + 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; + + 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::Imm32(0x7FC0_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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 => { + if !a.arch_supports_canonicalize_nan() { + Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminss, + ); + } else { + 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; + + 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::Imm32(0x8000_0000), // Negative zero + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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::Imm32(0x7FC0_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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, @@ -2952,11 +3769,34 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S32, loc, Location::GPR(tmp)); - a.emit_btc_gpr_imm8_32(31, tmp); - a.emit_mov(Size::S32, Location::GPR(tmp), ret); - self.machine.release_temp_gpr(tmp); + + if a.arch_has_fneg() { + let tmp = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp), + ); + a.arch_emit_f32_neg(tmp, tmp); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp), + ret, + ); + self.machine.release_temp_xmm(tmp); + } else { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + a.emit_btc_gpr_imm8_32(31, tmp); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } } Operator::F64Const { value } => { @@ -2990,18 +3830,234 @@ impl FunctionCodeGenerator 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 => { + if !a.arch_supports_canonicalize_nan() { + Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxsd, + ); + } else { + 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; + + 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(0x7FF8_0000_0000_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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 => { + if !a.arch_supports_canonicalize_nan() { + Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminsd, + ); + } else { + 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; + + 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(0x8000_0000_0000_0000), // Negative zero + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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(0x7FF8_0000_0000_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + a.emit_mov(Size::S64, Location::GPR(tmpg1), 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, @@ -3145,11 +4201,33 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S64, loc, Location::GPR(tmp)); - a.emit_btc_gpr_imm8_64(63, tmp); - a.emit_mov(Size::S64, Location::GPR(tmp), ret); - self.machine.release_temp_gpr(tmp); + if a.arch_has_fneg() { + let tmp = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp), + ); + a.arch_emit_f64_neg(tmp, tmp); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp), + ret, + ); + self.machine.release_temp_xmm(tmp); + } else { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + a.emit_btc_gpr_imm8_64(63, tmp); + a.emit_mov(Size::S64, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } } Operator::F64PromoteF32 => Self::emit_fp_unop_avx( @@ -3260,24 +4338,48 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S32, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i32_trunc_uf32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); - a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I32TruncSF32 => { @@ -3289,30 +4391,55 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S32, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f32_int_conv_check( - a, - &mut self.machine, - tmp_in, - -2147483904.0, - 2147483648.0, - ); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i32_trunc_sf32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - a.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -2147483904.0, + 2147483648.0, + ); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncSF32 => { @@ -3324,47 +4451,57 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S32, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f32_int_conv_check( - a, - &mut self.machine, - tmp_in, - -9223373136366403584.0, - 9223372036854775808.0, - ); - a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i64_trunc_sf32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -9223373136366403584.0, + 9223372036854775808.0, + ); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncUF32 => { - /* - ; movq xmm5, r15 - ; mov r15d, 1593835520u32 as i32 //float 9.22337203E+18 - ; movd xmm1, r15d - ; movd xmm2, Rd(reg as u8) - ; movd xmm3, Rd(reg as u8) - ; subss xmm2, xmm1 - ; cvttss2si Rq(reg as u8), xmm2 - ; mov r15, QWORD 0x8000000000000000u64 as i64 - ; xor r15, Rq(reg as u8) - ; cvttss2si Rq(reg as u8), xmm3 - ; ucomiss xmm3, xmm1 - ; cmovae Rq(reg as u8), r15 - ; movq r15, xmm5 - */ let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let ret = self.machine.acquire_locations( @@ -3373,54 +4510,79 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S32, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f32_int_conv_check( - a, - &mut self.machine, - tmp_in, - -1.0, - 18446744073709551616.0, - ); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i64_trunc_uf32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 - let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 - let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 - let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f32_int_conv_check( + a, + &mut self.machine, + tmp_in, + -1.0, + 18446744073709551616.0, + ); - a.emit_mov( - Size::S32, - Location::Imm32(1593835520u32), - Location::GPR(tmp), - ); //float 9.22337203E+18 - a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x1)); - a.emit_mov(Size::S32, Location::XMM(tmp_in), Location::XMM(tmp_x2)); - a.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); - a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov( - Size::S64, - Location::Imm64(0x8000000000000000u64), - Location::GPR(tmp), - ); - a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); - a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); - a.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); - a.emit_cmovae_gpr_64(tmp, tmp_out); - a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 - self.machine.release_temp_xmm(tmp_x2); - self.machine.release_temp_xmm(tmp_x1); - self.machine.release_temp_gpr(tmp); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_mov( + Size::S32, + Location::Imm32(1593835520u32), + Location::GPR(tmp), + ); //float 9.22337203E+18 + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x1)); + a.emit_mov(Size::S32, Location::XMM(tmp_in), Location::XMM(tmp_x2)); + a.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + a.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); + a.emit_cmovae_gpr_64(tmp, tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I32TruncUF64 => { @@ -3432,24 +4594,49 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S64, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i32_trunc_uf64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I32TruncSF64 => { @@ -3461,35 +4648,60 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - let real_in = match loc { - Location::Imm32(_) | Location::Imm64(_) => { - a.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); - a.emit_mov(Size::S64, Location::GPR(tmp_out), Location::XMM(tmp_in)); - tmp_in - } - Location::XMM(x) => x, - _ => { - a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); - tmp_in - } - }; + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i32_trunc_sf64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_f64_int_conv_check( - a, - &mut self.machine, - real_in, - -2147483649.0, - 2147483648.0, - ); + let real_in = match loc { + Location::Imm32(_) | Location::Imm64(_) => { + a.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); + a.emit_mov(Size::S64, Location::GPR(tmp_out), Location::XMM(tmp_in)); + tmp_in + } + Location::XMM(x) => x, + _ => { + a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + tmp_in + } + }; - a.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + real_in, + -2147483649.0, + 2147483648.0, + ); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); + a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncSF64 => { @@ -3501,30 +4713,55 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S64, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f64_int_conv_check( - a, - &mut self.machine, - tmp_in, - -9223372036854777856.0, - 9223372036854775808.0, - ); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i64_trunc_sf64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + tmp_in, + -9223372036854777856.0, + 9223372036854775808.0, + ); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncUF64 => { @@ -3536,54 +4773,79 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_gpr().unwrap(); - let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 - Self::emit_relaxed_binop( - a, - &mut self.machine, - Assembler::emit_mov, - Size::S64, - loc, - Location::XMM(tmp_in), - ); - Self::emit_f64_int_conv_check( - a, - &mut self.machine, - tmp_in, - -1.0, - 18446744073709551616.0, - ); + if a.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + a.arch_emit_i64_trunc_uf64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 - let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 - let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 - let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + tmp_in, + -1.0, + 18446744073709551616.0, + ); - a.emit_mov( - Size::S64, - Location::Imm64(4890909195324358656u64), - Location::GPR(tmp), - ); //double 9.2233720368547758E+18 - a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x1)); - a.emit_mov(Size::S64, Location::XMM(tmp_in), Location::XMM(tmp_x2)); - a.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov( - Size::S64, - Location::Imm64(0x8000000000000000u64), - Location::GPR(tmp), - ); - a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); - a.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); - a.emit_cmovae_gpr_64(tmp, tmp_out); - a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 - self.machine.release_temp_xmm(tmp_x2); - self.machine.release_temp_xmm(tmp_x1); - self.machine.release_temp_gpr(tmp); - self.machine.release_temp_xmm(tmp_in); - self.machine.release_temp_gpr(tmp_out); + a.emit_mov( + Size::S64, + Location::Imm64(4890909195324358656u64), + Location::GPR(tmp), + ); //double 9.2233720368547758E+18 + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x1)); + a.emit_mov(Size::S64, Location::XMM(tmp_in), Location::XMM(tmp_x2)); + a.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + a.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + a.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); + a.emit_cmovae_gpr_64(tmp, tmp_out); + a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::F32ConvertSI32 => { @@ -3595,15 +4857,40 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2ss_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f32_convert_si32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F32ConvertUI32 => { let loc = @@ -3614,15 +4901,39 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f32_convert_ui32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F32ConvertSI64 => { let loc = @@ -3633,15 +4944,39 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f32_convert_si64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F32ConvertUI64 => { let loc = @@ -3652,31 +4987,55 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - let tmp = self.machine.acquire_temp_gpr().unwrap(); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f32_convert_ui64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); - let do_convert = a.get_label(); - let end_convert = a.get_label(); + let do_convert = a.get_label(); + let end_convert = a.get_label(); - a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); - a.emit_test_gpr_64(tmp_in); - a.emit_jmp(Condition::Signed, do_convert); - a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_jmp(Condition::None, end_convert); - a.emit_label(do_convert); - a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); - a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); - a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); - a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); - a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_vaddss(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); - a.emit_label(end_convert); - a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_test_gpr_64(tmp_in); + a.emit_jmp(Condition::Signed, do_convert); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_jmp(Condition::None, end_convert); + a.emit_label(do_convert); + a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + a.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_vaddss(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + a.emit_label(end_convert); + a.emit_mov(Size::S32, Location::XMM(tmp_out), ret); - self.machine.release_temp_gpr(tmp); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertSI32 => { @@ -3688,15 +5047,40 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2sd_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f64_convert_si32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertUI32 => { let loc = @@ -3707,15 +5091,40 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f64_convert_ui32(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + a.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertSI64 => { let loc = @@ -3726,15 +5135,40 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); - a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f64_convert_si64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertUI64 => { let loc = @@ -3745,31 +5179,56 @@ impl FunctionCodeGenerator for X64FunctionCode { false, )[0]; self.value_stack.push(ret); - let tmp_out = self.machine.acquire_temp_xmm().unwrap(); - let tmp_in = self.machine.acquire_temp_gpr().unwrap(); - let tmp = self.machine.acquire_temp_gpr().unwrap(); - let do_convert = a.get_label(); - let end_convert = a.get_label(); + if a.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + a.arch_emit_f64_convert_ui64(tmp_in, tmp_out); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); - a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); - a.emit_test_gpr_64(tmp_in); - a.emit_jmp(Condition::Signed, do_convert); - a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_jmp(Condition::None, end_convert); - a.emit_label(do_convert); - a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); - a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); - a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); - a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); - a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); - a.emit_vaddsd(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); - a.emit_label(end_convert); - a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + let do_convert = a.get_label(); + let end_convert = a.get_label(); - self.machine.release_temp_gpr(tmp); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + a.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + a.emit_test_gpr_64(tmp_in); + a.emit_jmp(Condition::Signed, do_convert); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_jmp(Condition::None, end_convert); + a.emit_label(do_convert); + a.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + a.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + a.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + a.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + a.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + a.emit_vaddsd(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + a.emit_label(end_convert); + a.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::Call { function_index } => { @@ -3916,10 +5375,17 @@ impl FunctionCodeGenerator for X64FunctionCode { a, &mut self.machine, |a| { - a.emit_call_location(Location::Memory( - GPR::RAX, - (vm::Anyfunc::offset_func() as usize) as i32, - )); + if a.arch_requires_indirect_call_trampoline() { + a.arch_emit_indirect_call_with_trampoline(Location::Memory( + GPR::RAX, + (vm::Anyfunc::offset_func() as usize) as i32, + )); + } else { + a.emit_call_location(Location::Memory( + GPR::RAX, + (vm::Anyfunc::offset_func() as usize) as i32, + )); + } }, params.iter().map(|x| *x), Some((&mut self.fsm, &mut self.control_stack)), @@ -4153,9 +5619,15 @@ impl FunctionCodeGenerator for X64FunctionCode { a, &mut self.machine, |a| { - a.emit_call_location(Location::GPR(GPR::RAX)); + let label = a.get_label(); + let after = a.get_label(); + a.emit_jmp(Condition::None, after); + a.emit_label(label); + a.emit_host_redirection(GPR::RAX); + a.emit_label(after); + a.emit_call_label(label); }, - ::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( @@ -4192,10 +5664,16 @@ impl FunctionCodeGenerator for X64FunctionCode { a, &mut self.machine, |a| { - a.emit_call_location(Location::GPR(GPR::RAX)); + let label = a.get_label(); + let after = a.get_label(); + a.emit_jmp(Condition::None, after); + a.emit_label(label); + a.emit_host_redirection(GPR::RAX); + a.emit_label(after); + a.emit_call_label(label); }, - ::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, ); @@ -4989,7 +6467,9 @@ impl FunctionCodeGenerator for X64FunctionCode { a.emit_lea_label(table_label, Location::GPR(GPR::RCX)); a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); - a.emit_imul_imm32_gpr64(5, GPR::RDX); + + let instr_size = a.get_jmp_instr_size(); + a.emit_imul_imm32_gpr64(instr_size as _, GPR::RDX); a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); a.emit_jmp_location(Location::GPR(GPR::RDX)); diff --git a/lib/singlepass-backend/src/emitter_x64.rs b/lib/singlepass-backend/src/emitter_x64.rs index 2e747f7ec..0bf6fc2b0 100644 --- a/lib/singlepass-backend/src/emitter_x64.rs +++ b/lib/singlepass-backend/src/emitter_x64.rs @@ -1,5 +1,13 @@ use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; -pub use wasmer_runtime_core::state::x64::{GPR, XMM}; +use wasmer_runtime_core::backend::InlineBreakpointType; +pub use wasmer_runtime_core::state::x64_decl::{GPR, XMM}; + +fn _dummy(_a: &Assembler) { + dynasm!( + _a + ; .arch x64 + ); +} #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Location { @@ -36,7 +44,7 @@ pub enum Size { S64, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[allow(dead_code)] pub enum XMMOrMemory { XMM(XMM), @@ -56,6 +64,7 @@ pub trait Emitter { fn get_label(&mut self) -> Self::Label; fn get_offset(&self) -> Self::Offset; + fn get_jmp_instr_size(&self) -> u8; fn emit_u64(&mut self, x: u64); @@ -90,8 +99,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 +114,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 +150,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 +184,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); @@ -172,6 +195,100 @@ pub trait Emitter { fn emit_call_location(&mut self, loc: Location); fn emit_bkpt(&mut self); + + fn emit_host_redirection(&mut self, target: GPR); + fn emit_inline_breakpoint(&mut self, ty: InlineBreakpointType); + + fn arch_has_itruncf(&self) -> bool { + false + } + fn arch_emit_i32_trunc_sf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_sf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_uf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_uf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_sf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_sf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_uf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_uf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + + fn arch_has_fconverti(&self) -> bool { + false + } + fn arch_emit_f32_convert_si32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_si64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_ui32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_ui64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_si32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_si64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_ui32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_ui64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + + fn arch_has_fneg(&self) -> bool { + false + } + fn arch_emit_f32_neg(&mut self, _src: XMM, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_neg(&mut self, _src: XMM, _dst: XMM) { + unimplemented!() + } + + fn arch_has_xzcnt(&self) -> bool { + false + } + fn arch_emit_lzcnt(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!() + } + fn arch_emit_tzcnt(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!() + } + + fn arch_supports_canonicalize_nan(&self) -> bool { + true + } + + fn arch_requires_indirect_call_trampoline(&self) -> bool { + false + } + + fn arch_emit_indirect_call_with_trampoline(&mut self, _loc: Location) { + unimplemented!() + } + + // Emits entry trampoline just before the real function. + fn arch_emit_entry_trampoline(&mut self) {} } macro_rules! unop_gpr { @@ -369,6 +486,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 +504,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 +531,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 +549,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 +576,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 +594,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]), }, } } @@ -466,6 +631,10 @@ impl Emitter for Assembler { self.offset() } + fn get_jmp_instr_size(&self) -> u8 { + 5 + } + fn emit_u64(&mut self, x: u64) { self.push_u64(x); } @@ -647,18 +816,14 @@ impl Emitter for Assembler { match (sz, src) { (Size::S64, Location::Imm32(src)) => dynasm!(self ; push src as i32), (Size::S64, Location::GPR(src)) => dynasm!(self ; push Rq(src as u8)), - (Size::S64, Location::Memory(src, disp)) => { - dynasm!(self ; push QWORD [Rq(src as u8) + disp]) - } + (Size::S64, Location::Memory(src, disp)) => dynasm!(self ; push QWORD [Rq(src as u8) + disp]), _ => panic!("singlepass can't emit PUSH {:?} {:?}", sz, src), } } fn emit_pop(&mut self, sz: Size, dst: Location) { match (sz, dst) { (Size::S64, Location::GPR(dst)) => dynasm!(self ; pop Rq(dst as u8)), - (Size::S64, Location::Memory(dst, disp)) => { - dynasm!(self ; pop QWORD [Rq(dst as u8) + disp]) - } + (Size::S64, Location::Memory(dst, disp)) => dynasm!(self ; pop QWORD [Rq(dst as u8) + disp]), _ => panic!("singlepass can't emit POP {:?} {:?}", sz, dst), } } @@ -680,21 +845,13 @@ impl Emitter for Assembler { fn emit_neg(&mut self, sz: Size, value: Location) { match (sz, value) { (Size::S8, Location::GPR(value)) => dynasm!(self ; neg Rb(value as u8)), - (Size::S8, Location::Memory(value, disp)) => { - dynasm!(self ; neg [Rq(value as u8) + disp]) - } + (Size::S8, Location::Memory(value, disp)) => dynasm!(self ; neg [Rq(value as u8) + disp]), (Size::S16, Location::GPR(value)) => dynasm!(self ; neg Rw(value as u8)), - (Size::S16, Location::Memory(value, disp)) => { - dynasm!(self ; neg [Rq(value as u8) + disp]) - } + (Size::S16, Location::Memory(value, disp)) => dynasm!(self ; neg [Rq(value as u8) + disp]), (Size::S32, Location::GPR(value)) => dynasm!(self ; neg Rd(value as u8)), - (Size::S32, Location::Memory(value, disp)) => { - dynasm!(self ; neg [Rq(value as u8) + disp]) - } + (Size::S32, Location::Memory(value, disp)) => dynasm!(self ; neg [Rq(value as u8) + disp]), (Size::S64, Location::GPR(value)) => dynasm!(self ; neg Rq(value as u8)), - (Size::S64, Location::Memory(value, disp)) => { - dynasm!(self ; neg [Rq(value as u8) + disp]) - } + (Size::S64, Location::Memory(value, disp)) => dynasm!(self ; neg [Rq(value as u8) + disp]), _ => panic!("singlepass can't emit NEG {:?} {:?}", sz, value), } } @@ -753,17 +910,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 +1102,27 @@ 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 +1159,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,57 +1185,59 @@ 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)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; ucomiss Rx(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; ucomiss Rx(dst as u8), [Rq(base as u8) + disp]), } } fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM) { match src { XMMOrMemory::XMM(x) => dynasm!(self ; ucomisd Rx(dst as u8), Rx(x as u8)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; ucomisd Rx(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; ucomisd Rx(dst as u8), [Rq(base as u8) + disp]), } } fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR) { match src { XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rd(dst as u8), Rx(x as u8)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; cvttss2si Rd(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; cvttss2si Rd(dst as u8), [Rq(base as u8) + disp]), } } fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR) { match src { XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rq(dst as u8), Rx(x as u8)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; cvttss2si Rq(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; cvttss2si Rq(dst as u8), [Rq(base as u8) + disp]), } } fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR) { match src { XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rd(dst as u8), Rx(x as u8)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; cvttsd2si Rd(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; cvttsd2si Rd(dst as u8), [Rq(base as u8) + disp]), } } fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR) { match src { XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rq(dst as u8), Rx(x as u8)), - XMMOrMemory::Memory(base, disp) => { - dynasm!(self ; cvttsd2si Rq(dst as u8), [Rq(base as u8) + disp]) - } + XMMOrMemory::Memory(base, disp) => dynasm!(self ; cvttsd2si Rq(dst as u8), [Rq(base as u8) + disp]), } } @@ -1080,4 +1266,17 @@ impl Emitter for Assembler { fn emit_bkpt(&mut self) { dynasm!(self ; int 0x3); } + + fn emit_host_redirection(&mut self, target: GPR) { + self.emit_jmp_location(Location::GPR(target)); + } + + fn emit_inline_breakpoint(&mut self, ty: InlineBreakpointType) { + dynasm!(self + ; ud2 + ; .byte 0x0f ; .byte 0xb9u8 as i8 // ud + ; int -1 + ; .byte ty as u8 as i8 + ); + } } diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs index ccea7502e..5f3607b45 100644 --- a/lib/singlepass-backend/src/lib.rs +++ b/lib/singlepass-backend/src/lib.rs @@ -14,11 +14,17 @@ #[cfg(not(any( all(target_os = "macos", target_arch = "x86_64"), all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), )))] compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64"); extern crate dynasmrt; +extern crate serde; + +#[macro_use] +extern crate serde_derive; + #[macro_use] extern crate dynasm; @@ -33,6 +39,8 @@ mod codegen_x64; mod emitter_x64; mod machine; pub mod protect_unix; +#[cfg(target_arch = "aarch64")] +mod translator_aarch64; pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator; pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator; diff --git a/lib/singlepass-backend/src/machine.rs b/lib/singlepass-backend/src/machine.rs index 727b577d7..017ba1270 100644 --- a/lib/singlepass-backend/src/machine.rs +++ b/lib/singlepass-backend/src/machine.rs @@ -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); diff --git a/lib/singlepass-backend/src/translator_aarch64.rs b/lib/singlepass-backend/src/translator_aarch64.rs new file mode 100644 index 000000000..0c2b7a6ec --- /dev/null +++ b/lib/singlepass-backend/src/translator_aarch64.rs @@ -0,0 +1,1869 @@ +#![allow(dead_code)] + +use crate::emitter_x64::*; +use dynasmrt::{aarch64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; +use wasmer_runtime_core::backend::InlineBreakpointType; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub struct AX(pub u32); + +impl AX { + pub fn x(&self) -> u32 { + self.0 + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub struct AV(pub u32); + +impl AV { + pub fn v(&self) -> u32 { + self.0 + } +} + +/* +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum GPR { + RAX, + RCX, + RDX, + RBX, + RSP, + RBP, + RSI, + RDI, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum XMM { + XMM0, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7, +} +*/ + +pub fn map_gpr(gpr: GPR) -> AX { + use GPR::*; + + match gpr { + RAX => AX(0), + RCX => AX(1), + RDX => AX(2), + RBX => AX(3), + RSP => AX(28), + RBP => AX(5), + RSI => AX(6), + RDI => AX(7), + R8 => AX(8), + R9 => AX(9), + R10 => AX(10), + R11 => AX(11), + R12 => AX(12), + R13 => AX(13), + R14 => AX(14), + R15 => AX(15), + } +} + +pub fn map_xmm(xmm: XMM) -> AV { + use XMM::*; + + match xmm { + XMM0 => AV(0), + XMM1 => AV(1), + XMM2 => AV(2), + XMM3 => AV(3), + XMM4 => AV(4), + XMM5 => AV(5), + XMM6 => AV(6), + XMM7 => AV(7), + XMM8 => AV(8), + XMM9 => AV(9), + XMM10 => AV(10), + XMM11 => AV(11), + XMM12 => AV(12), + XMM13 => AV(13), + XMM14 => AV(14), + XMM15 => AV(15), + } +} + +pub fn get_aarch64_assembler() -> Assembler { + let a = Assembler::new().unwrap(); + dynasm!( + a + ; .arch aarch64 + ; .alias x_rsp, x28 + ; .alias x_tmp1, x27 + ; .alias w_tmp1, w27 + ; .alias x_tmp2, x26 + ; .alias w_tmp2, w26 + ; .alias x_tmp3, x25 + ; .alias w_tmp3, w25 + ; .alias d_tmp1, d28 + ; .alias d_tmp2, d27 + ; .alias v_tmp1, v28 + ; .alias v_tmp2, v27 + ); + a +} + +const X_TMP1: u32 = 27; +const X_TMP2: u32 = 26; +const X_TMP3: u32 = 25; +const V_TMP1: u32 = 28; +const V_TMP2: u32 = 27; + +macro_rules! binop_imm32_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler + ; b >after + ; data: + ; .dword src as i32 + ; after: + ; ldr w_tmp1, { + dynasm!($assembler + ; b >after + ; data: + ; .qword src as i64 + ; after: + ; ldr x_tmp1, $otherwise + } + }; +} + +macro_rules! binop_imm32_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::Memory(dst, disp)) => { + if disp >= 0 { + dynasm!($assembler ; add x_tmp3, X(map_gpr(dst).x()), disp as u32); + } else { + dynasm!($assembler ; sub x_tmp3, X(map_gpr(dst).x()), (-disp) as u32); + } + dynasm!($assembler + ; b >after + ; data: + ; .dword src as i32 + ; after: + ; ldr w_tmp1, { + if disp >= 0 { + dynasm!($assembler ; add x_tmp3, X(map_gpr(dst).x()), disp as u32); + } else { + dynasm!($assembler ; sub x_tmp3, X(map_gpr(dst).x()), (-disp) as u32); + } + dynasm!($assembler + ; b >after + ; data: + ; .qword src as i64 + ; after: + ; ldr x_tmp1, $otherwise + } + }; +} + +macro_rules! binop_gpr_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler + ; $ins W(map_gpr(dst).x()), W(map_gpr(dst).x()), W(map_gpr(src).x()) + ); + }, + (Size::S64, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler + ; $ins X(map_gpr(dst).x()), X(map_gpr(dst).x()), X(map_gpr(src).x()) + ); + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_gpr_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, $otherwise + } + }; +} + +macro_rules! binop_mem_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Memory(base, disp), Location::GPR(dst)) => { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, $otherwise + } + }; +} + +macro_rules! binop_all_nofp { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + binop_imm32_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_imm32_mem!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_mem!($ins, $assembler, $sz, $src, $dst, { + binop_mem_gpr!($ins, $assembler, $sz, $src, $dst, $otherwise) + }) + }) + }) + }) + }; +} + +macro_rules! binop_shift { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm8(imm), Location::GPR(dst)) => { + assert!(imm < 32); + dynasm!($assembler ; $ins W(map_gpr(dst).x()), W(map_gpr(dst).x()), imm as u32); + }, + (Size::S32, Location::Imm8(imm), Location::Memory(base, disp)) => { + assert!(imm < 32); + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + dynasm!($assembler ; $ins W(map_gpr(dst).x()), W(map_gpr(dst).x()), W(map_gpr(GPR::RCX).x())); + }, + (Size::S32, Location::GPR(GPR::RCX), Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + assert!(imm < 32); + dynasm!($assembler ; $ins X(map_gpr(dst).x()), X(map_gpr(dst).x()), imm as u32); + }, + (Size::S64, Location::Imm8(imm), Location::Memory(base, disp)) => { + assert!(imm < 32); + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + dynasm!($assembler ; $ins X(map_gpr(dst).x()), X(map_gpr(dst).x()), X(map_gpr(GPR::RCX).x())); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!($assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, $otherwise + } + } +} + +macro_rules! avx_fn { + ($ins:ident, $width:ident, $width_int:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + match src2 { + XMMOrMemory::XMM(src2) => dynasm!(self ; $ins $width(map_xmm(dst).v()), $width(map_xmm(src1).v()), $width(map_xmm(src2).v())), + XMMOrMemory::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + match src2 { + XMMOrMemory::XMM(src2) => { + dynasm!( + self + ; fcmpe $width(map_xmm(src1).v()), $width(map_xmm(src2).v()) + ; cset w_tmp1, $cmpty + ; mov V(map_xmm(dst).v()).$width[0], $width_int(X_TMP1) + ); + }, + XMMOrMemory::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + fn $name(&mut self, src1: XMM, _src2: XMMOrMemory, dst: XMM) { + dynasm!(self ; $ins $width(map_xmm(dst).v()), $width(map_xmm(src1).v())); + } + } +} + +macro_rules! avx_fn_cvt { + ($ins:ident, $width_src:ident, $width_dst:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, _src2: XMMOrMemory, dst: XMM) { + dynasm!(self ; $ins $width_dst(map_xmm(dst).v()), $width_src(map_xmm(src1).v())); + } + } +} + +impl Emitter for Assembler { + type Label = DynamicLabel; + type Offset = AssemblyOffset; + + fn get_label(&mut self) -> DynamicLabel { + self.new_dynamic_label() + } + + fn get_offset(&self) -> AssemblyOffset { + self.offset() + } + + fn get_jmp_instr_size(&self) -> u8 { + 4 + } + + fn emit_u64(&mut self, x: u64) { + self.push_u64(x); + } + + fn emit_label(&mut self, label: Self::Label) { + dynasm!(self ; => label); + } + + fn emit_nop(&mut self) { + dynasm!(self ; nop); + } + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S32, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; mov W(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S32, Location::Memory(base, disp), Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after; data: ; .dword x as i32; after: ; ldr w_tmp1, { + dynasm!(self ; b >after; data: ; .dword x as i32; after: ; ldr W(map_gpr(dst).x()), { + dynasm!(self ; b >after; data: ; .dword x as i32; after: ; ldr W(map_gpr(dst).x()), { + dynasm!(self ; mov X(map_gpr(dst).x()), X(map_gpr(src).x())); + } + (Size::S64, Location::Memory(base, disp), Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after; data: ; .qword x as i64; after: ; ldr x_tmp1, { + dynasm!(self ; b >after; data: ; .qword x as i64; after: ; ldr X(map_gpr(dst).x()), { + dynasm!(self ; b >after; data: ; .qword x as i64; after: ; ldr X(map_gpr(dst).x()), { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after; data: ; .dword x as i32; after: ; ldr w_tmp1, { + dynasm!(self ; b >after; data: ; .dword x as u8 as i32; after: ; ldr W(map_gpr(dst).x()), { + dynasm!(self ; b >after; data: ; .dword x as u8 as i32; after: ; ldr W(map_gpr(dst).x()), { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after; data: ; .dword x as i32; after: ; ldr w_tmp1, { + dynasm!(self ; b >after; data: ; .dword x as u16 as i32; after: ; ldr W(map_gpr(dst).x()), { + dynasm!(self ; b >after; data: ; .dword x as u16 as i32; after: ; ldr W(map_gpr(dst).x()), { + dynasm!(self ; fmov S(map_xmm(dst).v()), S(map_xmm(src).v())); + } + (Size::S32, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; fmov W(map_gpr(dst).x()), S(map_xmm(src).v())); + } + (Size::S32, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; fmov S(map_xmm(dst).v()), W(map_gpr(src).x())); + } + (Size::S32, Location::Memory(base, disp), Location::XMM(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + dynasm!(self ; fmov D(map_xmm(dst).v()), D(map_xmm(src).v())); + } + (Size::S64, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; fmov X(map_gpr(dst).x()), D(map_xmm(src).v())); + } + (Size::S64, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; fmov D(map_xmm(dst).v()), X(map_gpr(src).x())); + } + (Size::S64, Location::Memory(base, disp), Location::XMM(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, panic!("NOT IMPL: {:?} {:?} {:?}", sz, src, dst), + } + } + + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + } + fn emit_lea_label(&mut self, label: Self::Label, dst: Location) { + match dst { + Location::GPR(dst) => { + dynasm!(self ; adr X(map_gpr(dst).x()), =>label); + } + _ => unreachable!(), + } + } + + fn emit_cdq(&mut self) { + dynasm!( + self + ; b >after + ; bit_tester: + ; .dword 0x80000000u32 as i32 + ; all_ones: + ; .dword 0xffffffffu32 as i32 + ; after: + ; ldr w_tmp1, zero + ; not_zero: + ; ldr W(map_gpr(GPR::RDX).x()), after + ; zero: + ; mov W(map_gpr(GPR::RDX).x()), wzr + ; after: + ); + } + fn emit_cqo(&mut self) { + dynasm!( + self + ; b >after + ; bit_tester: + ; .qword 0x8000000000000000u64 as i64 + ; all_ones: + ; .qword 0xffffffffffffffffu64 as i64 + ; after: + ; ldr x_tmp1, zero + ; not_zero: + ; ldr X(map_gpr(GPR::RDX).x()), after + ; zero: + ; mov X(map_gpr(GPR::RDX).x()), xzr + ; after: + ); + } + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(eor, self, sz, src, dst, { unreachable!("xor") }); + } + fn emit_jmp(&mut self, condition: Condition, label: Self::Label) { + use Condition::*; + + match condition { + None => dynasm!(self ; b =>label), + Above => dynasm!(self ; b.hi =>label), + AboveEqual => dynasm!(self ; b.hs =>label), + Below => dynasm!(self ; b.lo =>label), + BelowEqual => dynasm!(self ; b.ls =>label), + Greater => dynasm!(self ; b.gt =>label), + GreaterEqual => dynasm!(self ; b.ge =>label), + Less => dynasm!(self ; b.lt =>label), + LessEqual => dynasm!(self ; b.le =>label), + Equal => dynasm!(self ; b.eq =>label), + NotEqual => dynasm!(self ; b.ne =>label), + Signed => dynasm!(self ; b.vs =>label), // TODO: Review this + } + } + + fn emit_jmp_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; br X(map_gpr(x).x())), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + } + + fn emit_conditional_trap(&mut self, condition: Condition) { + use Condition::*; + + match condition { + None => dynasm!(self ; b >fail), + Above => dynasm!(self ; b.hi >fail), + AboveEqual => dynasm!(self ; b.hs >fail), + Below => dynasm!(self ; b.lo >fail), + BelowEqual => dynasm!(self ; b.ls >fail), + Greater => dynasm!(self ; b.gt >fail), + GreaterEqual => dynasm!(self ; b.ge >fail), + Less => dynasm!(self ; b.lt >fail), + LessEqual => dynasm!(self ; b.le >fail), + Equal => dynasm!(self ; b.eq >fail), + NotEqual => dynasm!(self ; b.ne >fail), + Signed => dynasm!(self ; b.vs >fail), // TODO: Review this + } + dynasm!( + self + ; b >ok + ; fail: + ; .dword 0 ; .dword 0 + ; ok: + ); + } + + fn emit_set(&mut self, condition: Condition, dst: GPR) { + use Condition::*; + + match condition { + None => dynasm!(self ; b >set), + Above => dynasm!(self ; b.hi >set), + AboveEqual => dynasm!(self ; b.hs >set), + Below => dynasm!(self ; b.lo >set), + BelowEqual => dynasm!(self ; b.ls >set), + Greater => dynasm!(self ; b.gt >set), + GreaterEqual => dynasm!(self ; b.ge >set), + Less => dynasm!(self ; b.lt >set), + LessEqual => dynasm!(self ; b.le >set), + Equal => dynasm!(self ; b.eq >set), + NotEqual => dynasm!(self ; b.ne >set), + Signed => dynasm!(self ; b.vs >set), // TODO: Review this + } + dynasm!( + self + ; mov W(map_gpr(dst).x()), wzr + ; b >ok + ; set: + ; mov W(map_gpr(dst).x()), 1 + ; ok: + ); + } + + fn emit_push(&mut self, sz: Size, src: Location) { + match (sz, src) { + (Size::S64, Location::Imm32(src)) => dynasm!(self + ; b >after + ; data: + ; .dword src as i32 + ; after: + ; ldr w_tmp1, dynasm!(self + ; sub x_rsp, x_rsp, 8 + ; str X(map_gpr(src).x()), [x_rsp] + ), + (Size::S64, Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, panic!("push {:?} {:?}", sz, src), + } + } + fn emit_pop(&mut self, sz: Size, dst: Location) { + match (sz, dst) { + (Size::S64, Location::GPR(dst)) => dynasm!(self + ; ldr X(map_gpr(dst).x()), [x_rsp] + ; add x_rsp, x_rsp, 8 + ), + (Size::S64, Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, panic!("pop {:?} {:?}", sz, dst), + } + } + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location) { + match (sz, left, right) { + (Size::S32, Location::Imm32(left), Location::GPR(right)) => { + dynasm!(self + ; b >after + ; data: + ; .dword left as i32 + ; after: + ; ldr w_tmp1, { + dynasm!(self + ; b >after + ; data: + ; .qword left as i64 + ; after: + ; ldr x_tmp1, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after + ; data: + ; .dword left as i32 + ; after: + ; ldr w_tmp1, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, after + ; data: + ; .qword left as i64 + ; after: + ; ldr x_tmp1, dynasm!( + self + ; cmp W(map_gpr(right).x()), W(map_gpr(left).x()) + ), + (Size::S64, Location::GPR(left), Location::GPR(right)) => dynasm!( + self + ; cmp X(map_gpr(right).x()), X(map_gpr(left).x()) + ), + (Size::S32, Location::GPR(left), Location::Memory(base, disp)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + } + fn emit_add(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(add, self, sz, src, dst, { unreachable!("add") }); + } + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(sub, self, sz, src, dst, { unreachable!("sub") }); + } + + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(mul, self, sz, src, dst, { + binop_mem_gpr!(mul, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR) { + dynasm!( + self + ; b >after + ; data: + ; .dword src as i32 + ; after: + ; ldr w_tmp1, { + match divisor { + Location::GPR(x) => dynasm!( + self + ; mov w_tmp1, W(map_gpr(x).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + dynasm!( + self + ; mov w_tmp2, W(map_gpr(GPR::RAX).x()) + ; udiv W(map_gpr(GPR::RAX).x()), w_tmp2, w_tmp1 + ; msub W(map_gpr(GPR::RDX).x()), W(map_gpr(GPR::RAX).x()), w_tmp1, w_tmp2 + ) + } + Size::S64 => { + match divisor { + Location::GPR(x) => dynasm!( + self + ; mov x_tmp1, X(map_gpr(x).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + dynasm!( + self + ; mov x_tmp2, X(map_gpr(GPR::RAX).x()) + ; udiv X(map_gpr(GPR::RAX).x()), x_tmp2, x_tmp1 + ; msub X(map_gpr(GPR::RDX).x()), X(map_gpr(GPR::RAX).x()), x_tmp1, x_tmp2 + ) + } + _ => unreachable!(), + } + } + fn emit_idiv(&mut self, sz: Size, divisor: Location) { + match sz { + Size::S32 => { + match divisor { + Location::GPR(x) => dynasm!( + self + ; mov w_tmp1, W(map_gpr(x).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + dynasm!( + self + ; mov w_tmp2, W(map_gpr(GPR::RAX).x()) + ; sdiv W(map_gpr(GPR::RAX).x()), w_tmp2, w_tmp1 + ; msub W(map_gpr(GPR::RDX).x()), W(map_gpr(GPR::RAX).x()), w_tmp1, w_tmp2 + ) + } + Size::S64 => { + match divisor { + Location::GPR(x) => dynasm!( + self + ; mov x_tmp1, X(map_gpr(x).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + dynasm!( + self + ; mov x_tmp2, X(map_gpr(GPR::RAX).x()) + ; sdiv X(map_gpr(GPR::RAX).x()), x_tmp2, x_tmp1 + ; msub X(map_gpr(GPR::RDX).x()), X(map_gpr(GPR::RAX).x()), x_tmp1, x_tmp2 + ) + } + _ => unreachable!(), + } + } + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(lsl, self, sz, src, dst, { unreachable!("shl") }); + } + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(lsr, self, sz, src, dst, { unreachable!("shr") }); + } + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(asr, self, sz, src, dst, { unreachable!("sar") }); + } + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location) { + // TODO: We are changing content of `src` (possibly RCX) here. Will this break any assumptions? + match sz { + Size::S32 => match src { + Location::Imm8(x) => { + assert!(x < 32); + binop_shift!(ror, self, sz, Location::Imm8(32 - x), dst, { + unreachable!("rol") + }); + } + Location::GPR(GPR::RCX) => { + dynasm!( + self + ; mov w_tmp1, 32 + ; sub W(map_gpr(GPR::RCX).x()), w_tmp1, W(map_gpr(GPR::RCX).x()) + ); + binop_shift!(ror, self, sz, src, dst, { unreachable!("rol") }); + } + _ => unreachable!(), + }, + Size::S64 => match src { + Location::Imm8(x) => { + assert!(x < 64); + binop_shift!(ror, self, sz, Location::Imm8(64 - x), dst, { + unreachable!("rol") + }); + } + Location::GPR(GPR::RCX) => { + dynasm!( + self + ; mov x_tmp1, 64 + ; sub X(map_gpr(GPR::RCX).x()), x_tmp1, X(map_gpr(GPR::RCX).x()) + ); + binop_shift!(ror, self, sz, src, dst, { unreachable!("rol") }); + } + _ => unreachable!(), + }, + _ => unreachable!(), + } + } + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(ror, self, sz, src, dst, { unreachable!("ror") }); + } + fn emit_and(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(and, self, sz, src, dst, { unreachable!("and") }); + } + fn emit_or(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(orr, self, sz, src, dst, { unreachable!("or") }); + } + fn emit_bsr(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!("aarch64: bsr"); + } + fn emit_bsf(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!("aarch64: bsf"); + } + fn arch_has_xzcnt(&self) -> bool { + true + } + fn arch_emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) { + emit_clz_variant(self, sz, &src, &dst, false); + } + fn arch_emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location) { + emit_clz_variant(self, sz, &src, &dst, true); + } + fn emit_neg(&mut self, _sz: Size, _value: Location) { + unimplemented!("aarch64: neg"); + } + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location) { + match sz { + Size::S32 => { + match src { + Location::GPR(src) => dynasm!( + self + ; mov w_tmp1, W(map_gpr(src).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + match dst { + Location::GPR(dst) => { + dynasm!( + self + ; mov v_tmp1.S[0], w_tmp1 + ; cnt v_tmp1.B16, v_tmp1.B16 + ; mov w_tmp1, v_tmp1.S[0] + ; mov W(map_gpr(dst).x()), w_tmp1 + ; add W(map_gpr(dst).x()), W(map_gpr(dst).x()), w_tmp1, lsr 8 + ; add W(map_gpr(dst).x()), W(map_gpr(dst).x()), w_tmp1, lsr 16 + ; add W(map_gpr(dst).x()), W(map_gpr(dst).x()), w_tmp1, lsr 24 + ; and W(map_gpr(dst).x()), W(map_gpr(dst).x()), 255 + ); + } + _ => unreachable!(), + } + } + Size::S64 => { + match src { + Location::GPR(src) => dynasm!( + self + ; mov x_tmp1, X(map_gpr(src).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + match dst { + Location::GPR(dst) => { + dynasm!( + self + ; mov v_tmp1.D[0], x_tmp1 + ; cnt v_tmp1.B16, v_tmp1.B16 + ; mov x_tmp1, v_tmp1.D[0] + ; mov X(map_gpr(dst).x()), x_tmp1 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 8 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 16 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 24 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 32 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 40 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 48 + ; add X(map_gpr(dst).x()), X(map_gpr(dst).x()), x_tmp1, lsr 56 + ; and X(map_gpr(dst).x()), X(map_gpr(dst).x()), 255 + ); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } + fn emit_movzx(&mut self, sz_src: Size, src: Location, _sz_dst: Size, dst: Location) { + match (sz_src, src, dst) { + (Size::S8, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; uxtb W(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S16, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; uxth W(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S8, Location::Memory(base, disp), Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + } + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location) { + match (sz_src, src, sz_dst, dst) { + (Size::S8, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; sxtb W(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; sxth W(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S8, Location::Memory(base, disp), Size::S32, Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + dynasm!(self ; sxtb X(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; sxth X(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S32, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; sxtw X(map_gpr(dst).x()), W(map_gpr(src).x())); + } + (Size::S8, Location::Memory(base, disp), Size::S64, Location::GPR(dst)) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + } + + fn emit_xchg(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!("aarch64: xchg") + } + fn emit_lock_xadd(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!("aarch64: xadd") + } + fn emit_lock_cmpxchg(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!("aarch64: cmpxchg") + } + fn emit_vmovaps(&mut self, _src: XMMOrMemory, _dst: XMMOrMemory) { + unimplemented!("aarch64: vmovaps") + } + fn emit_vmovapd(&mut self, _src: XMMOrMemory, _dst: XMMOrMemory) { + unimplemented!("aarch64: vmovapd") + } + fn emit_vxorps(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vxorps") + } + fn emit_vxorpd(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vxorpd") + } + fn emit_vcmpunordss(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vcmpunordss") + } + fn emit_vcmpunordsd(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vcmpunordsd") + } + + fn emit_vcmpordss(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vcmpordss") + } + fn emit_vcmpordsd(&mut self, _src1: XMM, _src2: XMMOrMemory, _dst: XMM) { + unimplemented!("aarch64: vcmpordsd") + } + + fn emit_vblendvps(&mut self, _src1: XMM, _src2: XMMOrMemory, _mask: XMM, _dst: XMM) { + unimplemented!("aarch64: vblendvps") + } + fn emit_vblendvpd(&mut self, _src1: XMM, _src2: XMMOrMemory, _mask: XMM, _dst: XMM) { + unimplemented!("aarch64: vblendvpd") + } + + avx_fn!(fadd, S, W, emit_vaddss); + avx_fn!(fsub, S, W, emit_vsubss); + avx_fn!(fmul, S, W, emit_vmulss); + avx_fn!(fdiv, S, W, emit_vdivss); + avx_fn!(fmax, S, W, emit_vmaxss); + avx_fn!(fmin, S, W, emit_vminss); + avx_cmp!(gt, S, W, emit_vcmpgtss); + avx_cmp!(ge, S, W, emit_vcmpgess); + avx_cmp!(mi, S, W, emit_vcmpltss); + avx_cmp!(ls, S, W, emit_vcmpless); + avx_cmp!(eq, S, W, emit_vcmpeqss); + avx_cmp!(ne, S, W, emit_vcmpneqss); + avx_fn_unop!(fsqrt, S, emit_vsqrtss); + avx_fn_unop!(frintn, S, emit_vroundss_nearest); // to nearest with ties to even + avx_fn_unop!(frintm, S, emit_vroundss_floor); // toward minus infinity + avx_fn_unop!(frintp, S, emit_vroundss_ceil); // toward positive infinity + avx_fn_unop!(frintz, S, emit_vroundss_trunc); // toward zero + avx_fn_cvt!(fcvt, S, D, emit_vcvtss2sd); + + avx_fn!(fadd, D, X, emit_vaddsd); + avx_fn!(fsub, D, X, emit_vsubsd); + avx_fn!(fmul, D, X, emit_vmulsd); + avx_fn!(fdiv, D, X, emit_vdivsd); + avx_fn!(fmax, D, X, emit_vmaxsd); + avx_fn!(fmin, D, X, emit_vminsd); + avx_cmp!(gt, D, X, emit_vcmpgtsd); + avx_cmp!(ge, D, X, emit_vcmpgesd); + avx_cmp!(mi, D, X, emit_vcmpltsd); + avx_cmp!(ls, D, X, emit_vcmplesd); + avx_cmp!(eq, D, X, emit_vcmpeqsd); + avx_cmp!(ne, D, X, emit_vcmpneqsd); + avx_fn_unop!(fsqrt, D, emit_vsqrtsd); + avx_fn_unop!(frintn, D, emit_vroundsd_nearest); // to nearest with ties to even + avx_fn_unop!(frintm, D, emit_vroundsd_floor); // toward minus infinity + avx_fn_unop!(frintp, D, emit_vroundsd_ceil); // toward positive infinity + avx_fn_unop!(frintz, D, emit_vroundsd_trunc); // toward zero + avx_fn_cvt!(fcvt, D, S, emit_vcvtsd2ss); + + fn arch_has_itruncf(&self) -> bool { + true + } + fn arch_emit_i32_trunc_sf32(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzs W(map_gpr(dst).x()), S(map_xmm(src).v())); + } + fn arch_emit_i32_trunc_sf64(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzs W(map_gpr(dst).x()), D(map_xmm(src).v())); + } + fn arch_emit_i32_trunc_uf32(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzu W(map_gpr(dst).x()), S(map_xmm(src).v())); + } + fn arch_emit_i32_trunc_uf64(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzu W(map_gpr(dst).x()), D(map_xmm(src).v())); + } + fn arch_emit_i64_trunc_sf32(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzs X(map_gpr(dst).x()), S(map_xmm(src).v())); + } + fn arch_emit_i64_trunc_sf64(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzs X(map_gpr(dst).x()), D(map_xmm(src).v())); + } + fn arch_emit_i64_trunc_uf32(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzu X(map_gpr(dst).x()), S(map_xmm(src).v())); + } + fn arch_emit_i64_trunc_uf64(&mut self, src: XMM, dst: GPR) { + dynasm!(self ; fcvtzu X(map_gpr(dst).x()), D(map_xmm(src).v())); + } + + fn arch_has_fconverti(&self) -> bool { + true + } + fn arch_emit_f32_convert_si32(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; scvtf S(map_xmm(dst).v()), W(map_gpr(src).x())); + } + fn arch_emit_f32_convert_si64(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; scvtf S(map_xmm(dst).v()), X(map_gpr(src).x())); + } + fn arch_emit_f32_convert_ui32(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; ucvtf S(map_xmm(dst).v()), W(map_gpr(src).x())); + } + fn arch_emit_f32_convert_ui64(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; ucvtf S(map_xmm(dst).v()), X(map_gpr(src).x())); + } + fn arch_emit_f64_convert_si32(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; scvtf D(map_xmm(dst).v()), W(map_gpr(src).x())); + } + fn arch_emit_f64_convert_si64(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; scvtf D(map_xmm(dst).v()), X(map_gpr(src).x())); + } + fn arch_emit_f64_convert_ui32(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; ucvtf D(map_xmm(dst).v()), W(map_gpr(src).x())); + } + fn arch_emit_f64_convert_ui64(&mut self, src: GPR, dst: XMM) { + dynasm!(self ; ucvtf D(map_xmm(dst).v()), X(map_gpr(src).x())); + } + + fn arch_has_fneg(&self) -> bool { + true + } + fn arch_emit_f32_neg(&mut self, src: XMM, dst: XMM) { + dynasm!(self ; fneg S(map_xmm(dst).v()), S(map_xmm(src).v())); + } + fn arch_emit_f64_neg(&mut self, src: XMM, dst: XMM) { + dynasm!(self ; fneg D(map_xmm(dst).v()), D(map_xmm(src).v())); + } + + fn emit_btc_gpr_imm8_32(&mut self, _src: u8, _dst: GPR) { + unimplemented!(); + } + fn emit_btc_gpr_imm8_64(&mut self, _src: u8, _dst: GPR) { + unimplemented!(); + } + fn emit_cmovae_gpr_32(&mut self, _src: GPR, _dst: GPR) { + unimplemented!(); + } + fn emit_cmovae_gpr_64(&mut self, _src: GPR, _dst: GPR) { + unimplemented!(); + } + fn emit_ucomiss(&mut self, _src: XMMOrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_ucomisd(&mut self, _src: XMMOrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_cvttss2si_32(&mut self, _src: XMMOrMemory, _dst: GPR) { + unimplemented!(); + } + fn emit_cvttss2si_64(&mut self, _src: XMMOrMemory, _dst: GPR) { + unimplemented!(); + } + fn emit_cvttsd2si_32(&mut self, _src: XMMOrMemory, _dst: GPR) { + unimplemented!(); + } + fn emit_cvttsd2si_64(&mut self, _src: XMMOrMemory, _dst: GPR) { + unimplemented!(); + } + fn emit_vcvtsi2ss_32(&mut self, _src1: XMM, _src2: GPROrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_vcvtsi2ss_64(&mut self, _src1: XMM, _src2: GPROrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_vcvtsi2sd_32(&mut self, _src1: XMM, _src2: GPROrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_vcvtsi2sd_64(&mut self, _src1: XMM, _src2: GPROrMemory, _dst: XMM) { + unimplemented!(); + } + fn emit_test_gpr_64(&mut self, _reg: GPR) { + unimplemented!(); + } + + fn emit_ud2(&mut self) { + dynasm!(self ; .dword 0 ; .dword 2) + } + fn emit_ret(&mut self) { + dynasm!(self + ; ldr x_tmp1, [x_rsp] + ; add x_rsp, x_rsp, 8 + ; br x_tmp1 + ); + } + fn emit_call_label(&mut self, label: Self::Label) { + dynasm!(self + ; b >after + ; addr: + ; .qword =>label // Is this the offset? + ; after: + + // Calculate the target address. + ; ldr x_tmp1, done + ; str x_tmp2, [x_rsp] + + // Jump. + ; br x_tmp1 + ; done: + ); + } + fn emit_call_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self + // Push return address. + ; sub x_rsp, x_rsp, 8 + ; adr x_tmp1, >done + ; str x_tmp1, [x_rsp] + + // Jump. + ; br X(map_gpr(x).x()) + ; done: + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, done + ; str x_tmp1, [x_rsp] + + // Read memory. + ; ldr x_tmp1, [x_tmp3] + + // Jump. + ; br x_tmp1 + ; done: + ); + } + _ => unreachable!(), + } + } + + fn emit_bkpt(&mut self) { + dynasm!(self ; .dword 0 ; .dword 1) + } + + fn emit_host_redirection(&mut self, target: GPR) { + let target = map_gpr(target); + dynasm!( + self + ; sub sp, sp, 80 + ; str x30, [sp, 0] // LR + ; str X(target.x()), [sp, 8] + // Save callee-saved registers as required by x86-64 conventions. + ; str X(map_gpr(GPR::RBX).x()), [sp, 16] + ; str X(map_gpr(GPR::R12).x()), [sp, 24] + ; str X(map_gpr(GPR::R13).x()), [sp, 32] + ; str X(map_gpr(GPR::R14).x()), [sp, 40] + ; str X(map_gpr(GPR::R15).x()), [sp, 48] + ; str X(map_gpr(GPR::RBP).x()), [sp, 56] + ; str X(map_gpr(GPR::RSP).x()), [sp, 64] + ; adr x30, >after + + // Put parameters in correct order + ; sub sp, sp, 64 + ; str X(map_gpr(GPR::RDI).x()), [sp, 0] + ; str X(map_gpr(GPR::RSI).x()), [sp, 8] + ; str X(map_gpr(GPR::RDX).x()), [sp, 16] + ; str X(map_gpr(GPR::RCX).x()), [sp, 24] + ; str X(map_gpr(GPR::R8).x()), [sp, 32] + ; str X(map_gpr(GPR::R9).x()), [sp, 40] + ; ldr x0, [sp, 0] + ; ldr x1, [sp, 8] + ; ldr x2, [sp, 16] + ; ldr x3, [sp, 24] + ; ldr x4, [sp, 32] + ; ldr x5, [sp, 40] + ; add sp, sp, 64 + + // Branch to saved target + ; ldr x8, [sp, 8] + ; br x8 + + ; after: + ; ldr x30, [sp, 0] // LR + ; ldr X(map_gpr(GPR::RBX).x()), [sp, 16] + ; ldr X(map_gpr(GPR::R12).x()), [sp, 24] + ; ldr X(map_gpr(GPR::R13).x()), [sp, 32] + ; ldr X(map_gpr(GPR::R14).x()), [sp, 40] + ; ldr X(map_gpr(GPR::R15).x()), [sp, 48] + ; ldr X(map_gpr(GPR::RBP).x()), [sp, 56] + ; ldr X(map_gpr(GPR::RSP).x()), [sp, 64] + ; add sp, sp, 80 + + ; ldr x_tmp1, [x_rsp] + ; add x_rsp, x_rsp, 8 + ; br x_tmp1 + ); + } + + fn emit_inline_breakpoint(&mut self, ty: InlineBreakpointType) { + dynasm!(self + ; .dword 0 + ; .dword -1 + ; .dword (ty as u8 as i32) + ); + } + + fn arch_supports_canonicalize_nan(&self) -> bool { + false + } + + fn arch_requires_indirect_call_trampoline(&self) -> bool { + true + } + + fn arch_emit_indirect_call_with_trampoline(&mut self, loc: Location) { + match loc { + Location::GPR(x) => { + dynasm!(self + // Push return address. + ; sub x_rsp, x_rsp, 8 + ; adr x_tmp1, >done + ; str x_tmp1, [x_rsp] + ); + self.emit_host_redirection(x); + dynasm!(self ; done: ); + } + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(self ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, done + ; str x_tmp1, [x_rsp] + + // Read memory. + ; ldr X(map_gpr(GPR::RAX).x()), [x_tmp3] + ); + self.emit_host_redirection(GPR::RAX); + dynasm!(self ; done: ); + } + _ => unreachable!(), + } + } + + fn arch_emit_entry_trampoline(&mut self) { + dynasm!( + self + ; mov x18, x28 + ; mov x28, sp // WASM stack pointer + ; ldr x9, >v_65536 + ; sub sp, sp, x9 // Pre-allocate the WASM stack + ; sub x28, x28, 16 // for the last two arguments + + // Fixup param locations. + ; str x0, [sp, 0] + ; str x1, [sp, 8] + ; str x2, [sp, 16] + ; str x3, [sp, 24] + ; str x4, [sp, 32] + ; str x5, [sp, 40] + ; str x6, [x28, 0] + ; str x7, [x28, 8] + ; ldr X(map_gpr(GPR::RDI).x()), [sp, 0] + ; ldr X(map_gpr(GPR::RSI).x()), [sp, 8] + ; ldr X(map_gpr(GPR::RDX).x()), [sp, 16] + ; ldr X(map_gpr(GPR::RCX).x()), [sp, 24] + ; ldr X(map_gpr(GPR::R8).x()), [sp, 32] + ; ldr X(map_gpr(GPR::R9).x()), [sp, 40] + + ; str x19, [sp, 0] + ; str x20, [sp, 8] + ; str x21, [sp, 16] + ; str x22, [sp, 24] + ; str x23, [sp, 32] + ; str x24, [sp, 40] + ; str x25, [sp, 48] + ; str x26, [sp, 56] + ; str x27, [sp, 64] + ; str x18, [sp, 72] // previously x28 + ; str x29, [sp, 80] + ; str x30, [sp, 88] + + // return address + ; adr x20, >done + ; sub x28, x28, 8 + ; str x20, [x28] // Keep this consistent with RSP mapping in translator_aarch64 + + // Jump to target function! + ; b >real_entry + + ; done: + ; ldr x19, [sp, 0] + ; ldr x20, [sp, 8] + ; ldr x21, [sp, 16] + ; ldr x22, [sp, 24] + ; ldr x23, [sp, 32] + ; ldr x24, [sp, 40] + ; ldr x25, [sp, 48] + ; ldr x26, [sp, 56] + ; ldr x27, [sp, 64] + ; ldr x28, [sp, 72] + ; ldr x29, [sp, 80] + ; ldr x30, [sp, 88] + ; ldr x9, >v_65536 + ; add sp, sp, x9 // Resume stack pointer + ; br x30 // LR + + ; v_65536: + ; .qword 524288 + + ; real_entry: + ) + } +} + +fn emit_clz_variant( + assembler: &mut Assembler, + sz: Size, + src: &Location, + dst: &Location, + reversed: bool, +) { + match sz { + Size::S32 => { + match *src { + Location::GPR(src) => dynasm!( + assembler + ; mov w_tmp1, W(map_gpr(src).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + match *dst { + Location::GPR(dst) => { + if reversed { + dynasm!(assembler ; rbit w_tmp1, w_tmp1); + } + dynasm!( + assembler + ; clz W(map_gpr(dst).x()), w_tmp1 + ); + } + _ => unreachable!(), + } + } + Size::S64 => { + match *src { + Location::GPR(src) => dynasm!( + assembler + ; mov x_tmp1, X(map_gpr(src).x()) + ), + Location::Memory(base, disp) => { + if disp >= 0 { + dynasm!(assembler ; b >after ; disp: ; .dword disp ; after: ; ldr w_tmp3, after ; disp: ; .dword -disp ; after: ; ldr w_tmp3, unreachable!(), + } + match *dst { + Location::GPR(dst) => { + if reversed { + dynasm!(assembler ; rbit x_tmp1, x_tmp1) + } + dynasm!( + assembler + ; clz X(map_gpr(dst).x()), x_tmp1 + ); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } +} diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index 6f62ab098..81bd57669 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.8.0" +version = "0.11.0" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -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 = { path = "../runtime", version = "0.11.0", default-features = false} +wasmer-clif-backend = { path = "../clif-backend", version = "0.11.0", optional = true} +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", features = ["test"], optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.11.0", optional = true } [build-dependencies] wabt = "0.9.1" @@ -23,6 +23,6 @@ wabt = "0.9.1" [features] default = ["fast-tests"] fast-tests = [] -clif = [] -llvm = ["wasmer-llvm-backend"] -singlepass = ["wasmer-singlepass-backend"] +clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] +singlepass = ["wasmer-singlepass-backend", "wasmer-runtime/default-backend-singlepass"] +llvm = ["wasmer-llvm-backend", "wasmer-runtime/default-backend-llvm"] diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index e9309c3c2..6ef870479 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -1,46 +1,17 @@ use wabt::wat2wasm; -use wasmer_runtime_core::{ - backend::Compiler, - error, - global::Global, - memory::Memory, - prelude::*, - table::Table, +use wasmer_runtime::{ + compile, error, func, imports, types::{ElementType, MemoryDescriptor, TableDescriptor, Value}, units::Pages, + Ctx, Global, Memory, Table, }; -#[cfg(feature = "clif")] -fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() -} - -#[cfg(feature = "llvm")] -fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() -} - -#[cfg(feature = "singlepass")] -fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() -} - -#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] -fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() -} - static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm"); fn main() -> error::Result<()> { let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler())?; + let inner_module = compile(&wasm_binary)?; let memory_desc = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); let memory = Memory::new(memory_desc).unwrap(); @@ -71,7 +42,7 @@ fn main() -> error::Result<()> { "env" => inner_instance, }; - let outer_module = wasmer_runtime_core::compile_with(EXAMPLE_WASM, &get_compiler())?; + let outer_module = compile(EXAMPLE_WASM)?; let outer_instance = outer_module.instantiate(&outer_imports)?; let ret = outer_instance.call("main", &[Value::I32(42)])?; println!("ret: {:?}", ret); @@ -79,7 +50,7 @@ fn main() -> error::Result<()> { Ok(()) } -fn print_num(ctx: &mut vm::Ctx, n: i32) -> Result { +fn print_num(ctx: &mut Ctx, n: i32) -> Result { println!("print_num({})", n); let memory: &Memory = ctx.memory(0); diff --git a/lib/spectests/examples/test.rs b/lib/spectests/examples/test.rs index 8ce199695..eff7793b9 100644 --- a/lib/spectests/examples/test.rs +++ b/lib/spectests/examples/test.rs @@ -1,6 +1,5 @@ use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; -use wasmer_runtime_core::{backend::Compiler, import::ImportObject, Instance}; +use wasmer_runtime::{compile, ImportObject, Instance}; fn main() { let instance = create_module_1(); @@ -24,38 +23,12 @@ fn create_module_1() -> Instance { (elem (;1;) (i32.const 9) 1)) "#; let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()) - .expect("WASM can't be compiled"); + let module = compile(&wasm_binary[..]).expect("WASM can't be compiled"); module .instantiate(&generate_imports()) .expect("WASM can't be instantiated") } -#[cfg(feature = "clif")] -fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() -} - -#[cfg(feature = "llvm")] -fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() -} - -#[cfg(feature = "singlepass")] -fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() -} - -#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] -fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() -} - static IMPORT_MODULE: &str = r#" (module (type $t0 (func (param i32))) @@ -69,8 +42,7 @@ static IMPORT_MODULE: &str = r#" pub fn generate_imports() -> ImportObject { let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()) - .expect("WASM can't be compiled"); + let module = compile(&wasm_binary[..]).expect("WASM can't be compiled"); let instance = module .instantiate(&ImportObject::new()) .expect("WASM can't be instantiated"); diff --git a/lib/spectests/spectests/conversions.wast b/lib/spectests/spectests/conversions.wast index c0ae54a53..8022a1a60 100644 --- a/lib/spectests/spectests/conversions.wast +++ b/lib/spectests/spectests/conversions.wast @@ -1,6 +1,11 @@ (module (func (export "i64.extend_i32_s") (param $x i32) (result i64) (i64.extend_i32_s (local.get $x))) (func (export "i64.extend_i32_u") (param $x i32) (result i64) (i64.extend_i32_u (local.get $x))) + (func (export "i64.extend32_s") (param $x i64) (result i64) (i64.extend32_s (local.get $x))) + (func (export "i64.extend16_s") (param $x i64) (result i64) (i64.extend16_s (local.get $x))) + (func (export "i64.extend8_s") (param $x i64) (result i64) (i64.extend8_s (local.get $x))) + (func (export "i32.extend16_s") (param $x i32) (result i32) (i32.extend16_s (local.get $x))) + (func (export "i32.extend8_s") (param $x i32) (result i32) (i32.extend8_s (local.get $x))) (func (export "i32.wrap_i64") (param $x i64) (result i32) (i32.wrap_i64 (local.get $x))) (func (export "i32.trunc_f32_s") (param $x f32) (result i32) (i32.trunc_f32_s (local.get $x))) (func (export "i32.trunc_f32_u") (param $x f32) (result i32) (i32.trunc_f32_u (local.get $x))) @@ -497,3 +502,46 @@ (assert_invalid (module (func (result f64) (f64.convert_i64_u (i32.const 0)))) "type mismatch") (assert_invalid (module (func (result f64) (f64.promote_f32 (i32.const 0)))) "type mismatch") (assert_invalid (module (func (result f64) (f64.reinterpret_i64 (i32.const 0)))) "type mismatch") + +(assert_return (invoke "i32.extend8_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "i32.extend8_s" (i32.const 0x7f)) (i32.const 127)) +(assert_return (invoke "i32.extend8_s" (i32.const 0x80)) (i32.const -128)) +(assert_return (invoke "i32.extend8_s" (i32.const 0xff)) (i32.const -1)) +(assert_return (invoke "i32.extend8_s" (i32.const 0x012345_00)) (i32.const 0)) +(assert_return (invoke "i32.extend8_s" (i32.const 0xfedcba_80)) (i32.const -0x80)) +(assert_return (invoke "i32.extend8_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "i32.extend16_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "i32.extend16_s" (i32.const 0x7fff)) (i32.const 32767)) +(assert_return (invoke "i32.extend16_s" (i32.const 0x8000)) (i32.const -32768)) +(assert_return (invoke "i32.extend16_s" (i32.const 0xffff)) (i32.const -1)) +(assert_return (invoke "i32.extend16_s" (i32.const 0x0123_0000)) (i32.const 0)) +(assert_return (invoke "i32.extend16_s" (i32.const 0xfedc_8000)) (i32.const -0x8000)) +(assert_return (invoke "i32.extend16_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "i64.extend8_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend8_s" (i64.const 0x7f)) (i64.const 127)) +(assert_return (invoke "i64.extend8_s" (i64.const 0x80)) (i64.const -128)) +(assert_return (invoke "i64.extend8_s" (i64.const 0xff)) (i64.const -1)) +(assert_return (invoke "i64.extend8_s" (i64.const 0x01234567_89abcd_00)) (i64.const 0)) +(assert_return (invoke "i64.extend8_s" (i64.const 0xfedcba98_765432_80)) (i64.const -0x80)) +(assert_return (invoke "i64.extend8_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "i64.extend16_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend16_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "i64.extend16_s" (i64.const 0x8000)) (i64.const -32768)) +(assert_return (invoke "i64.extend16_s" (i64.const 0xffff)) (i64.const -1)) +(assert_return (invoke "i64.extend16_s" (i64.const 0x12345678_9abc_0000)) (i64.const 0)) +(assert_return (invoke "i64.extend16_s" (i64.const 0xfedcba98_7654_8000)) (i64.const -0x8000)) +(assert_return (invoke "i64.extend16_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "i64.extend32_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend32_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "i64.extend32_s" (i64.const 0x8000)) (i64.const 32768)) +(assert_return (invoke "i64.extend32_s" (i64.const 0xffff)) (i64.const 65535)) +(assert_return (invoke "i64.extend32_s" (i64.const 0x7fffffff)) (i64.const 0x7fffffff)) +(assert_return (invoke "i64.extend32_s" (i64.const 0x80000000)) (i64.const -0x80000000)) +(assert_return (invoke "i64.extend32_s" (i64.const 0xffffffff)) (i64.const -1)) +(assert_return (invoke "i64.extend32_s" (i64.const 0x01234567_00000000)) (i64.const 0)) +(assert_return (invoke "i64.extend32_s" (i64.const 0xfedcba98_80000000)) (i64.const -0x80000000)) +(assert_return (invoke "i64.extend32_s" (i64.const -1)) (i64.const -1)) \ No newline at end of file diff --git a/lib/spectests/tests/excludes.txt b/lib/spectests/tests/excludes.txt index dee265684..a2742b0dc 100644 --- a/lib/spectests/tests/excludes.txt +++ b/lib/spectests/tests/excludes.txt @@ -12,10 +12,12 @@ # Star line allows skipping an entire wast file # clif:skip:simd.wast:* # -# Excludes can also contain platform +# Excludes can also contain target family # clif:skip:data.wast:172:windows # clif:skip:data.wast:172:unix # +# Or target arch +# singlepass:skip:atomic.wast:*:*:aarch64 # Cranelift clif:skip:atomic.wast:* # Threads not implemented @@ -32,7 +34,6 @@ clif:fail:linking.wast:147 # AssertTrap - expected trap, got Runtime:Error "unkn clif:fail:linking.wast:149 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106883037 - illegal instruction" clif:fail:linking.wast:185 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106883062 - illegal instruction" clif:fail:linking.wast:187 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106883062 - illegal instruction" -clif:fail:linking.wast:387 # AssertReturn - result I32(0) ("0x0") does not match expected I32(104) ("0x68") clif:fail:linking.wast:388 # AssertReturn - Call failed RuntimeError: WebAssembly trap occurred during runtime: `call_indirect` out-of-bounds # clif:skip:skip-stack-guard-page.wast:2 # Slow test @@ -83,6 +84,7 @@ clif:fail:data.wast:227:windows # AssertUnlinkable - caught panic Any clif:fail:data.wast:258:windows # AssertUnlinkable - caught panic Any clif:fail:data.wast:273:windows # AssertUnlinkable - caught panic Any clif:fail:start.wast:92:windows # Module - caught panic Any +clif:skip:start.wast:98:windows clif:fail:align.wast:3:windows # Module - caught panic Any clif:fail:align.wast:4:windows # Module - caught panic Any @@ -269,7 +271,6 @@ llvm:fail:f32.wast:1621 # AssertReturn - result F32(0) ("0x0") does not match ex llvm:fail:f32.wast:2020 # AssertReturn - result F32(2147483648) ("0x80000000") does not match expected F32(0) ("0x0") llvm:fail:f64.wast:1621 # AssertReturn - result F64(0) ("0x0") does not match expected F64(9223372036854775808) ("0x8000000000000000") llvm:fail:f64.wast:2020 # AssertReturn - result F64(9223372036854775808) ("0x8000000000000000") does not match expected F64(0) ("0x0") -llvm:fail:linking.wast:387 # AssertReturn - result I32(0) ("0x0") does not match expected I32(104) ("0x68") llvm:fail:linking.wast:388 # AssertReturn - Call failed RuntimeError: WebAssembly trap occurred during runtime: incorrect `call_indirect` signature # LLVM Windows @@ -305,6 +306,8 @@ llvm:skip:simd_binaryen.wast:*:unix # Module - caught panic Any singlepass:skip:simd.wast:* # SIMD not implemented singlepass:skip:simd_binaryen.wast:* # SIMD not implemented +singlepass:skip:atomic.wast:*:*:aarch64 # Threads not yet supported on singlepass + singlepass:fail:address.wast:192 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:address.wast:194 # AssertTrap - expected trap, got [] singlepass:fail:address.wast:195 # AssertTrap - expected trap, got [] @@ -397,494 +400,74 @@ singlepass:fail:call_indirect.wast:493 # AssertTrap - expected trap, got Runtime singlepass:fail:call_indirect.wast:494 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:call_indirect.wast:500 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:call_indirect.wast:501 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:70 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:71 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:72 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:73 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:74 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:75 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:76 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:77 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:92 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:93 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:94 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:95 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:96 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:78 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:79 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:80 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:81 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:82 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:97 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:98 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:99 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:115 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:116 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:117 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:118 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:119 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:100 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:101 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:102 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:103 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:104 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:120 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:121 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:122 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:138 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:139 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:140 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:141 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:142 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:123 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:124 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:125 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:126 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:127 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:143 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:144 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:145 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:146 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:147 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:148 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:166 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:167 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:168 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:169 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:170 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:149 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:150 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:151 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:152 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:153 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:171 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:172 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:173 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:186 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:187 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:188 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:189 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:190 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:174 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:175 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:176 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:177 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:178 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:191 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:192 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:193 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:211 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:212 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:213 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:214 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:215 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:194 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:195 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:196 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:197 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:198 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:216 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:217 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:218 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:235 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:236 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:237 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:238 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:conversions.wast:239 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:219 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:220 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:221 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:222 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:223 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:240 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:241 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:conversions.wast:242 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:243 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:244 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:245 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:246 # AssertTrap - expected trap, got Runtime:Error unknown error +singlepass:fail:conversions.wast:247 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:elem.wast:353 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:f32.wast:1620 # AssertReturn - result F32(0) ("0x0") does not match expected F32(2147483648) ("0x80000000") -singlepass:fail:f32.wast:1652 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1654 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1656 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1658 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1692 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1694 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1696 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1698 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1732 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1734 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1736 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1738 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1772 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1774 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1776 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1778 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1812 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1814 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1816 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1818 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1852 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1854 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1856 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1858 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1892 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1894 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1896 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1898 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1932 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1934 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1936 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1938 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:1939 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.0) -singlepass:fail:f32.wast:1940 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.0) -singlepass:fail:f32.wast:1941 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.0) -singlepass:fail:f32.wast:1942 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.0) -singlepass:fail:f32.wast:1943 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.0) -singlepass:fail:f32.wast:1944 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.0) -singlepass:fail:f32.wast:1945 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.0) -singlepass:fail:f32.wast:1946 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.0) -singlepass:fail:f32.wast:1947 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1948 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1949 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1950 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1951 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1952 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1953 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1954 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:1955 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1956 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1957 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1958 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1959 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1960 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1961 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1962 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:1963 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.5) -singlepass:fail:f32.wast:1964 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.5) -singlepass:fail:f32.wast:1965 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.5) -singlepass:fail:f32.wast:1966 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.5) -singlepass:fail:f32.wast:1967 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.5) -singlepass:fail:f32.wast:1968 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.5) -singlepass:fail:f32.wast:1969 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.5) -singlepass:fail:f32.wast:1970 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.5) -singlepass:fail:f32.wast:1971 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-1.0) -singlepass:fail:f32.wast:1972 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-1.0) -singlepass:fail:f32.wast:1973 # "AssertReturnCanonicalNan" - value is not canonical nan F32(1.0) -singlepass:fail:f32.wast:1974 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(1.0) -singlepass:fail:f32.wast:1975 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-1.0) -singlepass:fail:f32.wast:1976 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-1.0) -singlepass:fail:f32.wast:1977 # "AssertReturnCanonicalNan" - value is not canonical nan F32(1.0) -singlepass:fail:f32.wast:1978 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(1.0) -singlepass:fail:f32.wast:1979 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-6.2831855) -singlepass:fail:f32.wast:1980 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-6.2831855) -singlepass:fail:f32.wast:1981 # "AssertReturnCanonicalNan" - value is not canonical nan F32(6.2831855) -singlepass:fail:f32.wast:1982 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(6.2831855) -singlepass:fail:f32.wast:1983 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-6.2831855) -singlepass:fail:f32.wast:1984 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-6.2831855) -singlepass:fail:f32.wast:1985 # "AssertReturnCanonicalNan" - value is not canonical nan F32(6.2831855) -singlepass:fail:f32.wast:1986 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(6.2831855) -singlepass:fail:f32.wast:1987 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1988 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1989 # "AssertReturnCanonicalNan" - value is not canonical nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1990 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1991 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1992 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1993 # "AssertReturnCanonicalNan" - value is not canonical nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1994 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:1995 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-inf) -singlepass:fail:f32.wast:1996 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-inf) -singlepass:fail:f32.wast:1997 # "AssertReturnCanonicalNan" - value is not canonical nan F32(inf) -singlepass:fail:f32.wast:1998 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(inf) -singlepass:fail:f32.wast:1999 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-inf) -singlepass:fail:f32.wast:2000 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-inf) -singlepass:fail:f32.wast:2001 # "AssertReturnCanonicalNan" - value is not canonical nan F32(inf) -singlepass:fail:f32.wast:2002 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(inf) -singlepass:fail:f32.wast:2005 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2006 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2009 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2010 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2013 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2014 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2017 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2018 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2021 # AssertReturn - result F32(2147483648) ("0x80000000") does not match expected F32(0) ("0x0") -singlepass:fail:f32.wast:2052 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2054 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2056 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2058 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2092 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2094 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2096 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2098 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2132 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2134 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2136 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2138 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2172 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2174 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2176 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2178 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2212 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2214 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2216 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2218 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2252 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2254 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2256 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2258 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2292 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2294 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2296 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2298 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2332 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2334 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2336 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2338 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2339 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.0) -singlepass:fail:f32.wast:2340 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.0) -singlepass:fail:f32.wast:2341 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.0) -singlepass:fail:f32.wast:2342 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.0) -singlepass:fail:f32.wast:2343 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.0) -singlepass:fail:f32.wast:2344 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.0) -singlepass:fail:f32.wast:2345 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.0) -singlepass:fail:f32.wast:2346 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.0) -singlepass:fail:f32.wast:2347 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2348 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2349 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2350 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2351 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2352 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2353 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2354 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000000000001) -singlepass:fail:f32.wast:2355 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2356 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2357 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2358 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2359 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2360 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2361 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2362 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.000000000000000000000000000000000000011754944) -singlepass:fail:f32.wast:2363 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.5) -singlepass:fail:f32.wast:2364 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.5) -singlepass:fail:f32.wast:2365 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.5) -singlepass:fail:f32.wast:2366 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.5) -singlepass:fail:f32.wast:2367 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-0.5) -singlepass:fail:f32.wast:2368 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-0.5) -singlepass:fail:f32.wast:2369 # "AssertReturnCanonicalNan" - value is not canonical nan F32(0.5) -singlepass:fail:f32.wast:2370 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(0.5) -singlepass:fail:f32.wast:2371 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-1.0) -singlepass:fail:f32.wast:2372 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-1.0) -singlepass:fail:f32.wast:2373 # "AssertReturnCanonicalNan" - value is not canonical nan F32(1.0) -singlepass:fail:f32.wast:2374 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(1.0) -singlepass:fail:f32.wast:2375 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-1.0) -singlepass:fail:f32.wast:2376 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-1.0) -singlepass:fail:f32.wast:2377 # "AssertReturnCanonicalNan" - value is not canonical nan F32(1.0) -singlepass:fail:f32.wast:2378 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(1.0) -singlepass:fail:f32.wast:2379 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-6.2831855) -singlepass:fail:f32.wast:2380 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-6.2831855) -singlepass:fail:f32.wast:2381 # "AssertReturnCanonicalNan" - value is not canonical nan F32(6.2831855) -singlepass:fail:f32.wast:2382 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(6.2831855) -singlepass:fail:f32.wast:2383 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-6.2831855) -singlepass:fail:f32.wast:2384 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-6.2831855) -singlepass:fail:f32.wast:2385 # "AssertReturnCanonicalNan" - value is not canonical nan F32(6.2831855) -singlepass:fail:f32.wast:2386 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(6.2831855) -singlepass:fail:f32.wast:2387 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2388 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2389 # "AssertReturnCanonicalNan" - value is not canonical nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2390 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2391 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2392 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2393 # "AssertReturnCanonicalNan" - value is not canonical nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2394 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(340282350000000000000000000000000000000.0) -singlepass:fail:f32.wast:2395 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-inf) -singlepass:fail:f32.wast:2396 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-inf) -singlepass:fail:f32.wast:2397 # "AssertReturnCanonicalNan" - value is not canonical nan F32(inf) -singlepass:fail:f32.wast:2398 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(inf) -singlepass:fail:f32.wast:2399 # "AssertReturnCanonicalNan" - value is not canonical nan F32(-inf) -singlepass:fail:f32.wast:2400 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(-inf) -singlepass:fail:f32.wast:2401 # "AssertReturnCanonicalNan" - value is not canonical nan F32(inf) -singlepass:fail:f32.wast:2402 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(inf) -singlepass:fail:f32.wast:2405 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2406 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2409 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2410 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2413 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2414 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2417 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f32.wast:2418 # "AssertReturnArithmeticNan" - value is not arithmetic nan F32(NaN) -singlepass:fail:f64.wast:1620 # AssertReturn - result F64(0) ("0x0") does not match expected F64(9223372036854775808) ("0x8000000000000000") -singlepass:fail:f64.wast:1652 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1654 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1656 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1658 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1692 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1694 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1696 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1698 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1732 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1734 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1736 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1738 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1772 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1774 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1776 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1778 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1812 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1814 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1816 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1818 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1852 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1854 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1856 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1858 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1892 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1894 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1896 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1898 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1932 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1934 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1936 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1938 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:1939 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.0) -singlepass:fail:f64.wast:1940 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.0) -singlepass:fail:f64.wast:1941 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.0) -singlepass:fail:f64.wast:1942 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.0) -singlepass:fail:f64.wast:1943 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.0) -singlepass:fail:f64.wast:1944 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.0) -singlepass:fail:f64.wast:1945 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.0) -singlepass:fail:f64.wast:1946 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.0) -singlepass:fail:f64.wast:1947 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1948 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1949 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1950 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1951 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1952 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1953 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1954 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:1955 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1956 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1957 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1958 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1959 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1960 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1961 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1962 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:1963 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.5) -singlepass:fail:f64.wast:1964 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.5) -singlepass:fail:f64.wast:1965 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.5) -singlepass:fail:f64.wast:1966 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.5) -singlepass:fail:f64.wast:1967 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.5) -singlepass:fail:f64.wast:1968 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.5) -singlepass:fail:f64.wast:1969 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.5) -singlepass:fail:f64.wast:1970 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.5) -singlepass:fail:f64.wast:1971 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-1.0) -singlepass:fail:f64.wast:1972 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-1.0) -singlepass:fail:f64.wast:1973 # "AssertReturnCanonicalNan" - value is not canonical nan F64(1.0) -singlepass:fail:f64.wast:1974 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(1.0) -singlepass:fail:f64.wast:1975 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-1.0) -singlepass:fail:f64.wast:1976 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-1.0) -singlepass:fail:f64.wast:1977 # "AssertReturnCanonicalNan" - value is not canonical nan F64(1.0) -singlepass:fail:f64.wast:1978 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(1.0) -singlepass:fail:f64.wast:1979 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-6.283185307179586) -singlepass:fail:f64.wast:1980 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-6.283185307179586) -singlepass:fail:f64.wast:1981 # "AssertReturnCanonicalNan" - value is not canonical nan F64(6.283185307179586) -singlepass:fail:f64.wast:1982 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(6.283185307179586) -singlepass:fail:f64.wast:1983 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-6.283185307179586) -singlepass:fail:f64.wast:1984 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-6.283185307179586) -singlepass:fail:f64.wast:1985 # "AssertReturnCanonicalNan" - value is not canonical nan F64(6.283185307179586) -singlepass:fail:f64.wast:1986 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(6.283185307179586) -singlepass:fail:f64.wast:1987 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1988 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1989 # "AssertReturnCanonicalNan" - value is not canonical nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1990 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1991 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1992 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1993 # "AssertReturnCanonicalNan" - value is not canonical nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1994 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:1995 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-inf) -singlepass:fail:f64.wast:1996 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-inf) -singlepass:fail:f64.wast:1997 # "AssertReturnCanonicalNan" - value is not canonical nan F64(inf) -singlepass:fail:f64.wast:1998 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(inf) -singlepass:fail:f64.wast:1999 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-inf) -singlepass:fail:f64.wast:2000 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-inf) -singlepass:fail:f64.wast:2001 # "AssertReturnCanonicalNan" - value is not canonical nan F64(inf) -singlepass:fail:f64.wast:2002 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(inf) -singlepass:fail:f64.wast:2005 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2006 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2009 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2010 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2013 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2014 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2017 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2018 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2021 # AssertReturn - result F64(9223372036854775808) ("0x8000000000000000") does not match expected F64(0) ("0x0") -singlepass:fail:f64.wast:2052 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2054 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2056 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2058 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2092 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2094 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2096 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2098 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2132 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2134 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2136 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2138 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2172 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2174 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2176 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2178 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2212 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2214 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2216 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2218 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2252 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2254 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2256 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2258 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2292 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2294 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2296 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2298 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2332 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2334 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2336 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2338 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2339 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.0) -singlepass:fail:f64.wast:2340 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.0) -singlepass:fail:f64.wast:2341 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.0) -singlepass:fail:f64.wast:2342 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.0) -singlepass:fail:f64.wast:2343 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.0) -singlepass:fail:f64.wast:2344 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.0) -singlepass:fail:f64.wast:2345 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.0) -singlepass:fail:f64.wast:2346 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.0) -singlepass:fail:f64.wast:2347 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2348 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2349 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2350 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2351 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2352 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2353 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2354 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005) -singlepass:fail:f64.wast:2355 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2356 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2357 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2358 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2359 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2360 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2361 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2362 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014) -singlepass:fail:f64.wast:2363 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.5) -singlepass:fail:f64.wast:2364 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.5) -singlepass:fail:f64.wast:2365 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.5) -singlepass:fail:f64.wast:2366 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.5) -singlepass:fail:f64.wast:2367 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-0.5) -singlepass:fail:f64.wast:2368 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-0.5) -singlepass:fail:f64.wast:2369 # "AssertReturnCanonicalNan" - value is not canonical nan F64(0.5) -singlepass:fail:f64.wast:2370 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(0.5) -singlepass:fail:f64.wast:2371 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-1.0) -singlepass:fail:f64.wast:2372 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-1.0) -singlepass:fail:f64.wast:2373 # "AssertReturnCanonicalNan" - value is not canonical nan F64(1.0) -singlepass:fail:f64.wast:2374 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(1.0) -singlepass:fail:f64.wast:2375 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-1.0) -singlepass:fail:f64.wast:2376 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-1.0) -singlepass:fail:f64.wast:2377 # "AssertReturnCanonicalNan" - value is not canonical nan F64(1.0) -singlepass:fail:f64.wast:2378 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(1.0) -singlepass:fail:f64.wast:2379 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-6.283185307179586) -singlepass:fail:f64.wast:2380 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-6.283185307179586) -singlepass:fail:f64.wast:2381 # "AssertReturnCanonicalNan" - value is not canonical nan F64(6.283185307179586) -singlepass:fail:f64.wast:2382 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(6.283185307179586) -singlepass:fail:f64.wast:2383 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-6.283185307179586) -singlepass:fail:f64.wast:2384 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-6.283185307179586) -singlepass:fail:f64.wast:2385 # "AssertReturnCanonicalNan" - value is not canonical nan F64(6.283185307179586) -singlepass:fail:f64.wast:2386 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(6.283185307179586) -singlepass:fail:f64.wast:2387 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2388 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2389 # "AssertReturnCanonicalNan" - value is not canonical nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2390 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2391 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2392 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2393 # "AssertReturnCanonicalNan" - value is not canonical nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2394 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0) -singlepass:fail:f64.wast:2395 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-inf) -singlepass:fail:f64.wast:2396 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-inf) -singlepass:fail:f64.wast:2397 # "AssertReturnCanonicalNan" - value is not canonical nan F64(inf) -singlepass:fail:f64.wast:2398 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(inf) -singlepass:fail:f64.wast:2399 # "AssertReturnCanonicalNan" - value is not canonical nan F64(-inf) -singlepass:fail:f64.wast:2400 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(-inf) -singlepass:fail:f64.wast:2401 # "AssertReturnCanonicalNan" - value is not canonical nan F64(inf) -singlepass:fail:f64.wast:2402 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(inf) -singlepass:fail:f64.wast:2405 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2406 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2409 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2410 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2413 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2414 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2417 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) -singlepass:fail:f64.wast:2418 # "AssertReturnArithmeticNan" - value is not arithmetic nan F64(NaN) singlepass:fail:func_ptrs.wast:78 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:func_ptrs.wast:79 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:func_ptrs.wast:80 # AssertTrap - expected trap, got Runtime:Error unknown error @@ -902,15 +485,6 @@ singlepass:fail:i32.wast:99 # AssertTrap - expected trap, got Runtime:Error unkn singlepass:fail:i32.wast:100 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i32.wast:120 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i32.wast:121 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:i32.wast:242 # AssertReturn - result I32(31) ("0x1f") does not match expected I32(0) ("0x0") -singlepass:fail:i32.wast:243 # AssertReturn - result I32(0) ("0x0") does not match expected I32(32) ("0x20") -singlepass:fail:i32.wast:244 # AssertReturn - result I32(15) ("0xf") does not match expected I32(16) ("0x10") -singlepass:fail:i32.wast:245 # AssertReturn - result I32(7) ("0x7") does not match expected I32(24) ("0x18") -singlepass:fail:i32.wast:246 # AssertReturn - result I32(31) ("0x1f") does not match expected I32(0) ("0x0") -singlepass:fail:i32.wast:247 # AssertReturn - result I32(0) ("0x0") does not match expected I32(31) ("0x1f") -singlepass:fail:i32.wast:248 # AssertReturn - result I32(1) ("0x1") does not match expected I32(30) ("0x1e") -singlepass:fail:i32.wast:249 # AssertReturn - result I32(30) ("0x1e") does not match expected I32(1) ("0x1") -singlepass:fail:i32.wast:252 # AssertReturn - result I32(0) ("0x0") does not match expected I32(32) ("0x20") singlepass:fail:i64.wast:62 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i64.wast:63 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i64.wast:64 # AssertTrap - expected trap, got Runtime:Error unknown error @@ -920,15 +494,6 @@ singlepass:fail:i64.wast:99 # AssertTrap - expected trap, got Runtime:Error unkn singlepass:fail:i64.wast:100 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i64.wast:120 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:i64.wast:121 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:i64.wast:242 # AssertReturn - result I64(63) ("0x3f") does not match expected I64(0) ("0x0") -singlepass:fail:i64.wast:243 # AssertReturn - result I64(0) ("0x0") does not match expected I64(64) ("0x40") -singlepass:fail:i64.wast:244 # AssertReturn - result I64(15) ("0xf") does not match expected I64(48) ("0x30") -singlepass:fail:i64.wast:245 # AssertReturn - result I64(7) ("0x7") does not match expected I64(56) ("0x38") -singlepass:fail:i64.wast:246 # AssertReturn - result I64(63) ("0x3f") does not match expected I64(0) ("0x0") -singlepass:fail:i64.wast:247 # AssertReturn - result I64(0) ("0x0") does not match expected I64(63) ("0x3f") -singlepass:fail:i64.wast:248 # AssertReturn - result I64(1) ("0x1") does not match expected I64(62) ("0x3e") -singlepass:fail:i64.wast:249 # AssertReturn - result I64(62) ("0x3e") does not match expected I64(1) ("0x1") -singlepass:fail:i64.wast:252 # AssertReturn - result I64(0) ("0x0") does not match expected I64(64) ("0x40") singlepass:fail:if.wast:440 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:imports.wast:283 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:imports.wast:286 # AssertTrap - expected trap, got Runtime:Error unknown error @@ -975,9 +540,7 @@ singlepass:fail:linking.wast:190 # AssertTrap - expected trap, got Runtime:Error singlepass:fail:linking.wast:225 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:linking.wast:236 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:linking.wast:248 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:linking.wast:387 # AssertReturn - result I32(0) ("0x0") does not match expected I32(104) ("0x68") singlepass:fail:linking.wast:388 # AssertReturn - Call failed RuntimeError: unknown error -singlepass:fail:load.wast:201 # AssertReturn - result I32(0) ("0x0") does not match expected I32(32) ("0x20") singlepass:fail:memory_grow.wast:15 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_grow.wast:16 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_grow.wast:17 # AssertTrap - expected trap, got Runtime:Error unknown error @@ -985,7 +548,6 @@ singlepass:fail:memory_grow.wast:18 # AssertTrap - expected trap, got Runtime:Er singlepass:fail:memory_grow.wast:24 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_grow.wast:25 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_grow.wast:286 # AssertTrap - expected trap, got Runtime:Error unknown error -singlepass:fail:memory_grow.wast:299 # AssertReturn - result I32(0) ("0x0") does not match expected I32(31) ("0x1f") singlepass:fail:memory_trap.wast:23 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_trap.wast:24 # AssertTrap - expected trap, got Runtime:Error unknown error singlepass:fail:memory_trap.wast:25 # AssertTrap - expected trap, got Runtime:Error unknown error diff --git a/lib/spectests/tests/semantics.rs b/lib/spectests/tests/semantics.rs index 0bffa9dca..eadbeef0e 100644 --- a/lib/spectests/tests/semantics.rs +++ b/lib/spectests/tests/semantics.rs @@ -1,10 +1,9 @@ #[cfg(test)] mod tests { use wabt::wat2wasm; - use wasmer_clif_backend::CraneliftCompiler; - use wasmer_runtime_core::{ + use wasmer_runtime::{ error::{CallError, RuntimeError}, - import::ImportObject, + ImportObject, }; // The semantics of stack overflow are documented at: @@ -22,8 +21,7 @@ mod tests { (elem (;0;) (i32.const 0) 0)) "#; let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); + let module = wasmer_runtime::compile(&wasm_binary[..]).expect("WASM can't be compiled"); let instance = module .instantiate(&ImportObject::new()) .expect("WASM can't be instantiated"); diff --git a/lib/spectests/tests/spectest.rs b/lib/spectests/tests/spectest.rs index 62bfc7d53..3b03b598f 100644 --- a/lib/spectests/tests/spectest.rs +++ b/lib/spectests/tests/spectest.rs @@ -18,6 +18,7 @@ mod tests { // TODO Files could be run with multiple threads // TODO Allow running WAST &str directly (E.g. for use outside of spectests) + use std::collections::HashSet; use std::sync::{Arc, Mutex}; struct SpecFailure { @@ -46,15 +47,14 @@ mod tests { pub fn add_failure( &mut self, failure: SpecFailure, - testkey: &str, - excludes: &HashMap, + _testkey: &str, + excludes: &Vec, + line: u64, ) { - if excludes.contains_key(testkey) { - self.allowed_failure += 1; - return; - } - let platform_key = format!("{}:{}", testkey, get_platform()); - if excludes.contains_key(&platform_key) { + if excludes + .iter() + .any(|e| e.line_matches(line) && e.exclude_kind == ExcludeKind::Fail) + { self.allowed_failure += 1; return; } @@ -63,31 +63,6 @@ mod tests { } } - #[cfg(feature = "clif")] - fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - - #[cfg(feature = "llvm")] - fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() - } - - #[cfg(feature = "singlepass")] - fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() - } - - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] - fn get_compiler() -> impl Compiler { - panic!("compiler not specified, activate a compiler via features"); - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - #[cfg(feature = "clif")] fn get_compiler_name() -> &'static str { "clif" @@ -104,15 +79,113 @@ mod tests { } #[cfg(unix)] - fn get_platform() -> &'static str { + fn get_target_family() -> &'static str { "unix" } #[cfg(windows)] - fn get_platform() -> &'static str { + fn get_target_family() -> &'static str { "windows" } + fn get_target_arch() -> &'static str { + if cfg!(target_arch = "x86_64") { + "x86_64" + } else if cfg!(target_arch = "aarch64") { + "aarch64" + } else if cfg!(target_arch = "x86") { + "x86" + } else if cfg!(target_arch = "mips") { + "mips" + } else if cfg!(target_arch = "powerpc") { + "powerpc" + } else if cfg!(target_arch = "powerpc64") { + "powerpc64" + } else if cfg!(target_arch = "arm") { + "arm" + } else { + panic!("unknown target arch") + } + } + + // clif:skip:data.wast:172:unix:x86 + #[allow(dead_code)] + struct Exclude { + backend: Option, + exclude_kind: ExcludeKind, + file: String, + line: Option, + target_family: Option, + target_arch: Option, + } + + impl Exclude { + fn line_matches(&self, value: u64) -> bool { + self.line.is_none() || self.line.unwrap() == value + } + + fn line_exact_match(&self, value: u64) -> bool { + self.line.is_some() && self.line.unwrap() == value + } + + fn matches_backend(&self, value: &str) -> bool { + self.backend.is_none() || self.backend.as_ref().unwrap() == value + } + + fn matches_target_family(&self, value: &str) -> bool { + self.target_family.is_none() || self.target_family.as_ref().unwrap() == value + } + + fn matches_target_arch(&self, value: &str) -> bool { + self.target_arch.is_none() || self.target_arch.as_ref().unwrap() == value + } + + fn from( + backend: &str, + exclude_kind: &str, + file: &str, + line: &str, + target_family: &str, + target_arch: &str, + ) -> Exclude { + let backend: Option = match backend { + "*" => None, + "clif" => Some("clif".to_string()), + "singlepass" => Some("singlepass".to_string()), + "llvm" => Some("llvm".to_string()), + _ => panic!("backend {:?} not recognized", backend), + }; + let exclude_kind = match exclude_kind { + "skip" => ExcludeKind::Skip, + "fail" => ExcludeKind::Fail, + _ => panic!("exclude kind {:?} not recognized", exclude_kind), + }; + let line = match line { + "*" => None, + _ => Some( + line.parse::() + .expect(&format!("expected * or int: {:?}", line)), + ), + }; + let target_family = match target_family { + "*" => None, + _ => Some(target_family.to_string()), + }; + let target_arch = match target_arch { + "*" => None, + _ => Some(target_arch.to_string()), + }; + Exclude { + backend, + exclude_kind, + file: file.to_string(), + line, + target_family, + target_arch, + } + } + } + #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] fn get_compiler_name() -> &'static str { panic!("compiler not specified, activate a compiler via features"); @@ -143,24 +216,20 @@ mod tests { use std::panic::AssertUnwindSafe; use std::path::PathBuf; use wabt::script::{Action, Command, CommandKind, ScriptParser, Value}; - use wasmer_runtime_core::backend::{Compiler, CompilerConfig, Features}; - use wasmer_runtime_core::error::CompileError; - use wasmer_runtime_core::import::ImportObject; - use wasmer_runtime_core::Instance; - use wasmer_runtime_core::{ - export::Export, - global::Global, - import::LikeNamespace, - memory::Memory, - table::Table, + use wasmer_runtime::{ + compile_with_config, + error::CompileError, + func, imports, types::{ElementType, MemoryDescriptor, TableDescriptor}, units::Pages, + CompilerConfig, Ctx, Export, Features, Global, ImportObject, Instance, LikeNamespace, + Memory, Table, }; - use wasmer_runtime_core::{func, imports, vm::Ctx}; fn parse_and_run( path: &PathBuf, - excludes: &HashMap, + file_excludes: &HashSet, + excludes: &HashMap>, ) -> Result { let mut test_report = TestReport { failures: vec![], @@ -171,21 +240,16 @@ mod tests { let filename = path.file_name().unwrap().to_str().unwrap(); let source = fs::read(&path).unwrap(); - let backend = get_compiler_name(); - let platform = get_platform(); - let star_key = format!("{}:{}:*", backend, filename); - let platform_star_key = format!("{}:{}:*:{}", backend, filename, platform); - if (excludes.contains_key(&star_key) && *excludes.get(&star_key).unwrap() == Exclude::Skip) - || (excludes.contains_key(&platform_star_key) - && *excludes.get(&platform_star_key).unwrap() == Exclude::Skip) - { + // Entire file is excluded by line * and skip + if file_excludes.contains(filename) { return Ok(test_report); } let mut features = wabt::Features::new(); features.enable_simd(); features.enable_threads(); + features.enable_sign_extension(); let mut parser: ScriptParser = ScriptParser::from_source_and_name_with_features(&source, filename, features) .expect(&format!("Failed to parse script {}", &filename)); @@ -197,21 +261,27 @@ mod tests { let mut registered_modules: HashMap>> = HashMap::new(); // + let empty_excludes = vec![]; + let excludes = if excludes.contains_key(filename) { + excludes.get(filename).unwrap() + } else { + &empty_excludes + }; + + let backend = get_compiler_name(); while let Some(Command { kind, line }) = parser.next().map_err(|e| format!("Parse err: {:?}", e))? { let test_key = format!("{}:{}:{}", backend, filename, line); - let test_platform_key = format!("{}:{}:{}:{}", backend, filename, line, platform); // Use this line to debug which test is running println!("Running test: {}", test_key); - if (excludes.contains_key(&test_key) - && *excludes.get(&test_key).unwrap() == Exclude::Skip) - || (excludes.contains_key(&test_platform_key) - && *excludes.get(&test_platform_key).unwrap() == Exclude::Skip) + // Skip tests that match this line + if excludes + .iter() + .any(|e| e.line_exact_match(line) && e.exclude_kind == ExcludeKind::Skip) { - // println!("Skipping test: {}", test_key); continue; } @@ -228,12 +298,8 @@ mod tests { }, ..Default::default() }; - let module = wasmer_runtime_core::compile_with_config( - &module.into_vec(), - &get_compiler(), - config, - ) - .expect("WASM can't be compiled"); + let module = compile_with_config(&module.into_vec(), config) + .expect("WASM can't be compiled"); let i = module .instantiate(&spectest_import_object) .expect("WASM can't be instantiated"); @@ -250,6 +316,7 @@ mod tests { }, &test_key, excludes, + line, ); instance = None; } @@ -274,7 +341,7 @@ mod tests { &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }, @@ -289,6 +356,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -303,6 +371,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(values) => { @@ -319,7 +388,7 @@ mod tests { "result {:?} ({:?}) does not match expected {:?} ({:?})", v, to_hex(v.clone()), expected_value, to_hex(expected_value.clone()) ), - }, &test_key, excludes); + }, &test_key, excludes, line); } else { test_report.count_passed(); } @@ -349,6 +418,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let export: Export = maybe_call_result.unwrap(); @@ -372,6 +442,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -385,6 +456,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -401,7 +473,7 @@ mod tests { } => { let maybe_call_result = with_instance(instance.clone(), &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }); @@ -415,6 +487,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -429,6 +502,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(values) => { @@ -445,12 +519,14 @@ mod tests { "AssertReturnCanonicalNan" ), message: format!( - "value is not canonical nan {:?}", - v + "value is not canonical nan {:?} ({:?})", + v, + value_to_hex(v.clone()), ), }, &test_key, excludes, + line, ); } } @@ -468,7 +544,7 @@ mod tests { } => { let maybe_call_result = with_instance(instance.clone(), &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }); @@ -482,6 +558,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -496,6 +573,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(values) => { @@ -512,12 +590,14 @@ mod tests { "AssertReturnArithmeticNan" ), message: format!( - "value is not arithmetic nan {:?}", - v + "value is not arithmetic nan {:?} ({:?})", + v, + value_to_hex(v.clone()), ), }, &test_key, excludes, + line, ); } } @@ -535,7 +615,7 @@ mod tests { } => { let maybe_call_result = with_instance(instance.clone(), &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }); @@ -549,10 +629,11 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); - use wasmer_runtime_core::error::{CallError, RuntimeError}; + use wasmer_runtime::error::{CallError, RuntimeError}; match call_result { Err(e) => { match e { @@ -566,6 +647,7 @@ mod tests { }, &test_key, excludes, + line, ); } CallError::Runtime(r) => { @@ -587,6 +669,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -603,6 +686,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -620,11 +704,7 @@ mod tests { }, ..Default::default() }; - wasmer_runtime_core::compile_with_config( - &module.into_vec(), - &get_compiler(), - config, - ) + compile_with_config(&module.into_vec(), config) }); match result { Ok(module) => { @@ -646,6 +726,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -659,6 +740,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -674,11 +756,7 @@ mod tests { }, ..Default::default() }; - wasmer_runtime_core::compile_with_config( - &module.into_vec(), - &get_compiler(), - config, - ) + compile_with_config(&module.into_vec(), config) }); match result { @@ -701,6 +779,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -714,14 +793,46 @@ mod tests { }, &test_key, excludes, + line, ); } } } - 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 = compile_with_config(&module.into_vec(), 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, + line, + ); + } + }; + } CommandKind::AssertExhaustion { action, message: _ } => { match action { Action::Invoke { @@ -734,7 +845,7 @@ mod tests { &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }, @@ -749,6 +860,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -770,6 +882,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -789,12 +902,8 @@ mod tests { }, ..Default::default() }; - let module = wasmer_runtime_core::compile_with_config( - &module.into_vec(), - &get_compiler(), - config, - ) - .expect("WASM can't be compiled"); + let module = compile_with_config(&module.into_vec(), config) + .expect("WASM can't be compiled"); module.instantiate(&spectest_import_object) })); match result { @@ -808,6 +917,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(result) => match result { @@ -823,10 +933,11 @@ mod tests { }, &test_key, excludes, + line, ); } Err(e) => match e { - wasmer_runtime_core::error::Error::LinkError(_) => { + wasmer_runtime::error::Error::LinkError(_) => { test_report.count_passed(); } _ => { @@ -839,6 +950,7 @@ mod tests { }, &test_key, excludes, + line, ); } }, @@ -872,6 +984,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -883,7 +996,7 @@ mod tests { } => { let maybe_call_result = with_instance(instance.clone(), &named_modules, &module, |instance| { - let params: Vec = + let params: Vec = args.iter().cloned().map(convert_value).collect(); instance.call(&field, ¶ms[..]) }); @@ -897,6 +1010,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -911,6 +1025,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(_values) => { @@ -929,22 +1044,32 @@ mod tests { Ok(test_report) } - fn is_canonical_nan(val: wasmer_runtime_core::types::Value) -> bool { + fn is_canonical_nan(val: wasmer_runtime::types::Value) -> bool { match val { - wasmer_runtime_core::types::Value::F32(x) => x.is_canonical_nan(), - wasmer_runtime_core::types::Value::F64(x) => x.is_canonical_nan(), + wasmer_runtime::types::Value::F32(x) => x.is_canonical_nan(), + wasmer_runtime::types::Value::F64(x) => x.is_canonical_nan(), _ => panic!("value is not a float {:?}", val), } } - fn is_arithmetic_nan(val: wasmer_runtime_core::types::Value) -> bool { + fn is_arithmetic_nan(val: wasmer_runtime::types::Value) -> bool { match val { - wasmer_runtime_core::types::Value::F32(x) => x.is_quiet_nan(), - wasmer_runtime_core::types::Value::F64(x) => x.is_quiet_nan(), + wasmer_runtime::types::Value::F32(x) => x.is_quiet_nan(), + wasmer_runtime::types::Value::F64(x) => x.is_quiet_nan(), _ => panic!("value is not a float {:?}", val), } } + fn value_to_hex(val: wasmer_runtime::types::Value) -> String { + match val { + wasmer_runtime::types::Value::I32(x) => format!("{:#x}", x), + wasmer_runtime::types::Value::I64(x) => format!("{:#x}", x), + wasmer_runtime::types::Value::F32(x) => format!("{:#x}", x.to_bits()), + wasmer_runtime::types::Value::F64(x) => format!("{:#x}", x.to_bits()), + wasmer_runtime::types::Value::V128(x) => format!("{:#x}", x), + } + } + #[derive(Debug, Clone, Eq, PartialEq)] pub enum SpectestValue { I32(i32), @@ -954,13 +1079,13 @@ mod tests { V128(u128), } - fn convert_wasmer_value(other: wasmer_runtime_core::types::Value) -> SpectestValue { + fn convert_wasmer_value(other: wasmer_runtime::types::Value) -> SpectestValue { match other { - wasmer_runtime_core::types::Value::I32(v) => SpectestValue::I32(v), - wasmer_runtime_core::types::Value::I64(v) => SpectestValue::I64(v), - wasmer_runtime_core::types::Value::F32(v) => SpectestValue::F32(v.to_bits()), - wasmer_runtime_core::types::Value::F64(v) => SpectestValue::F64(v.to_bits()), - wasmer_runtime_core::types::Value::V128(v) => SpectestValue::V128(v), + wasmer_runtime::types::Value::I32(v) => SpectestValue::I32(v), + wasmer_runtime::types::Value::I64(v) => SpectestValue::I64(v), + wasmer_runtime::types::Value::F32(v) => SpectestValue::F32(v.to_bits()), + wasmer_runtime::types::Value::F64(v) => SpectestValue::F64(v.to_bits()), + wasmer_runtime::types::Value::V128(v) => SpectestValue::V128(v), } } @@ -974,13 +1099,13 @@ mod tests { } } - fn convert_value(other: Value) -> wasmer_runtime_core::types::Value { + fn convert_value(other: Value) -> wasmer_runtime::types::Value { match other { - Value::I32(v) => wasmer_runtime_core::types::Value::I32(v), - Value::I64(v) => wasmer_runtime_core::types::Value::I64(v), - Value::F32(v) => wasmer_runtime_core::types::Value::F32(v), - Value::F64(v) => wasmer_runtime_core::types::Value::F64(v), - Value::V128(v) => wasmer_runtime_core::types::Value::V128(v), + Value::I32(v) => wasmer_runtime::types::Value::I32(v), + Value::I64(v) => wasmer_runtime::types::Value::I64(v), + Value::F32(v) => wasmer_runtime::types::Value::F32(v), + Value::F64(v) => wasmer_runtime::types::Value::F64(v), + Value::V128(v) => wasmer_runtime::types::Value::V128(v), } } @@ -1024,9 +1149,9 @@ mod tests { let memory_desc = MemoryDescriptor::new(Pages(1), Some(Pages(2)), false).unwrap(); let memory = Memory::new(memory_desc).unwrap(); - let global_i32 = Global::new(wasmer_runtime_core::types::Value::I32(666)); - let global_f32 = Global::new(wasmer_runtime_core::types::Value::F32(666.0)); - let global_f64 = Global::new(wasmer_runtime_core::types::Value::F64(666.0)); + let global_i32 = Global::new(wasmer_runtime::types::Value::I32(666)); + let global_f32 = Global::new(wasmer_runtime::types::Value::F32(666.0)); + let global_f64 = Global::new(wasmer_runtime::types::Value::F64(666.0)); let table = Table::new(TableDescriptor { element: ElementType::Anyfunc, @@ -1058,7 +1183,7 @@ mod tests { } #[derive(Debug, Copy, Clone, PartialEq, Eq)] - enum Exclude { + enum ExcludeKind { Skip, Fail, } @@ -1068,13 +1193,18 @@ mod tests { use std::io::{BufRead, BufReader}; /// Reads the excludes.txt file into a hash map - fn read_excludes() -> HashMap { + fn read_excludes() -> (HashMap>, HashSet) { let mut excludes_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); excludes_path.push("tests"); excludes_path.push("excludes.txt"); let input = File::open(excludes_path).unwrap(); let buffered = BufReader::new(input); let mut result = HashMap::new(); + let mut file_excludes = HashSet::new(); + let current_backend = get_compiler_name(); + let current_target_family = get_target_family(); + let current_target_arch = get_target_arch(); + for line in buffered.lines() { let mut line = line.unwrap(); if line.trim().is_empty() || line.starts_with("#") { @@ -1089,26 +1219,53 @@ mod tests { // ::: let split: Vec<&str> = line.trim().split(':').collect(); - let kind = match *split.get(1).unwrap() { - "skip" => Exclude::Skip, - "fail" => Exclude::Fail, - _ => panic!("unknown exclude kind"), + let file = *split.get(2).unwrap(); + let exclude = match split.len() { + 0..=3 => panic!("expected at least 4 exclude conditions"), + 4 => Exclude::from( + *split.get(0).unwrap(), + *split.get(1).unwrap(), + *split.get(2).unwrap(), + *split.get(3).unwrap(), + "*", + "*", + ), + 5 => Exclude::from( + *split.get(0).unwrap(), + *split.get(1).unwrap(), + *split.get(2).unwrap(), + *split.get(3).unwrap(), + *split.get(4).unwrap(), + "*", + ), + 6 => Exclude::from( + *split.get(0).unwrap(), + *split.get(1).unwrap(), + *split.get(2).unwrap(), + *split.get(3).unwrap(), + *split.get(4).unwrap(), + *split.get(5).unwrap(), + ), + _ => panic!("too many exclude conditions {}", split.len()), }; - let has_platform = split.len() > 4; - let backend = split.get(0).unwrap(); - let testfile = split.get(2).unwrap(); - let line = split.get(3).unwrap(); - let key = if has_platform { - let platform = split.get(4).unwrap(); - format!("{}:{}:{}:{}", backend, testfile, line, platform) - } else { - format!("{}:{}:{}", backend, testfile, line) - }; - result.insert(key, kind); + if exclude.matches_backend(current_backend) + && exclude.matches_target_family(current_target_family) + && exclude.matches_target_arch(current_target_arch) + { + // Skip the whole file for line * and skip + if exclude.line.is_none() && exclude.exclude_kind == ExcludeKind::Skip { + file_excludes.insert(file.to_string()); + } + + if !result.contains_key(file) { + result.insert(file.to_string(), vec![]); + } + result.get_mut(file).unwrap().push(exclude); + } } } - result + (result, file_excludes) } #[test] @@ -1116,7 +1273,7 @@ mod tests { let mut success = true; let mut test_reports = vec![]; - let excludes = read_excludes(); + let (excludes, file_excludes) = read_excludes(); let mut glob_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); glob_path.push("spectests"); @@ -1126,7 +1283,7 @@ mod tests { for entry in glob(glob_str).expect("Failed to read glob pattern") { match entry { Ok(wast_path) => { - let result = parse_and_run(&wast_path, &excludes); + let result = parse_and_run(&wast_path, &file_excludes, &excludes); match result { Ok(test_report) => { if test_report.has_failures() { diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index 580f0303d..9b84e5ea9 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.8.0" +version = "0.11.0" description = "Tests for our WASI implementation" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,22 +9,21 @@ 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" } +# We set default features to false to be able to use the singlepass backend properly +wasmer-runtime = { path = "../runtime", version = "0.11.0", default-features = false } +wasmer-wasi = { path = "../wasi", version = "0.11.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-clif-backend = { path = "../clif-backend", version = "0.11.0", optional = true} +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.11.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.11.0", features = ["test"], 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-dev-utils = { path = "../dev-utils", version = "0.11.0"} [features] -clif = [] -singlepass = ["wasmer-singlepass-backend"] -llvm = ["wasmer-llvm-backend"] +clif = ["wasmer-clif-backend", "wasmer-runtime/default-backend-cranelift"] +singlepass = ["wasmer-singlepass-backend", "wasmer-runtime/default-backend-singlepass"] +llvm = ["wasmer-llvm-backend", "wasmer-runtime/default-backend-llvm"] diff --git a/lib/wasi-tests/build/wasitests.rs b/lib/wasi-tests/build/wasitests.rs index 0144da920..5ccda63c9 100644 --- a/lib/wasi-tests/build/wasitests.rs +++ b/lib/wasi-tests/build/wasitests.rs @@ -83,7 +83,7 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { .arg("+nightly") .arg("--target=wasm32-wasi") .arg("-C") - .arg("opt-level=s") + .arg("opt-level=z") .arg(file) .arg("-o") .arg(&wasm_out_name) diff --git a/lib/wasi-tests/src/lib.rs b/lib/wasi-tests/src/lib.rs index 4508e432e..dcfa5bc8b 100644 --- a/lib/wasi-tests/src/lib.rs +++ b/lib/wasi-tests/src/lib.rs @@ -1,10 +1,10 @@ #![cfg(test)] -use wasmer_runtime::{compile, Func}; -use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime::{compile, Ctx, Func}; use wasmer_wasi::{state::*, *}; use std::ffi::c_void; +#[cfg(not(feature = "singlepass"))] #[test] fn serializing_works() { let args = vec![ @@ -16,7 +16,13 @@ fn serializing_works() { b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(), ]; let wasm_binary = include_bytes!("../wasitests/fd_read.wasm"); - let import_object = generate_import_object( + let module = compile(&wasm_binary[..]) + .map_err(|e| format!("Can't compile module: {:?}", e)) + .unwrap(); + + let wasi_version = get_wasi_version(&module).expect("WASI module"); + let import_object = generate_import_object_for_version( + wasi_version, args.clone(), envs.clone(), vec![], @@ -25,9 +31,6 @@ fn serializing_works() { std::path::PathBuf::from("wasitests/test_fs/hamlet"), )], ); - let module = compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e)) - .unwrap(); let state_bytes = { let instance = module.instantiate(&import_object).unwrap(); diff --git a/lib/wasi-tests/tests/wasitests/_common.rs b/lib/wasi-tests/tests/wasitests/_common.rs index 958fdf6ad..41bf14e44 100644 --- a/lib/wasi-tests/tests/wasitests/_common.rs +++ b/lib/wasi-tests/tests/wasitests/_common.rs @@ -1,39 +1,22 @@ macro_rules! assert_wasi_output { ($file:expr, $name:expr, $po_dir_args: expr, $mapdir_args:expr, $envvar_args:expr, $expected:expr) => {{ use wasmer_dev_utils::stdio::StdioCapturer; - use wasmer_runtime_core::{backend::Compiler, Func}; - use wasmer_wasi::generate_import_object; - - #[cfg(feature = "clif")] - fn get_compiler() -> impl Compiler { - use wasmer_clif_backend::CraneliftCompiler; - CraneliftCompiler::new() - } - - #[cfg(feature = "llvm")] - fn get_compiler() -> impl Compiler { - use wasmer_llvm_backend::LLVMCompiler; - LLVMCompiler::new() - } - - #[cfg(feature = "singlepass")] - fn get_compiler() -> impl Compiler { - use wasmer_singlepass_backend::SinglePassCompiler; - SinglePassCompiler::new() - } - - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] - fn get_compiler() -> impl Compiler { - compile_error!("compiler not specified, activate a compiler via features"); - unreachable!(); - } + use wasmer_runtime::Func; + use wasmer_wasi::{generate_import_object_for_version, get_wasi_version}; let wasm_bytes = include_bytes!($file); - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler()) - .expect("WASM can't be compiled"); + let module = wasmer_runtime::compile(&wasm_bytes[..]).expect("WASM can't be compiled"); - let import_object = generate_import_object(vec![], vec![], $po_dir_args, $mapdir_args); + let wasi_version = get_wasi_version(&module).expect("WASI module"); + + let import_object = generate_import_object_for_version( + wasi_version, + vec![], + vec![], + $po_dir_args, + $mapdir_args, + ); let instance = module .instantiate(&import_object) diff --git a/lib/wasi-tests/tests/wasitests/fd_append.rs b/lib/wasi-tests/tests/wasitests/fd_append.rs new file mode 100644 index 000000000..0ac3ea6eb --- /dev/null +++ b/lib/wasi-tests/tests/wasitests/fd_append.rs @@ -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_append() { + assert_wasi_output!( + "../../wasitests/fd_append.wasm", + "fd_append", + vec![], + vec![( + ".".to_string(), + ::std::path::PathBuf::from("wasitests/test_fs/temp") + ),], + vec![], + "../../wasitests/fd_append.out" + ); +} diff --git a/lib/wasi-tests/tests/wasitests/fd_close.rs b/lib/wasi-tests/tests/wasitests/fd_close.rs new file mode 100644 index 000000000..8bff35789 --- /dev/null +++ b/lib/wasi-tests/tests/wasitests/fd_close.rs @@ -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" + ); +} diff --git a/lib/wasi-tests/tests/wasitests/mod.rs b/lib/wasi-tests/tests/wasitests/mod.rs index e469511fd..d226dc0ee 100644 --- a/lib/wasi-tests/tests/wasitests/mod.rs +++ b/lib/wasi-tests/tests/wasitests/mod.rs @@ -9,6 +9,8 @@ mod close_preopen_fd; mod create_dir; mod envvar; mod fd_allocate; +mod fd_append; +mod fd_close; mod fd_pread; mod fd_read; mod fd_sync; diff --git a/lib/wasi-tests/wasitests/close_preopen_fd.wasm b/lib/wasi-tests/wasitests/close_preopen_fd.wasm index 56d37f646..5badfb755 100755 Binary files a/lib/wasi-tests/wasitests/close_preopen_fd.wasm and b/lib/wasi-tests/wasitests/close_preopen_fd.wasm differ diff --git a/lib/wasi-tests/wasitests/create_dir.wasm b/lib/wasi-tests/wasitests/create_dir.wasm index f4a9898f0..0c2fb0bbf 100755 Binary files a/lib/wasi-tests/wasitests/create_dir.wasm and b/lib/wasi-tests/wasitests/create_dir.wasm differ diff --git a/lib/wasi-tests/wasitests/envvar.wasm b/lib/wasi-tests/wasitests/envvar.wasm index 1e394c49f..b05151905 100755 Binary files a/lib/wasi-tests/wasitests/envvar.wasm and b/lib/wasi-tests/wasitests/envvar.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_allocate.wasm b/lib/wasi-tests/wasitests/fd_allocate.wasm index ee688bf93..e604f6e40 100755 Binary files a/lib/wasi-tests/wasitests/fd_allocate.wasm and b/lib/wasi-tests/wasitests/fd_allocate.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_append.out b/lib/wasi-tests/wasitests/fd_append.out new file mode 100644 index 000000000..e9d92ef26 --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_append.out @@ -0,0 +1 @@ +"Hello, world!\nGoodbye, world!\n" diff --git a/lib/wasi-tests/wasitests/fd_append.rs b/lib/wasi-tests/wasitests/fd_append.rs new file mode 100644 index 000000000..2e311652e --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_append.rs @@ -0,0 +1,51 @@ +// Args: +// mapdir: .:wasitests/test_fs/temp + +use std::fs::OpenOptions; +use std::io::{Read, Write}; +use std::path::PathBuf; + +static STR1: &str = "Hello, world!\n"; +static STR2: &str = "Goodbye, world!\n"; + +fn main() { + let file = { + #[cfg(not(target_os = "wasi"))] + let mut base = PathBuf::from("wasitests/test_fs/temp"); + #[cfg(target_os = "wasi")] + let mut base = PathBuf::from("."); + + base.push("fd_append_test"); + base + }; + + { + let mut file_handle = OpenOptions::new() + .create_new(true) + .append(true) + .open(&file) + .expect("Couldn't create file"); + file_handle.write(STR1.as_bytes()).unwrap(); + } + { + let mut file_handle = OpenOptions::new() + .append(true) + .open(&file) + .expect("Couldn't reopen file to append"); + file_handle.write(STR2.as_bytes()).unwrap(); + } + + { + let mut file_handle = OpenOptions::new() + .read(true) + .open(&file) + .expect("Couldn't reopen file to read"); + + let mut test = String::new(); + file_handle.read_to_string(&mut test); + + assert_eq!(&test, &format!("{}{}", STR1, STR2)); + println!("{:?}", &test); + } + std::fs::remove_file(&file).unwrap(); +} diff --git a/lib/wasi-tests/wasitests/fd_append.wasm b/lib/wasi-tests/wasitests/fd_append.wasm new file mode 100755 index 000000000..af40f8af9 Binary files /dev/null and b/lib/wasi-tests/wasitests/fd_append.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_close.out b/lib/wasi-tests/wasitests/fd_close.out new file mode 100644 index 000000000..53c9c2727 --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_close.out @@ -0,0 +1,3 @@ +Successfully closed file! +Successfully closed stderr! +Successfully closed stdin! diff --git a/lib/wasi-tests/wasitests/fd_close.rs b/lib/wasi-tests/wasitests/fd_close.rs new file mode 100644 index 000000000..e5cdaafd9 --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_close.rs @@ -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!"); + } +} diff --git a/lib/wasi-tests/wasitests/fd_close.wasm b/lib/wasi-tests/wasitests/fd_close.wasm new file mode 100755 index 000000000..162e96875 Binary files /dev/null and b/lib/wasi-tests/wasitests/fd_close.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_pread.wasm b/lib/wasi-tests/wasitests/fd_pread.wasm index 11031b32e..7b48e4959 100755 Binary files a/lib/wasi-tests/wasitests/fd_pread.wasm and b/lib/wasi-tests/wasitests/fd_pread.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_read.wasm b/lib/wasi-tests/wasitests/fd_read.wasm index 227157f98..799ca1645 100755 Binary files a/lib/wasi-tests/wasitests/fd_read.wasm and b/lib/wasi-tests/wasitests/fd_read.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_sync.wasm b/lib/wasi-tests/wasitests/fd_sync.wasm index 497b63c9b..5fde9b627 100755 Binary files a/lib/wasi-tests/wasitests/fd_sync.wasm and b/lib/wasi-tests/wasitests/fd_sync.wasm differ diff --git a/lib/wasi-tests/wasitests/file_metadata.wasm b/lib/wasi-tests/wasitests/file_metadata.wasm index 938fdc1f2..68c599172 100755 Binary files a/lib/wasi-tests/wasitests/file_metadata.wasm and b/lib/wasi-tests/wasitests/file_metadata.wasm differ diff --git a/lib/wasi-tests/wasitests/fs_sandbox_test.wasm b/lib/wasi-tests/wasitests/fs_sandbox_test.wasm index 3f0889850..8a8c63494 100755 Binary files a/lib/wasi-tests/wasitests/fs_sandbox_test.wasm and b/lib/wasi-tests/wasitests/fs_sandbox_test.wasm differ diff --git a/lib/wasi-tests/wasitests/fseek.wasm b/lib/wasi-tests/wasitests/fseek.wasm index ce68b75f0..d3a7c8f84 100755 Binary files a/lib/wasi-tests/wasitests/fseek.wasm and b/lib/wasi-tests/wasitests/fseek.wasm differ diff --git a/lib/wasi-tests/wasitests/hello.wasm b/lib/wasi-tests/wasitests/hello.wasm index 1175a7f72..a2625868b 100755 Binary files a/lib/wasi-tests/wasitests/hello.wasm and b/lib/wasi-tests/wasitests/hello.wasm differ diff --git a/lib/wasi-tests/wasitests/mapdir.wasm b/lib/wasi-tests/wasitests/mapdir.wasm index 849a3a6ae..5675534ef 100755 Binary files a/lib/wasi-tests/wasitests/mapdir.wasm and b/lib/wasi-tests/wasitests/mapdir.wasm differ diff --git a/lib/wasi-tests/wasitests/path_link.wasm b/lib/wasi-tests/wasitests/path_link.wasm index 306a911e6..eae511069 100755 Binary files a/lib/wasi-tests/wasitests/path_link.wasm and b/lib/wasi-tests/wasitests/path_link.wasm differ diff --git a/lib/wasi-tests/wasitests/path_rename.wasm b/lib/wasi-tests/wasitests/path_rename.wasm index f76120ab8..7e613b960 100755 Binary files a/lib/wasi-tests/wasitests/path_rename.wasm and b/lib/wasi-tests/wasitests/path_rename.wasm differ diff --git a/lib/wasi-tests/wasitests/path_symlink.wasm b/lib/wasi-tests/wasitests/path_symlink.wasm index 61bb0a022..c30b16378 100755 Binary files a/lib/wasi-tests/wasitests/path_symlink.wasm and b/lib/wasi-tests/wasitests/path_symlink.wasm differ diff --git a/lib/wasi-tests/wasitests/poll_oneoff.wasm b/lib/wasi-tests/wasitests/poll_oneoff.wasm index 9510f5b83..009a57e80 100755 Binary files a/lib/wasi-tests/wasitests/poll_oneoff.wasm and b/lib/wasi-tests/wasitests/poll_oneoff.wasm differ diff --git a/lib/wasi-tests/wasitests/quine.wasm b/lib/wasi-tests/wasitests/quine.wasm index 8cf55c4ac..21fb813a3 100755 Binary files a/lib/wasi-tests/wasitests/quine.wasm and b/lib/wasi-tests/wasitests/quine.wasm differ diff --git a/lib/wasi-tests/wasitests/readlink.wasm b/lib/wasi-tests/wasitests/readlink.wasm index 26480cf84..d8ef0c74f 100755 Binary files a/lib/wasi-tests/wasitests/readlink.wasm and b/lib/wasi-tests/wasitests/readlink.wasm differ diff --git a/lib/wasi-tests/wasitests/wasi_sees_virtual_root.wasm b/lib/wasi-tests/wasitests/wasi_sees_virtual_root.wasm index 7f6d31253..0abf77294 100755 Binary files a/lib/wasi-tests/wasitests/wasi_sees_virtual_root.wasm and b/lib/wasi-tests/wasitests/wasi_sees_virtual_root.wasm differ diff --git a/lib/wasi-tests/wasitests/writing.wasm b/lib/wasi-tests/wasitests/writing.wasm index ce7e0180a..0b8b96abc 100755 Binary files a/lib/wasi-tests/wasitests/writing.wasm and b/lib/wasi-tests/wasitests/writing.wasm differ diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 067894dde..f118c1b1c 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-wasi" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" +keywords = ["wasm", "webassembly", "wasi", "sandbox", "ABI"] +categories = ["wasm"] edition = "2018" [dependencies] @@ -13,11 +15,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.11.0" } [target.'cfg(windows)'.dependencies] winapi = "0.3" diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 978517c7c..712bfa4c7 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -12,7 +12,7 @@ //! Wasmer's WASI implementation //! -//! Use `generate_import_object` to create an `ImportObject`. This `ImportObject` +//! Use `generate_import_object` to create an [`ImportObject`]. This [`ImportObject`] //! can be combined with a module to create an `Instance` which can execute WASI //! Wasm functions. //! @@ -37,7 +37,7 @@ use self::syscalls::*; use std::ffi::c_void; use std::path::PathBuf; -pub use self::utils::is_wasi_module; +pub use self::utils::{get_wasi_version, is_wasi_module, WasiVersion}; use wasmer_runtime_core::{func, import::ImportObject, imports}; @@ -47,7 +47,8 @@ pub struct ExitCode { pub code: syscalls::types::__wasi_exitcode_t, } -/// Creates a Wasi [`ImportObject`] with [`WasiState`]. +/// Creates a Wasi [`ImportObject`] with [`WasiState`] with the latest snapshot +/// of WASI. pub fn generate_import_object( args: Vec>, envs: Vec>, @@ -79,7 +80,7 @@ pub fn generate_import_object( imports! { // This generates the wasi state. state_gen, - "wasi_unstable" => { + "wasi_snapshot_preview1" => { "args_get" => func!(args_get), "args_sizes_get" => func!(args_sizes_get), "clock_res_get" => func!(clock_res_get), @@ -128,3 +129,103 @@ pub fn generate_import_object( }, } } + +/// Creates a Wasi [`ImportObject`] with [`WasiState`] for the given [`WasiVersion`]. +pub fn generate_import_object_for_version( + version: WasiVersion, + args: Vec>, + envs: Vec>, + preopened_files: Vec, + mapped_dirs: Vec<(String, PathBuf)>, +) -> ImportObject { + match version { + WasiVersion::Snapshot0 => { + generate_import_object_snapshot0(args, envs, preopened_files, mapped_dirs) + } + WasiVersion::Snapshot1 | WasiVersion::Latest => { + generate_import_object(args, envs, preopened_files, mapped_dirs) + } + } +} + +/// Creates a legacy Wasi [`ImportObject`] with [`WasiState`]. +fn generate_import_object_snapshot0( + args: Vec>, + envs: Vec>, + preopened_files: Vec, + mapped_dirs: Vec<(String, PathBuf)>, +) -> ImportObject { + let state_gen = move || { + // TODO: look into removing all these unnecessary clones + fn state_destructor(data: *mut c_void) { + unsafe { + drop(Box::from_raw(data as *mut WasiState)); + } + } + let preopened_files = preopened_files.clone(); + let mapped_dirs = mapped_dirs.clone(); + //let wasi_builder = create_wasi_instance(); + + let state = Box::new(WasiState { + fs: WasiFs::new(&preopened_files, &mapped_dirs).expect("Could not create WASI FS"), + args: args.clone(), + envs: envs.clone(), + }); + + ( + Box::into_raw(state) as *mut c_void, + state_destructor as fn(*mut c_void), + ) + }; + imports! { + // This generates the wasi state. + state_gen, + "wasi_unstable" => { + "args_get" => func!(args_get), + "args_sizes_get" => func!(args_sizes_get), + "clock_res_get" => func!(clock_res_get), + "clock_time_get" => func!(clock_time_get), + "environ_get" => func!(environ_get), + "environ_sizes_get" => func!(environ_sizes_get), + "fd_advise" => func!(fd_advise), + "fd_allocate" => func!(fd_allocate), + "fd_close" => func!(fd_close), + "fd_datasync" => func!(fd_datasync), + "fd_fdstat_get" => func!(fd_fdstat_get), + "fd_fdstat_set_flags" => func!(fd_fdstat_set_flags), + "fd_fdstat_set_rights" => func!(fd_fdstat_set_rights), + "fd_filestat_get" => func!(legacy::snapshot0::fd_filestat_get), + "fd_filestat_set_size" => func!(fd_filestat_set_size), + "fd_filestat_set_times" => func!(fd_filestat_set_times), + "fd_pread" => func!(fd_pread), + "fd_prestat_get" => func!(fd_prestat_get), + "fd_prestat_dir_name" => func!(fd_prestat_dir_name), + "fd_pwrite" => func!(fd_pwrite), + "fd_read" => func!(fd_read), + "fd_readdir" => func!(fd_readdir), + "fd_renumber" => func!(fd_renumber), + "fd_seek" => func!(legacy::snapshot0::fd_seek), + "fd_sync" => func!(fd_sync), + "fd_tell" => func!(fd_tell), + "fd_write" => func!(fd_write), + "path_create_directory" => func!(path_create_directory), + "path_filestat_get" => func!(legacy::snapshot0::path_filestat_get), + "path_filestat_set_times" => func!(path_filestat_set_times), + "path_link" => func!(path_link), + "path_open" => func!(path_open), + "path_readlink" => func!(path_readlink), + "path_remove_directory" => func!(path_remove_directory), + "path_rename" => func!(path_rename), + "path_symlink" => func!(path_symlink), + "path_unlink_file" => func!(path_unlink_file), + "poll_oneoff" => func!(legacy::snapshot0::poll_oneoff), + "proc_exit" => func!(proc_exit), + "proc_raise" => func!(proc_raise), + "random_get" => func!(random_get), + "sched_yield" => func!(sched_yield), + "sock_recv" => func!(sock_recv), + "sock_send" => func!(sock_send), + "sock_shutdown" => func!(sock_shutdown), + }, + } +} diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index 39fd38378..04b206c30 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -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, /// for fds still open after the file has been deleted pub orphan_fds: HashMap, - - pub stdout: Box, - pub stderr: Box, - pub stdin: Box, } 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>, WasiFsError> { + self.std_dev_get(__WASI_STDOUT_FILENO) + } + /// Get the `WasiFile` object at stdout mutably + pub fn stdout_mut(&mut self) -> Result<&mut Option>, WasiFsError> { + self.std_dev_get_mut(__WASI_STDOUT_FILENO) + } + + /// Get the `WasiFile` object at stderr + pub fn stderr(&self) -> Result<&Option>, WasiFsError> { + self.std_dev_get(__WASI_STDERR_FILENO) + } + /// Get the `WasiFile` object at stderr mutably + pub fn stderr_mut(&mut self) -> Result<&mut Option>, WasiFsError> { + self.std_dev_get_mut(__WASI_STDERR_FILENO) + } + + /// Get the `WasiFile` object at stdin + pub fn stdin(&self) -> Result<&Option>, WasiFsError> { + self.std_dev_get(__WASI_STDIN_FILENO) + } + /// Get the `WasiFile` object at stdin mutably + pub fn stdin_mut(&mut self) -> Result<&mut Option>, 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>, 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>, 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, ) -> Result>, 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, + 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 { diff --git a/lib/wasi/src/state/types.rs b/lib/wasi/src/state/types.rs index 2f97a5fe3..5eb55c670 100644 --- a/lib/wasi/src/state/types.rs +++ b/lib/wasi/src/state/types.rs @@ -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, diff --git a/lib/wasi/src/syscalls/legacy/mod.rs b/lib/wasi/src/syscalls/legacy/mod.rs new file mode 100644 index 000000000..62cc08f9e --- /dev/null +++ b/lib/wasi/src/syscalls/legacy/mod.rs @@ -0,0 +1,5 @@ +//! These modules provide wrappers and implementations for older version of WASI. +//! +//! If you are relying on legacy WASI, please upgrade for the best experience. + +pub mod snapshot0; diff --git a/lib/wasi/src/syscalls/legacy/snapshot0.rs b/lib/wasi/src/syscalls/legacy/snapshot0.rs new file mode 100644 index 000000000..67566a50c --- /dev/null +++ b/lib/wasi/src/syscalls/legacy/snapshot0.rs @@ -0,0 +1,178 @@ +use crate::ptr::{Array, WasmPtr}; +use crate::syscalls; +use crate::syscalls::types::{self, snapshot0}; +use wasmer_runtime_core::{debug, vm::Ctx}; + +/// Wrapper around `syscalls::fd_filestat_get` with extra logic to handle the size +/// difference of `wasi_filestat_t` +/// +/// WARNING: this function involves saving, clobbering, and restoring unrelated +/// Wasm memory. If the memory clobbered by the current syscall is also used by +/// that syscall, then it may break. +pub fn fd_filestat_get( + ctx: &mut Ctx, + fd: types::__wasi_fd_t, + buf: WasmPtr, +) -> types::__wasi_errno_t { + let memory = ctx.memory(0); + + // transmute the WasmPtr into a WasmPtr where T2 > T1, this will read extra memory. + // The edge case of this causing an OOB is not handled, if the new field is OOB, then the entire + // memory access will fail. + let new_buf: WasmPtr = unsafe { std::mem::transmute(buf) }; + + // Copy the data including the extra data + let new_filestat_setup: types::__wasi_filestat_t = + wasi_try!(new_buf.deref(memory)).get().clone(); + + // Set up complete, make the call with the pointer that will write to the + // struct and some unrelated memory after the struct. + let result = syscalls::fd_filestat_get(ctx, fd, new_buf); + + // reborrow memory + let memory = ctx.memory(0); + + // get the values written to memory + let new_filestat = wasi_try!(new_buf.deref(memory)).get(); + // translate the new struct into the old struct in host memory + let old_stat = snapshot0::__wasi_filestat_t { + st_dev: new_filestat.st_dev, + st_ino: new_filestat.st_ino, + st_filetype: new_filestat.st_filetype, + st_nlink: new_filestat.st_nlink as u32, + st_size: new_filestat.st_size, + st_atim: new_filestat.st_atim, + st_mtim: new_filestat.st_mtim, + st_ctim: new_filestat.st_ctim, + }; + + // write back the original values at the pointer's memory locations + // (including the memory unrelated to the pointer) + wasi_try!(new_buf.deref(memory)).set(new_filestat_setup); + + // Now that this memory is back as it was, write the translated filestat + // into memory leaving it as it should be + wasi_try!(buf.deref(memory)).set(old_stat); + + result +} + +/// Wrapper around `syscalls::path_filestat_get` with extra logic to handle the size +/// difference of `wasi_filestat_t` +pub fn path_filestat_get( + ctx: &mut Ctx, + fd: types::__wasi_fd_t, + flags: types::__wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + buf: WasmPtr, +) -> types::__wasi_errno_t { + // see `fd_filestat_get` in this file for an explanation of this strange behavior + let memory = ctx.memory(0); + + let new_buf: WasmPtr = unsafe { std::mem::transmute(buf) }; + let new_filestat_setup: types::__wasi_filestat_t = + wasi_try!(new_buf.deref(memory)).get().clone(); + + let result = syscalls::path_filestat_get(ctx, fd, flags, path, path_len, new_buf); + + let memory = ctx.memory(0); + let new_filestat = wasi_try!(new_buf.deref(memory)).get(); + let old_stat = snapshot0::__wasi_filestat_t { + st_dev: new_filestat.st_dev, + st_ino: new_filestat.st_ino, + st_filetype: new_filestat.st_filetype, + st_nlink: new_filestat.st_nlink as u32, + st_size: new_filestat.st_size, + st_atim: new_filestat.st_atim, + st_mtim: new_filestat.st_mtim, + st_ctim: new_filestat.st_ctim, + }; + + wasi_try!(new_buf.deref(memory)).set(new_filestat_setup); + wasi_try!(buf.deref(memory)).set(old_stat); + + result +} + +/// Wrapper around `syscalls::fd_seek` with extra logic to remap the values +/// of `__wasi_whence_t` +pub fn fd_seek( + ctx: &mut Ctx, + fd: types::__wasi_fd_t, + offset: types::__wasi_filedelta_t, + whence: snapshot0::__wasi_whence_t, + newoffset: WasmPtr, +) -> types::__wasi_errno_t { + let new_whence = match whence { + snapshot0::__WASI_WHENCE_CUR => types::__WASI_WHENCE_CUR, + snapshot0::__WASI_WHENCE_END => types::__WASI_WHENCE_END, + snapshot0::__WASI_WHENCE_SET => types::__WASI_WHENCE_SET, + // if it's invalid, let the new fd_seek handle it + _ => whence, + }; + syscalls::fd_seek(ctx, fd, offset, new_whence, newoffset) +} + +/// Wrapper around `syscalls::poll_oneoff` with extra logic to add the removed +/// userdata field back +pub fn poll_oneoff( + ctx: &mut Ctx, + in_: WasmPtr, + out_: WasmPtr, + nsubscriptions: u32, + nevents: WasmPtr, +) -> types::__wasi_errno_t { + // in this case the new type is smaller than the old type, so it all fits into memory, + // we just need to readjust and copy it + + // we start by adjusting `in_` into a format that the new code can understand + let memory = ctx.memory(0); + let mut in_origs: Vec = vec![]; + for in_sub in wasi_try!(in_.deref(memory, 0, nsubscriptions)) { + in_origs.push(in_sub.get().clone()); + } + + // get a pointer to the smaller new type + let in_new_type_ptr: WasmPtr = + unsafe { std::mem::transmute(in_) }; + + for (in_sub_new, orig) in wasi_try!(in_new_type_ptr.deref(memory, 0, nsubscriptions)) + .iter() + .zip(in_origs.iter()) + { + in_sub_new.set(types::__wasi_subscription_t { + userdata: orig.userdata, + type_: orig.type_, + u: if orig.type_ == types::__WASI_EVENTTYPE_CLOCK { + types::__wasi_subscription_u { + clock: types::__wasi_subscription_clock_t { + clock_id: unsafe { orig.u.clock.clock_id }, + timeout: unsafe { orig.u.clock.timeout }, + precision: unsafe { orig.u.clock.precision }, + flags: unsafe { orig.u.clock.flags }, + }, + } + } else { + types::__wasi_subscription_u { + fd_readwrite: unsafe { orig.u.fd_readwrite }, + } + }, + }); + } + + // make the call + let result = syscalls::poll_oneoff(ctx, in_new_type_ptr, out_, nsubscriptions, nevents); + + // replace the old values of in, in case the calling code reuses the memory + let memory = ctx.memory(0); + + for (in_sub, orig) in wasi_try!(in_.deref(memory, 0, nsubscriptions)) + .iter() + .zip(in_origs.into_iter()) + { + in_sub.set(orig); + } + + result +} diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 90dbfd458..af6ccab26 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -5,6 +5,8 @@ pub mod unix; #[cfg(any(target_os = "windows"))] pub mod windows; +pub mod legacy; + use self::types::*; use crate::{ ptr::{Array, WasmPtr}, @@ -15,7 +17,6 @@ use crate::{ }, ExitCode, }; -use rand::{thread_rng, Rng}; use std::borrow::Borrow; use std::cell::Cell; use std::convert::{Infallible, TryInto}; @@ -374,6 +375,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 +664,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 +812,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 +898,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 +1245,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)); @@ -1676,12 +1726,25 @@ pub fn path_open( } } let mut open_options = std::fs::OpenOptions::new(); + let write_permission = adjusted_rights & __WASI_RIGHT_FD_WRITE != 0; + // append, truncate, and create all require the permission to write + let (append_permission, truncate_permission, create_permission) = + if write_permission { + ( + fs_flags & __WASI_FDFLAG_APPEND != 0, + o_flags & __WASI_O_TRUNC != 0, + o_flags & __WASI_O_CREAT != 0, + ) + } else { + (false, false, false) + }; let open_options = open_options .read(true) // TODO: ensure these rights are actually valid given parent, etc. - .write(adjusted_rights & __WASI_RIGHT_FD_WRITE != 0) - .create(o_flags & __WASI_O_CREAT != 0) - .truncate(o_flags & __WASI_O_TRUNC != 0); + .write(write_permission) + .create(create_permission) + .append(append_permission) + .truncate(truncate_permission); open_flags |= Fd::READ; if adjusted_rights & __WASI_RIGHT_FD_WRITE != 0 { open_flags |= Fd::WRITE; @@ -1750,6 +1813,7 @@ pub fn path_open( let mut open_options = std::fs::OpenOptions::new(); let open_options = open_options .read(true) + .append(fs_flags & __WASI_FDFLAG_APPEND != 0) // TODO: ensure these rights are actually valid given parent, etc. // write access is required for creating a file .write(true) @@ -2292,9 +2356,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 +2468,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, 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()` diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs index d2aaa0554..5f5559ce4 100644 --- a/lib/wasi/src/syscalls/types.rs +++ b/lib/wasi/src/syscalls/types.rs @@ -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)] @@ -416,7 +416,7 @@ pub struct __wasi_iovec_t { unsafe impl ValueType for __wasi_iovec_t {} -pub type __wasi_linkcount_t = u32; +pub type __wasi_linkcount_t = u64; pub type __wasi_lookupflags_t = u32; pub const __WASI_LOOKUP_SYMLINK_FOLLOW: u32 = 1 << 0; @@ -457,8 +457,8 @@ pub const __WASI_RIGHT_FD_FILESTAT_GET: u64 = 1 << 21; pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: u64 = 1 << 22; pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: u64 = 1 << 23; pub const __WASI_RIGHT_PATH_SYMLINK: u64 = 1 << 24; -pub const __WASI_RIGHT_PATH_UNLINK_FILE: u64 = 1 << 25; -pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: u64 = 1 << 26; +pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: u64 = 1 << 25; +pub const __WASI_RIGHT_PATH_UNLINK_FILE: u64 = 1 << 26; pub const __WASI_RIGHT_POLL_FD_READWRITE: u64 = 1 << 27; pub const __WASI_RIGHT_SOCK_SHUTDOWN: u64 = 1 << 28; @@ -522,32 +522,36 @@ pub const __WASI_SHUT_WR: u8 = 1 << 1; pub type __wasi_siflags_t = u16; pub type __wasi_signal_t = u8; -pub const __WASI_SIGABRT: u8 = 0; -pub const __WASI_SIGALRM: u8 = 1; -pub const __WASI_SIGBUS: u8 = 2; -pub const __WASI_SIGCHLD: u8 = 3; -pub const __WASI_SIGCONT: u8 = 4; -pub const __WASI_SIGFPE: u8 = 5; -pub const __WASI_SIGHUP: u8 = 6; -pub const __WASI_SIGILL: u8 = 7; -pub const __WASI_SIGINT: u8 = 8; +pub const __WASI_SIGHUP: u8 = 1; +pub const __WASI_SIGINT: u8 = 2; +pub const __WASI_SIGQUIT: u8 = 3; +pub const __WASI_SIGILL: u8 = 4; +pub const __WASI_SIGTRAP: u8 = 5; +pub const __WASI_SIGABRT: u8 = 6; +pub const __WASI_SIGBUS: u8 = 7; +pub const __WASI_SIGFPE: u8 = 8; pub const __WASI_SIGKILL: u8 = 9; -pub const __WASI_SIGPIPE: u8 = 10; -pub const __WASI_SIGQUIT: u8 = 11; -pub const __WASI_SIGSEGV: u8 = 12; -pub const __WASI_SIGSTOP: u8 = 13; -pub const __WASI_SIGSYS: u8 = 14; +pub const __WASI_SIGUSR1: u8 = 10; +pub const __WASI_SIGSEGV: u8 = 11; +pub const __WASI_SIGUSR2: u8 = 12; +pub const __WASI_SIGPIPE: u8 = 13; +pub const __WASI_SIGALRM: u8 = 14; pub const __WASI_SIGTERM: u8 = 15; -pub const __WASI_SIGTRAP: u8 = 16; -pub const __WASI_SIGTSTP: u8 = 17; -pub const __WASI_SIGTTIN: u8 = 18; -pub const __WASI_SIGTTOU: u8 = 19; -pub const __WASI_SIGURG: u8 = 20; -pub const __WASI_SIGUSR1: u8 = 21; -pub const __WASI_SIGUSR2: u8 = 22; -pub const __WASI_SIGVTALRM: u8 = 23; -pub const __WASI_SIGXCPU: u8 = 24; -pub const __WASI_SIGXFSZ: u8 = 25; +pub const __WASI_SIGCHLD: u8 = 16; +pub const __WASI_SIGCONT: u8 = 17; +pub const __WASI_SIGSTOP: u8 = 18; +pub const __WASI_SIGTSTP: u8 = 19; +pub const __WASI_SIGTTIN: u8 = 20; +pub const __WASI_SIGTTOU: u8 = 21; +pub const __WASI_SIGURG: u8 = 22; +pub const __WASI_SIGXCPU: u8 = 23; +pub const __WASI_SIGXFSZ: u8 = 24; +pub const __WASI_SIGVTALRM: u8 = 25; +pub const __WASI_SIGPROF: u8 = 26; +pub const __WASI_SIGWINCH: u8 = 27; +pub const __WASI_SIGPOLL: u8 = 28; +pub const __WASI_SIGPWR: u8 = 29; +pub const __WASI_SIGSYS: u8 = 30; pub type __wasi_subclockflags_t = u16; pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: u16 = 1 << 0; @@ -555,7 +559,6 @@ pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: u16 = 1 << 0; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(C)] pub struct __wasi_subscription_clock_t { - pub userdata: __wasi_userdata_t, pub clock_id: __wasi_clockid_t, pub timeout: __wasi_timestamp_t, pub precision: __wasi_timestamp_t, @@ -571,8 +574,8 @@ pub struct __wasi_subscription_fs_readwrite_t { #[derive(Copy, Clone)] #[repr(C)] pub union __wasi_subscription_u { - clock: __wasi_subscription_clock_t, - fd_readwrite: __wasi_subscription_fs_readwrite_t, + pub clock: __wasi_subscription_clock_t, + pub fd_readwrite: __wasi_subscription_fs_readwrite_t, } #[derive(Copy, Clone)] @@ -694,6 +697,107 @@ pub type __wasi_timestamp_t = u64; pub type __wasi_userdata_t = u64; pub type __wasi_whence_t = u8; -pub const __WASI_WHENCE_CUR: u8 = 0; -pub const __WASI_WHENCE_END: u8 = 1; -pub const __WASI_WHENCE_SET: u8 = 2; +pub const __WASI_WHENCE_SET: u8 = 0; +pub const __WASI_WHENCE_CUR: u8 = 1; +pub const __WASI_WHENCE_END: u8 = 2; + +pub mod snapshot0 { + use serde::{Deserialize, Serialize}; + pub type __wasi_linkcount_t = u32; + use wasmer_runtime_core::types::ValueType; + + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[repr(C)] + pub struct __wasi_subscription_clock_t { + pub userdata: super::__wasi_userdata_t, + pub clock_id: super::__wasi_clockid_t, + pub timeout: super::__wasi_timestamp_t, + pub precision: super::__wasi_timestamp_t, + pub flags: super::__wasi_subclockflags_t, + } + + #[derive(Copy, Clone)] + #[repr(C)] + pub union __wasi_subscription_u { + pub clock: __wasi_subscription_clock_t, + pub fd_readwrite: super::__wasi_subscription_fs_readwrite_t, + } + + #[derive(Copy, Clone)] + #[repr(C)] + pub struct __wasi_subscription_t { + pub userdata: super::__wasi_userdata_t, + pub type_: super::__wasi_eventtype_t, + pub u: __wasi_subscription_u, + } + + unsafe impl ValueType for __wasi_subscription_t {} + + pub type __wasi_whence_t = u8; + pub const __WASI_WHENCE_CUR: u8 = 0; + pub const __WASI_WHENCE_END: u8 = 1; + pub const __WASI_WHENCE_SET: u8 = 2; + + #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[repr(C)] + pub struct __wasi_filestat_t { + pub st_dev: super::__wasi_device_t, + pub st_ino: super::__wasi_inode_t, + pub st_filetype: super::__wasi_filetype_t, + pub st_nlink: __wasi_linkcount_t, + pub st_size: super::__wasi_filesize_t, + pub st_atim: super::__wasi_timestamp_t, + pub st_mtim: super::__wasi_timestamp_t, + pub st_ctim: super::__wasi_timestamp_t, + } + + unsafe impl ValueType for __wasi_filestat_t {} + + impl std::fmt::Debug for __wasi_filestat_t { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let convert_ts_into_time_string = |ts| { + let tspec = + time::Timespec::new(ts as i64 / 1_000_000_000, (ts % 1_000_000_000) as i32); + let tm = time::at(tspec); + let out_time = tm.rfc822(); + format!("{} ({})", out_time, ts) + }; + f.debug_struct("__wasi_filestat_t") + .field("st_dev", &self.st_dev) + .field("st_ino", &self.st_ino) + .field( + "st_filetype", + &format!( + "{} ({})", + super::wasi_filetype_to_name(self.st_filetype), + self.st_filetype, + ), + ) + .field("st_nlink", &self.st_nlink) + .field("st_size", &self.st_size) + .field("st_atim", &convert_ts_into_time_string(self.st_atim)) + .field("st_mtim", &convert_ts_into_time_string(self.st_mtim)) + .field("st_ctim", &convert_ts_into_time_string(self.st_ctim)) + .finish() + } + } + + impl std::fmt::Debug for __wasi_subscription_t { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("__wasi_subscription_t") + .field("userdata", &self.userdata) + .field("type", &super::eventtype_to_str(self.type_)) + .field( + "u", + match self.type_ { + super::__WASI_EVENTTYPE_CLOCK => unsafe { &self.u.clock }, + super::__WASI_EVENTTYPE_FD_READ | super::__WASI_EVENTTYPE_FD_WRITE => unsafe { + &self.u.fd_readwrite + }, + _ => &"INVALID EVENTTYPE", + }, + ) + .finish() + } + } +} diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs index f96e35958..d57510eb5 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -1,18 +1,54 @@ use wasmer_runtime_core::module::Module; -/// Check if a provided module is compiled with WASI support +#[allow(dead_code)] +/// Check if a provided module is compiled for some version of WASI. +/// Use [`get_wasi_version`] to find out which version of WASI the module is. pub fn is_wasi_module(module: &Module) -> bool { - if module.info().imported_functions.is_empty() { - return false; - } - for (_, import_name) in &module.info().imported_functions { - let namespace = module - .info() - .namespace_table - .get(import_name.namespace_index); - if namespace != "wasi_unstable" { - return false; - } - } - true + get_wasi_version(module).is_some() +} + +/// The version of WASI. This is determined by the imports namespace +/// string. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WasiVersion { + /// `wasi_unstable`. + Snapshot0, + /// `wasi_snapshot_preview1`. + Snapshot1, + + /// Latest version. + /// + /// It's a “floating” version, i.e. it's an alias to the latest + /// version (for the moment, `Snapshot1`). Using this version is a + /// way to ensure that modules will run only if they come with the + /// latest WASI version (in case of security issues for instance), + /// by just updating the runtime. + /// + /// Note that this version is never returned by an API. It is + /// provided only by the user. + Latest, +} + +/// Detect the version of WASI being used from the namespace +pub fn get_wasi_version(module: &Module) -> Option { + let mut import_iter = module + .info() + .imported_functions + .iter() + .map(|(_, import_name)| import_name.namespace_index); + + // returns None if empty + let first = import_iter.next()?; + if import_iter.all(|idx| idx == first) { + // once we know that all the namespaces are the same, we can use it to + // detect which version of WASI this is + match module.info().namespace_table.get(first) { + "wasi_unstable" => Some(WasiVersion::Snapshot0), + "wasi_snapshot_preview1" => Some(WasiVersion::Snapshot1), + _ => None, + } + } else { + // not all funcs have the same namespace, therefore it's not WASI + None + } } diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 12e5f6c0b..17f89a0a6 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.8.0" +version = "0.11.0" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -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.11.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" diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index c448392b0..712214c8b 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -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, *const u64, *mut u64); type CallProtectedResult = Result<(), CallProtectedData>; #[repr(C)] diff --git a/scripts/binary-name.sh b/scripts/binary-name.sh index 4ac8ef8e3..0245690b7 100755 --- a/scripts/binary-name.sh +++ b/scripts/binary-name.sh @@ -5,9 +5,11 @@ initArch() { if [ -n "$WASMER_ARCH" ]; then ARCH="$WASMER_ARCH" fi + # If you modify this list, please also modify install.sh case $ARCH in amd64) ARCH="amd64";; x86_64) ARCH="amd64";; + aarch64) ARCH="arm64";; i386) ARCH="386";; *) echo "Architecture ${ARCH} is not supported by this installation script"; exit 1;; esac diff --git a/scripts/update_version_numbers.sh b/scripts/update_version_numbers.sh index 21b28a916..af1485a3f 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.7.0' -NEXT_VERSION='0.8.0' +PREVIOUS_VERSION='0.10.2' +NEXT_VERSION='0.11.0' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/src/bin/kwasmd.rs b/src/bin/kwasmd.rs index 62e22b731..79949417b 100644 --- a/src/bin/kwasmd.rs +++ b/src/bin/kwasmd.rs @@ -63,6 +63,7 @@ fn handle_client(mut stream: UnixStream) { enforce_stack_check: true, track_state: false, features: Default::default(), + ..Default::default() }, &SinglePassCompiler::new(), ) diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 4c51d9770..457b6f510 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -10,7 +10,7 @@ extern crate structopt; use std::env; -use std::fs::{read_to_string, File}; +use std::fs::{metadata, read_to_string, File}; use std::io; use std::io::Read; use std::path::PathBuf; @@ -18,15 +18,16 @@ use std::process::exit; use std::str::FromStr; use std::collections::HashMap; -use structopt::StructOpt; +use structopt::{clap, StructOpt}; use wasmer::*; +#[cfg(feature = "backend-cranelift")] use wasmer_clif_backend::CraneliftCompiler; #[cfg(feature = "backend-llvm")] use wasmer_llvm_backend::{LLVMCompiler, LLVMOptions}; use wasmer_runtime::{ cache::{Cache as BaseCache, FileSystemCache, WasmHash}, - Func, Value, VERSION, + Value, VERSION, }; #[cfg(feature = "managed")] use wasmer_runtime_core::tiering::{run_tiering, InteractiveShellContext, ShellExitOperation}; @@ -36,30 +37,9 @@ use wasmer_runtime_core::{ debug, loader::{Instance as LoadedInstance, LocalLoader}, }; -#[cfg(feature = "backend-singlepass")] -use wasmer_singlepass_backend::SinglePassCompiler; #[cfg(feature = "wasi")] use wasmer_wasi; -// stub module to make conditional compilation happy -#[cfg(not(feature = "wasi"))] -mod wasmer_wasi { - use wasmer_runtime_core::{import::ImportObject, module::Module}; - - pub fn is_wasi_module(_module: &Module) -> bool { - false - } - - pub fn generate_import_object( - _args: Vec>, - _envs: Vec>, - _preopened_files: Vec, - _mapped_dirs: Vec<(String, std::path::PathBuf)>, - ) -> ImportObject { - unimplemented!() - } -} - #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.", author)] /// The options for the wasmer Command Line Interface @@ -81,7 +61,7 @@ enum CLIOptions { SelfUpdate, } -#[derive(Debug, StructOpt)] +#[derive(Debug, StructOpt, Clone)] struct PrestandardFeatures { /// Enable support for the SIMD proposal. #[structopt(long = "enable-simd")] @@ -96,6 +76,29 @@ struct PrestandardFeatures { all: bool, } +impl PrestandardFeatures { + /// Generate [`wabt::Features`] struct from CLI options + pub fn into_wabt_features(&self) -> wabt::Features { + let mut features = wabt::Features::new(); + if self.simd || self.all { + features.enable_simd(); + } + if self.threads || self.all { + features.enable_threads(); + } + features.enable_sign_extension(); + features + } + + /// Generate [`Features`] struct from CLI options + pub fn into_backend_features(&self) -> Features { + Features { + simd: self.simd || self.all, + threads: self.threads || self.all, + } + } +} + #[cfg(feature = "backend-llvm")] #[derive(Debug, StructOpt, Clone)] /// LLVM backend flags. @@ -113,7 +116,7 @@ pub struct LLVMCLIOptions { obj_file: Option, } -#[derive(Debug, StructOpt)] +#[derive(Debug, StructOpt, Clone)] struct Run { /// Disable the cache #[structopt(long = "disable-cache")] @@ -123,15 +126,30 @@ struct Run { #[structopt(parse(from_os_str))] path: PathBuf, - // Disable the cache + /// Name of the backend to use. (x86_64) + #[cfg(target_arch = "x86_64")] #[structopt( long = "backend", - default_value = "cranelift", + default_value = "auto", case_insensitive = true, possible_values = Backend::variants(), )] backend: Backend, + /// Name of the backend to use. (aarch64) + #[cfg(target_arch = "aarch64")] + #[structopt( + long = "backend", + default_value = "singlepass", + case_insensitive = true, + possible_values = Backend::variants(), + )] + backend: Backend, + + /// Invoke a specified function + #[structopt(long = "invoke", short = "i")] + invoke: Option, + /// Emscripten symbol map #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] em_symbol_map: Option, @@ -177,8 +195,16 @@ 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, + + // Enable the CallTrace middleware. + #[structopt(long = "call-trace")] + call_trace: bool, + + // Enable the BlockTrace middleware. + #[structopt(long = "block-trace")] + block_trace: 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 @@ -308,6 +334,7 @@ fn get_mapped_dirs(input: &[String]) -> Result, String> { Ok(md) } +#[cfg(feature = "wasi")] fn get_env_var_args(input: &[String]) -> Result, String> { let mut ev = vec![]; for entry in input.iter() { @@ -323,11 +350,148 @@ fn get_env_var_args(input: &[String]) -> Result, String> { Ok(ev) } +/// Helper function for `execute_wasm` (the `Run` command) +#[cfg(feature = "wasi")] +fn execute_wasi( + wasi_version: wasmer_wasi::WasiVersion, + options: &Run, + env_vars: Vec<(&str, &str)>, + module: wasmer_runtime_core::Module, + mapped_dirs: Vec<(String, PathBuf)>, + _wasm_binary: &[u8], +) -> Result<(), String> { + let args = if let Some(cn) = &options.command_name { + [cn.clone()] + } else { + [options.path.to_str().unwrap().to_owned()] + } + .iter() + .chain(options.args.iter()) + .cloned() + .map(|arg| arg.into_bytes()) + .collect(); + let envs = env_vars + .into_iter() + .map(|(k, v)| format!("{}={}", k, v).into_bytes()) + .collect(); + let preopened_files = options.pre_opened_directories.clone(); + + let import_object = wasmer_wasi::generate_import_object_for_version( + wasi_version, + args, + envs, + preopened_files, + mapped_dirs, + ); + + #[allow(unused_mut)] // mut used in feature + let mut instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate WASI module: {:?}", e))?; + + let start: wasmer_runtime::Func<(), ()> = + instance.func("_start").map_err(|e| format!("{:?}", e))?; + + #[cfg(feature = "managed")] + { + let start_raw: extern "C" fn(&mut wasmer_runtime_core::vm::Ctx) = + unsafe { ::std::mem::transmute(start.get_vm_func()) }; + + unsafe { + run_tiering( + module.info(), + &_wasm_binary, + if let Some(ref path) = options.resume { + let mut f = File::open(path).unwrap(); + let mut out: Vec = vec![]; + f.read_to_end(&mut out).unwrap(); + Some( + wasmer_runtime_core::state::InstanceImage::from_bytes(&out) + .expect("failed to decode image"), + ) + } else { + None + }, + &import_object, + start_raw, + &mut instance, + options.backend, + options + .optimized_backends + .iter() + .map( + |&backend| -> (Backend, Box Box + Send>) { + let options = options.clone(); + ( + backend, + Box::new(move || { + get_compiler_by_backend(backend, &options).unwrap() + }), + ) + }, + ) + .collect(), + interactive_shell, + )? + }; + } + + #[cfg(not(feature = "managed"))] + { + use wasmer_runtime::error::RuntimeError; + #[cfg(unix)] + use wasmer_runtime_core::{ + fault::{pop_code_version, push_code_version}, + state::CodeVersion, + }; + + let result; + + #[cfg(unix)] + { + let cv_pushed = + if let Some(msm) = instance.module.runnable_module.get_module_state_map() { + push_code_version(CodeVersion { + baseline: true, + msm: msm, + base: instance.module.runnable_module.get_code().unwrap().as_ptr() as usize, + backend: options.backend, + }); + true + } else { + false + }; + result = start.call(); + if cv_pushed { + pop_code_version().unwrap(); + } + } + #[cfg(not(unix))] + { + result = start.call(); + } + + if let Err(ref err) = result { + match err { + RuntimeError::Trap { msg } => return Err(format!("wasm trap occured: {}", msg)), + RuntimeError::Error { data } => { + if let Some(error_code) = data.downcast_ref::() { + std::process::exit(error_code.code as i32) + } + } + } + return Err(format!("error: {:?}", err)); + } + } + Ok(()) +} + /// Execute a wasm/wat file fn execute_wasm(options: &Run) -> Result<(), String> { let disable_cache = options.disable_cache; let mapped_dirs = get_mapped_dirs(&options.mapped_dirs[..])?; + #[cfg(feature = "wasi")] let env_vars = get_env_var_args(&options.env_vars[..])?; let wasm_path = &options.path; @@ -389,18 +553,12 @@ fn execute_wasm(options: &Run) -> Result<(), String> { } if !utils::is_wasm_binary(&wasm_binary) { - let mut features = wabt::Features::new(); - if options.features.simd || options.features.all { - features.enable_simd(); - } - if options.features.threads || options.features.all { - features.enable_threads(); - } + let features = options.features.into_wabt_features(); wasm_binary = wabt::wat2wasm_with_features(wasm_binary, features) .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; } - let compiler: Box = match get_compiler_by_backend(options.backend) { + let compiler: Box = match get_compiler_by_backend(options.backend, options) { Some(x) => x, None => return Err("the requested backend is not enabled".into()), }; @@ -419,7 +577,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { } } - let track_state = !options.no_track_state; + let track_state = options.track_state; #[cfg(feature = "loader-kernel")] let is_kernel_loader = if let Some(LoaderName::Kernel) = options.loader { @@ -439,10 +597,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> { memory_bound_check_mode: MemoryBoundCheckMode::Disable, enforce_stack_check: true, track_state, - features: Features { - simd: options.features.simd || options.features.all, - threads: options.features.threads || options.features.all, - }, + features: options.features.into_backend_features(), + ..Default::default() }, &*compiler, ) @@ -453,10 +609,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { CompilerConfig { symbol_map: em_symbol_map.clone(), track_state, - features: Features { - simd: options.features.simd || options.features.all, - threads: options.features.threads || options.features.all, - }, + features: options.features.into_backend_features(), ..Default::default() }, &*compiler, @@ -501,10 +654,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { CompilerConfig { symbol_map: em_symbol_map.clone(), track_state, - features: Features { - simd: options.features.simd || options.features.all, - threads: options.features.threads || options.features.all, - }, + features: options.features.into_backend_features(), ..Default::default() }, &*compiler, @@ -582,90 +732,23 @@ fn execute_wasm(options: &Run) -> Result<(), String> { ) .map_err(|e| format!("{:?}", e))?; } else { - if cfg!(feature = "wasi") && wasmer_wasi::is_wasi_module(&module) { - let import_object = wasmer_wasi::generate_import_object( - if let Some(cn) = &options.command_name { - [cn.clone()] - } else { - [options.path.to_str().unwrap().to_owned()] - } - .iter() - .chain(options.args.iter()) - .cloned() - .map(|arg| arg.into_bytes()) - .collect(), - env_vars - .into_iter() - .map(|(k, v)| format!("{}={}", k, v).into_bytes()) - .collect(), - options.pre_opened_directories.clone(), + #[cfg(feature = "wasi")] + let wasi_version = wasmer_wasi::get_wasi_version(&module); + #[cfg(feature = "wasi")] + let is_wasi = wasi_version.is_some(); + #[cfg(not(feature = "wasi"))] + let is_wasi = false; + + if is_wasi { + #[cfg(feature = "wasi")] + execute_wasi( + wasi_version.unwrap(), + options, + env_vars, + module, mapped_dirs, - ); - - #[allow(unused_mut)] // mut used in feature - let mut instance = module - .instantiate(&import_object) - .map_err(|e| format!("Can't instantiate WASI module: {:?}", e))?; - - let start: Func<(), ()> = instance.func("_start").map_err(|e| format!("{:?}", e))?; - - #[cfg(feature = "managed")] - { - let start_raw: extern "C" fn(&mut wasmer_runtime_core::vm::Ctx) = - unsafe { ::std::mem::transmute(start.get_vm_func()) }; - - unsafe { - run_tiering( - module.info(), - &wasm_binary, - if let Some(ref path) = options.resume { - let mut f = File::open(path).unwrap(); - let mut out: Vec = vec![]; - f.read_to_end(&mut out).unwrap(); - Some( - wasmer_runtime_core::state::InstanceImage::from_bytes(&out) - .expect("failed to decode image"), - ) - } else { - None - }, - &import_object, - start_raw, - &mut instance, - options - .optimized_backends - .iter() - .map(|&backend| -> Box Box + Send> { - Box::new(move || get_compiler_by_backend(backend).unwrap()) - }) - .collect(), - interactive_shell, - )? - }; - } - - #[cfg(not(feature = "managed"))] - { - use wasmer_runtime::error::RuntimeError; - let result = start.call(); - - if let Err(ref err) = result { - match err { - RuntimeError::Trap { msg } => { - return Err(format!("wasm trap occured: {}", msg)) - } - #[cfg(feature = "wasi")] - RuntimeError::Error { data } => { - if let Some(error_code) = data.downcast_ref::() { - std::process::exit(error_code.code as i32) - } - } - #[cfg(not(feature = "wasi"))] - RuntimeError::Error { .. } => (), - } - return Err(format!("error: {:?}", err)); - } - } + &wasm_binary, + )?; } else { let import_object = wasmer_runtime_core::import::ImportObject::new(); let instance = module @@ -683,11 +766,16 @@ fn execute_wasm(options: &Run) -> Result<(), String> { args.push(Value::I32(x)); } - instance - .dyn_func("main") + let invoke_fn = match options.invoke.as_ref() { + Some(fun) => fun, + _ => "main", + }; + let result = instance + .dyn_func(&invoke_fn) .map_err(|e| format!("{:?}", e))? .call(&args) .map_err(|e| format!("{:?}", e))?; + println!("main() returned: {:?}", result); } } @@ -767,8 +855,30 @@ fn interactive_shell(mut ctx: InteractiveShellContext) -> ShellExitOperation { } } -fn run(options: Run) { - match execute_wasm(&options) { +fn update_backend(options: &mut Run) { + let binary_size = match metadata(&options.path) { + Ok(wasm_binary) => wasm_binary.len(), + Err(_e) => 0, + }; + + // Update backend when a backend flag is `auto`. + // Use the Singlepass backend if it's enabled and the file provided is larger + // than 10MiB (10485760 bytes), or it's enabled and the target architecture + // is AArch64. Otherwise, use the Cranelift backend. + if options.backend == Backend::Auto { + if Backend::variants().contains(&Backend::Singlepass.to_string()) + && (binary_size > 10485760 || cfg!(target_arch = "aarch64")) + { + options.backend = Backend::Singlepass; + } else { + options.backend = Backend::Cranelift; + } + } +} + +fn run(options: &mut Run) { + update_backend(options); + match execute_wasm(options) { Ok(()) => {} Err(message) => { eprintln!("Error: {}", message); @@ -798,10 +908,7 @@ fn validate_wasm(validate: Validate) -> Result<(), String> { wasmer_runtime_core::validate_and_report_errors_with_features( &wasm_binary, - Features { - simd: validate.features.simd || validate.features.all, - threads: validate.features.threads || validate.features.all, - }, + validate.features.into_backend_features(), ) .map_err(|err| format!("Validation failed: {}", err))?; @@ -819,24 +926,62 @@ fn validate(validate: Validate) { } } -fn get_compiler_by_backend(backend: Backend) -> Option> { +fn get_compiler_by_backend(backend: Backend, _opts: &Run) -> Option> { Some(match backend { #[cfg(feature = "backend-singlepass")] - Backend::Singlepass => Box::new(SinglePassCompiler::new()), + Backend::Singlepass => { + use wasmer_runtime_core::codegen::MiddlewareChain; + use wasmer_runtime_core::codegen::StreamingCompiler; + use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG; + + let opts = _opts.clone(); + let middlewares_gen = move || { + let mut middlewares = MiddlewareChain::new(); + if opts.call_trace { + use wasmer_middleware_common::call_trace::CallTrace; + middlewares.push(CallTrace::new()); + } + if opts.block_trace { + use wasmer_middleware_common::block_trace::BlockTrace; + middlewares.push(BlockTrace::new()); + } + middlewares + }; + + let c: StreamingCompiler = + StreamingCompiler::new(middlewares_gen); + Box::new(c) + } #[cfg(not(feature = "backend-singlepass"))] Backend::Singlepass => return None, + #[cfg(feature = "backend-cranelift")] Backend::Cranelift => Box::new(CraneliftCompiler::new()), + #[cfg(not(feature = "backend-cranelift"))] + Backend::Cranelift => return None, #[cfg(feature = "backend-llvm")] Backend::LLVM => Box::new(LLVMCompiler::new()), #[cfg(not(feature = "backend-llvm"))] Backend::LLVM => return None, + Backend::Auto => return None, }) } fn main() { - let options = CLIOptions::from_args(); + // We try to run wasmer with the normal arguments. + // Eg. `wasmer ` + // In case that fails, we fallback trying the Run subcommand directly. + // Eg. `wasmer myfile.wasm --dir=.` + let options = CLIOptions::from_iter_safe(env::args()).unwrap_or_else(|e| { + match e.kind { + // This fixes a issue that: + // 1. Shows the version twice when doing `wasmer -V` + // 2. Shows the run help (instead of normal help) when doing `wasmer --help` + clap::ErrorKind::VersionDisplayed | clap::ErrorKind::HelpDisplayed => e.exit(), + _ => CLIOptions::Run(Run::from_args()), + } + }); match options { - CLIOptions::Run(options) => run(options), + CLIOptions::Run(mut options) => run(&mut options), #[cfg(not(target_os = "windows"))] CLIOptions::SelfUpdate => update::self_update(), #[cfg(target_os = "windows")] diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index a5a24951a..0fd5cf639 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.8.0 +AppVersion=0.11.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 diff --git a/wapm-cli b/wapm-cli index b15715356..3562d6dda 160000 --- a/wapm-cli +++ b/wapm-cli @@ -1 +1 @@ -Subproject commit b157153568fc45f0d01ba8443c54fc3c2ce0cb23 +Subproject commit 3562d6dda52df526e6e1917dd33bb2454917ab9c