diff --git a/.dockerignore b/.dockerignore index e861ea7b657..29fd6ce6b1e 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 00000000000..4dbbd44d8e3 --- /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 bb0737d8a7e..4e92f951ce7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,53 @@ ## **[Unreleased]** +- [#1056](https://github.com/wasmerio/wasmer/pull/1056) Improved `--invoke` args parsing (supporting `i32`, `i64`, `f32` and `f32`) in Wasmer CLI +- [#1054](https://github.com/wasmerio/wasmer/pull/1054) Improve `--invoke` output in Wasmer CLI +- [#1053](https://github.com/wasmerio/wasmer/pull/1053) For RuntimeError and breakpoints, use Box instead of Box. +- [#1052](https://github.com/wasmerio/wasmer/pull/1052) Fix minor panic and improve Error handling in singlepass backend. +- [#1050](https://github.com/wasmerio/wasmer/pull/1050) Attach C & C++ headers to releases. +- [#1033](https://github.com/wasmerio/wasmer/pull/1033) Set cranelift backend as default compiler backend again, require at least one backend to be enabled for Wasmer CLI +- [#1030](https://github.com/wasmerio/wasmer/pull/1030) Ability to generate `ImportObject` for a specific version WASI version with the C API. +- [#1028](https://github.com/wasmerio/wasmer/pull/1028) Introduce strict/non-strict modes for `get_wasi_version` +- [#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 diff --git a/Cargo.lock b/Cargo.lock index 9012e4f6ce8..0ee127d57ad 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,18 +28,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -43,6 +45,34 @@ name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.66 (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 = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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" @@ -50,7 +80,7 @@ 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.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -60,11 +90,11 @@ 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.12 (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)", ] @@ -76,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.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -84,6 +114,16 @@ 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.3" @@ -94,18 +134,21 @@ dependencies = [ [[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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.102 (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" @@ -116,16 +159,16 @@ dependencies = [ "log 0.4.8 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.7 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.46" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -160,7 +203,7 @@ name = "cmake" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -168,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.66 (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" @@ -188,7 +271,7 @@ dependencies = [ "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.12 (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)", ] @@ -221,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.1 (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.65 (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.66 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (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)", ] @@ -253,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.1 (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.12 (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.6.6 (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.2 (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)", ] @@ -287,6 +378,14 @@ 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" @@ -296,6 +395,16 @@ dependencies = [ "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)", +] + [[package]] name = "csv" version = "1.1.1" @@ -305,7 +414,7 @@ dependencies = [ "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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -322,7 +431,7 @@ 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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -333,28 +442,32 @@ 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.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]] @@ -363,12 +476,11 @@ 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -376,7 +488,7 @@ name = "erased-serde" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -385,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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -395,7 +507,15 @@ 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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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]] @@ -403,6 +523,7 @@ name = "failure" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "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)", ] @@ -413,12 +534,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.1 (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 = "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.66 (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 = "field-offset" +name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -427,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" @@ -434,11 +607,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "generational-arena" -version = "0.2.4" +version = "0.2.6" 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)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -455,7 +628,7 @@ version = "0.1.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)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -466,7 +639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -489,12 +662,29 @@ 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.5.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]] @@ -502,7 +692,7 @@ 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)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -510,37 +700,125 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "http" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.3.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)", - "serde 1.0.102 (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#bd0d09a8041dc2217f698cd3631b6d4d4c0e696b" +source = "git+https://github.com/TheDan64/inkwell?rev=781620e9fa30e51a6e03bd0d49b5f5bb7a782520#781620e9fa30e51a6e03bd0d49b5f5bb7a782520" dependencies = [ "either 1.5.3 (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.65 (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.66 (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#bd0d09a8041dc2217f698cd3631b6d4d4c0e696b" +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)", ] @@ -561,12 +839,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (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.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itertools" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -597,7 +883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.65" +version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -605,16 +891,16 @@ name = "llvm-sys" version = "80.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (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)", + "libc 0.2.66 (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)", @@ -628,6 +914,16 @@ dependencies = [ "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" @@ -638,16 +934,7 @@ name = "memchr" version = "2.2.1" 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 = "memmap" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "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)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -655,78 +942,185 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.2" +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 = "nix" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.46 (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.14" +name = "mime" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "num-traits" -version = "0.2.8" +name = "mime_guess" +version = "2.0.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)", + "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 = "num_cpus" -version = "1.11.0" +name = "miniz_oxide" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "owning_ref" -version = "0.3.3" +name = "mio" +version = "0.6.19" 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)", + "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.66 (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 = "page_size" -version = "0.4.1" +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)", - "libc 0.2.65 (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 = "parallel" +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.66 (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.66 (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.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (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.66 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.66 (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.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.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)", +] + +[[package]] +name = "page_size" +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.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +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.9.0", - "wasmer-runtime-core 0.9.0", + "wasmer-runtime 0.11.0", + "wasmer-runtime-core 0.11.0", ] [[package]] @@ -742,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)", ] @@ -754,13 +1148,28 @@ 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)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.12 (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 = "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]] name = "plain" version = "0.2.3" @@ -782,7 +1191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -802,9 +1211,16 @@ dependencies = [ ] [[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" @@ -822,18 +1238,45 @@ dependencies = [ "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.66 (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]] name = "rand" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", + "libc 0.2.66 (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" @@ -864,6 +1307,14 @@ dependencies = [ "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]] name = "rand_hc" version = "0.2.0" @@ -872,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.66 (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" @@ -879,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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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" @@ -900,30 +1386,30 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (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)", + "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.0 (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.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -971,6 +1457,44 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "reqwest" +version = "0.9.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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" @@ -992,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" @@ -1016,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.66 (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" @@ -1031,10 +1583,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1043,40 +1595,64 @@ 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.102 (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_bytes" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (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.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (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.41" +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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (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_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.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 = "0.6.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1084,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" @@ -1091,33 +1675,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.3" +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.3 (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.3" +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.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.7 (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]] @@ -1132,7 +1706,7 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1140,30 +1714,17 @@ dependencies = [ "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.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (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" @@ -1171,7 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.41 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1180,7 +1741,7 @@ version = "3.1.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)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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)", @@ -1208,7 +1769,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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)", ] @@ -1218,16 +1779,131 @@ name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (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 = "toml" -version = "0.4.10" +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 = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "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]] @@ -1235,7 +1911,20 @@ name = "toml" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +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 = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1251,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.102 (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)", ] @@ -1262,12 +1951,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.7 (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.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1277,17 +1990,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" -version = "0.0.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "unicode-xid" -version = "0.2.0" +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]] @@ -1295,6 +2036,16 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +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" @@ -1305,9 +2056,9 @@ name = "wabt" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (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)", ] @@ -1316,7 +2067,7 @@ name = "wabt-sys" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (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)", ] @@ -1331,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" @@ -1338,52 +2099,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmer" -version = "0.9.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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.3 (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.9.0", - "wasmer-dev-utils 0.9.0", - "wasmer-emscripten 0.9.0", - "wasmer-emscripten-tests 0.9.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.9.0", - "wasmer-middleware-common 0.9.0", - "wasmer-middleware-common-tests 0.9.0", - "wasmer-runtime 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.0", - "wasmer-wasi 0.9.0", - "wasmer-wasi-tests 0.9.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.9.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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.102 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.3 (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.9.0", - "wasmer-win-exception-handler 0.9.0", - "wasmparser 0.39.2 (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)", ] @@ -1394,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.12 (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)", ] @@ -1409,225 +2170,236 @@ dependencies = [ "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.2 (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.9.0" +version = "0.11.0" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.9.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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.9.0", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-emscripten-tests" -version = "0.9.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.9.0", - "wasmer-dev-utils 0.9.0", - "wasmer-emscripten 0.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.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.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.9.0", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-llvm-backend" -version = "0.9.0" +version = "0.11.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.12 (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.9.0", - "wasmparser 0.39.2 (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-llvm-backend-tests" +version = "0.10.2" +dependencies = [ + "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.9.0" +version = "0.11.0" dependencies = [ - "wasmer-runtime-core 0.9.0", + "wasmer-runtime-core 0.11.0", ] [[package]] name = "wasmer-middleware-common-tests" -version = "0.9.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.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-middleware-common 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.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.9.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.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.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.9.0" +version = "0.11.0" dependencies = [ "cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-wasi 0.9.0", + "libc 0.2.66 (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.9.0" +version = "0.11.0" dependencies = [ "bincode 1.2.0 (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.46 (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.48 (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.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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.102 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.3 (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-runtime-core-tests" -version = "0.9.0" +version = "0.11.0" dependencies = [ "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.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-singlepass-backend" -version = "0.9.0" +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.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.9.0", + "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.9.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.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.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.9.0" +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)", - "generational-arena 0.2.4 (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)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (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.9.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.9.0" +version = "0.11.0" dependencies = [ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.9.0", - "wasmer-dev-utils 0.9.0", - "wasmer-llvm-backend 0.9.0", - "wasmer-runtime 0.9.0", - "wasmer-runtime-core 0.9.0", - "wasmer-singlepass-backend 0.9.0", - "wasmer-wasi 0.9.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.9.0" +version = "0.11.0" dependencies = [ "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.9.0", + "libc 0.2.66 (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.2" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1667,169 +2439,265 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +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)", +] + +[[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.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"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.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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" +"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 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.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 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.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" +"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" "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.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 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 enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" +"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 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 field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68" +"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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fd04ad33021a0409d3f1afbfb89d9f02c10caee73c28f5ac197474dd53e7cf7c" +"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.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 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/wasmerio/inkwell?branch=llvm8-0)" = "" -"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)" = "" +"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.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87fa75c9dea7b07be3138c49abbb83fd4bea199b5cdc76f9804458edc5da0d6e" +"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.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc" +"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.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155394f924cdddf08149da25bfb932d226b4a593ca7468b08191ff6335941af5" -"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 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.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.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"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 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.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.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" +"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.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" -"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" -"checksum smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "533e29e15d0748f28afbaf4ff7cab44d73e483a8e50b38c40bd13b7f3d48f542" +"checksum serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae" +"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4f66a4c0ddf7aee4677995697366de0749b0139057342eccbb609b12d0affc" -"checksum structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe0c13e476b4e21ff7f5c4ace3818b6d7bdc16897c31c73862471bc1663acae" -"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.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" -"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 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 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.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49f5526225fd8b77342d5986ab5f6055552e9c0776193b5b63fd53b46debfad7" +"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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82" +"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 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 2a47f943de4..3d1a6b847d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "wasmer" -version = "0.9.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" } @@ -49,6 +50,7 @@ members = [ "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", + "lib/llvm-backend-tests", "lib/wasi", "lib/middleware-common", "lib/kernel-loader", @@ -57,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", ] @@ -76,28 +78,27 @@ default = ["fast-tests", "wasi", "backend-cranelift"] "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 43fcda359fc..73507be300e 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 e8d11bed05e..935a6a69218 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 @@ -97,12 +97,13 @@ cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests- 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 @@ -151,7 +152,7 @@ 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 . @@ -191,6 +192,12 @@ check: check-bench # as default, and test a minimal set of features with only one backend # at a time. cargo check --manifest-path lib/runtime/Cargo.toml + # Check some of the cases where deterministic execution could matter + cargo check --manifest-path lib/runtime/Cargo.toml --features "deterministic-execution" + cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features \ + --features=default-backend-singlepass,deterministic-execution + cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features \ + --features=default-backend-llvm,deterministic-execution cargo check --release --manifest-path lib/runtime/Cargo.toml $(RUNTIME_CHECK) \ @@ -220,13 +227,13 @@ check: check-bench # 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 @@ -265,4 +272,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 7e7f39cb154..0391bbd9b97 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 @@ -59,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 - -Installing Wasmer through `wasmer.io` includes -[`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/). +### Docs -wapm allows you to easily download, run, and distribute WebAssembly binaries. +Wasmer documentation lives on [docs.wasmer.io](https://docs.wasmer.io). -```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 @@ -167,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. @@ -238,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` @@ -262,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 db05b2d340d..6ad407923c4 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') @@ -100,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 @@ -123,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) @@ -134,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: | @@ -145,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 @@ -163,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 @@ -182,6 +190,7 @@ jobs: make capi make test-capi cp target/release/libwasmer_runtime_c_api.so ./artifacts + find target/release/build -name 'wasmer.h*' -exec cp {} ./artifacts ';' displayName: Build c-api (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - bash: | @@ -205,7 +214,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 @@ -216,14 +226,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: @@ -250,3 +269,4 @@ trigger: - master - staging - trying + - refs/tags/* diff --git a/bors.toml b/bors.toml index 1003d9678c1..936db84337a 100644 --- a/bors.toml +++ b/bors.toml @@ -1,5 +1,6 @@ status = [ - "wasmerio.wasmer" + "wasmerio.wasmer", + "continuous-integration/travis-ci/push" ] required_approvals = 1 timeout_sec = 7200 diff --git a/docs/feature_matrix.md b/docs/feature_matrix.md index 81602762bbc..18d30327051 100644 --- a/docs/feature_matrix.md +++ b/docs/feature_matrix.md @@ -4,11 +4,11 @@ |   | Singlepass | Cranelift | LLVM | | - | :-: | :-: | :-: | -| Caching | ⬜ | ✅ | ✅ | +| Caching | ✅ | ✅ | ✅ | | Emscripten | ✅ | ✅ | ✅ | | Metering | ✅ | ⬜ | ✅ | | Multi-value return | ⬜ | ⬜ | ⬜ | -| OSR | 🔄 | ❓ | ❓ | +| OSR | 🔄 | ⬜ | 🔄 | | SIMD | ⬜ | ⬜ | ✅ | | WASI | ✅ | ✅ | ✅ | | WASMER_BACKTRACE | ✅ | ⬜ | ⬜ | @@ -18,7 +18,7 @@ | - | :-: | :-: | :-: | | Cranelift Backend | ✅ | ✅ | ✅ | | LLVM Backend | ✅ | ✅ | ✅ | -| Singlepass Backend | [#347](https://github.com/wasmerio/wasmer/issues/347) | ✅ | ✅ | +| Singlepass Backend | ✅ | ✅ | [#347](https://github.com/wasmerio/wasmer/issues/347) | | WASI | ✅ | ✅ | ✅* | * `poll_fd` is not fully implemented for Windows yet diff --git a/examples/fib.wat b/examples/fib.wat new file mode 100644 index 00000000000..a797fdae484 --- /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 00000000000..a71d719ed41 --- /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 00000000000..ca24b9cead6 --- /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 043bc02a1d4..66ed10405f2 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 71344335595..8d6a38140ea 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 00939ee5a74..36f7e0c4ca4 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 4abe062f510..e36d8c39630 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 a4fc2b841e6..e6c874e2a0e 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 22af9de037b..1dea62a2415 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,15 +1,17 @@ [package] name = "wasmer-clif-backend" -version = "0.9.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.9.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.9.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 f1d8489da13..d86d19b4f0f 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 } @@ -691,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, @@ -763,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 ddecdbaf08a..06bffc791cc 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/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 116da3f56ef..5526cc4e215 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -26,12 +26,12 @@ pub use self::unix::*; pub use self::windows::*; thread_local! { - pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } pub enum CallProtError { Trap(WasmTrapInfo), - Error(Box), + Error(Box), } pub struct Caller { @@ -67,7 +67,7 @@ impl RunnableModule for Caller { args: *const u64, rets: *mut u64, trap_info: *mut WasmTrapInfo, - user_error: *mut Option>, + user_error: *mut Option>, invoke_env: Option>, ) -> bool { let handler_data = &*invoke_env.unwrap().cast().as_ptr(); @@ -108,7 +108,7 @@ impl RunnableModule for Caller { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: Box) -> ! { TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); trigger_trap() } diff --git a/lib/dev-utils/Cargo.toml b/lib/dev-utils/Cargo.toml index 3bb7c6f4f76..81fb49757d7 100644 --- a/lib/dev-utils/Cargo.toml +++ b/lib/dev-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-dev-utils" -version = "0.9.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 8f2725f5722..2439b429578 100644 --- a/lib/emscripten-tests/Cargo.toml +++ b/lib/emscripten-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten-tests" -version = "0.9.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.9.0" } -wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true } +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.9.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 1da0abc7315..59bcf90ebf7 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 1e5dfba6e64..2e6c590d907 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 f51ee1a724b..dffbe09422b 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-emscripten" -version = "0.9.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,7 +14,7 @@ byteorder = "1.3" lazy_static = "1.4" libc = "0.2.60" time = "0.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.9.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } [target.'cfg(windows)'.dependencies] getrandom = "0.1" diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 7dc808bde2b..1c9b8a1f70d 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 cb015af3a26..62d807c0273 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, @@ -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() ); diff --git a/lib/llvm-backend-tests/Cargo.toml b/lib/llvm-backend-tests/Cargo.toml new file mode 100644 index 00000000000..bb6b24b511c --- /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 00000000000..ea79b78721e --- /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 00000000000..1e32f6dd08d --- /dev/null +++ b/lib/llvm-backend-tests/tests/compile.rs @@ -0,0 +1,68 @@ +use wasmer_llvm_backend::{InkwellMemoryBuffer, InkwellModule, LLVMBackendConfig, LLVMCallbacks}; +use wasmer_llvm_backend_tests::{get_compiler, wat2wasm}; +use wasmer_runtime::{imports, CompilerConfig}; +use wasmer_runtime_core::{backend::BackendCompilerConfig, compile_with, compile_with_config}; + +use std::cell::RefCell; +use std::rc::Rc; + +#[test] +fn crash_return_with_float_on_stack() { + const MODULE: &str = r#" +(module + (type (func)) + (type (func (param f64) (result f64))) + (func $_start (type 0)) + (func $fmod (type 1) (param f64) (result f64) + local.get 0 + f64.const 0x0p+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(); +} + +#[derive(Debug, Default)] +pub struct RecordPreOptIR { + preopt_ir: String, +} + +impl LLVMCallbacks for RecordPreOptIR { + fn preopt_ir_callback(&mut self, module: &InkwellModule) { + self.preopt_ir = module.print_to_string().to_string(); + } +} + +#[test] +fn crash_select_with_mismatched_pending() { + const WAT: &str = r#" + (module + (func (param f64) (result f64) + f64.const 0x0p+0 + local.get 0 + f64.add + f64.const 0x0p+0 + i32.const 0 + select)) +"#; + let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default())); + let compiler_config = CompilerConfig { + backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig { + callbacks: Some(record_pre_opt_ir.clone()), + }))), + ..Default::default() + }; + let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed"); + let module = compile_with_config(&wasm_binary, &get_compiler(), compiler_config).unwrap(); + module.instantiate(&imports! {}).unwrap(); + const LLVM: &str = r#" + %s3 = fadd double 0.000000e+00, %s2 + %nan = fcmp uno double %s3, 0.000000e+00 + %2 = select i1 %nan, double 0x7FF8000000000000, double %s3 + %s5 = select i1 false, double %2, double 0.000000e+00 + br label %return +"#; + assert!(&record_pre_opt_ir.borrow().preopt_ir.contains(LLVM)); +} diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 4cd3e247923..b9fa7e6a720 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,12 +1,16 @@ [package] name = "wasmer-llvm-backend" -version = "0.9.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.9.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.11.0" } wasmparser = "0.39.1" smallvec = "0.6" goblin = "0.0.24" @@ -14,8 +18,8 @@ libc = "0.2.60" 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"] @@ -37,3 +41,4 @@ wabt = "0.9.1" [features] debug = ["wasmer-runtime-core/debug"] +test = [] diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index a5d26203521..05831733ffa 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -2,6 +2,7 @@ use super::stackmap::StackmapRegistry; use crate::{ intrinsics::Intrinsics, structs::{Callbacks, LLVMModule, LLVMResult, MemProtect}, + LLVMCallbacks, }; use inkwell::{ memory_buffer::MemoryBuffer, @@ -13,8 +14,6 @@ use std::{ any::Any, cell::RefCell, ffi::{c_void, CString}, - fs::File, - io::Write, mem, ops::Deref, ptr::{self, NonNull}, @@ -67,7 +66,7 @@ extern "C" { params: *const u64, results: *mut u64, trap_out: *mut WasmTrapInfo, - user_error: *mut Option>, + user_error: *mut Option>, invoke_env: Option>, ) -> bool; } @@ -176,23 +175,22 @@ impl LLVMBackend { _stackmaps: &StackmapRegistry, _module_info: &ModuleInfo, target_machine: &TargetMachine, + llvm_callbacks: &Option>>, ) -> (Self, LLVMCache) { let memory_buffer = target_machine .write_to_memory_buffer(&module.borrow_mut(), FileType::Object) .unwrap(); - let mem_buf_slice = memory_buffer.as_slice(); - if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.obj_file } { - let mut file = File::create(path).unwrap(); - let mut pos = 0; - while pos < mem_buf_slice.len() { - pos += file.write(&mem_buf_slice[pos..]).unwrap(); - } + if let Some(callbacks) = llvm_callbacks { + callbacks + .borrow_mut() + .obj_memory_buffer_callback(&memory_buffer); } let callbacks = get_callbacks(); let mut module: *mut LLVMModule = ptr::null_mut(); + let mem_buf_slice = memory_buffer.as_slice(); let res = unsafe { module_load( mem_buf_slice.as_ptr(), @@ -429,7 +427,7 @@ impl RunnableModule for LLVMBackend { self.msm.clone() } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: Box) -> ! { throw_any(Box::leak(data)) } } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 7b5f2d1a233..f2d52349296 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -5,6 +5,7 @@ use crate::{ stackmap::{StackmapEntry, StackmapEntryKind, StackmapRegistry, ValueSemantic}, state::{ControlFrame, ExtraInfo, IfElseState, State}, trampolines::generate_trampolines, + LLVMBackendConfig, LLVMCallbacks, }; use inkwell::{ builder::Builder, @@ -22,6 +23,8 @@ use inkwell::{ use smallvec::SmallVec; use std::{ cell::RefCell, + collections::HashMap, + mem::ManuallyDrop, rc::Rc, sync::{Arc, RwLock}, }; @@ -38,12 +41,12 @@ use wasmer_runtime_core::{ }; use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; -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()) @@ -66,7 +69,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(), @@ -76,7 +79,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(), @@ -85,13 +88,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( @@ -105,18 +108,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. @@ -224,11 +227,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, @@ -260,8 +263,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); @@ -274,11 +277,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, ) { @@ -325,8 +328,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( @@ -338,11 +341,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(); @@ -367,8 +370,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( @@ -380,144 +383,161 @@ fn trap_if_zero( builder.position_at_end(&shouldnt_trap_block); } -fn v128_into_int_vec( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_int_vec<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, - int_vec_ty: VectorType, -) -> VectorValue { - let value = match info { - ExtraInfo::None => value, - ExtraInfo::PendingF32NaN => { - let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); - canonicalize_nans(builder, intrinsics, value) - } - ExtraInfo::PendingF64NaN => { - let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); - canonicalize_nans(builder, intrinsics, value) - } + 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() + ( + builder + .build_bitcast(value, int_vec_ty, "") + .into_vector_value(), + info, + ) } -fn v128_into_i8x16( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_i8x16<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { +) -> (VectorValue<'ctx>, ExtraInfo) { v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i8x16_ty) } -fn v128_into_i16x8( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_i16x8<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { +) -> (VectorValue<'ctx>, ExtraInfo) { v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i16x8_ty) } -fn v128_into_i32x4( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_i32x4<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { +) -> (VectorValue<'ctx>, ExtraInfo) { v128_into_int_vec(builder, intrinsics, value, info, intrinsics.i32x4_ty) } -fn v128_into_i64x2( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_i64x2<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { +) -> (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( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_f32x4<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { - let value = if info == ExtraInfo::PendingF64NaN { +) -> (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) + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) } else { - value + (value, info) }; - builder - .build_bitcast(value, intrinsics.f32x4_ty, "") - .into_vector_value() + ( + 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( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn v128_into_f64x2<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> VectorValue { - let value = if info == ExtraInfo::PendingF32NaN { +) -> (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) + ( + canonicalize_nans(builder, intrinsics, value), + info.strip_pending(), + ) } else { - value + (value, info) }; - builder - .build_bitcast(value, intrinsics.f64x2_ty, "") - .into_vector_value() + ( + builder + .build_bitcast(value, intrinsics.f64x2_ty, "") + .into_vector_value(), + info, + ) } -fn apply_pending_canonicalization( - builder: &Builder, - intrinsics: &Intrinsics, - value: BasicValueEnum, +fn apply_pending_canonicalization<'ctx>( + builder: &Builder<'ctx>, + intrinsics: &Intrinsics<'ctx>, + value: BasicValueEnum<'ctx>, info: ExtraInfo, -) -> BasicValueEnum { - match info { - ExtraInfo::None => value, - ExtraInfo::PendingF32NaN => { - 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) - } +) -> 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) } - ExtraInfo::PendingF64NaN => { - 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 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(); @@ -551,18 +571,18 @@ fn canonicalize_nans( canonicalized } -fn resolve_memory_ptr( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - module: Rc>, - 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 { +) -> 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 { @@ -577,14 +597,14 @@ fn resolve_memory_ptr( .into_pointer_value(); let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); tbaa_label( - module.clone(), + &module, intrinsics, "dynamic_memory_base", base.as_instruction_value().unwrap(), Some(0), ); tbaa_label( - module.clone(), + &module, intrinsics, "dynamic_memory_bounds", bounds.as_instruction_value().unwrap(), @@ -662,8 +682,8 @@ 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( ptr_in_bounds, &in_bounds_continue_block, @@ -686,16 +706,16 @@ fn resolve_memory_ptr( .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(); @@ -739,9 +759,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, @@ -770,13 +790,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 => { @@ -808,8 +828,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); @@ -836,50 +856,54 @@ pub unsafe extern "C" fn callback_trampoline( callback: *mut BreakpointHandler, ) { let callback = Box::from_raw(callback); - let result: Result<(), Box> = callback(BreakpointInfo { fault: None }); + let result: Result<(), Box> = + callback(BreakpointInfo { fault: None }); match result { Ok(()) => *b = None, Err(e) => *b = Some(e), } } -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: Rc>, + personality_func: ManuallyDrop>, + module: ManuallyDrop>>>, stackmaps: Rc>, track_state: bool, target_machine: TargetMachine, + llvm_callbacks: Option>>, } -pub struct LLVMFunctionCodeGenerator { - context: Option, - builder: Option, - alloca_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>, + module: Rc>>, } -impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { +impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ctx> { fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { Ok(()) } @@ -930,7 +954,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() @@ -945,10 +969,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); @@ -1025,7 +1046,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ctx.internal_field(idx, intrinsics, self.module.clone(), builder); let result = builder.build_load(field_ptr, "get_internal"); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "internal", result.as_instruction_value().unwrap(), @@ -1042,7 +1063,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let v = state.pop1()?; let store = builder.build_store(field_ptr, v); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "internal", store, @@ -1090,7 +1111,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) { @@ -1107,8 +1128,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); @@ -1213,7 +1234,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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, @@ -1279,9 +1300,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); @@ -1409,14 +1430,11 @@ 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 frame = state.outermost_frame()?; for phi in frame.phis().to_vec().iter() { let (arg, info) = state.pop1_extra()?; @@ -1424,6 +1442,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { phi.add_incoming(&[(&arg, ¤t_block)]); } + let frame = state.outermost_frame()?; + builder.build_unconditional_branch(frame.br_dest()); + state.reachable = false; } @@ -1485,21 +1506,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(); @@ -1508,11 +1549,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, @@ -1522,10 +1583,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, @@ -1535,10 +1597,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, @@ -1547,10 +1609,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, @@ -1559,7 +1621,7 @@ 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, i) = state.pop1_extra()?; @@ -1595,7 +1657,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let pointer_value = locals[local_index as usize]; let v = builder.build_load(pointer_value, &state.var_name()); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "local", v.as_instruction_value().unwrap(), @@ -1608,26 +1670,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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.clone(), - intrinsics, - "local", - store, - Some(local_index), - ); + tbaa_label(&self.module, intrinsics, "local", store, Some(local_index)); } Operator::TeeLocal { local_index } => { let pointer_value = locals[local_index as usize]; 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.clone(), - intrinsics, - "local", - store, - Some(local_index), - ); + tbaa_label(&self.module, intrinsics, "local", store, Some(local_index)); } Operator::GetGlobal { global_index } => { @@ -1640,7 +1690,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { GlobalCache::Mut { ptr_to_value } => { let value = builder.build_load(ptr_to_value, "global_value"); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "global", value.as_instruction_value().unwrap(), @@ -1659,7 +1709,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { GlobalCache::Mut { ptr_to_value } => { let store = builder.build_store(ptr_to_value, value); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "global", store, @@ -1675,7 +1725,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(), @@ -1683,7 +1752,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); @@ -1692,7 +1773,7 @@ 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 @@ -1722,15 +1803,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .collect(); - let func_ptr = ctx.local_func( - local_func_index, - llvm_sig, - intrinsics, - self.module.clone(), - 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) = @@ -1766,7 +1841,6 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .collect(); let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic); - let func_ptr = builder.build_pointer_cast( func_ptr_untyped, func_ptr_ty, @@ -1911,9 +1985,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, @@ -1952,9 +2026,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, @@ -2062,49 +2136,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, 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 (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, 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 (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, 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 (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, 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 (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, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i8x16(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2113,10 +2193,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8AddSaturateS => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i16x8(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2125,10 +2209,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I8x16AddSaturateU => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i8x16(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2137,10 +2225,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8AddSaturateU => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i16x8(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2148,49 +2240,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, 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 (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, 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 (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, 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 (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, 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 (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, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i8x16(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2199,10 +2297,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8SubSaturateS => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i16x8(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2211,10 +2313,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I8x16SubSaturateU => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i8x16(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2223,10 +2329,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8SubSaturateU => { let ((v1, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1).as_basic_value_enum(); - let v2 = v128_into_i16x8(builder, intrinsics, v2, i2).as_basic_value_enum(); + 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(); @@ -2234,37 +2344,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, 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 (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, 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 (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, 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 (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); @@ -2273,7 +2387,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); @@ -2282,7 +2398,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 { @@ -2327,7 +2445,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); @@ -2378,15 +2498,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, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1); + 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, ""); @@ -2402,8 +2525,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8Shl => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1); + 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, ""); @@ -2419,8 +2543,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4Shl => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i32x4(builder, intrinsics, v1, i1); + 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( @@ -2435,8 +2560,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2Shl => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i64x2(builder, intrinsics, v1, i1); + 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, ""); @@ -2452,15 +2578,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, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1); + 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, ""); @@ -2476,8 +2605,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ShrS => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1); + 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, ""); @@ -2493,8 +2623,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ShrS => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i32x4(builder, intrinsics, v1, i1); + 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( @@ -2509,8 +2640,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2ShrS => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i64x2(builder, intrinsics, v1, i1); + 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, ""); @@ -2526,14 +2658,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, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1); + 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, ""); @@ -2549,8 +2684,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I16x8ShrU => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1); + 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, ""); @@ -2566,8 +2702,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ShrU => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i32x4(builder, intrinsics, v1, i1); + 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( @@ -2582,8 +2719,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I64x2ShrU => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i64x2(builder, intrinsics, v1, i1); + 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, ""); @@ -2599,7 +2737,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 = { @@ -2611,7 +2751,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 = { @@ -2623,7 +2765,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 = { @@ -2635,7 +2779,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 = { @@ -2647,7 +2793,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( @@ -2658,10 +2805,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( @@ -2672,10 +2820,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( @@ -2686,10 +2835,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( @@ -2700,25 +2850,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(); @@ -2729,7 +2881,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(); @@ -2740,7 +2892,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_f64()); } /*************************** @@ -2748,116 +2900,152 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions ***************************/ Operator::F32Add => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64Add => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Add => { 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 (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_extra(res, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Add => { 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 (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_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32Sub => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64Sub => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Sub => { 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 (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_extra(res, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Sub => { 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 (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_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32Mul => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64Mul => { - let (v1, v2) = state.pop2()?; + 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, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32x4Mul => { 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 (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_extra(res, ExtraInfo::PendingF32NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(), + ); } Operator::F64x2Mul => { 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 (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_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra( + res, + (i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(), + ); } Operator::F32Div => { 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::PendingF32NaN); + 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::PendingF64NaN); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32x4Div => { 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 (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_extra(res, ExtraInfo::PendingF32NaN); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64x2Div => { 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 (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_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32Sqrt => { let input = state.pop1()?; @@ -2866,7 +3054,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1_extra(res, ExtraInfo::PendingF32NaN); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64Sqrt => { let input = state.pop1()?; @@ -2875,11 +3063,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .try_as_basic_value() .left() .unwrap(); - state.push1_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra(res, ExtraInfo::pending_f64_nan()); } Operator::F32x4Sqrt => { let (v, i) = state.pop1_extra()?; - let v = v128_into_f32x4(builder, intrinsics, v, i); + let (v, _) = v128_into_f32x4(builder, intrinsics, v, i); let res = builder .build_call( intrinsics.sqrt_f32x4, @@ -2890,11 +3078,11 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .left() .unwrap(); let bits = builder.build_bitcast(res, intrinsics.i128_ty, "bits"); - state.push1_extra(bits, ExtraInfo::PendingF32NaN); + state.push1_extra(bits, ExtraInfo::pending_f32_nan()); } Operator::F64x2Sqrt => { let (v, i) = state.pop1_extra()?; - let v = v128_into_f64x2(builder, intrinsics, v, i); + let (v, _) = v128_into_f64x2(builder, intrinsics, v, i); let res = builder .build_call( intrinsics.sqrt_f64x2, @@ -2959,7 +3147,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64Min => { // This implements the same logic as LLVM's @llvm.minimum @@ -3013,15 +3201,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + 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 intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. 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 (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 @@ -3079,15 +3267,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + 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 intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. 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 (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 @@ -3145,7 +3333,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f64()); } Operator::F32Max => { // This implements the same logic as LLVM's @llvm.maximum @@ -3198,7 +3386,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + state.push1_extra(res, ExtraInfo::arithmetic_f32()); } Operator::F64Max => { // This implements the same logic as LLVM's @llvm.maximum @@ -3251,15 +3439,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { builder.build_select(builder.build_or(v1_is_nan, min_cmp, ""), v1, v2, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + 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 intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. 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 (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 @@ -3316,15 +3504,15 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + 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 intrinsic // encounters a fatal error in LLVM 8 and LLVM 9. 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 (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 @@ -3381,43 +3569,43 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // Because inputs were canonicalized, we always produce // canonical NaN outputs. No pending NaN cleanup. - state.push1(res); + 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_extra(res, ExtraInfo::PendingF32NaN); + 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_extra(res, ExtraInfo::PendingF64NaN); + 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_extra(res, ExtraInfo::PendingF32NaN); + 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_extra(res, ExtraInfo::PendingF64NaN); + state.push1_extra(res, info | ExtraInfo::pending_f64_nan()); } Operator::F32Trunc => { let (v, i) = state.pop1_extra()?; @@ -3485,7 +3673,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .unwrap(); // The exact NaN returned by F32Abs is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F64Abs => { let (v, i) = state.pop1_extra()?; @@ -3501,7 +3689,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .unwrap(); // The exact NaN returned by F64Abs is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F32x4Abs => { let (v, i) = state.pop1_extra()?; @@ -3519,7 +3707,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // The exact NaN returned by F32x4Abs is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F64x2Abs => { let (v, i) = state.pop1_extra()?; @@ -3533,7 +3721,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // The exact NaN returned by F32x4Abs is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F32x4Neg => { let (v, i) = state.pop1_extra()?; @@ -3544,7 +3732,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // The exact NaN returned by F32x4Neg is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F64x2Neg => { let (v, i) = state.pop1_extra()?; @@ -3555,7 +3743,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); // The exact NaN returned by F64x2Neg is fully defined. Do not // adjust. - state.push1(res); + state.push1_extra(res, i.strip_pending()); } Operator::F32Neg | Operator::F64Neg => { let (v, i) = state.pop1_extra()?; @@ -3564,7 +3752,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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(res); + state.push1_extra(res, i.strip_pending()); } Operator::F32Copysign => { let ((mag, mag_info), (sgn, sgn_info)) = state.pop2_extra()?; @@ -3577,7 +3765,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .unwrap(); // The exact NaN returned by F32Copysign is fully defined. // Do not adjust. - state.push1(res); + state.push1_extra(res, mag_info.strip_pending()); } Operator::F64Copysign => { let ((mag, mag_info), (sgn, sgn_info)) = state.pop2_extra()?; @@ -3590,7 +3778,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .unwrap(); // The exact NaN returned by F32Copysign is fully defined. // Do not adjust. - state.push1(res); + state.push1_extra(res, mag_info.strip_pending()); } /*************************** @@ -3598,16 +3786,21 @@ 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, 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 (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, ""); @@ -3615,8 +3808,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8Eq => { 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 (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, ""); @@ -3624,24 +3817,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4Eq => { 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 (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, 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 (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, ""); @@ -3649,8 +3847,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8Ne => { 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 (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, ""); @@ -3658,24 +3856,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4Ne => { 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 (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, 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 (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, ""); @@ -3683,8 +3886,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8LtS => { 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 (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, ""); @@ -3692,15 +3895,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4LtS => { 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 (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()); @@ -3708,8 +3913,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I8x16LtU => { 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 (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, ""); @@ -3717,8 +3922,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8LtU => { 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 (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, ""); @@ -3726,24 +3931,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4LtU => { 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 (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, 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 (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, ""); @@ -3751,8 +3961,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8LeS => { 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 (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, ""); @@ -3760,24 +3970,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4LeS => { 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 (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, 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 (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, ""); @@ -3785,8 +4000,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8LeU => { 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 (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, ""); @@ -3794,24 +4009,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4LeU => { 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 (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, 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 (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, ""); @@ -3819,8 +4039,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8GtS => { 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 (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, ""); @@ -3828,24 +4048,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4GtS => { 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 (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, 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 (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, ""); @@ -3853,8 +4078,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8GtU => { 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 (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, ""); @@ -3862,15 +4087,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4GtU => { 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 (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()); @@ -3878,8 +4105,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I8x16GeS => { 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 (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, ""); @@ -3887,8 +4114,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8GeS => { 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 (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, ""); @@ -3896,24 +4123,29 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4GeS => { 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 (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, 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 (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, ""); @@ -3921,8 +4153,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8GeU => { 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 (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, ""); @@ -3930,8 +4162,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I32x4GeU => { 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 (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, ""); @@ -3948,12 +4180,15 @@ 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, 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 (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, ""); @@ -3961,8 +4196,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Eq => { 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 (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, ""); @@ -3974,12 +4209,15 @@ 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, 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 (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, ""); @@ -3987,8 +4225,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Ne => { 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 (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, ""); @@ -4000,12 +4238,15 @@ 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, 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 (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, ""); @@ -4013,8 +4254,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Lt => { 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 (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, ""); @@ -4026,12 +4267,15 @@ 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, 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 (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, ""); @@ -4039,8 +4283,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Le => { 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 (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, ""); @@ -4052,12 +4296,15 @@ 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, 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 (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, ""); @@ -4065,8 +4312,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Gt => { 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 (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, ""); @@ -4078,12 +4325,15 @@ 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, 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 (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, ""); @@ -4091,8 +4341,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::F64x2Ge => { 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 (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, ""); @@ -4104,22 +4354,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, @@ -4135,7 +4393,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, @@ -4151,7 +4411,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, @@ -4167,7 +4429,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, @@ -4315,36 +4579,44 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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::PendingF32NaN); + state.push1_extra(res, ExtraInfo::pending_f32_nan()); } Operator::F64PromoteF32 => { 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::PendingF64NaN); + 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 => { @@ -4391,23 +4663,23 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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, 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); } /*************************** @@ -4473,8 +4745,13 @@ 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.clone(), + &self.module, intrinsics, "memory", result.as_instruction_value().unwrap(), @@ -4496,8 +4773,13 @@ 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.clone(), + &self.module, intrinsics, "memory", result.as_instruction_value().unwrap(), @@ -4519,8 +4801,13 @@ 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.clone(), + &self.module, intrinsics, "memory", result.as_instruction_value().unwrap(), @@ -4542,8 +4829,13 @@ 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.clone(), + &self.module, intrinsics, "memory", result.as_instruction_value().unwrap(), @@ -4565,8 +4857,13 @@ 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.clone(), + &self.module, intrinsics, "memory", result.as_instruction_value().unwrap(), @@ -4590,7 +4887,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 4, )?; let store = builder.build_store(effective_address, value); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64Store { ref memarg } => { let value = state.pop1()?; @@ -4607,7 +4905,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 8, )?; let store = builder.build_store(effective_address, value); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::F32Store { ref memarg } => { let (v, i) = state.pop1_extra()?; @@ -4625,7 +4924,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 4, )?; let store = builder.build_store(effective_address, v); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::F64Store { ref memarg } => { let (v, i) = state.pop1_extra()?; @@ -4643,7 +4943,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 8, )?; let store = builder.build_store(effective_address, v); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::V128Store { ref memarg } => { let (v, i) = state.pop1_extra()?; @@ -4661,7 +4962,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 16, )?; let store = builder.build_store(effective_address, v); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( @@ -4676,18 +4978,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 narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + 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 } => { @@ -4704,8 +5012,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 2, )?; 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), @@ -4734,8 +5047,13 @@ 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), @@ -4761,8 +5079,13 @@ 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), @@ -4785,18 +5108,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 narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &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()); + let result = builder.build_int_s_extend( + narrow_result.into_int_value(), + intrinsics.i64_ty, + &state.var_name(), + ); state.push1(result); } @@ -4812,20 +5141,26 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { memarg, intrinsics.i8_ptr_ty, 1, - )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); + )?; + 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + 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( @@ -4840,19 +5175,25 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); + 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); - state.push1(result); + 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( @@ -4867,19 +5208,25 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i8_ptr_ty, 1, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); + 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + 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( @@ -4894,19 +5241,25 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { intrinsics.i16_ptr_ty, 2, )?; - let narrow_result = builder - .build_load(effective_address, &state.var_name()) - .into_int_value(); + 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.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + 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( @@ -4921,19 +5274,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 narrow_result = builder.build_load(effective_address, &state.var_name()); + narrow_result + .as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", narrow_result.as_instruction_value().unwrap(), Some(0), ); - let result = - builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); - state.push1(result); + 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 } => { @@ -4953,7 +5312,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let narrow_value = builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); let store = builder.build_store(effective_address, narrow_value); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + 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(); @@ -4972,7 +5332,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let narrow_value = builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); let store = builder.build_store(effective_address, narrow_value); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + 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(); @@ -4991,32 +5352,33 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { let narrow_value = builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); let store = builder.build_store(effective_address, narrow_value); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + store.set_alignment(1).unwrap(); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I8x16Neg => { let (v, i) = state.pop1_extra()?; - let v = v128_into_i8x16(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i16x8(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i32x4(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i64x2(builder, intrinsics, v, i); + 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); @@ -5031,8 +5393,8 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { | Operator::I16x8AnyTrue | Operator::I32x4AnyTrue | Operator::I64x2AnyTrue => { - let (v, i) = state.pop1_extra()?; - let v = apply_pending_canonicalization(builder, intrinsics, v, i).into_int_value(); + // 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, v, @@ -5040,7 +5402,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 @@ -5069,11 +5434,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, i) = state.pop1_extra()?; - let v = v128_into_i8x16(builder, intrinsics, v, i); + 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()) @@ -5083,17 +5451,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I8x16ExtractLaneU { lane } => { let (v, i) = state.pop1_extra()?; - let v = v128_into_i8x16(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i16x8(builder, intrinsics, v, i); + 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()) @@ -5103,45 +5471,45 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8ExtractLaneU { lane } => { let (v, i) = state.pop1_extra()?; - let v = v128_into_i16x8(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i32x4(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_i64x2(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_f32x4(builder, intrinsics, v, i); + 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, i) = state.pop1_extra()?; - let v = v128_into_f64x2(builder, intrinsics, v, i); + 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, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i8x16(builder, intrinsics, v1, i1); + 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); @@ -5151,7 +5519,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { } Operator::I16x8ReplaceLane { lane } => { let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i16x8(builder, intrinsics, v1, i1); + 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); @@ -5160,42 +5528,96 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { state.push1(res); } Operator::I32x4ReplaceLane { lane } => { - let ((v1, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i32x4(builder, intrinsics, v1, i1); + 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, i1), (v2, _)) = state.pop2_extra()?; - let v1 = v128_into_i64x2(builder, intrinsics, v1, i1); + 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, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_f32x4(builder, intrinsics, v1, i1); - let v2 = - apply_pending_canonicalization(builder, intrinsics, v2, i2).into_float_value(); + 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, i1), (v2, i2)) = state.pop2_extra()?; - let v1 = v128_into_f64x2(builder, intrinsics, v1, i1); - let v2 = - apply_pending_canonicalization(builder, intrinsics, v2, i2).into_float_value(); + 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, i1), (v2, i2)) = state.pop2_extra()?; @@ -5295,8 +5717,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 1, )?; let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", elem.as_instruction_value().unwrap(), @@ -5326,8 +5752,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 2, )?; let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", elem.as_instruction_value().unwrap(), @@ -5357,8 +5787,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 4, )?; let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", elem.as_instruction_value().unwrap(), @@ -5388,8 +5822,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 8, )?; let elem = builder.build_load(effective_address, ""); + elem.as_instruction_value() + .unwrap() + .set_alignment(1) + .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", elem.as_instruction_value().unwrap(), @@ -5440,7 +5878,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(4).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); state.push1(result); } Operator::I64AtomicLoad { ref memarg } => { @@ -5469,7 +5907,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(8).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", load, Some(0)); state.push1(result); } Operator::I32AtomicLoad8U { ref memarg } => { @@ -5500,10 +5938,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(1).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + 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( @@ -5533,10 +5971,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(2).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + 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( @@ -5566,10 +6004,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(1).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + 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( @@ -5599,10 +6037,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(2).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + 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( @@ -5632,10 +6070,10 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { load.set_alignment(4).unwrap(); load.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", load, Some(0)); + 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()?; @@ -5664,7 +6102,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64AtomicStore { ref memarg } => { let value = state.pop1()?; @@ -5693,7 +6131,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicStore8 { ref memarg } | Operator::I64AtomicStore8 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5724,7 +6162,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicStore16 { ref memarg } | Operator::I64AtomicStore16 { ref memarg } => { @@ -5756,7 +6194,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I64AtomicStore32 { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5787,7 +6225,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { store .set_atomic_ordering(AtomicOrdering::SequentiallyConsistent) .unwrap(); - tbaa_label(self.module.clone(), intrinsics, "memory", store, Some(0)); + tbaa_label(&self.module, intrinsics, "memory", store, Some(0)); } Operator::I32AtomicRmw8UAdd { ref memarg } => { let value = state.pop1()?.into_int_value(); @@ -5822,14 +6260,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -5864,14 +6302,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -5904,7 +6342,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -5945,14 +6383,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -5987,14 +6425,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6029,14 +6467,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6069,7 +6507,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6110,14 +6548,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6152,14 +6590,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6192,7 +6630,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6233,14 +6671,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6275,14 +6713,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6317,14 +6755,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6357,7 +6795,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6398,14 +6836,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6440,14 +6878,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6480,7 +6918,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6521,14 +6959,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6563,14 +7001,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6605,14 +7043,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6645,7 +7083,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6686,14 +7124,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6728,14 +7166,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6768,14 +7206,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6810,14 +7248,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6852,14 +7290,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6894,14 +7332,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -6934,7 +7372,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -6975,14 +7413,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7017,14 +7455,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7057,7 +7495,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7098,14 +7536,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7140,14 +7578,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7182,14 +7620,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7222,7 +7660,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7263,14 +7701,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7305,14 +7743,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7345,7 +7783,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7386,14 +7824,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7428,14 +7866,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7470,14 +7908,14 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &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(); @@ -7510,7 +7948,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7519,7 +7957,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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, @@ -7555,7 +7995,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7566,10 +8006,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .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, @@ -7605,7 +8047,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7616,10 +8058,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .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, @@ -7651,7 +8095,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7661,7 +8105,9 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { 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, @@ -7697,7 +8143,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7708,10 +8154,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .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, @@ -7747,7 +8195,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7758,10 +8206,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .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, @@ -7797,7 +8247,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7808,10 +8258,12 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { .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, @@ -7843,7 +8295,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { ) .unwrap(); tbaa_label( - self.module.clone(), + &self.module, intrinsics, "memory", old.as_instruction_value().unwrap(), @@ -7969,28 +8421,67 @@ 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"); - 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 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, - &TargetMachine::get_host_cpu_name().to_string(), - &TargetMachine::get_host_cpu_features().to_string(), + &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, @@ -8014,16 +8505,18 @@ impl ModuleCodeGenerator context: Some(context), builder: Some(builder), intrinsics: Some(intrinsics), - module: Rc::new(RefCell::new(module)), + module: ManuallyDrop::new(Rc::new(RefCell::new(module))), functions: vec![], 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, + llvm_callbacks: None, } } @@ -8038,7 +8531,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) => ( @@ -8053,23 +8546,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.borrow_mut().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 @@ -8119,7 +8608,8 @@ impl ModuleCodeGenerator 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(), @@ -8130,7 +8620,7 @@ impl ModuleCodeGenerator index: local_func_index, opcode_offset: 0, track_state: self.track_state, - module: self.module.clone(), + module: (*self.module).clone(), }; self.functions.push(code); Ok(self.functions.last_mut().unwrap()) @@ -8168,48 +8658,75 @@ impl ModuleCodeGenerator message: format!("trampolines generation error: {:?}", e), })?; - if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.pre_opt_ir } { - self.module.borrow_mut().print_to_file(path).unwrap(); + if let Some(ref mut callbacks) = self.llvm_callbacks { + callbacks + .borrow_mut() + .preopt_ir_callback(&*self.module.borrow_mut()); } 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_slp_vectorize_pass(); - pass_manager.run_on(&*self.module.borrow_mut()); + pass_manager.add_early_cse_pass(); - if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.post_opt_ir } { - self.module.borrow_mut().print_to_file(path).unwrap(); + pass_manager.run_on(&*self.module.borrow_mut()); + if let Some(ref mut callbacks) = self.llvm_callbacks { + callbacks + .borrow_mut() + .postopt_ir_callback(&*self.module.borrow_mut()); } let stackmaps = self.stackmaps.borrow(); let (backend, cache_gen) = LLVMBackend::new( - self.module.clone(), + (*self.module).clone(), self.intrinsics.take().unwrap(), &*stackmaps, module_info, &self.target_machine, + &mut self.llvm_callbacks, ); Ok((backend, Box::new(cache_gen))) } fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> { self.track_state = config.track_state; + if let Some(backend_compiler_config) = &config.backend_specific_config { + if let Some(llvm_config) = backend_compiler_config.get_specific::() { + self.llvm_callbacks = llvm_config.callbacks.clone(); + } + } Ok(()) } @@ -8233,6 +8750,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(()) } @@ -8255,3 +8782,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 4c34b6d584a..c983e41752a 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -3,9 +3,7 @@ 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, InstructionValue, IntValue, PointerValue, VectorValue, @@ -14,21 +12,19 @@ use inkwell::{ }; 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, @@ -38,124 +34,124 @@ fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { } } -pub struct Intrinsics { - pub ctlz_i32: FunctionValue, - pub ctlz_i64: FunctionValue, - - pub cttz_i32: FunctionValue, - pub cttz_i64: FunctionValue, - - pub ctpop_i32: FunctionValue, - pub ctpop_i64: FunctionValue, - - pub sqrt_f32: FunctionValue, - pub sqrt_f64: FunctionValue, - pub sqrt_f32x4: FunctionValue, - pub sqrt_f64x2: FunctionValue, - - pub ceil_f32: FunctionValue, - pub ceil_f64: FunctionValue, - - pub floor_f32: FunctionValue, - pub floor_f64: FunctionValue, - - pub trunc_f32: FunctionValue, - pub trunc_f64: FunctionValue, - - pub nearbyint_f32: FunctionValue, - pub nearbyint_f64: FunctionValue, - - pub fabs_f32: FunctionValue, - pub fabs_f64: FunctionValue, - pub fabs_f32x4: FunctionValue, - pub fabs_f64x2: FunctionValue, - - pub copysign_f32: FunctionValue, - pub copysign_f64: FunctionValue, - - pub sadd_sat_i8x16: FunctionValue, - pub sadd_sat_i16x8: FunctionValue, - pub uadd_sat_i8x16: FunctionValue, - pub uadd_sat_i16x8: FunctionValue, - - pub ssub_sat_i8x16: FunctionValue, - pub ssub_sat_i16x8: FunctionValue, - pub usub_sat_i8x16: FunctionValue, - pub usub_sat_i16x8: FunctionValue, - - pub expect_i1: FunctionValue, - pub trap: FunctionValue, - - 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 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 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 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 struct Intrinsics<'ctx> { + pub ctlz_i32: FunctionValue<'ctx>, + pub ctlz_i64: FunctionValue<'ctx>, + + pub cttz_i32: FunctionValue<'ctx>, + pub cttz_i64: FunctionValue<'ctx>, + + pub ctpop_i32: FunctionValue<'ctx>, + pub ctpop_i64: FunctionValue<'ctx>, + + pub sqrt_f32: FunctionValue<'ctx>, + pub sqrt_f64: FunctionValue<'ctx>, + pub sqrt_f32x4: FunctionValue<'ctx>, + pub sqrt_f64x2: FunctionValue<'ctx>, + + pub ceil_f32: FunctionValue<'ctx>, + pub ceil_f64: FunctionValue<'ctx>, + + pub floor_f32: FunctionValue<'ctx>, + pub floor_f64: FunctionValue<'ctx>, + + pub trunc_f32: FunctionValue<'ctx>, + pub trunc_f64: FunctionValue<'ctx>, + + pub nearbyint_f32: FunctionValue<'ctx>, + pub nearbyint_f64: FunctionValue<'ctx>, + + pub fabs_f32: FunctionValue<'ctx>, + pub fabs_f64: FunctionValue<'ctx>, + pub fabs_f32x4: FunctionValue<'ctx>, + pub fabs_f64x2: FunctionValue<'ctx>, + + pub copysign_f32: FunctionValue<'ctx>, + pub copysign_f64: FunctionValue<'ctx>, + + 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 ssub_sat_i8x16: FunctionValue<'ctx>, + pub ssub_sat_i16x8: FunctionValue<'ctx>, + pub usub_sat_i8x16: FunctionValue<'ctx>, + pub usub_sat_i16x8: FunctionValue<'ctx>, + + pub expect_i1: FunctionValue<'ctx>, + pub trap: FunctionValue<'ctx>, + + 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 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 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 anyfunc_ty: StructType<'ctx>, + + 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 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_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 throw_trap: FunctionValue, - pub throw_breakpoint: FunctionValue, - - pub experimental_stackmap: FunctionValue, - - pub ctx_ptr_ty: PointerType, + 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<'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<'ctx>, + pub throw_breakpoint: FunctionValue<'ctx>, + + pub experimental_stackmap: FunctionValue<'ctx>, + + 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(); @@ -210,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; @@ -558,66 +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(), @@ -631,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; } @@ -664,9 +661,9 @@ impl<'a> CtxType<'a> { pub fn memory( &mut self, index: MemoryIndex, - intrinsics: &Intrinsics, - module: Rc>, - ) -> MemoryCache { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + ) -> MemoryCache<'ctx> { let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, self.info, @@ -711,7 +708,7 @@ impl<'a> CtxType<'a> { .build_load(memory_array_ptr_ptr, "memory_array_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, field_name, memory_array_ptr.as_instruction_value().unwrap(), @@ -729,7 +726,7 @@ impl<'a> CtxType<'a> { .build_load(memory_ptr_ptr, "memory_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, "memory_ptr", memory_ptr.as_instruction_value().unwrap(), @@ -758,14 +755,14 @@ impl<'a> CtxType<'a> { .build_load(ptr_to_bounds, "bounds") .into_int_value(); tbaa_label( - module.clone(), + &module, intrinsics, "static_memory_base", base_ptr.as_instruction_value().unwrap(), Some(index as u32), ); tbaa_label( - module.clone(), + &module, intrinsics, "static_memory_bounds", bounds.as_instruction_value().unwrap(), @@ -785,9 +782,9 @@ impl<'a> CtxType<'a> { pub fn table_prepare( &mut self, index: TableIndex, - intrinsics: &Intrinsics, - module: Rc>, - ) -> (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, @@ -828,7 +825,7 @@ impl<'a> CtxType<'a> { .build_load(table_array_ptr_ptr, "table_array_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, field_name, table_array_ptr.as_instruction_value().unwrap(), @@ -842,7 +839,7 @@ impl<'a> CtxType<'a> { .build_load(table_ptr_ptr, "table_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, "table_ptr", table_array_ptr.as_instruction_value().unwrap(), @@ -868,10 +865,10 @@ impl<'a> CtxType<'a> { pub fn table( &mut self, index: TableIndex, - intrinsics: &Intrinsics, - module: Rc>, - builder: &Builder, - ) -> (PointerValue, IntValue) { + 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 @@ -879,14 +876,14 @@ impl<'a> CtxType<'a> { .into_pointer_value(); let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); tbaa_label( - module.clone(), + &module, intrinsics, "table_base_ptr", base_ptr.as_instruction_value().unwrap(), Some(index.index() as u32), ); tbaa_label( - module.clone(), + &module, intrinsics, "table_bounds", bounds.as_instruction_value().unwrap(), @@ -895,56 +892,11 @@ impl<'a> CtxType<'a> { (base_ptr, bounds) } - pub fn local_func( + pub fn dynamic_sigindex( &mut self, - index: LocalFuncIndex, - fn_ty: FunctionType, - intrinsics: &Intrinsics, - module: Rc>, - 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(); - tbaa_label( - module.clone(), - intrinsics, - "context_field_ptr_to_local_funcs", - local_func_array_ptr.as_instruction_value().unwrap(), - None, - ); - let local_func_ptr_ptr = unsafe { - builder.build_in_bounds_gep( - local_func_array_ptr, - &[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(); - tbaa_label( - module.clone(), - intrinsics, - "local_func_ptr", - local_func_ptr.as_instruction_value().unwrap(), - Some(index.index() as u32), - ); - builder.build_pointer_cast( - local_func_ptr, - fn_ty.ptr_type(AddressSpace::Generic), - "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, @@ -981,9 +933,9 @@ impl<'a> CtxType<'a> { pub fn global_cache( &mut self, index: GlobalIndex, - intrinsics: &Intrinsics, - module: Rc>, - ) -> GlobalCache { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + ) -> GlobalCache<'ctx> { let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, self.ctx_ptr_value, @@ -1034,10 +986,10 @@ impl<'a> CtxType<'a> { .build_load(globals_array_ptr_ptr, "global_array_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, field_name, - globals_array_ptr_ptr.as_instruction_value().unwrap(), + global_array_ptr.as_instruction_value().unwrap(), None, ); let const_index = intrinsics.i32_ty.const_int(index, false); @@ -1052,10 +1004,10 @@ impl<'a> CtxType<'a> { .build_load(global_ptr_ptr, "global_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, "global_ptr", - globals_array_ptr_ptr.as_instruction_value().unwrap(), + global_ptr.as_instruction_value().unwrap(), Some(index as u32), ); @@ -1069,7 +1021,7 @@ impl<'a> CtxType<'a> { } else { let value = cache_builder.build_load(global_ptr_typed, "global_value"); tbaa_label( - module.clone(), + &module, intrinsics, "global", value.as_instruction_value().unwrap(), @@ -1083,9 +1035,9 @@ impl<'a> CtxType<'a> { pub fn imported_func( &mut self, index: ImportedFuncIndex, - intrinsics: &Intrinsics, - module: Rc>, - ) -> (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, @@ -1104,7 +1056,7 @@ impl<'a> CtxType<'a> { .build_load(func_array_ptr_ptr, "func_array_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, "context_field_ptr_to_imported_funcs", func_array_ptr.as_instruction_value().unwrap(), @@ -1118,28 +1070,32 @@ 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.clone(), + &module, intrinsics, "imported_func_ptr", func_ptr.as_instruction_value().unwrap(), Some(index.index() as u32), ); tbaa_label( - module.clone(), + &module, intrinsics, "imported_func_ctx_ptr", ctx_ptr.as_instruction_value().unwrap(), @@ -1155,10 +1111,10 @@ impl<'a> CtxType<'a> { pub fn internal_field( &mut self, index: usize, - intrinsics: &Intrinsics, - module: Rc>, - builder: &Builder, - ) -> PointerValue { + intrinsics: &Intrinsics<'ctx>, + module: Rc>>, + builder: &Builder<'ctx>, + ) -> PointerValue<'ctx> { assert!(index < INTERNALS_SIZE); let local_internals_ptr_ptr = unsafe { @@ -1172,10 +1128,10 @@ impl<'a> CtxType<'a> { .build_load(local_internals_ptr_ptr, "local_internals_ptr") .into_pointer_value(); tbaa_label( - module.clone(), + &module, intrinsics, "context_field_ptr_to_internals", - local_internals_ptr_ptr.as_instruction_value().unwrap(), + local_internals_ptr.as_instruction_value().unwrap(), None, ); unsafe { @@ -1190,11 +1146,11 @@ impl<'a> CtxType<'a> { // Given an instruction that operates on memory, mark the access as not aliasing // other memory accesses which have a different (label, index) pair. -pub fn tbaa_label( - module: Rc>, - intrinsics: &Intrinsics, +pub fn tbaa_label<'ctx>( + module: &Rc>>, + intrinsics: &Intrinsics<'ctx>, label: &str, - instruction: InstructionValue, + instruction: InstructionValue<'ctx>, index: Option, ) { // To convey to LLVM that two pointers must be pointing to distinct memory, @@ -1213,6 +1169,12 @@ pub fn tbaa_label( 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") diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 8194f797a45..a2db088a76a 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -21,35 +21,27 @@ mod state; mod structs; mod trampolines; -use std::path::PathBuf; - pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator; 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, >; -#[derive(Debug, Clone)] -/// LLVM backend flags. -pub struct LLVMOptions { - /// Emit LLVM IR before optimization pipeline. - pub pre_opt_ir: Option, - - /// Emit LLVM IR after optimization pipeline. - pub post_opt_ir: Option, +pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>; +pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer; - /// Emit LLVM generated native code object file. - pub obj_file: Option, +pub trait LLVMCallbacks: std::any::Any + 'static { + fn preopt_ir_callback(&mut self, _module: &InkwellModule) {} + fn postopt_ir_callback(&mut self, _module: &InkwellModule) {} + fn obj_memory_buffer_callback(&mut self, _memory_buffer: &InkwellMemoryBuffer) {} } -pub static mut GLOBAL_OPTIONS: LLVMOptions = LLVMOptions { - pre_opt_ir: None, - post_opt_ir: None, - obj_file: None, -}; +pub struct LLVMBackendConfig { + pub callbacks: Option>>, +} diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs index a56c3c6a38d..4a9dbf81633 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 0d46dc5c099..e870b6627fe 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,36 +68,140 @@ impl ControlFrame { } } -#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] -pub enum ExtraInfo { - None, - - // This values is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm +#[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. - PendingF32NaN, + // + // 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 values is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm + // 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. - PendingF64NaN, + // + // 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; + } } -impl Default for ExtraInfo { - fn default() -> Self { - ExtraInfo::None + +// 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<(BasicValueEnum, ExtraInfo)>, - 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![], @@ -106,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, @@ -124,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", @@ -142,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", @@ -150,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, @@ -164,26 +269,28 @@ impl State { s } - pub fn push1(&mut self, value: T) { - self.push1_extra(value, ExtraInfo::None); + pub fn push1>(&mut self, value: T) { + self.push1_extra(value, Default::default()); } - pub fn push1_extra(&mut self, value: T, info: ExtraInfo) { + 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 { + pub fn pop1(&mut self) -> Result, BinaryReaderError> { Ok(self.pop1_extra()?.0) } - pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> { + 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)) @@ -191,28 +298,25 @@ impl State { pub fn pop2_extra( &mut self, - ) -> Result<((BasicValueEnum, ExtraInfo), (BasicValueEnum, ExtraInfo)), BinaryReaderError> { + ) -> Result< + ( + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + ), + BinaryReaderError, + > { let v2 = self.pop1_extra()?; let v1 = self.pop1_extra()?; Ok((v1, v2)) } - pub fn pop3( - &mut self, - ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { - let v3 = self.pop1()?; - let v2 = self.pop1()?; - let v1 = self.pop1()?; - Ok((v1, v2, v3)) - } - pub fn pop3_extra( &mut self, ) -> Result< ( - (BasicValueEnum, ExtraInfo), - (BasicValueEnum, ExtraInfo), - (BasicValueEnum, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), + (BasicValueEnum<'ctx>, ExtraInfo), ), BinaryReaderError, > { @@ -222,7 +326,7 @@ impl State { Ok((v1, v2, v3)) } - pub fn peek1_extra(&self) -> Result<(BasicValueEnum, ExtraInfo), BinaryReaderError> { + pub fn peek1_extra(&self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), BinaryReaderError> { self.stack .get(self.stack.len() - 1) .ok_or(BinaryReaderError { @@ -232,26 +336,26 @@ impl State { .map(|v| *v) } - pub fn peekn(&self, n: usize) -> Result, BinaryReaderError> { + pub fn peekn(&self, n: usize) -> Result>, BinaryReaderError> { Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect()) } pub fn peekn_extra( &self, n: usize, - ) -> Result<&[(BasicValueEnum, ExtraInfo)], BinaryReaderError> { - self.stack - .get(self.stack.len() - n..) - .ok_or(BinaryReaderError { - message: "invalid value stack", - offset: -1isize as 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, BinaryReaderError> { + ) -> Result, ExtraInfo)>, BinaryReaderError> { let v = self.peekn_extra(n)?.to_vec(); self.popn(n)?; Ok(v) @@ -270,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, @@ -278,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, @@ -292,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 c9bd50e1b47..c4894ef84ef 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 e27bb4a9cf0..1541a096cf6 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.9.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.9.0" } -wasmer-middleware-common = { path = "../middleware-common", version = "0.9.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true } +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 79796646ad9..7c425ce991e 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 0b6754c5f65..1515f5cd2a6 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 3d4def8703c..3cddcefcc90 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,11 +1,13 @@ [package] name = "wasmer-middleware-common" -version = "0.9.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.9.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 00000000000..f104ab086b4 --- /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 04a763abcdf..5cb77534c56 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 c7900c83a73..85885383f1d 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 8d5dbb61401..0a278b6ba2e 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.9.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,17 +19,17 @@ libc = "0.2.60" [dependencies.wasmer-runtime] default-features = false path = "../runtime" -version = "0.9.0" +version = "0.11.0" [dependencies.wasmer-runtime-core] default-features = false path = "../runtime-core" -version = "0.9.0" +version = "0.11.0" [dependencies.wasmer-wasi] default-features = false path = "../wasi" -version = "0.9.0" +version = "0.11.0" optional = true [features] diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md index 8ce84a130d4..39e2c21a9fa 100644 --- a/lib/runtime-c-api/README.md +++ b/lib/runtime-c-api/README.md @@ -105,10 +105,14 @@ 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 -$ cargo test -- --nocapture +$ cargo test --release -- --nocapture ``` To run tests manually, enter the `lib/runtime-c-api/tests` directory diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs index 1db19a19790..5897001b043 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/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 5b44eb2b557..2bca1e3902c 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -8,7 +8,7 @@ use crate::{ value::wasmer_value_tag, wasmer_byte_array, wasmer_result_t, }; -use libc::c_uint; +use libc::{c_uchar, c_uint}; use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc}; use wasmer_runtime::{Global, Memory, Module, Table}; use wasmer_runtime_core::{ diff --git a/lib/runtime-c-api/src/import/wasi.rs b/lib/runtime-c-api/src/import/wasi.rs index 3df3c7f700b..0ff833a61f5 100644 --- a/lib/runtime-c-api/src/import/wasi.rs +++ b/lib/runtime-c-api/src/import/wasi.rs @@ -1,6 +1,35 @@ use super::*; use crate::get_slice_checked; -use std::path::PathBuf; +use std::{path::PathBuf, ptr, str}; +use wasmer_wasi as wasi; + +#[derive(Debug, PartialEq)] +#[repr(u8)] +pub enum Version { + /// Version cannot be detected or is unknown. + Unknown = 0, + + /// Latest version. See `wasmer_wasi::WasiVersion::Latest` to + /// leran more. + Latest = 1, + + /// `wasi_unstable`. + Snapshot0 = 2, + + /// `wasi_snapshot_preview1`. + Snapshot1 = 3, +} + +impl From for Version { + fn from(value: c_uchar) -> Self { + match value { + 1 => Self::Latest, + 2 => Self::Snapshot0, + 3 => Self::Snapshot1, + _ => Self::Unknown, + } + } +} /// Opens a directory that's visible to the WASI module as `alias` but /// is backed by the host file at `host_file_path` @@ -14,9 +43,9 @@ pub struct wasmer_wasi_map_dir_entry_t { 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> { + pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), str::Utf8Error> { let alias = self.alias.as_str()?.to_owned(); - let host_path = std::path::PathBuf::from(self.host_file_path.as_str()?); + let host_path = PathBuf::from(self.host_file_path.as_str()?); Ok((alias, host_path)) } @@ -44,21 +73,77 @@ pub unsafe extern "C" fn wasmer_wasi_generate_import_object( let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize); wasmer_wasi_generate_import_object_inner( + Version::Latest, arg_list, env_list, preopened_file_list, mapped_dir_list, ) - .unwrap_or(std::ptr::null_mut()) + .unwrap_or(ptr::null_mut()) +} + +/// Creates a WASI import object for a specific version. +/// +/// This function is similar to `wasmer_wasi_generate_import_object` +/// except that the first argument describes the WASI version. +/// +/// The version is expected to be of kind `Version`. +#[no_mangle] +pub unsafe extern "C" fn wasmer_wasi_generate_import_object_for_version( + version: c_uchar, + 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( + version.into(), + arg_list, + env_list, + preopened_file_list, + mapped_dir_list, + ) + .unwrap_or(ptr::null_mut()) +} + +/// Find the version of WASI used by the module. +/// +/// In case of error, the returned version is `Version::Unknown`. +#[no_mangle] +pub unsafe extern "C" fn wasmer_wasi_get_version(module: *const wasmer_module_t) -> Version { + if module.is_null() { + return Version::Unknown; + } + + let module = &*(module as *const Module); + + match wasi::get_wasi_version(module, false) { + Some(version) => match version { + wasi::WasiVersion::Snapshot0 => Version::Snapshot0, + wasi::WasiVersion::Snapshot1 => Version::Snapshot1, + wasi::WasiVersion::Latest => Version::Latest, + }, + None => Version::Unknown, + } } /// Inner function that wraps error handling fn wasmer_wasi_generate_import_object_inner( + version: Version, 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> { +) -> Result<*mut wasmer_import_object_t, str::Utf8Error> { let arg_vec = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect(); let env_vec = env_list .iter() @@ -73,7 +158,15 @@ fn wasmer_wasi_generate_import_object_inner( .map(|entry| unsafe { entry.as_tuple() }) .collect::, _>>()?; - let import_object = Box::new(wasmer_wasi::generate_import_object( + let version = match version { + Version::Latest => wasi::WasiVersion::Latest, + Version::Snapshot0 => wasi::WasiVersion::Snapshot0, + Version::Snapshot1 => wasi::WasiVersion::Snapshot1, + _ => panic!("Version {:?} is invalid.", version), + }; + + let import_object = Box::new(wasi::generate_import_object_for_version( + version, arg_vec, env_vec, po_file_vec, @@ -90,12 +183,20 @@ fn wasmer_wasi_generate_import_object_inner( #[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![], - )); + let import_object = Box::new(wasi::generate_import_object(vec![], vec![], vec![], vec![])); Box::into_raw(import_object) as *mut wasmer_import_object_t } + +#[cfg(test)] +mod tests { + use super::Version; + + #[test] + fn test_versions_from_uint() { + assert_eq!(Version::Unknown, 0.into()); + assert_eq!(Version::Latest, 1.into()); + assert_eq!(Version::Snapshot0, 2.into()); + assert_eq!(Version::Snapshot1, 3.into()); + } +} diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index 17e96a4c343..f0464075467 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; diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index 923622aa509..ed7b8971bf0 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 b64fa610c7e..cbc7f3b1fcf 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -10,20 +10,20 @@ compile_commands.json CTestTestfile.cmake _deps rust-build +test-context test-exported-memory test-exports test-globals test-import-function +test-import-object test-imports test-instantiate test-memory test-module test-module-exports +test-module-import-instantiate test-module-imports test-module-serialize test-tables test-validate -test-context -test-module-import-instantiate -test-wasi-import-object - +test-wasi-import-object \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index d8f206c4939..20f7dea13bd 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -7,7 +7,6 @@ add_executable(test-globals test-globals.c) add_executable(test-import-function test-import-function.c) add_executable(test-imports test-imports.c) add_executable(test-import-object test-import-object.c) -add_executable(test-wasi-import-object test-wasi-import-object.c) add_executable(test-instantiate test-instantiate.c) add_executable(test-memory test-memory.c) add_executable(test-module test-module.c) @@ -19,9 +18,13 @@ 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/ + WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll + PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/ ) if(NOT WASMER_LIB) @@ -64,9 +67,12 @@ target_link_libraries(test-import-object general ${WASMER_LIB}) target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS}) add_test(test-import-object test-import-object) -target_link_libraries(test-wasi-import-object general ${WASMER_LIB}) -target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS}) -add_test(test-wasi-import-object test-wasi-import-object) + +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}) 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 0e32577860f..ed414636374 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-import-object b/lib/runtime-c-api/tests/test-import-object index 42e1496de50..bb0a97db3c9 100755 Binary files a/lib/runtime-c-api/tests/test-import-object and b/lib/runtime-c-api/tests/test-import-object differ diff --git a/lib/runtime-c-api/tests/test-wasi-import-object b/lib/runtime-c-api/tests/test-wasi-import-object deleted file mode 100755 index 912bc8c0d7c..00000000000 Binary files a/lib/runtime-c-api/tests/test-wasi-import-object and /dev/null differ diff --git a/lib/runtime-c-api/tests/test-wasi-import-object.c b/lib/runtime-c-api/tests/test-wasi-import-object.c index bd1478dc137..da94849963e 100644 --- a/lib/runtime-c-api/tests/test-wasi-import-object.c +++ b/lib/runtime-c-api/tests/test-wasi-import-object.c @@ -28,9 +28,9 @@ void print_wasmer_error() // 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]); - } + for (int i = 0; i < arr->bytes_len; ++i) { + putchar(arr->bytes[i]); + } } int main() @@ -164,20 +164,7 @@ int main() }; 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 + // Read the Wasm file bytes. FILE *file = fopen("assets/extended_wasi.wasm", "r"); assert(file); fseek(file, 0, SEEK_END); @@ -191,16 +178,39 @@ int main() // 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); + // Detect the WASI version if any. This step is not mandatory, we + // use it to test the WASI version API. + Version wasi_version = wasmer_wasi_get_version(module); + + printf("WASI version: %d\n", wasi_version); + + // Create the WASI import object + wasmer_import_object_t *import_object = + wasmer_wasi_generate_import_object_for_version(wasi_version, + 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); + // 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(); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index eb9dab50dbc..de921e3b04d 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 @@ -6,6 +25,27 @@ #include #include +enum Version { + /** + * Version cannot be detected or is unknown. + */ + Unknown = 0, + /** + * Latest version. See `wasmer_wasi::WasiVersion::Latest` to + * leran more. + */ + Latest = 1, + /** + * `wasi_unstable`. + */ + Snapshot0 = 2, + /** + * `wasi_snapshot_preview1`. + */ + Snapshot1 = 3, +}; +typedef uint8_t Version; + /** * List of export/import kinds. */ @@ -162,17 +202,23 @@ 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 @@ -780,6 +826,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. */ @@ -787,39 +834,52 @@ 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 @@ -851,4 +911,29 @@ wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_arr const wasmer_wasi_map_dir_entry_t *mapped_dirs, unsigned int mapped_dirs_len); +/** + * Creates a WASI import object for a specific version. + * + * This function is similar to `wasmer_wasi_generate_import_object` + * except that the first argument describes the WASI version. + * + * The version is expected to be of kind `Version`. + */ +wasmer_import_object_t *wasmer_wasi_generate_import_object_for_version(unsigned char version, + 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); + +/** + * Find the version of WASI used by the module. + * + * In case of error, the returned version is `Version::Unknown`. + */ +Version wasmer_wasi_get_version(const wasmer_module_t *module); + #endif /* WASMER_H */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 04a095c4834..09b58f86100 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 @@ -6,6 +25,18 @@ #include #include +enum class Version : uint8_t { + /// Version cannot be detected or is unknown. + Unknown = 0, + /// Latest version. See `wasmer_wasi::WasiVersion::Latest` to + /// leran more. + Latest = 1, + /// `wasi_unstable`. + Snapshot0 = 2, + /// `wasi_snapshot_preview1`. + Snapshot1 = 3, +}; + /// List of export/import kinds. enum class wasmer_import_export_kind : uint32_t { WASM_FUNCTION = 0, @@ -146,17 +177,23 @@ 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` @@ -612,32 +649,46 @@ 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); @@ -663,6 +714,27 @@ wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_arr const wasmer_wasi_map_dir_entry_t *mapped_dirs, unsigned int mapped_dirs_len); +/// Creates a WASI import object for a specific version. +/// +/// This function is similar to `wasmer_wasi_generate_import_object` +/// except that the first argument describes the WASI version. +/// +/// The version is expected to be of kind `Version`. +wasmer_import_object_t *wasmer_wasi_generate_import_object_for_version(unsigned char version, + 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); + +/// Find the version of WASI used by the module. +/// +/// In case of error, the returned version is `Version::Unknown`. +Version wasmer_wasi_get_version(const wasmer_module_t *module); + } // extern "C" #endif // WASMER_H diff --git a/lib/runtime-core-tests/Cargo.toml b/lib/runtime-core-tests/Cargo.toml index d2db29f2bd0..57e9e19f9cb 100644 --- a/lib/runtime-core-tests/Cargo.toml +++ b/lib/runtime-core-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core-tests" -version = "0.9.0" +version = "0.11.0" description = "Tests for the Wasmer runtime core crate" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,13 +9,13 @@ publish = false [dependencies] wabt = "0.9.1" -wasmer-runtime-core = { path = "../runtime-core", version = "0.9" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.9", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9", optional = true } +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"] \ No newline at end of file +backend-llvm = ["wasmer-llvm-backend"] diff --git a/lib/runtime-core-tests/tests/imports.rs b/lib/runtime-core-tests/tests/imports.rs index 69b9040cdc6..6b7223c6735 100644 --- a/lib/runtime-core-tests/tests/imports.rs +++ b/lib/runtime-core-tests/tests/imports.rs @@ -1,31 +1,131 @@ use wasmer_runtime_core::{ compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func, - types::MemoryDescriptor, units::Pages, vm, + types::MemoryDescriptor, units::Pages, vm, Instance, }; use wasmer_runtime_core_tests::{get_compiler, wat2wasm}; -#[test] -fn imported_functions_forms() { +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)) + 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"); @@ -33,85 +133,77 @@ fn imported_functions_forms() { let memory_descriptor = MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap(); let memory = Memory::new(memory_descriptor).unwrap(); - const SHIFT: i32 = 10; memory.view()[0].set(SHIFT); let import_object = imports! { "env" => { "memory" => memory.clone(), + + // 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(); - macro_rules! call_and_assert { - ($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 - ) - ), - } - }; - } - - call_and_assert!(function_fn, Ok(2)); - call_and_assert!(function_fn_with_vmctx, Ok(2 + SHIFT)); - call_and_assert!( - function_fn_trap, - Err(RuntimeError::Error { - data: Box::new(format!("foo {}", 1)) - }) - ); - call_and_assert!( - function_fn_trap_with_vmctx, - Err(RuntimeError::Error { - data: Box::new(format!("baz {}", 2 + SHIFT)) - }) - ); + test(&instance); } fn callback_fn(n: i32) -> Result { @@ -120,18 +212,83 @@ fn callback_fn(n: i32) -> Result { fn callback_fn_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result { let memory = vmctx.memory(0); - let shift: i32 = memory.view()[0].get(); + let shift_: i32 = memory.view()[0].get(); - Ok(shift + n + 1) + Ok(shift_ + n + 1) } fn callback_fn_trap(n: i32) -> Result { - Err(format!("foo {}", n)) + 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(); + let shift_: i32 = memory.view()[0].get(); + + Err(format!("baz {}", shift_ + n + 1)) +} - 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 7e8e80c5ff3..1e28133c969 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-runtime-core" -version = "0.9.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" @@ -58,3 +57,4 @@ trace = ["debug"] "backend-singlepass" = [] "backend-llvm" = [] managed = [] +deterministic-execution = ["wasmparser/deterministic"] diff --git a/lib/runtime-core/build.rs b/lib/runtime-core/build.rs index 81884f0e18b..3a549e45fd4 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/src/backend.rs b/lib/runtime-core/src/backend.rs index f1086de0d68..a1647817660 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::*; @@ -120,6 +200,19 @@ pub struct Features { pub threads: bool, } +/// Use this to point to a compiler config struct provided by the backend. +/// The backend struct must support runtime reflection with `Any`, which is any +/// struct that does not contain a non-`'static` reference. +#[derive(Debug)] +pub struct BackendCompilerConfig(pub Box); + +impl BackendCompilerConfig { + /// Obtain the backend-specific compiler config struct. + pub fn get_specific(&self) -> Option<&T> { + self.0.downcast_ref::() + } +} + /// Configuration data for the compiler #[derive(Debug, Default)] pub struct CompilerConfig { @@ -129,6 +222,13 @@ pub struct CompilerConfig { pub enforce_stack_check: bool, pub track_state: bool, pub features: Features, + + // Target info. Presently only supported by LLVM. + pub triple: Option, + pub cpu_name: Option, + pub cpu_features: Option, + + pub backend_specific_config: Option, } pub trait Compiler { @@ -171,7 +271,7 @@ pub trait RunnableModule: Send + Sync { /// signature and an invoke function that can call the trampoline. fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; - unsafe fn do_early_trap(&self, data: Box) -> !; + unsafe fn do_early_trap(&self, data: Box) -> !; /// Returns the machine code associated with this module. fn get_code(&self) -> Option<&[u8]> { diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index ff21e9195f1..1b50abaf6ab 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 89bbaf79665..e924cd9f5dd 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 3db1b374368..bf8c5868265 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>; + 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 { @@ -145,6 +186,9 @@ pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingPa enable_simd: features.simd, enable_bulk_memory: false, enable_multi_value: false, + + #[cfg(feature = "deterministic-execution")] + deterministic_only: true, }, } } @@ -182,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, @@ -215,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>, @@ -270,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 9aac82fabfb..391386e3b4f 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 7960d76e699..8729d979752 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 bc21052fae0..89d5b800ea7 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,28 +43,40 @@ 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 { jmpbuf: SetJmpBuffer, // in breakpoints: Option, - payload: Option>, // out + 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,10 +178,11 @@ 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, -) -> Result> { +) -> Result> { let unwind = UNWIND.with(|x| x.get()); let old = (*unwind).take(); *unwind = Some(UnwindInfo { @@ -164,7 +204,8 @@ pub unsafe fn catch_unsafe_unwind R>( } } -pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { +/// Begins an unsafe unwind. +pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { let unwind = UNWIND.with(|x| x.get()); let inner = (*unwind) .as_mut() @@ -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, @@ -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), + } + + fault.ip.set(ip + magic_size); + return true; + } + break; + } + } + false + }) + }); + if should_unwind { + begin_unsafe_unwind(unwind_result); + } + if early_return { + return; + } - let mut unwind_result: Box = Box::new(()); - - 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)); @@ -236,13 +348,14 @@ extern "C" fn signal_trap_handler( match Signal::from_c_int(signum) { Ok(SIGTRAP) => { // breakpoint - let out: Option>> = with_breakpoint_map(|bkpt_map| { - bkpt_map.and_then(|x| x.get(&(fault.ip as usize))).map(|x| { - x(BreakpointInfo { - fault: Some(&fault), + let out: Option>> = + with_breakpoint_map(|bkpt_map| { + bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| { + x(BreakpointInfo { + fault: Some(&fault), + }) }) - }) - }); + }); match out { Some(Ok(())) => { return false; @@ -267,17 +380,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 +395,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 +421,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 +450,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, + /// 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,9 +575,8 @@ 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; 32] = [None; 32]; known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[REG_R15 as usize] as _); @@ -397,32 +597,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])); - 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])); + 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 { @@ -431,7 +642,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 { @@ -489,8 +700,8 @@ 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; 32] = [None; 32]; @@ -532,7 +743,7 @@ pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> 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 b59d1599b8e..be9eaf6ae91 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 9d86205b156..284ff1725a5 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 85e9f8f768b..eb47c98e191 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 26a76f7dbf6..5b46677a864 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, @@ -145,6 +159,9 @@ pub fn validate_and_report_errors_with_features( enable_multi_value: false, enable_reference_types: false, enable_threads: features.threads, + + #[cfg(feature = "deterministic-execution")] + deterministic_only: true, }, }; let mut parser = wasmparser::ValidatingParser::new(wasm, Some(config)); @@ -158,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 f50d3a7a032..84d2bbbfb83 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!("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!("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,14 +133,17 @@ 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!("CodeMemory::new"); } + /// Makes this code memory executable. pub fn make_executable(&self) { unimplemented!("CodeMemory::make_executable"); } + /// Makes this code memory writable. pub fn make_writable(&self) { unimplemented!("CodeMemory::make_writable"); } @@ -136,6 +151,7 @@ impl CodeMemory { #[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 6dbb93ca09c..71e8c2e2f7a 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 332a37acb3d..885c47dbee6 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 60fb87c4dd3..75a02e2276a 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 abed45bfb67..5e31627c20f 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 957e53eafd7..c6355b2a2ee 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 4dbaa5bd52d..762e1a614b9 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 1bf914a463d..eb87b85d3b9 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 { + /// 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 e3e2c162e1d..ab6c79cbc0d 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 6025abe609f..7465f735e84 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, @@ -428,7 +652,7 @@ pub mod x64 { image: InstanceImage, vmctx: &mut Ctx, breakpoints: Option, - ) -> Result> { + ) -> Result> { let mut stack: Vec = vec![0; 1048576 * 8 / 8]; // 8MB stack let mut stack_offset: usize = stack.len(); @@ -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(); @@ -772,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, @@ -807,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; 32], mut initial_address: Option, + max_depth: Option, ) -> ExecutionStateImage { 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); @@ -829,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) @@ -1016,105 +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, - XMM8, - XMM9, - XMM10, - XMM11, - XMM12, - XMM13, - XMM14, - XMM15, - } - - #[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), - 25 => X64Register::XMM(XMM::XMM8), - 26 => X64Register::XMM(XMM::XMM9), - 27 => X64Register::XMM(XMM::XMM10), - 28 => X64Register::XMM(XMM::XMM11), - 29 => X64Register::XMM(XMM::XMM12), - 30 => X64Register::XMM(XMM::XMM13), - 31 => X64Register::XMM(XMM::XMM14), - 32 => X64Register::XMM(XMM::XMM15), - _ => return None, - }) - } - } } diff --git a/lib/runtime-core/src/structures/boxed.rs b/lib/runtime-core/src/structures/boxed.rs index 710dc70a6a4..439bad68075 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 2d4f3323edd..08773b50a4b 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 ca7a449dfdc..42af0d2c07a 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 8574b2d5bc0..5ac0d6190a5 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 fc60d732b6f..051dc1f0ebf 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 888aeff4dfb..411dee5b779 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 4a63c8f6249..38cb578f27a 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 8e5c0e03323..997ce5eb1e4 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 21a9ea2d377..7d2a27d874e 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 80de3e0d6bc..3d07484c715 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 2a38beb1e97..82b49b33cc3 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,3 +1,5 @@ +//! 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}, @@ -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,12 +62,15 @@ impl fmt::Display for WasmTrapInfo { /// of the `Func` struct. pub trait Kind {} +/// 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: Trampoline, vmctx: *mut vm::Ctx, @@ -65,7 +78,7 @@ pub type Invoke = unsafe extern "C" fn( args: *const u64, rets: *mut u64, trap_info: *mut WasmTrapInfo, - user_error: *mut Option>, + user_error: *mut Option>, extra: Option>, ) -> bool; @@ -80,6 +93,7 @@ pub struct Wasm { } impl Wasm { + /// Create new `Wasm` from given parts. pub unsafe fn from_raw_parts( trampoline: Trampoline, invoke: Invoke, @@ -102,8 +116,10 @@ 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. @@ -175,14 +191,18 @@ where 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, { - type Error: 'static; + /// The error type for this trait. + type Error: Send + 'static; + /// Get returns or error result. fn report(self) -> Result; } @@ -199,7 +219,7 @@ where impl TrapEarly for Result where Rets: WasmTypeList, - E: 'static, + E: Send + 'static, { type Error = E; fn report(self) -> Result { @@ -210,8 +230,9 @@ where /// Represents a function that can be used by WebAssembly. pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { inner: Inner, - f: NonNull, - ctx: *mut vm::Ctx, + func: NonNull, + func_env: Option>, + vmctx: *mut vm::Ctx, _phantom: PhantomData<(&'a (), Args, Rets)>, } @@ -225,19 +246,22 @@ where { pub(crate) unsafe fn from_raw_parts( inner: Wasm, - f: NonNull, - ctx: *mut vm::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 } } @@ -246,15 +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 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, } } @@ -315,88 +343,9 @@ impl WasmTypeList for Infallible { } } -impl WasmTypeList for (A,) -where - A: WasmExternType, -{ - 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 vm::Ctx, - ) -> Result - where - Rets: WasmTypeList, - { - 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 ),* > ( $( <$x as WasmExternType>::Native ),* ) where @@ -428,8 +377,8 @@ macro_rules! impl_traits { ( $( 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) ),* ) @@ -439,7 +388,7 @@ macro_rules! impl_traits { &[$( $x::Native::TYPE ),*] } - #[allow(non_snake_case)] + #[allow(unused_parens, non_snake_case)] unsafe fn call( self, f: NonNull, @@ -449,7 +398,6 @@ macro_rules! impl_traits { where Rets: WasmTypeList { - #[allow(unused_parens)] let ( $( $x ),* ) = self; let args = [ $( $x.to_native().to_binary()),* ]; let mut rets = Rets::empty_ret_array(); @@ -482,56 +430,113 @@ macro_rules! impl_traits { $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap, + 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, )* Rets, Trap, FN>( - vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* - ) -> Rets::CStruct - where - $( $x: WasmExternType, )* - Rets: WasmTypeList, - Trap: TrapEarly, - FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap, - { - let f: FN = unsafe { mem::transmute_copy(&()) }; - - let err = match panic::catch_unwind( - panic::AssertUnwindSafe( - || { - f(vmctx $( , WasmExternType::from_native($x) )* ).report() - } - ) - ) { - Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => { - let b: Box<_> = err.into(); - b as Box - }, - Err(err) => err, - }; - - unsafe { - (&*vmctx.module).runnable_module.do_early_trap(err) - } + 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; + + // 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(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 { - 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 + ) } } @@ -540,56 +545,110 @@ macro_rules! impl_traits { $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, - FN: Fn($( $x, )*) -> Trap, + FN: Fn($( $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, )* Rets, Trap, FN>( - vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* - ) -> Rets::CStruct - where - $( $x: WasmExternType, )* - Rets: WasmTypeList, - Trap: TrapEarly, - FN: Fn($( $x, )*) -> Trap, - { - let f: FN = unsafe { mem::transmute_copy(&()) }; - - let err = match panic::catch_unwind( - panic::AssertUnwindSafe( - || { - f($( WasmExternType::from_native($x), )* ).report() - } - ) - ) { - Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => { - let b: Box<_> = err.into(); - b as Box - }, - Err(err) => err, - }; - - unsafe { - (&*vmctx.module).runnable_module.do_early_trap(err) - } + 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) } - - 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 { - 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 + ) } } @@ -598,15 +657,16 @@ macro_rules! impl_traits { $( $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.func, self.inner, - self.ctx + self.vmctx ) } } @@ -644,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 { @@ -674,8 +737,14 @@ mod tests { vec![$($x),*].iter().sum() } - let _func = Func::new(with_vmctx); - let _func = Func::new(without_vmctx); + 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() + }); } } } @@ -692,6 +761,8 @@ mod tests { 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); diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index bea89ffe39d..1b19bbc3d91 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 e8232efc8aa..2e8f2c0a595 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 2bb734a04d8..352836ebff2 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. +/// 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); + +/// 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 Func(InnerFunc); +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) + } -/// An imported function, which contains the vmctx that owns this function. + /// 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), ); } } @@ -918,7 +1069,7 @@ mod vm_ctx_tests { fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option { unimplemented!("generate_module::get_trampoline") } - unsafe fn do_early_trap(&self, _: Box) -> ! { + unsafe fn do_early_trap(&self, _: Box) -> ! { unimplemented!("generate_module::do_early_trap") } } diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index d2bcb83a7b9..56430f051a4 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,25 +1,27 @@ [package] name = "wasmer-runtime" -version = "0.9.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.9.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.9.0" +version = "0.11.0" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.9.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"] @@ -41,6 +44,7 @@ singlepass = ["wasmer-singlepass-backend"] default-backend-singlepass = ["singlepass"] default-backend-llvm = ["llvm"] default-backend-cranelift = ["cranelift"] +deterministic-execution = ["wasmer-singlepass-backend/deterministic-execution", "wasmer-runtime-core/deterministic-execution"] [[bench]] name = "nginx" diff --git a/lib/runtime/benches/many_instances.rs b/lib/runtime/benches/many_instances.rs index e1c655c789d..c10cf34e96f 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 fe39b512aea..30e08c1a077 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::{ @@ -100,7 +104,7 @@ impl Cache for FileSystemCache { unsafe { wasmer_runtime_core::load_cache_with( serialized_cache, - super::compiler_for_backend(backend) + crate::compiler_for_backend(backend) .ok_or_else(|| CacheError::UnsupportedBackend(backend))? .as_ref(), ) @@ -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 aedd938ae69..58046148290 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, @@ -70,7 +71,7 @@ //! let value = add_one.call(42)?; //! //! assert_eq!(value, 43); -//! +//! //! Ok(()) //! } //! ``` @@ -86,11 +87,11 @@ //! [`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; @@ -104,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}; } @@ -117,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::*; } @@ -126,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 @@ -198,12 +208,14 @@ 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" ) ), all( + not(feature = "docs"), feature = "default-backend-cranelift", feature = "default-backend-singlepass" ) @@ -212,13 +224,13 @@ 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(feature = "default-backend-singlepass")] + #[cfg(all(feature = "default-backend-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() @@ -234,7 +246,7 @@ pub fn compiler_for_backend(backend: Backend) -> Option> { #[cfg(feature = "cranelift")] Backend::Cranelift => Some(Box::new(wasmer_clif_backend::CraneliftCompiler::new())), - #[cfg(feature = "singlepass")] + #[cfg(any(feature = "singlepass"))] Backend::Singlepass => Some(Box::new( wasmer_singlepass_backend::SinglePassCompiler::new(), )), @@ -242,11 +254,18 @@ 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") - ))] + Backend::Auto => { + #[cfg(feature = "default-backend-singlepass")] + return Some(Box::new( + wasmer_singlepass_backend::SinglePassCompiler::new(), + )); + #[cfg(feature = "default-backend-cranelift")] + return Some(Box::new(wasmer_clif_backend::CraneliftCompiler::new())); + #[cfg(feature = "default-backend-llvm")] + return Some(Box::new(wasmer_llvm_backend::LLVMCompiler::new())); + } + + #[cfg(not(all(feature = "llvm", feature = "singlepass", feature = "cranelift")))] _ => None, } } diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index 7abd4a1586a..7b9e424b501 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,19 +1,28 @@ [package] name = "wasmer-singlepass-backend" -version = "0.9.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.9.0" } -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 = ["wasmer-runtime-core/deterministic-execution"] diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index f4411a74ce4..57cffc7851a 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -1,8 +1,14 @@ #![allow(clippy::forget_copy)] // Used by dynasm. #![warn(unused_imports)] -use crate::{emitter_x64::*, machine::*, protect_unix}; -use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; +use crate::emitter_x64::*; +use crate::machine::*; +use crate::protect_unix; +#[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::{ any::Any, @@ -16,7 +22,10 @@ use std::{ }; 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::*, @@ -38,6 +47,14 @@ use wasmer_runtime_core::{ 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 = { @@ -126,6 +143,45 @@ lazy_static! { }; } +#[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 + }; +} + pub struct X64ModuleCodeGenerator { functions: Vec, signatures: Option>>, @@ -150,7 +206,7 @@ pub struct X64FunctionCode { breakpoints: Option< HashMap< AssemblyOffset, - Box Result<(), Box> + Send + Sync + 'static>, + Box Result<(), Box> + Send + Sync + 'static>, >, >, returns: SmallVec<[WpType; 1]>, @@ -175,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>, @@ -185,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, @@ -203,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, @@ -264,7 +359,7 @@ impl RunnableModule for X64ExecutionContext { args: *const u64, rets: *mut u64, trap_info: *mut WasmTrapInfo, - user_error: *mut Option>, + user_error: *mut Option>, num_params_plus_one: Option>, ) -> bool { let rm: &Box = &(&*(*ctx).module).runnable_module; @@ -273,15 +368,133 @@ impl RunnableModule for X64ExecutionContext { let args = slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1); - let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); + let ret = match protect_unix::call_protected( || { - 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()), ) { @@ -320,7 +533,7 @@ impl RunnableModule for X64ExecutionContext { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: Box) -> ! { protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data))); protect_unix::trigger_trap(); } @@ -359,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 } @@ -401,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(), @@ -502,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), )) } @@ -547,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))); @@ -554,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; @@ -583,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, + }) } } @@ -611,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, @@ -645,33 +944,50 @@ impl X64FunctionCode { mut src: Location, sz_dst: Size, dst: Location, - ) { - let tmp_src = m.acquire_temp_gpr().unwrap(); - let tmp_dst = m.acquire_temp_gpr().unwrap(); - - match src { + ) -> Result<(), CodegenError> { + let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst { Location::Imm32(_) | Location::Imm64(_) => { - a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); - src = Location::GPR(tmp_src); + return Err(CodegenError { + message: format!("emit_relaxed_zx_sx dst Imm: unreachable code"), + }) } - Location::Memory(_, _) | Location::GPR(_) => {} - _ => unreachable!(), - } - - 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); + Ok(()) } Location::GPR(_) => { op(a, sz_src, src, sz_dst, dst); + Ok(()) } - _ => unreachable!(), - } + _ => { + return Err(CodegenError { + message: format!("emit_relaxed_zx_sx dst: unreachable code"), + }) + } + }; + + 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); - m.release_temp_gpr(tmp_dst); - m.release_temp_gpr(tmp_src); + inner(m, a, src)?; + + m.release_temp_gpr(tmp_src); + } + Location::GPR(_) | Location::Memory(_, _) => inner(m, a, src)?, + _ => { + return Err(CodegenError { + message: format!("emit_relaxed_zx_sx src: unreachable code"), + }) + } + } + Ok(()) } /// Moves `src` and `dst` to valid locations for generic instructions. @@ -754,7 +1070,7 @@ impl X64FunctionCode { src1: Location, src2: Location, dst: Location, - ) { + ) -> Result<(), CodegenError> { Self::emit_relaxed_avx_base( a, m, @@ -762,7 +1078,8 @@ impl X64FunctionCode { src1, src2, dst, - ) + )?; + Ok(()) } /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. @@ -773,7 +1090,7 @@ impl X64FunctionCode { src1: Location, src2: Location, dst: Location, - ) { + ) -> Result<(), CodegenError> { let tmp1 = m.acquire_temp_xmm().unwrap(); let tmp2 = m.acquire_temp_xmm().unwrap(); let tmp3 = m.acquire_temp_xmm().unwrap(); @@ -795,7 +1112,11 @@ impl X64FunctionCode { a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp1)); tmp1 } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_relaxed_avx_base src1: unreachable code"), + }) + } }; let src2 = match src2 { @@ -815,7 +1136,11 @@ impl X64FunctionCode { a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp2)); XMMOrMemory::XMM(tmp2) } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_relaxed_avx_base src2: unreachable code"), + }) + } }; match dst { @@ -826,13 +1151,18 @@ impl X64FunctionCode { op(a, m, src1, src2, tmp3); a.emit_mov(Size::S64, Location::XMM(tmp3), dst); } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_relaxed_avx_base dst: unreachable code"), + }) + } } m.release_temp_gpr(tmpg); m.release_temp_xmm(tmp3); m.release_temp_xmm(tmp2); m.release_temp_xmm(tmp1); + Ok(()) } /// I32 binary operation with both operands popped from the virtual stack. @@ -928,7 +1258,7 @@ impl X64FunctionCode { value_stack: &mut Vec, c: Condition, loc_b: Location, - ) { + ) -> Result<(), CodegenError> { // Using Red Zone here. let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); @@ -951,9 +1281,14 @@ impl X64FunctionCode { a.emit_mov(Size::S32, Location::GPR(tmp), ret); m.release_temp_gpr(tmp); } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_cmpop_i32_dynamic_b ret: unreachable code"), + }) + } } value_stack.push(ret); + Ok(()) } /// I32 comparison with both operands popped from the virtual stack. @@ -962,9 +1297,10 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, c: Condition, - ) { + ) -> Result<(), CodegenError> { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); - Self::emit_cmpop_i32_dynamic_b(a, m, value_stack, c, loc_b); + Self::emit_cmpop_i32_dynamic_b(a, m, value_stack, c, loc_b)?; + Ok(()) } /// I64 comparison with `loc_b` from input. @@ -974,7 +1310,7 @@ impl X64FunctionCode { value_stack: &mut Vec, c: Condition, loc_b: Location, - ) { + ) -> Result<(), CodegenError> { // Using Red Zone here. let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); @@ -997,9 +1333,14 @@ impl X64FunctionCode { a.emit_mov(Size::S32, Location::GPR(tmp), ret); m.release_temp_gpr(tmp); } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_cmpop_i64_dynamic_b ret: unreachable code"), + }) + } } value_stack.push(ret); + Ok(()) } /// I64 comparison with both operands popped from the virtual stack. @@ -1008,9 +1349,10 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, c: Condition, - ) { + ) -> Result<(), CodegenError> { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); - Self::emit_cmpop_i64_dynamic_b(a, m, value_stack, c, loc_b); + Self::emit_cmpop_i64_dynamic_b(a, m, value_stack, c, loc_b)?; + Ok(()) } /// I32 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. @@ -1019,7 +1361,7 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, f: fn(&mut Assembler, Size, Location, Location), - ) { + ) -> Result<(), CodegenError> { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, @@ -1051,9 +1393,14 @@ impl X64FunctionCode { f(a, Size::S32, loc, ret); } } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_xcnt_i32 loc: unreachable code"), + }) + } } value_stack.push(ret); + Ok(()) } /// I64 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. @@ -1062,7 +1409,7 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, f: fn(&mut Assembler, Size, Location, Location), - ) { + ) -> Result<(), CodegenError> { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, @@ -1094,9 +1441,14 @@ impl X64FunctionCode { f(a, Size::S64, loc, ret); } } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_xcnt_i64 loc: unreachable code"), + }) + } } value_stack.push(ret); + Ok(()) } /// I32 shift with both operands popped from the virtual stack. @@ -1155,7 +1507,7 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), - ) { + ) -> Result<(), CodegenError> { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( @@ -1165,7 +1517,8 @@ impl X64FunctionCode { )[0]; value_stack.push(ret); - Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret)?; + Ok(()) } /// Floating point (AVX) comparison with both operands popped from the virtual stack. @@ -1174,7 +1527,7 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), - ) { + ) -> Result<(), CodegenError> { let loc_b = get_location_released(a, m, value_stack.pop().unwrap()); let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( @@ -1184,8 +1537,12 @@ impl X64FunctionCode { )[0]; 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? + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret)?; + + // 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); + Ok(()) } /// Floating point (AVX) binary operation with both operands popped from the virtual stack. @@ -1194,7 +1551,7 @@ impl X64FunctionCode { m: &mut Machine, value_stack: &mut Vec, f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), - ) { + ) -> Result<(), CodegenError> { let loc = get_location_released(a, m, value_stack.pop().unwrap()); let ret = m.acquire_locations( a, @@ -1203,7 +1560,8 @@ impl X64FunctionCode { )[0]; value_stack.push(ret); - Self::emit_relaxed_avx(a, m, f, loc, loc, ret); + Self::emit_relaxed_avx(a, m, f, loc, loc, ret)?; + Ok(()) } /// Emits a System V call sequence. @@ -1215,7 +1573,7 @@ impl X64FunctionCode { cb: F, params: I, state_context: Option<(&mut FunctionStateMap, &mut [ControlFrame])>, - ) { + ) -> Result<(), CodegenError> { // Values pushed in this function are above the shadow region. m.state.stack_values.push(MachineValue::ExplicitShadow); @@ -1226,7 +1584,11 @@ impl X64FunctionCode { for r in used_gprs.iter() { a.emit_push(Size::S64, Location::GPR(*r)); let content = m.state.register_values[X64Register::GPR(*r).to_index().0].clone(); - assert!(content != MachineValue::Undefined); + if content == MachineValue::Undefined { + return Err(CodegenError { + message: format!("emit_call_sysv: Undefined used_gprs content"), + }); + } m.state.stack_values.push(content); } @@ -1251,7 +1613,11 @@ impl X64FunctionCode { } for r in used_xmms.iter().rev() { let content = m.state.register_values[X64Register::XMM(*r).to_index().0].clone(); - assert!(content != MachineValue::Undefined); + if content == MachineValue::Undefined { + return Err(CodegenError { + message: format!("emit_call_sysv: Undefined used_xmms content"), + }); + } m.state.stack_values.push(content); } } @@ -1306,7 +1672,9 @@ impl X64FunctionCode { } Location::Memory(reg, offset) => { if reg != GPR::RBP { - unreachable!(); + return Err(CodegenError { + message: format!("emit_call_sysv loc param: unreachable code"), + }); } m.state .stack_values @@ -1352,7 +1720,11 @@ impl X64FunctionCode { _ => a.emit_push(Size::S64, *param), } } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("emit_call_sysv loc: unreachable code"), + }) + } } } @@ -1373,7 +1745,11 @@ impl X64FunctionCode { Machine::get_param_location(0), ); // vmctx - assert!(m.state.stack_values.len() % 2 == 1); // explicit shadow takes one slot + if (m.state.stack_values.len() % 2) != 1 { + return Err(CodegenError { + message: format!("emit_call_sysv: explicit shadow takes one slot"), + }); + } cb(a); @@ -1400,7 +1776,11 @@ impl X64FunctionCode { Location::Imm32(stack_offset as u32), Location::GPR(GPR::RSP), ); - assert!(stack_offset % 8 == 0); + if (stack_offset % 8) != 0 { + return Err(CodegenError { + message: format!("emit_call_sysv: Bad restoring stack alignement"), + }); + } for _ in 0..stack_offset / 8 { m.state.stack_values.pop().unwrap(); } @@ -1433,10 +1813,12 @@ impl X64FunctionCode { m.state.stack_values.pop().unwrap(); } - assert_eq!( - m.state.stack_values.pop().unwrap(), - MachineValue::ExplicitShadow - ); + if m.state.stack_values.pop().unwrap() != MachineValue::ExplicitShadow { + return Err(CodegenError { + message: format!("emit_call_sysv: Popped value is not ExplicitShadow"), + }); + } + Ok(()) } /// Emits a System V call sequence, specialized for labels as the call target. @@ -1446,12 +1828,13 @@ impl X64FunctionCode { label: DynamicLabel, params: I, state_context: Option<(&mut FunctionStateMap, &mut [ControlFrame])>, - ) { - Self::emit_call_sysv(a, m, |a| a.emit_call_label(label), params, state_context) + ) -> Result<(), CodegenError> { + Self::emit_call_sysv(a, m, |a| a.emit_call_label(label), params, state_context)?; + Ok(()) } /// Emits a memory operation. - fn emit_memory_op( + fn emit_memory_op Result<(), CodegenError>>( module_info: &ModuleInfo, config: &CodegenConfig, a: &mut Assembler, @@ -1461,7 +1844,7 @@ impl X64FunctionCode { check_alignment: bool, value_size: usize, cb: F, - ) { + ) -> Result<(), CodegenError> { // If the memory is dynamic, we need to do bound checking at runtime. let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) { LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index], @@ -1551,7 +1934,11 @@ impl X64FunctionCode { 1 => 2, 2 => 4, 3 => 8, - _ => unreachable!("this match is fully covered"), + _ => { + return Err(CodegenError { + message: format!("emit_memory_op align: unreachable value"), + }) + } }; if check_alignment && align != 1 { let tmp_aligncheck = m.acquire_temp_gpr().unwrap(); @@ -1569,9 +1956,10 @@ impl X64FunctionCode { m.release_temp_gpr(tmp_aligncheck); } - cb(a, m, tmp_addr); + cb(a, m, tmp_addr)?; m.release_temp_gpr(tmp_addr); + Ok(()) } /// Emits a memory operation. @@ -1588,8 +1976,12 @@ impl X64FunctionCode { memory_sz: Size, stack_sz: Size, cb: F, - ) { - assert!(memory_sz <= stack_sz); + ) -> Result<(), CodegenError> { + if memory_sz > stack_sz { + return Err(CodegenError { + message: format!("emit_compare_and_swap: memory size > stac size"), + }); + } let compare = m.reserve_unused_temp_gpr(GPR::RAX); let value = if loc == Location::GPR(GPR::R14) { @@ -1618,13 +2010,15 @@ impl X64FunctionCode { a.emit_mov(stack_sz, Location::GPR(compare), ret); cb(a, m, compare, value); a.emit_lock_cmpxchg(memory_sz, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_jmp(Condition::NotEqual, retry); a.emit_pop(Size::S64, Location::GPR(value)); m.release_temp_gpr(compare); + Ok(()) } // Checks for underflow/overflow/nan before IxxTrunc{U/S}F32. @@ -1845,8 +2239,11 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::GPR(GPR::RAX), ); - assert_eq!(self.machine.state.wasm_inst_offset, usize::MAX); - + if self.machine.state.wasm_inst_offset != usize::MAX { + return Err(CodegenError { + message: format!("begin_body: wasm_inst_offset not usize::MAX"), + }); + } Ok(()) } @@ -1909,16 +2306,27 @@ 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) => { let idx = idx as usize; - assert!(idx < INTERNALS_SIZE); + if idx >= INTERNALS_SIZE { + return Err(CodegenError { + message: format!("GetInternal: incorrect index value"), + }); + } let tmp = self.machine.acquire_temp_gpr().unwrap(); @@ -1953,7 +2361,11 @@ impl FunctionCodeGenerator for X64FunctionCode { } InternalEvent::SetInternal(idx) => { let idx = idx as usize; - assert!(idx < INTERNALS_SIZE); + if idx >= INTERNALS_SIZE { + return Err(CodegenError { + message: format!("SetInternal: incorrect index value"), + }); + } let tmp = self.machine.acquire_temp_gpr().unwrap(); @@ -2073,7 +2485,11 @@ impl FunctionCodeGenerator for X64FunctionCode { ); } else { global_index -= module_info.imported_globals.len(); - assert!(global_index < module_info.globals.len()); + if global_index >= module_info.globals.len() { + return Err(CodegenError { + message: format!("SetGlobal: incorrect global_index value"), + }); + } a.emit_mov( Size::S64, Location::Memory( @@ -2319,20 +2735,20 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Condition::Equal, - ), + )?, Operator::I32Ne => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::NotEqual, - ), + )?, Operator::I32Eqz => Self::emit_cmpop_i32_dynamic_b( a, &mut self.machine, &mut self.value_stack, Condition::Equal, Location::Imm32(0), - ), + )?, Operator::I32Clz => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); @@ -2343,7 +2759,11 @@ impl FunctionCodeGenerator for X64FunctionCode { tmp } Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I32Clz src: unreachable code"), + }) + } }; let ret = self.machine.acquire_locations( @@ -2356,20 +2776,28 @@ impl FunctionCodeGenerator for X64FunctionCode { let dst = match ret { Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I32Clz dst: unreachable code"), + }) + } }; - 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); + 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(_, _) => { @@ -2395,7 +2823,11 @@ impl FunctionCodeGenerator for X64FunctionCode { tmp } Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I32Ctz src: unreachable code"), + }) + } }; let ret = self.machine.acquire_locations( @@ -2408,19 +2840,27 @@ impl FunctionCodeGenerator for X64FunctionCode { let dst = match ret { Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I32Ctz dst: unreachable code"), + }) + } }; - 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); + 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(_, _) => { @@ -2441,7 +2881,7 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Assembler::emit_popcnt, - ), + )?, Operator::I32Shl => Self::emit_shift_i32( a, &mut self.machine, @@ -2477,46 +2917,46 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Condition::Below, - ), + )?, Operator::I32LeU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::BelowEqual, - ), + )?, Operator::I32GtU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Above, - ), + )?, Operator::I32GeU => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::AboveEqual, - ), + )?, Operator::I32LtS => { - Self::emit_cmpop_i32(a, &mut self.machine, &mut self.value_stack, Condition::Less) + Self::emit_cmpop_i32(a, &mut self.machine, &mut self.value_stack, Condition::Less)?; } Operator::I32LeS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::LessEqual, - ), + )?, Operator::I32GtS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::Greater, - ), + )?, Operator::I32GeS => Self::emit_cmpop_i32( a, &mut self.machine, &mut self.value_stack, Condition::GreaterEqual, - ), + )?, Operator::I64Const { value } => { let value = value as u64; self.value_stack.push(Location::Imm64(value)); @@ -2701,20 +3141,20 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Condition::Equal, - ), + )?, Operator::I64Ne => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::NotEqual, - ), + )?, Operator::I64Eqz => Self::emit_cmpop_i64_dynamic_b( a, &mut self.machine, &mut self.value_stack, Condition::Equal, Location::Imm64(0), - ), + )?, Operator::I64Clz => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); @@ -2725,7 +3165,11 @@ impl FunctionCodeGenerator for X64FunctionCode { tmp } Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64Clz src: unreachable code"), + }) + } }; let ret = self.machine.acquire_locations( @@ -2738,20 +3182,28 @@ impl FunctionCodeGenerator for X64FunctionCode { let dst = match ret { Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64Clz dst: unreachable code"), + }) + } }; - 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); + 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(_, _) => { @@ -2777,7 +3229,11 @@ impl FunctionCodeGenerator for X64FunctionCode { tmp } Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64Ctz src: unreachable code"), + }) + } }; let ret = self.machine.acquire_locations( @@ -2790,19 +3246,27 @@ impl FunctionCodeGenerator for X64FunctionCode { let dst = match ret { Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), Location::GPR(reg) => reg, - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64Ctz dst: unreachable code"), + }) + } }; - 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); + 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(_, _) => { @@ -2823,7 +3287,7 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Assembler::emit_popcnt, - ), + )?, Operator::I64Shl => Self::emit_shift_i64( a, &mut self.machine, @@ -2859,46 +3323,46 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Condition::Below, - ), + )?, Operator::I64LeU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::BelowEqual, - ), + )?, Operator::I64GtU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Above, - ), + )?, Operator::I64GeU => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::AboveEqual, - ), + )?, Operator::I64LtS => { - Self::emit_cmpop_i64(a, &mut self.machine, &mut self.value_stack, Condition::Less) + Self::emit_cmpop_i64(a, &mut self.machine, &mut self.value_stack, Condition::Less)?; } Operator::I64LeS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::LessEqual, - ), + )?, Operator::I64GtS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::Greater, - ), + )?, Operator::I64GeS => Self::emit_cmpop_i64( a, &mut self.machine, &mut self.value_stack, Condition::GreaterEqual, - ), + )?, Operator::I64ExtendUI32 => { let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); @@ -2934,7 +3398,107 @@ impl FunctionCodeGenerator for X64FunctionCode { loc, Size::S64, 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 = @@ -2967,296 +3531,343 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Assembler::emit_vaddss, - ), + )?, Operator::F32Sub => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsubss, - ), + )?, Operator::F32Mul => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmulss, - ), + )?, Operator::F32Div => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vdivss, - ), + )?, Operator::F32Max => { - let src2 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let src1 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let ret = self.machine.acquire_locations( - a, - &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], - false, - )[0]; - self.value_stack.push(ret); + 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 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 + 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 + } + _ => { + return Err(CodegenError { + message: format!("F32Max src1: unreachable code"), + }) + } + }; + 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 + } + _ => { + return Err(CodegenError { + message: format!("F32Max src2: unreachable code"), + }) + } + }; + + 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); + } + _ => { + return Err(CodegenError { + message: format!("F32Max ret: unreachable code"), + }) + } } - _ => unreachable!(), - }; - let tmp_xmm1 = XMM::XMM8; - let tmp_xmm2 = XMM::XMM9; - let tmp_xmm3 = XMM::XMM10; - - static CANONICAL_NAN: u128 = 0x7FC0_0000; - a.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1)); - a.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2)); - a.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1)); - a.emit_vmaxss(src1, XMMOrMemory::XMM(src2), tmp_xmm1); - let label1 = a.get_label(); - let label2 = a.get_label(); - a.emit_jmp(Condition::NotEqual, label1); - a.emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); - a.emit_jmp(Condition::None, label2); - a.emit_label(label1); - a.emit_vxorps(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2); - a.emit_label(label2); - a.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3); - a.emit_vblendvps(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1); - a.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1); - // load float canonical nan - a.emit_mov( - Size::S64, - Location::Imm64((&CANONICAL_NAN as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2)); - a.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); - match ret { - Location::XMM(x) => { - a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); - } - Location::Memory(_, _) | Location::GPR(_) => { - a.emit_mov(Size::S64, Location::XMM(src1), ret); - } - _ => unreachable!(), + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); } - - self.machine.release_temp_gpr(tmpg2); - self.machine.release_temp_gpr(tmpg1); - self.machine.release_temp_xmm(tmp2); - self.machine.release_temp_xmm(tmp1); } Operator::F32Min => { - let src2 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let src1 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let ret = self.machine.acquire_locations( - a, - &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], - false, - )[0]; - self.value_stack.push(ret); + 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 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 + 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 + } + _ => { + return Err(CodegenError { + message: format!("F32Min src1: unreachable code"), + }) + } + }; + 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 + } + _ => { + return Err(CodegenError { + message: format!("F32Min src2: unreachable code"), + }) + } + }; + + 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); + } + _ => { + return Err(CodegenError { + message: format!("F32Min ret: unreachable code"), + }) + } } - _ => unreachable!(), - }; - let tmp_xmm1 = XMM::XMM8; - let tmp_xmm2 = XMM::XMM9; - let tmp_xmm3 = XMM::XMM10; - - static NEG_ZERO: u128 = 0x8000_0000; - static CANONICAL_NAN: u128 = 0x7FC0_0000; - a.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1)); - a.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2)); - a.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1)); - a.emit_vminss(src1, XMMOrMemory::XMM(src2), tmp_xmm1); - let label1 = a.get_label(); - let label2 = a.get_label(); - a.emit_jmp(Condition::NotEqual, label1); - a.emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); - a.emit_jmp(Condition::None, label2); - a.emit_label(label1); - // load float -0.0 - a.emit_mov( - Size::S64, - Location::Imm64((&NEG_ZERO as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov( - Size::S64, - Location::Memory(tmpg1, 0), - Location::XMM(tmp_xmm2), - ); - a.emit_label(label2); - a.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3); - a.emit_vblendvps(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1); - a.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1); - // load float canonical nan - a.emit_mov( - Size::S64, - Location::Imm64((&CANONICAL_NAN as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2)); - a.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); - match ret { - Location::XMM(x) => { - a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); - } - Location::Memory(_, _) | Location::GPR(_) => { - a.emit_mov(Size::S64, Location::XMM(src1), ret); - } - _ => unreachable!(), + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); } - - 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, &mut self.value_stack, Assembler::emit_vcmpeqss, - ), + )?, Operator::F32Ne => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpneqss, - ), + )?, Operator::F32Lt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpltss, - ), + )?, Operator::F32Le => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpless, - ), + )?, Operator::F32Gt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgtss, - ), + )?, Operator::F32Ge => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgess, - ), + )?, Operator::F32Nearest => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_nearest, - ), + )?, Operator::F32Floor => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_floor, - ), + )?, Operator::F32Ceil => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_ceil, - ), + )?, Operator::F32Trunc => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundss_trunc, - ), + )?, Operator::F32Sqrt => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsqrtss, - ), + )?, Operator::F32Copysign => { let loc_b = @@ -3319,11 +3930,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 } => { @@ -3338,296 +3972,343 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Assembler::emit_vaddsd, - ), + )?, Operator::F64Sub => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsubsd, - ), + )?, Operator::F64Mul => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vmulsd, - ), + )?, Operator::F64Div => Self::emit_fp_binop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vdivsd, - ), + )?, Operator::F64Max => { - let src2 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let src1 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let ret = self.machine.acquire_locations( - a, - &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], - false, - )[0]; - self.value_stack.push(ret); + 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 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 + 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 + } + _ => { + return Err(CodegenError { + message: format!("F64Max src1: unreachable code"), + }) + } + }; + 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 + } + _ => { + return Err(CodegenError { + message: format!("F64Max src2: unreachable code"), + }) + } + }; + + 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); + } + _ => { + return Err(CodegenError { + message: format!("F64Max ret: unreachable code"), + }) + } } - _ => unreachable!(), - }; - let tmp_xmm1 = XMM::XMM8; - let tmp_xmm2 = XMM::XMM9; - let tmp_xmm3 = XMM::XMM10; - - static CANONICAL_NAN: u128 = 0x7FF8_0000_0000_0000; - a.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1)); - a.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2)); - a.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1)); - a.emit_vmaxsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1); - let label1 = a.get_label(); - let label2 = a.get_label(); - a.emit_jmp(Condition::NotEqual, label1); - a.emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); - a.emit_jmp(Condition::None, label2); - a.emit_label(label1); - a.emit_vxorpd(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2); - a.emit_label(label2); - a.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3); - a.emit_vblendvpd(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1); - a.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1); - // load float canonical nan - a.emit_mov( - Size::S64, - Location::Imm64((&CANONICAL_NAN as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2)); - a.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); - match ret { - Location::XMM(x) => { - a.emit_vmovapd(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); - } - Location::Memory(_, _) | Location::GPR(_) => { - a.emit_mov(Size::S64, Location::XMM(src1), ret); - } - _ => unreachable!(), + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); } - - self.machine.release_temp_gpr(tmpg2); - self.machine.release_temp_gpr(tmpg1); - self.machine.release_temp_xmm(tmp2); - self.machine.release_temp_xmm(tmp1); } Operator::F64Min => { - let src2 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let src1 = - get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let ret = self.machine.acquire_locations( - a, - &[(WpType::F64, MachineValue::WasmStack(self.value_stack.len()))], - false, - )[0]; - self.value_stack.push(ret); + 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 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 + 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 + } + _ => { + return Err(CodegenError { + message: format!("F64Min src1: unreachable code"), + }) + } + }; + 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 + } + _ => { + return Err(CodegenError { + message: format!("F64Min src2: unreachable code"), + }) + } + }; + + 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); + } + _ => { + return Err(CodegenError { + message: format!("F64Min ret: unreachable code"), + }) + } } - _ => unreachable!(), - }; - let tmp_xmm1 = XMM::XMM8; - let tmp_xmm2 = XMM::XMM9; - let tmp_xmm3 = XMM::XMM10; - - static NEG_ZERO: u128 = 0x8000_0000_0000_0000; - static CANONICAL_NAN: u128 = 0x7FF8_0000_0000_0000; - a.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1)); - a.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2)); - a.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1)); - a.emit_vminsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1); - let label1 = a.get_label(); - let label2 = a.get_label(); - a.emit_jmp(Condition::NotEqual, label1); - a.emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); - a.emit_jmp(Condition::None, label2); - a.emit_label(label1); - // load float -0.0 - a.emit_mov( - Size::S64, - Location::Imm64((&NEG_ZERO as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov( - Size::S64, - Location::Memory(tmpg1, 0), - Location::XMM(tmp_xmm2), - ); - a.emit_label(label2); - a.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3); - a.emit_vblendvpd(tmp_xmm3, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm1, tmp_xmm1); - a.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1); - // load float canonical nan - a.emit_mov( - Size::S64, - Location::Imm64((&CANONICAL_NAN as *const u128) as u64), - Location::GPR(tmpg1), - ); - a.emit_mov(Size::S64, Location::Memory(tmpg1, 0), Location::XMM(src2)); - a.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); - match ret { - Location::XMM(x) => { - a.emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); - } - Location::Memory(_, _) | Location::GPR(_) => { - a.emit_mov(Size::S64, Location::XMM(src1), ret); - } - _ => unreachable!(), + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); } - - 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, &mut self.value_stack, Assembler::emit_vcmpeqsd, - ), + )?, Operator::F64Ne => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpneqsd, - ), + )?, Operator::F64Lt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpltsd, - ), + )?, Operator::F64Le => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmplesd, - ), + )?, Operator::F64Gt => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgtsd, - ), + )?, Operator::F64Ge => Self::emit_fp_cmpop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcmpgesd, - ), + )?, Operator::F64Nearest => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_nearest, - ), + )?, Operator::F64Floor => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_floor, - ), + )?, Operator::F64Ceil => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_ceil, - ), + )?, Operator::F64Trunc => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vroundsd_trunc, - ), + )?, Operator::F64Sqrt => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vsqrtsd, - ), + )?, Operator::F64Copysign => { let loc_b = @@ -3705,11 +4386,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( @@ -3717,13 +4420,13 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self.machine, &mut self.value_stack, Assembler::emit_vcvtss2sd, - ), + )?, Operator::F32DemoteF64 => Self::emit_fp_unop_avx( a, &mut self.machine, &mut self.value_stack, Assembler::emit_vcvtsd2ss, - ), + )?, Operator::I32ReinterpretF32 => { let loc = @@ -3820,24 +4523,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 => { @@ -3849,30 +4576,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, - ); - - 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); + 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(); + + 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, + ); + + 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 => { @@ -3884,47 +4636,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( @@ -3933,54 +4695,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 + + 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, + ); - 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 + 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 - 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); + 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 => { @@ -3992,24 +4779,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(); + + 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); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + 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); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I32TruncSF64 => { @@ -4021,35 +4833,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 - } - }; - Self::emit_f64_int_conv_check( - a, - &mut self.machine, - real_in, - -2147483649.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::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(); + + 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 + } + }; + + Self::emit_f64_int_conv_check( + a, + &mut self.machine, + real_in, + -2147483649.0, + 2147483648.0, + ); - a.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); - a.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + 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); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncSF64 => { @@ -4061,30 +4898,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(); + + 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, + ); - a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); - a.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + 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); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } } Operator::I64TruncUF64 => { @@ -4096,54 +4958,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); - - 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); + 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 + + 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 => { @@ -4155,15 +5042,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 = @@ -4174,15 +5086,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 = @@ -4193,15 +5129,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 = @@ -4212,31 +5172,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(); - - 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); - - self.machine.release_temp_gpr(tmp); - self.machine.release_temp_gpr(tmp_in); - self.machine.release_temp_xmm(tmp_out); + 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(); + + 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); + } } Operator::F64ConvertSI32 => { @@ -4248,15 +5232,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(); + + 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); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertUI32 => { let loc = @@ -4267,15 +5276,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(); + + 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); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertSI64 => { let loc = @@ -4286,15 +5320,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(); + + 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); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } } Operator::F64ConvertUI64 => { let loc = @@ -4305,31 +5364,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(); - - 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); + 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(); + + 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_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 } => { @@ -4365,7 +5449,7 @@ impl FunctionCodeGenerator for X64FunctionCode { label, params.iter().map(|x| *x), Some((&mut self.fsm, &mut self.control_stack)), - ); + )?; self.machine.release_locations_only_stack(a, ¶ms); @@ -4383,7 +5467,11 @@ impl FunctionCodeGenerator for X64FunctionCode { } } Operator::CallIndirect { index, table_index } => { - assert_eq!(table_index, 0); + if table_index != 0 { + return Err(CodegenError { + message: format!("CallIndirect: table_index is not 0"), + }); + } let sig = self.signatures.get(SigIndex::new(index as usize)).unwrap(); let param_types: SmallVec<[WpType; 8]> = sig.params().iter().cloned().map(type_to_wp_type).collect(); @@ -4476,14 +5564,21 @@ 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)), - ); + )?; self.machine.release_locations_only_stack(a, ¶ms); @@ -4516,7 +5611,7 @@ impl FunctionCodeGenerator for X64FunctionCode { WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { - message: format!("multi-value returns not yet implemented"), + message: format!("If: multi-value returns not yet implemented"), }) } }, @@ -4564,7 +5659,11 @@ impl FunctionCodeGenerator for X64FunctionCode { a.emit_label(label); frame.if_else = IfElseState::Else; } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("Else: frame.if_else unreachable code"), + }) + } } } Operator::Select => { @@ -4627,7 +5726,7 @@ impl FunctionCodeGenerator for X64FunctionCode { WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { - message: format!("multi-value returns not yet implemented"), + message: format!("Block: multi-value returns not yet implemented"), }) } }, @@ -4656,7 +5755,7 @@ impl FunctionCodeGenerator for X64FunctionCode { WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty], _ => { return Err(CodegenError { - message: format!("multi-value returns not yet implemented"), + message: format!("Loop: multi-value returns not yet implemented"), }) } }, @@ -4713,11 +5812,17 @@ 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); }, iter::once(Location::Imm32(memory_index.index() as u32)), None, - ); + )?; let ret = self.machine.acquire_locations( a, &[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))], @@ -4752,12 +5857,18 @@ 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); }, iter::once(Location::Imm32(memory_index.index() as u32)) .chain(iter::once(param_pages)), None, - ); + )?; self.machine.release_locations_only_stack(a, &[param_pages]); @@ -4797,8 +5908,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::F32Load { ref memarg } => { let target = @@ -4828,8 +5940,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I32Load8U { ref memarg } => { let target = @@ -4859,9 +5972,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32Load8S { ref memarg } => { let target = @@ -4891,9 +6005,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32Load16U { ref memarg } => { let target = @@ -4923,9 +6038,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32Load16S { ref memarg } => { let target = @@ -4955,9 +6071,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32Store { ref memarg } => { let target_value = @@ -4983,8 +6100,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::F32Store { ref memarg } => { let target_value = @@ -5010,8 +6128,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I32Store8 { ref memarg } => { let target_value = @@ -5037,8 +6156,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I32Store16 { ref memarg } => { let target_value = @@ -5064,8 +6184,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64Load { ref memarg } => { let target = @@ -5095,8 +6216,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::F64Load { ref memarg } => { let target = @@ -5126,8 +6248,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I64Load8U { ref memarg } => { let target = @@ -5157,9 +6280,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64Load8S { ref memarg } => { let target = @@ -5189,9 +6313,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64Load16U { ref memarg } => { let target = @@ -5221,9 +6346,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64Load16S { ref memarg } => { let target = @@ -5253,9 +6379,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64Load32U { ref memarg } => { let target = @@ -5286,7 +6413,11 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(base, offset + 4), ); // clear upper bits } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64Load32U ret: unreachable code"), + }) + } } Self::emit_relaxed_binop( a, @@ -5296,8 +6427,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I64Load32S { ref memarg } => { let target = @@ -5327,9 +6459,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64Store { ref memarg } => { let target_value = @@ -5355,8 +6488,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::F64Store { ref memarg } => { let target_value = @@ -5382,8 +6516,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64Store8 { ref memarg } => { let target_value = @@ -5409,8 +6544,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64Store16 { ref memarg } => { let target_value = @@ -5436,8 +6572,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64Store32 { ref memarg } => { let target_value = @@ -5463,8 +6600,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::Unreachable => { Self::mark_trappable(a, &self.machine, &mut self.fsm, &mut self.control_stack); @@ -5474,7 +6612,11 @@ impl FunctionCodeGenerator for X64FunctionCode { Operator::Return => { let frame = &self.control_stack[0]; if frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!("Return: incorrect frame.returns"), + }); + } let loc = *self.value_stack.last().unwrap(); Self::emit_relaxed_binop( a, @@ -5494,7 +6636,11 @@ impl FunctionCodeGenerator for X64FunctionCode { let frame = &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; if !frame.loop_like && frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!("Br: incorrect frame.returns"), + }); + } let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } @@ -5520,7 +6666,11 @@ impl FunctionCodeGenerator for X64FunctionCode { let frame = &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; if !frame.loop_like && frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!("BrIf: incorrect frame.returns"), + }); + } let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } @@ -5531,7 +6681,9 @@ impl FunctionCodeGenerator for X64FunctionCode { a.emit_label(after); } Operator::BrTable { ref table } => { - let (targets, default_target) = table.read_table().unwrap(); + let (targets, default_target) = table.read_table().map_err(|e| CodegenError { + message: format!("BrTable read_table: {:?}", e), + })?; let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); let table_label = a.get_label(); @@ -5549,7 +6701,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)); @@ -5560,7 +6714,14 @@ impl FunctionCodeGenerator for X64FunctionCode { let frame = &self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; if !frame.loop_like && frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!( + "BrTable: incorrect frame.returns for {:?}", + target + ), + }); + } let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } @@ -5574,7 +6735,11 @@ impl FunctionCodeGenerator for X64FunctionCode { let frame = &self.control_stack [self.control_stack.len() - 1 - (default_target as usize)]; if !frame.loop_like && frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!("BrTable: incorrect frame.returns"), + }); + } let loc = *self.value_stack.last().unwrap(); a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); } @@ -5627,7 +6792,11 @@ impl FunctionCodeGenerator for X64FunctionCode { } if frame.returns.len() > 0 { - assert_eq!(frame.returns.len(), 1); + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!("End: incorrect frame.returns"), + }); + } let loc = self.machine.acquire_locations( a, &[( @@ -5678,8 +6847,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I32AtomicLoad8U { ref memarg } => { let target = @@ -5709,9 +6879,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32AtomicLoad16U { ref memarg } => { let target = @@ -5741,9 +6912,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S32, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I32AtomicStore { ref memarg } => { let target_value = @@ -5769,8 +6941,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I32AtomicStore8 { ref memarg } => { let target_value = @@ -5796,8 +6969,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I32AtomicStore16 { ref memarg } => { let target_value = @@ -5823,8 +6997,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64AtomicLoad { ref memarg } => { let target = @@ -5854,8 +7029,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I64AtomicLoad8U { ref memarg } => { let target = @@ -5885,9 +7061,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64AtomicLoad16U { ref memarg } => { let target = @@ -5917,9 +7094,10 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), Size::S64, ret, - ); + )?; + Ok(()) }, - ); + )?; } Operator::I64AtomicLoad32U { ref memarg } => { let target = @@ -5950,7 +7128,11 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(base, offset + 4), ); // clear upper bits } - _ => unreachable!(), + _ => { + return Err(CodegenError { + message: format!("I64AtomicLoad32U ret: unreachable code"), + }) + } } Self::emit_relaxed_binop( a, @@ -5960,8 +7142,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ret, ); + Ok(()) }, - ); + )?; } Operator::I64AtomicStore { ref memarg } => { let target_value = @@ -5987,8 +7170,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64AtomicStore8 { ref memarg } => { let target_value = @@ -6014,8 +7198,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64AtomicStore16 { ref memarg } => { let target_value = @@ -6041,8 +7226,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I64AtomicStore32 { ref memarg } => { let target_value = @@ -6068,8 +7254,9 @@ impl FunctionCodeGenerator for X64FunctionCode { target_value, Location::Memory(addr, 0), ); + Ok(()) }, - ); + )?; } Operator::I32AtomicRmwAdd { ref memarg } => { let loc = @@ -6095,9 +7282,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 4, |a, _m, addr| { - a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6125,9 +7317,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 8, |a, _m, addr| { - a.emit_lock_xadd(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6155,9 +7352,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6185,9 +7383,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6215,9 +7418,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6245,9 +7449,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6275,9 +7484,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 4, |a, _m, addr| { - a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6306,9 +7520,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 4, |a, _m, addr| { - a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6337,9 +7556,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 8, |a, _m, addr| { - a.emit_lock_xadd(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6368,9 +7592,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6399,9 +7624,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6430,9 +7660,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6461,9 +7692,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_lock_xadd(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6492,9 +7728,14 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_lock_xadd(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -6525,7 +7766,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmwAnd { ref memarg } => { let loc = @@ -6554,7 +7795,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw8UAnd { ref memarg } => { let loc = @@ -6583,7 +7824,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw16UAnd { ref memarg } => { let loc = @@ -6612,7 +7853,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw8UAnd { ref memarg } => { let loc = @@ -6641,7 +7882,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw16UAnd { ref memarg } => { let loc = @@ -6670,7 +7911,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw32UAnd { ref memarg } => { let loc = @@ -6699,7 +7940,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmwOr { ref memarg } => { let loc = @@ -6728,7 +7969,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmwOr { ref memarg } => { let loc = @@ -6757,7 +7998,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw8UOr { ref memarg } => { let loc = @@ -6786,7 +8027,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw16UOr { ref memarg } => { let loc = @@ -6815,7 +8056,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw8UOr { ref memarg } => { let loc = @@ -6844,7 +8085,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw16UOr { ref memarg } => { let loc = @@ -6873,7 +8114,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw32UOr { ref memarg } => { let loc = @@ -6902,7 +8143,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmwXor { ref memarg } => { let loc = @@ -6931,7 +8172,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmwXor { ref memarg } => { let loc = @@ -6960,7 +8201,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw8UXor { ref memarg } => { let loc = @@ -6989,7 +8230,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmw16UXor { ref memarg } => { let loc = @@ -7018,7 +8259,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw8UXor { ref memarg } => { let loc = @@ -7047,7 +8288,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw16UXor { ref memarg } => { let loc = @@ -7076,7 +8317,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I64AtomicRmw32UXor { ref memarg } => { let loc = @@ -7105,7 +8346,7 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, _m, src, dst| { a.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); }, - ); + )?; } Operator::I32AtomicRmwXchg { ref memarg } => { let loc = @@ -7131,9 +8372,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 4, |a, _m, addr| { - a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7161,9 +8403,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 8, |a, _m, addr| { - a.emit_xchg(Size::S64, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S64, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7191,9 +8434,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7221,9 +8465,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S32, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7251,9 +8496,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 1, |a, _m, addr| { - a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S8, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7281,9 +8527,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 2, |a, _m, addr| { - a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S16, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7311,9 +8558,10 @@ impl FunctionCodeGenerator for X64FunctionCode { true, 4, |a, _m, addr| { - a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)) + a.emit_xchg(Size::S32, Location::GPR(value), Location::Memory(addr, 0)); + Ok(()) }, - ); + )?; a.emit_mov(Size::S64, Location::GPR(value), ret); self.machine.release_temp_gpr(value); } @@ -7361,8 +8609,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_mov(Size::S32, Location::GPR(compare), ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7410,8 +8659,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_mov(Size::S64, Location::GPR(compare), ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7459,8 +8709,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_movzx(Size::S8, Location::GPR(compare), Size::S32, ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7508,8 +8759,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_movzx(Size::S16, Location::GPR(compare), Size::S32, ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7557,8 +8809,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_movzx(Size::S8, Location::GPR(compare), Size::S64, ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7606,8 +8859,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_movzx(Size::S16, Location::GPR(compare), Size::S64, ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } @@ -7655,8 +8909,9 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(addr, 0), ); a.emit_mov(Size::S32, Location::GPR(compare), ret); + Ok(()) }, - ); + )?; a.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } diff --git a/lib/singlepass-backend/src/emitter_x64.rs b/lib/singlepass-backend/src/emitter_x64.rs index 251868231f1..0bf6fc2b079 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 { @@ -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); @@ -186,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 { @@ -528,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); } @@ -709,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), } } @@ -742,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), } } @@ -1009,30 +1104,18 @@ impl Emitter for Assembler { 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)) - } + (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)) - } + (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), }; } @@ -1104,77 +1187,57 @@ impl Emitter for Assembler { 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)) - } + 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)) - } + 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]), } } @@ -1203,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 ccea7502ee9..5f3607b4576 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/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs index 136f9bf081a..e145d367e18 100644 --- a/lib/singlepass-backend/src/protect_unix.rs +++ b/lib/singlepass-backend/src/protect_unix.rs @@ -16,7 +16,7 @@ use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensur use wasmer_runtime_core::typed_func::WasmTrapInfo; thread_local! { - pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } pub unsafe fn trigger_trap() -> ! { @@ -25,7 +25,7 @@ pub unsafe fn trigger_trap() -> ! { pub enum CallProtError { Trap(WasmTrapInfo), - Error(Box), + Error(Box), } pub fn call_protected( @@ -48,6 +48,6 @@ pub fn call_protected( } } -pub unsafe fn throw(payload: Box) -> ! { +pub unsafe fn throw(payload: Box) -> ! { begin_unsafe_unwind(payload); } diff --git a/lib/singlepass-backend/src/translator_aarch64.rs b/lib/singlepass-backend/src/translator_aarch64.rs new file mode 100644 index 00000000000..0c2b7a6ecbf --- /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 88df563293d..81bd57669e8 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.9.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.9.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.9.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.9.0", optional = true } +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 e9309c3c295..6ef87047913 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 8ce199695fc..eff7793b9b6 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 c0ae54a53b2..8022a1a6063 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 c7776693908..a2742b0dcc2 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 @@ -304,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 [] @@ -396,73 +400,73 @@ 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: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 diff --git a/lib/spectests/tests/semantics.rs b/lib/spectests/tests/semantics.rs index 0bffa9dcaa6..eadbeef0e78 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 19114ff6783..3b03b598f4b 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) => { @@ -452,6 +526,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -469,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[..]) }); @@ -483,6 +558,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -497,6 +573,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(values) => { @@ -520,6 +597,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -537,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[..]) }); @@ -551,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 { @@ -568,6 +647,7 @@ mod tests { }, &test_key, excludes, + line, ); } CallError::Runtime(r) => { @@ -589,6 +669,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -605,6 +686,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -622,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) => { @@ -648,6 +726,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -661,6 +740,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -676,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 { @@ -703,6 +779,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -716,6 +793,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -729,12 +807,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 result = panic::catch_unwind(AssertUnwindSafe(|| { module .instantiate(&spectest_import_object) @@ -754,6 +828,7 @@ mod tests { }, &test_key, excludes, + line, ); } }; @@ -770,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[..]) }, @@ -785,6 +860,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -806,6 +882,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -825,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 { @@ -844,6 +917,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(result) => match result { @@ -859,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(); } _ => { @@ -875,6 +950,7 @@ mod tests { }, &test_key, excludes, + line, ); } }, @@ -908,6 +984,7 @@ mod tests { }, &test_key, excludes, + line, ); } } @@ -919,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[..]) }); @@ -933,6 +1010,7 @@ mod tests { }, &test_key, excludes, + line, ); } else { let call_result = maybe_call_result.unwrap(); @@ -947,6 +1025,7 @@ mod tests { }, &test_key, excludes, + line, ); } Ok(_values) => { @@ -965,29 +1044,29 @@ 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_core::types::Value) -> String { + fn value_to_hex(val: wasmer_runtime::types::Value) -> String { match val { - wasmer_runtime_core::types::Value::I32(x) => format!("{:#x}", x), - wasmer_runtime_core::types::Value::I64(x) => format!("{:#x}", x), - wasmer_runtime_core::types::Value::F32(x) => format!("{:#x}", x.to_bits()), - wasmer_runtime_core::types::Value::F64(x) => format!("{:#x}", x.to_bits()), - wasmer_runtime_core::types::Value::V128(x) => format!("{:#x}", x), + 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), } } @@ -1000,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), } } @@ -1020,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), } } @@ -1070,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, @@ -1104,7 +1183,7 @@ mod tests { } #[derive(Debug, Copy, Clone, PartialEq, Eq)] - enum Exclude { + enum ExcludeKind { Skip, Fail, } @@ -1114,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("#") { @@ -1135,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 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) + 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()), }; - 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] @@ -1162,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"); @@ -1172,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 96626f6cff1..9b84e5ea91e 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi-tests" -version = "0.9.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.9.0" } -wasmer-runtime = { path = "../runtime", version = "0.9.0" } -wasmer-wasi = { path = "../wasi", version = "0.9.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.9.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.9.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.9.0" } -wasmer-dev-utils = { path = "../dev-utils", version = "0.9.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 0144da92056..5ccda63c9fe 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 4508e432e02..6bf3626dd71 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, true).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 958fdf6ad26..04b830a91dc 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, true).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 00000000000..0ac3ea6ebf8 --- /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/mod.rs b/lib/wasi-tests/tests/wasitests/mod.rs index 75a91d6f003..d226dc0ee0d 100644 --- a/lib/wasi-tests/tests/wasitests/mod.rs +++ b/lib/wasi-tests/tests/wasitests/mod.rs @@ -9,6 +9,7 @@ mod close_preopen_fd; mod create_dir; mod envvar; mod fd_allocate; +mod fd_append; mod fd_close; mod fd_pread; mod fd_read; diff --git a/lib/wasi-tests/wasitests/close_preopen_fd.wasm b/lib/wasi-tests/wasitests/close_preopen_fd.wasm index 56d37f64630..5badfb755e5 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 f4a9898f0c7..0c2fb0bbf08 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 1e394c49f97..b05151905a2 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 ee688bf93ad..e604f6e407d 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 00000000000..e9d92ef26a6 --- /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 00000000000..2e311652e4d --- /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 00000000000..af40f8af9c1 Binary files /dev/null and b/lib/wasi-tests/wasitests/fd_append.wasm differ diff --git a/lib/wasi-tests/wasitests/fd_close.wasm b/lib/wasi-tests/wasitests/fd_close.wasm index 5ace5206203..162e968755a 100755 Binary files a/lib/wasi-tests/wasitests/fd_close.wasm 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 11031b32ef0..7b48e4959fc 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 227157f989d..799ca164514 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 497b63c9bc8..5fde9b62744 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 938fdc1f256..68c59917238 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 3f088985069..8a8c6349451 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 ce68b75f05a..d3a7c8f8494 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 1175a7f7236..a2625868b71 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 849a3a6ae2f..5675534ef0b 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 306a911e651..eae511069d0 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 f76120ab85a..7e613b96011 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 61bb0a0224b..c30b1637864 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 9510f5b83ee..009a57e8061 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 8cf55c4aca2..21fb813a378 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 26480cf84aa..d8ef0c74fee 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 7f6d3125374..0abf7729494 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 ce7e0180a7d..0b8b96abcdf 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 e73b57ea474..f118c1b1cf7 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "wasmer-wasi" -version = "0.9.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] @@ -17,7 +19,7 @@ getrandom = "0.1" time = "0.1" typetag = "0.1" serde = { version = "1", features = ["derive"] } -wasmer-runtime-core = { path = "../runtime-core", version = "0.9.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 978517c7c44..712bfa4c737 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/syscalls/legacy/mod.rs b/lib/wasi/src/syscalls/legacy/mod.rs new file mode 100644 index 00000000000..62cc08f9ef4 --- /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 00000000000..67566a50c7d --- /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 5854690352d..af6ccab2648 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}, @@ -1724,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; @@ -1798,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) diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs index d0082ef69fb..5f5559ce497 100644 --- a/lib/wasi/src/syscalls/types.rs +++ b/lib/wasi/src/syscalls/types.rs @@ -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 f96e35958df..8a72a215a43 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -1,18 +1,81 @@ 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; + get_wasi_version(module, false).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, +} + +/// Namespace for the `Snapshot0` version. +const SNAPSHOT0_NAMESPACE: &'static str = "wasi_unstable"; + +/// Namespace for the `Snapshot1` version. +const SNAPSHOT1_NAMESPACE: &'static str = "wasi_snapshot_preview1"; + +/// Detect the version of WASI being used based on the import +/// namespaces. +/// +/// A strict detection expects that all imports live in a single WASI +/// namespace. A non-strict detection expects that at least one WASI +/// namespace exits to detect the version. Note that the strict +/// detection is faster than the non-strict one. +pub fn get_wasi_version(module: &Module, strict: bool) -> Option { + let module_info = &module.info(); + let mut imports = module_info.imported_functions.iter(); + + if strict { + let mut imports = imports.map(|(_, import_name)| import_name.namespace_index); + + // Returns `None` if empty. + let first = imports.next()?; + + // If there is only one namespace… + if imports.all(|index| index == first) { + // … and that this namespace is a WASI one. + match module_info.namespace_table.get(first) { + SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0), + SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1), + _ => None, + } + } else { + None } + } else { + let namespace_table = &module_info.namespace_table; + + // Check that at least a WASI namespace exists, and use the + // first one in the list to detect the WASI version. + imports.find_map(|(_, import_name)| { + let namespace_index = import_name.namespace_index; + + match namespace_table.get(namespace_index) { + SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0), + SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1), + _ => None, + } + }) } - true } diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index b3281c51161..17f89a0a68d 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.9.0" +version = "0.11.0" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.9.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" diff --git a/scripts/binary-name.sh b/scripts/binary-name.sh index 4ac8ef8e36b..0245690b726 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 d93bdefb4be..af1485a3f06 100755 --- a/scripts/update_version_numbers.sh +++ b/scripts/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.8.0' -NEXT_VERSION='0.9.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 62e22b731a7..79949417b10 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 58cead255d4..bb0377fc00c 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -9,24 +9,27 @@ )] extern crate structopt; +use std::collections::HashMap; 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; 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_llvm_backend::{ + InkwellMemoryBuffer, InkwellModule, LLVMBackendConfig, LLVMCallbacks, LLVMCompiler, +}; 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}; @@ -35,30 +38,22 @@ use wasmer_runtime_core::{ backend::{Backend, Compiler, CompilerConfig, Features, MemoryBoundCheckMode}, debug, loader::{Instance as LoadedInstance, LocalLoader}, + Module, }; -#[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}; +#[cfg(feature = "backend-llvm")] +use std::{cell::RefCell, io::Write, rc::Rc}; +#[cfg(feature = "backend-llvm")] +use wasmer_runtime_core::backend::BackendCompilerConfig; - 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!() - } -} +#[cfg(not(any( + feature = "backend-cranelift", + feature = "backend-llvm", + feature = "backend-singlepass" +)))] +compile_error!("Please enable one or more of the compiler backends"); #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.", author)] @@ -81,7 +76,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 +91,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 +131,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 +141,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, @@ -180,6 +213,14 @@ struct Run { #[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 /// help commands and error messages of the running wasm program @@ -204,6 +245,14 @@ struct Run { args: Vec, } +impl Run { + /// Used with the `invoke` argument + fn parse_args(&self, module: &Module, fn_name: &str) -> Result, String> { + utils::parse_args(module, fn_name, &self.args) + .map_err(|e| format!("Invoke failed: {:?}", e)) + } +} + #[allow(dead_code)] #[derive(Debug, Copy, Clone)] enum LoaderName { @@ -308,6 +357,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 +373,184 @@ 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) + .map_err(|_| format!("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 + }; + + if let Some(invoke_fn) = options.invoke.as_ref() { + eprintln!("WARNING: Invoking aribtrary functions with WASI is not officially supported in the WASI standard yet. Use this feature at your own risk!"); + let args = options.parse_args(&module, invoke_fn)?; + let invoke_result = instance + .dyn_func(invoke_fn) + .map_err(|e| format!("Invoke failed: {:?}", e))? + .call(&args) + .map_err(|e| format!("Calling invoke fn failed: {:?}", e))?; + println!("{}({:?}) returned {:?}", invoke_fn, args, invoke_result); + return Ok(()); + } else { + result = start.call(); + } + + #[cfg(unix)] + { + if cv_pushed { + pop_code_version().unwrap(); + } + } + + 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(()) +} + +#[cfg(feature = "backend-llvm")] +impl LLVMCallbacks for LLVMCLIOptions { + fn preopt_ir_callback(&mut self, module: &InkwellModule) { + if let Some(filename) = &self.pre_opt_ir { + module.print_to_file(filename).unwrap(); + } + } + + fn postopt_ir_callback(&mut self, module: &InkwellModule) { + if let Some(filename) = &self.post_opt_ir { + module.print_to_file(filename).unwrap(); + } + } + + fn obj_memory_buffer_callback(&mut self, memory_buffer: &InkwellMemoryBuffer) { + if let Some(filename) = &self.obj_file { + let mem_buf_slice = memory_buffer.as_slice(); + let mut file = File::create(filename).unwrap(); + let mut pos = 0; + while pos < mem_buf_slice.len() { + pos += file.write(&mem_buf_slice[pos..]).unwrap(); + } + } + } +} + /// 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,33 +612,27 @@ 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) { - Some(x) => x, - None => return Err("the requested backend is not enabled".into()), - }; + let compiler: Box = get_compiler_by_backend(options.backend, options) + .ok_or_else(|| { + format!( + "the requested backend, \"{}\", is not enabled", + options.backend.to_string() + ) + })?; + #[allow(unused_mut)] + let mut backend_specific_config = None; #[cfg(feature = "backend-llvm")] { if options.backend == Backend::LLVM { - let options = options.backend_llvm_options.clone(); - unsafe { - wasmer_llvm_backend::GLOBAL_OPTIONS = LLVMOptions { - pre_opt_ir: options.pre_opt_ir, - post_opt_ir: options.post_opt_ir, - obj_file: options.obj_file, - } - } + backend_specific_config = Some(BackendCompilerConfig(Box::new(LLVMBackendConfig { + callbacks: Some(Rc::new(RefCell::new(options.backend_llvm_options.clone()))), + }))) } } @@ -439,10 +656,9 @@ 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(), + backend_specific_config, + ..Default::default() }, &*compiler, ) @@ -453,10 +669,8 @@ 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(), + backend_specific_config, ..Default::default() }, &*compiler, @@ -472,7 +686,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { let mut cache = unsafe { FileSystemCache::new(wasmer_cache_dir).map_err(|e| format!("Cache error: {:?}", e))? }; - let mut load_cache_key = || -> Result<_, String> { + let load_cache_key = || -> Result<_, String> { if let Some(ref prehashed_cache_key) = options.cache_key { if let Ok(module) = WasmHash::decode(prehashed_cache_key).and_then(|prehashed_key| { @@ -501,10 +715,8 @@ 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(), + backend_specific_config, ..Default::default() }, &*compiler, @@ -583,112 +795,41 @@ 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, true); + #[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) - .map_err(|_| format!("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 .instantiate(&import_object) .map_err(|e| format!("Can't instantiate module: {:?}", e))?; - let mut args: Vec = Vec::new(); - for arg in options.args.iter() { - let x = arg.as_str().parse().map_err(|_| { - format!( - "Can't parse the provided argument {:?} as a integer", - arg.as_str() - ) - })?; - args.push(Value::I32(x)); - } + let invoke_fn = match options.invoke.as_ref() { + Some(fun) => fun, + _ => "main", + }; + let args = options.parse_args(&module, invoke_fn)?; - instance - .dyn_func("main") + let result = instance + .dyn_func(&invoke_fn) .map_err(|e| format!("{:?}", e))? .call(&args) .map_err(|e| format!("{:?}", e))?; + println!("{}({:?}) returned {:?}", invoke_fn, args, result); } } @@ -768,8 +909,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); @@ -799,10 +962,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))?; @@ -820,24 +980,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 cce60f98468..0fd5cf639fe 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=0.9.0 +AppVersion=0.11.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 diff --git a/src/utils.rs b/src/utils.rs index fd96dc15bb0..1feec86b28a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,121 @@ //! Utility functions for the WebAssembly module +use wasmer_runtime::{types::Type, Module, Value}; +use wasmer_runtime_core::{backend::SigRegistry, module::ExportIndex}; + /// Detect if a provided binary is a Wasm file pub fn is_wasm_binary(binary: &[u8]) -> bool { binary.starts_with(&[b'\0', b'a', b's', b'm']) } + +#[derive(Debug, Clone)] +pub enum InvokeError { + CouldNotFindFunction, + ExportNotFunction, + WrongNumArgs { expected: u16, found: u16 }, + CouldNotParseArg(String), +} + +/// Parses arguments for the `--invoke` flag on the run command +pub fn parse_args( + module: &Module, + fn_name: &str, + args: &[String], +) -> Result, InvokeError> { + let export_index = module + .info() + .exports + .get(fn_name) + .ok_or(InvokeError::CouldNotFindFunction)?; + + let signature = if let ExportIndex::Func(func_index) = export_index { + let sig_index = module + .info() + .func_assoc + .get(*func_index) + .expect("broken invariant, incorrect func index"); + SigRegistry.lookup_signature_ref(&module.info().signatures[*sig_index]) + } else { + return Err(InvokeError::ExportNotFunction); + }; + + let parameter_types = signature.params(); + let mut arg_error = None; + + if args.len() != parameter_types.len() { + return Err(InvokeError::WrongNumArgs { + expected: parameter_types.len() as _, + found: args.len() as _, + }); + } else { + args.iter() + .enumerate() + .try_fold( + Vec::with_capacity(args.len()), + |mut accumulator, (nth, argument)| { + if let Some(value) = match parameter_types[nth] { + Type::I32 => argument + .parse::() + .map(|v| Some(Value::I32(v))) + .unwrap_or_else(|_| { + arg_error = Some(InvokeError::CouldNotParseArg(format!( + "Failed to parse `{:?}` as an `i32`", + argument + ))); + None + }), + Type::I64 => argument + .parse::() + .map(|v| Some(Value::I64(v))) + .unwrap_or_else(|_| { + arg_error = Some(InvokeError::CouldNotParseArg(format!( + "Failed to parse `{:?}` as an `i64`", + argument + ))); + None + }), + Type::V128 => argument + .parse::() + .map(|v| Some(Value::V128(v))) + .unwrap_or_else(|_| { + arg_error = Some(InvokeError::CouldNotParseArg(format!( + "Failed to parse `{:?}` as an `i128`", + argument + ))); + None + }), + Type::F32 => argument + .parse::() + .map(|v| Some(Value::F32(v))) + .unwrap_or_else(|_| { + arg_error = Some(InvokeError::CouldNotParseArg(format!( + "Failed to parse `{:?}` as an `f32`", + argument + ))); + None + }), + Type::F64 => argument + .parse::() + .map(|v| Some(Value::F64(v))) + .unwrap_or_else(|_| { + arg_error = Some(InvokeError::CouldNotParseArg(format!( + "Failed to parse `{:?}` as an `f64`", + argument + ))); + None + }), + } { + accumulator.push(value); + + Some(accumulator) + } else { + None + } + }, + ) + .map_or_else( + || Err(arg_error.unwrap()), + |arguments: Vec| Ok(arguments), + ) + } +} diff --git a/wapm-cli b/wapm-cli index b157153568f..3562d6dda52 160000 --- a/wapm-cli +++ b/wapm-cli @@ -1 +1 @@ -Subproject commit b157153568fc45f0d01ba8443c54fc3c2ce0cb23 +Subproject commit 3562d6dda52df526e6e1917dd33bb2454917ab9c