diff --git a/.circleci/config.yml b/.circleci/config.yml index fa356bec2..e2905a425 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,14 @@ run_install_dependencies: &run_install_dependencies version: 2 jobs: + changelog: + docker: + - image: docker:stable-git + steps: + - checkout + - run: + command: ! git diff --exit-code CHANGELOG.md + # Job used for testing lint: docker: @@ -21,7 +29,8 @@ jobs: - checkout - restore_cache: keys: - - v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-lint-{{ arch }} - <<: *run_install_dependencies - run: name: Install lint deps @@ -40,7 +49,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} test: docker: @@ -50,14 +59,17 @@ jobs: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-test-cargo-cache-linux-stable-{{ arch }} - <<: *run_install_dependencies - run: name: Tests command: make test - run: name: Emscripten Tests - command: make test-emscripten + command: | + make test-emscripten-clif + make test-emscripten-llvm - run: name: Integration Tests command: make integration-tests @@ -67,7 +79,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} test-macos: macos: @@ -76,7 +88,8 @@ jobs: - checkout - restore_cache: keys: - - v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-stable-{{ arch }} - run: name: Install crate dependencies command: | @@ -90,7 +103,7 @@ jobs: - run: name: Install Rust command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + curl -sSf https://sh.rustup.rs | sh -s -- -y export PATH="$HOME/.cargo/bin:$PATH" cargo --version - run: @@ -112,7 +125,8 @@ jobs: # We increase the ulimit for fixing cargo unclosed files in mac ulimit -n 8000 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 - make test-emscripten + make test-emscripten-clif + make test-emscripten-llvm - run: name: Integration Tests command: | @@ -129,22 +143,30 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }} test-and-build: docker: - image: circleci/rust:latest steps: - checkout + - run: + name: "Pull dependencies" + command: | + git submodule init + git submodule update --remote - restore_cache: keys: - - v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-linux-nightly-{{ arch }} - run: name: Install dependencies command: | sudo apt-get install -y cmake curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + # Use rust nightly (for singlepass, for now) + - run: rustup default nightly-2019-04-11 - run: name: Tests command: | @@ -154,18 +176,21 @@ jobs: name: Emscripten Tests command: | export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" - make test-emscripten + make test-emscripten-clif + make test-emscripten-llvm - run: name: Release Build command: | export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" - make release + make production-release + cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry mkdir -p artifacts VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # GIT_VERSION=$(git describe --exact-match --tags) echo "${VERSION}" >> artifacts/version echo "${CIRCLE_TAG}" >> artifacts/git_version - cp ./target/release/wasmer ./artifacts/$(./binary-name.sh) + make build-install + cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) - run: name: Debug flag checked command: | @@ -183,16 +208,25 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + - wapm-cli/target/release/.fingerprint + - wapm-cli/target/release/build + - wapm-cli/target/release/deps + key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} test-and-build-macos: macos: xcode: "9.0" steps: - checkout + - run: + name: "Pull dependencies" + command: | + git submodule init + git submodule update --remote - restore_cache: keys: - - v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-darwin-nightly-{{ arch }} - run: name: Install crate dependencies command: | @@ -206,9 +240,15 @@ jobs: - run: name: Install Rust command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly export PATH="$HOME/.cargo/bin:$PATH" cargo --version + # Use rust nightly (for singlepass, for now) + # - run: + # name: Install Rust nightly + # command: | + # export PATH="$HOME/.rustup/bin:$PATH" + # rustup default nightly-2019-04-11 - run: name: Tests command: | @@ -228,18 +268,21 @@ jobs: # We increase the ulimit for fixing cargo unclosed files in mac ulimit -n 8000 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 - make test-emscripten + make test-emscripten-clif + make test-emscripten-singlepass - run: name: Release Build command: | export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH" export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" - make release + make production-release + cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry mkdir -p artifacts + make build-install + cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) # VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # echo "${VERSION}" >> artifacts/version - cp ./target/release/wasmer ./artifacts/$(./binary-name.sh) - persist_to_workspace: root: . paths: @@ -253,7 +296,10 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + - wapm-cli/target/release/.fingerprint + - wapm-cli/target/release/build + - wapm-cli/target/release/deps + key: v8-cargo-cache-darwin-nightly-{ arch }}-{{ checksum "Cargo.lock" }} test-rust-nightly: docker: @@ -262,27 +308,28 @@ jobs: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly + - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-cargo-cache-linux-nightly-{{ arch }} - run: name: Install dependencies command: | sudo apt-get install -y cmake curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz - - run: rustup default nightly-2019-02-27 + - run: rustup default nightly-2019-04-11 - run: | export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" make test - make test-nightly - make test-emscripten - make test-emscripten-nightly + make test-singlepass + make test-emscripten-clif + make test-emscripten-singlepass - save_cache: paths: - /usr/local/cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly + key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly publish-github-release: docker: @@ -330,6 +377,7 @@ workflows: version: 2 main: jobs: + - changelog - lint - test: filters: diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e861ea7b6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +# Ignore everything +** +!lib/** +!src/** +!Cargo.toml +!Cargo.lock \ No newline at end of file diff --git a/.gitignore b/.gitignore index 141efbf78..d1122a798 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .DS_Store .idea **/.vscode +install/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..af96cdf0e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wapm-cli"] + path = wapm-cli + url = https://github.com/wasmerio/wapm-cli.git diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..00d7e67e6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,54 @@ +# Changelog + +All PRs to the Wasmer repository must add to this file. + +Blocks of changes will separated by version increments. + +## **[Unreleased]** + +- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs +- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API +- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements +- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits + +## 0.4.1 - 2018-05-06 + +- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 +- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm +- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm +- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI +- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows +- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule +- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path + +## 0.4.0 - 2018-04-23 + +- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli. +- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends +- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors. +- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions. +- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types +- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering +- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory +- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend. +- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365). +- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction. +- [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing +- [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0 +- [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages) +- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI. +- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md). + +## 0.3.0 - 2018-04-12 + +- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer) +- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func. +- [#325](https://github.com/wasmerio/wasmer/pull/325) Fixed func_index in debug mode +- [#323](https://github.com/wasmerio/wasmer/pull/323) Add validate subcommand to validate Wasm files +- [#321](https://github.com/wasmerio/wasmer/pull/321) Upgrade to Cranelift 0.3.0 +- [#319](https://github.com/wasmerio/wasmer/pull/319) Add Export and GlobalDescriptor to Runtime API +- [#310](https://github.com/wasmerio/wasmer/pull/310) Cleanup warnings +- [#299](https://github.com/wasmerio/wasmer/pull/299) [#300](https://github.com/wasmerio/wasmer/pull/300) [#301](https://github.com/wasmerio/wasmer/pull/301) [#303](https://github.com/wasmerio/wasmer/pull/303) [#304](https://github.com/wasmerio/wasmer/pull/304) [#305](https://github.com/wasmerio/wasmer/pull/305) [#306](https://github.com/wasmerio/wasmer/pull/306) [#307](https://github.com/wasmerio/wasmer/pull/307) Add support for WASI 🎉 +- [#286](https://github.com/wasmerio/wasmer/pull/286) Add extend to imports +- [#278](https://github.com/wasmerio/wasmer/pull/278) Add versioning to cache +- [#250](https://github.com/wasmerio/wasmer/pull/250) Setup bors diff --git a/Cargo.lock b/Cargo.lock index 732daab18..4a0e4d5d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,9 +1,11 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "MacTypes-sys" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -13,7 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aho-corasick" -version = "0.6.10" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -39,7 +41,7 @@ name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -60,9 +62,9 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -72,15 +74,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -88,8 +90,8 @@ name = "backtrace-sys" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -117,7 +119,7 @@ dependencies = [ "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -143,7 +145,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -165,6 +167,24 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "capstone" version = "0.5.0" @@ -178,7 +198,7 @@ name = "capstone-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -188,24 +208,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cbindgen" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.31" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -230,7 +250,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -258,10 +278,10 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.35" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -275,7 +295,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -283,77 +303,77 @@ name = "core-foundation-sys" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-bforest" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-codegen" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-bforest 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-meta 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-codegen-meta" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-entity" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cranelift-frontend" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-native" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-wasm" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -381,7 +401,7 @@ dependencies = [ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -389,8 +409,8 @@ dependencies = [ "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.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -478,11 +498,13 @@ dependencies = [ [[package]] name = "csv" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -517,7 +539,7 @@ dependencies = [ "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -532,7 +554,7 @@ dependencies = [ [[package]] name = "either" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -560,7 +582,7 @@ dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -570,8 +592,8 @@ 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.50 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -580,7 +602,7 @@ 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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -588,7 +610,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -599,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -614,8 +636,8 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -624,7 +646,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -667,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -675,7 +697,7 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -684,6 +706,14 @@ name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "generational-arena" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "generic-array" version = "0.12.0" @@ -715,8 +745,8 @@ dependencies = [ "byteorder 1.3.1 (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.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -731,7 +761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -749,7 +779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "http" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -776,10 +806,10 @@ version = "0.12.25" 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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (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.17 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -803,7 +833,7 @@ 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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -829,10 +859,10 @@ name = "inkwell" version = "0.1.0" source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#a14e62977504ef574dc2e933edc559cc79781ca7" dependencies = [ - "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -842,7 +872,7 @@ version = "0.1.0" source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#a14e62977504ef574dc2e933edc559cc79781ca7" dependencies = [ "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -850,7 +880,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -859,7 +889,7 @@ name = "itertools" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -888,7 +918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -906,8 +936,8 @@ name = "libloading" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -915,8 +945,8 @@ name = "linked-hash-map" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_test 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_test 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -924,10 +954,10 @@ name = "llvm-sys" version = "70.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -953,7 +983,7 @@ name = "lz4" version = "1.23.1" source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "lz4-sys 1.8.3 (git+https://github.com/zboxfs/lz4-rs.git)", ] @@ -962,8 +992,8 @@ name = "lz4-sys" version = "1.8.3" source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -976,7 +1006,7 @@ name = "memchr" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -984,8 +1014,8 @@ name = "memmap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -993,8 +1023,8 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1034,9 +1064,9 @@ name = "miniz_oxide_c_api" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1050,7 +1080,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (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)", @@ -1075,7 +1105,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1092,8 +1122,8 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1102,9 +1132,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1114,9 +1144,9 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1152,7 +1182,7 @@ name = "num_cpus" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1164,7 +1194,7 @@ dependencies = [ "cfg-if 0.1.7 (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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1178,8 +1208,8 @@ name = "openssl-sys" version = "0.9.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1207,7 +1237,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1225,11 +1255,11 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1287,6 +1317,15 @@ name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plugin-for-example" +version = "0.1.0" + +[[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.27" @@ -1319,10 +1358,10 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1331,7 +1370,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1340,7 +1379,7 @@ dependencies = [ "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.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1386,9 +1425,9 @@ name = "rand_jitter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1398,10 +1437,10 @@ 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.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1436,7 +1475,7 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1446,7 +1485,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1457,7 +1496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1471,7 +1510,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1479,24 +1518,24 @@ name = "redox_termios" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1507,27 +1546,27 @@ name = "remove_dir_all" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "reqwest" -version = "0.9.12" +version = "0.9.13" 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)", "encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.25 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1536,7 +1575,7 @@ dependencies = [ "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.10 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1555,7 +1594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1590,7 +1629,7 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1614,7 +1653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1624,7 +1663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1635,7 +1674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1653,10 +1692,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1665,7 +1704,7 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1673,17 +1712,17 @@ name = "serde_bytes" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1693,15 +1732,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_test" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1711,7 +1750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1762,7 +1801,7 @@ dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1777,7 +1816,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.29" +version = "0.15.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1800,7 +1839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1815,14 +1854,14 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "target-lexicon" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1845,11 +1884,11 @@ version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1865,8 +1904,8 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1891,9 +1930,9 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1901,7 +1940,7 @@ name = "tinytemplate" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1911,7 +1950,7 @@ version = "0.1.18" 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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1928,7 +1967,7 @@ name = "tokio-current-thread" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1938,7 +1977,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1947,7 +1986,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1957,7 +1996,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1975,7 +2014,7 @@ version = "0.1.4" 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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1984,7 +2023,7 @@ 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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1999,7 +2038,7 @@ 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.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2013,7 +2052,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2031,7 +2070,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2118,7 +2157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uuid" -version = "0.7.3" +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)", @@ -2149,8 +2188,8 @@ name = "wabt" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2160,8 +2199,8 @@ name = "wabt-sys" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2171,7 +2210,7 @@ version = "2.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2180,149 +2219,144 @@ name = "want" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (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 = "wasmer" -version = "0.2.1" +version = "0.4.1" dependencies = [ "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.2.0", - "wasmer-dynasm-backend 0.1.0", - "wasmer-emscripten 0.2.1", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime 0.2.1", - "wasmer-runtime-abi 0.2.1", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-emscripten 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-middleware-common 0.4.1", + "wasmer-runtime 0.4.1", + "wasmer-runtime-abi 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", + "wasmer-wasi 0.4.1", ] [[package]] name = "wasmer-clif-backend" -version = "0.2.0" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-native 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (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.10.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.2.1", - "wasmer-win-exception-handler 0.2.0", - "wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmer-dynasm-backend" -version = "0.1.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dynasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.2.1", - "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmer-win-exception-handler 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.2.1" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.2.0", - "wasmer-dynasm-backend 0.1.0", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-llvm-backend" -version = "0.1.0" +version = "0.4.1" dependencies = [ "capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.2.1", - "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-middleware-common" +version = "0.4.1" +dependencies = [ + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime" -version = "0.2.1" +version = "0.4.1" dependencies = [ "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.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.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.2.0", - "wasmer-dynasm-backend 0.1.0", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-runtime-abi" -version = "0.2.1" +version = "0.4.1" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.2.1", - "wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", "zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)", "zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-runtime-c-api" -version = "0.2.1" +version = "0.4.1" dependencies = [ - "cbindgen 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.2.1", - "wasmer-runtime-core 0.2.1", + "cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.4.1", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime-core" -version = "0.2.1" +version = "0.4.1" dependencies = [ "blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2332,54 +2366,75 @@ dependencies = [ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.12.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.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (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.90 (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.10.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmer-singlepass-backend" +version = "0.4.1" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dynasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-spectests" -version = "0.2.0" +version = "0.4.1" dependencies = [ "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.2.0", - "wasmer-dynasm-backend 0.1.0", - "wasmer-llvm-backend 0.1.0", - "wasmer-runtime-core 0.2.1", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", +] + +[[package]] +name = "wasmer-wasi" +version = "0.4.1" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-win-exception-handler" -version = "0.2.0" +version = "0.4.1" dependencies = [ "bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.2.1", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.4.1", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmparser" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasmparser" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasmparser" -version = "0.28.0" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2388,7 +2443,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2398,7 +2453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2420,7 +2475,7 @@ name = "winapi-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2433,7 +2488,7 @@ name = "wincolor" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2451,13 +2506,13 @@ name = "xattr" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zbox" version = "0.6.1" -source = "git+https://github.com/wasmerio/zbox?branch=bundle-libsodium#ada40126425cf4d1adb14ebbc08980d01a0d520b" +source = "git+https://github.com/wasmerio/zbox?branch=bundle-libsodium#113c62bf3f94124c4978959043efcf98222fa626" dependencies = [ "android_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2468,11 +2523,25 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lz4 1.23.1 (git+https://github.com/zboxfs/lz4-rs.git)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)", "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zip" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2488,7 +2557,7 @@ name = "zstd-safe" version = "1.4.7+zstd.1.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "zstd-sys 1.4.8+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2498,15 +2567,15 @@ version = "1.4.8+zstd.1.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "blob 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" "checksum android_log-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e" "checksum android_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf44378e81264148f08e58336674542f82d0021f685d0be0320c82e1653dbe0b" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -2514,7 +2583,7 @@ dependencies = [ "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" -"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" +"checksum backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1" @@ -2524,27 +2593,29 @@ dependencies = [ "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "checksum capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00be9d203fa0e078b93b24603633fb081851dfe0c1086364431f52587a47157e" "checksum capstone-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc8d32bc5c1e6d0fcde10af411c98b07d93498d51654f678757f08fa2acd6a6" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum cbindgen 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f61c5411fe3ac196fae7ea397dd13959b1323edda046eec50d648a8e92015a53" -"checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d" +"checksum cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31f70db109be74a3dfcb0af4d0d191e52230351477f14c2ed10707c2b0dcfa2e" +"checksum cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "30f813bf45048a18eda9190fd3c6b78644146056740c43172a5a3699118588fd" "checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" +"checksum cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "96210eec534fc3fbfc0452a63769424eaa80205fda6cea98e5b61cb3d97bcec8" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" -"checksum cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f8ff24e9a6c89b8a846b14df9a34d2cac17cea7bdb5c81ed6b4744ee0e38bf" -"checksum cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "42f5b809bd885c368e01aeec8fe04f21dcb07569834b907d75b4a7bed8d067eb" -"checksum cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "014c23ed3ebdc8377d41540af638245207dd169f421df042dfccc867465734ed" -"checksum cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4df40e26c0cf7b4d86919cb995bb412ee3001cc18e4f3c83a903f30b7007d8b" -"checksum cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "789907218eeebebcea8122c2053d71affac91c96ce72cea35ebfdbbf547e82af" -"checksum cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "474bee81d620a473bf43411a3d6f10ffbf7965141dc5e5b76d8d2151dde3285d" -"checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a" +"checksum cranelift-bforest 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5a357d20666bf4a8c2d626a19f1b59dbca66cd844fb1e66c5612254fd0f7505" +"checksum cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab00cb149a5bb0f7e6dd391357356a5d71c335a431e8eece94f32da2d5a043f7" +"checksum cranelift-codegen-meta 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3797a2f450ac71297e083dd440d0cdd0d3bceabe4a3ca6bcb9e4077e9c0327d" +"checksum cranelift-entity 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b66e28877b75b3d2b31250f780bb5db8f68ae3df681cd56add803b2567ac4fd" +"checksum cranelift-frontend 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b72d55fd732b1f7a99d043a36c54a5679b6ec8bc777c8d954fb97c4fa0fce7eb" +"checksum cranelift-native 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0239f34836621a127c2132980b2f5c32a1be1c40e2d1a9a1a9bd5af33c12aee" +"checksum cranelift-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740ebfba28c8433f06750f84819f1eb663ea9f5e4b9a81c01f4e52262d868b56" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "1c6e5ee5b9652d4f851418c448af105642e1f99e9a2741a8ff45c0d2c911b1e0" @@ -2556,13 +2627,13 @@ dependencies = [ "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" -"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04" +"checksum csv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f0782c7154d8dd08f4adeb5aa22ab178c10281915f7da68d10bb646f03aaee73" "checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum dynasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b77e128faecc4d16cff7cae96c0c9e809f687f748a0dbc4d017996e48240a991" "checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68" -"checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" "checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" @@ -2579,9 +2650,10 @@ dependencies = [ "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.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" +"checksum futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "62941eff9507c8177d448bd83a44d9b9760856e184081d8cd79ba9f03dd24981" "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4024f96ffa0ebaaf36aa589cd41f2fd69f3a5e6fd02c86e11e12cdf41d5b46a3" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum goblin 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "84473a5302fa5094d3d9911c2f312f522f9a37462a777f195f63fae1bf7faf4d" @@ -2589,7 +2661,7 @@ dependencies = [ "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" +"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5b6658b016965ae301fa995306db965c93677880ea70765a84235a96eae896" @@ -2604,7 +2676,7 @@ dependencies = [ "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" +"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" "checksum libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "7346a83e8a2c3958d44d24225d905385dc31fc16e89dffb356c457b278914d20" "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" @@ -2649,6 +2721,7 @@ dependencies = [ "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" @@ -2669,12 +2742,12 @@ dependencies = [ "checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" +"checksum redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)" = "d32b3053e5ced86e4bc0411fec997389532bf56b000e66cb4884eeeb41413d69" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" -"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" +"checksum regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "559008764a17de49a3146b234641644ed37d118d1ef641a0bb573d146edc6ce0" +"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum reqwest 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "962fa64e670e70b9d3a81c3688832eb59293ef490e0af5ad169763f62016ac5e" +"checksum reqwest 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3c4ef83e0beb14bfe38b9f01330a5bc8e965a9f9628690aa28383746dac1e925" "checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" "checksum rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" @@ -2689,12 +2762,12 @@ dependencies = [ "checksum security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6696852716b589dff9e886ff83778bb635150168e83afa8ac6b8a78cb82abc" "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.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" +"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" "checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" "checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" -"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c" +"checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"checksum serde_test 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "70807e147558b5253cb70f55d343db1d07204d773087c96d0f35fced295dba82" +"checksum serde_test 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce17ed207fa61e7f4701a778a6c111da84a441ca9a8f50b92808f4223dd240b" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" @@ -2705,12 +2778,12 @@ dependencies = [ "checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1" "checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" +"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2167ff53da2a661702b3299f71a91b61b1dffef36b4b2884b1f9c67254c0133" -"checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" +"checksum target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6923974ce4eb5bd28814756256d8ab71c28dd6e7483313fe7ab6614306bf633" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" @@ -2743,7 +2816,7 @@ dependencies = [ "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "600ef8213e9f8a0ac1f876e470e90780ae0478eabce7f76aff41b0f4ef0fd5c0" +"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "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" @@ -2752,12 +2825,10 @@ dependencies = [ "checksum wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a6265b25719e82598d104b3717375e37661d41753e2c84cde3f51050c7ed7e3c" "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" -"checksum wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e666ecb4a406483a59a49f9d0c17f327e70da53a128eccddae2eadb95865c" -"checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060" -"checksum wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f426b1929bd26517fb10702e2a8e520d1845c49567aa4d244f426f10b206c1" +"checksum wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "981a8797cf89762e0233ec45fae731cb79a4dfaee12d9f0fe6cee01e4ac58d00" "checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "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" @@ -2766,6 +2837,7 @@ dependencies = [ "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" "checksum zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)" = "" +"checksum zip 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cbbddef6339155bc4fa8e2609040078ff18f3011117b55caa9f0516d544a357" "checksum zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6f042dd18d52854d302d3d92f66d0a63c2d520d7b7034a9d43cde7441d1b4ddd" "checksum zstd-safe 1.4.7+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "63febf0b0dcd076db81e6b3110ed254cfb8ed54378a4c3cfbb68956e839d4f59" "checksum zstd-sys 1.4.8+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4cb187d624025a7d9878ecf13437491869423426183ded2fa40d4651b85f7ae7" diff --git a/Cargo.toml b/Cargo.toml index 776d61a5b..5de0f17ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.2.1" +version = "0.4.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -24,25 +24,34 @@ structopt = "0.2.11" wabt = "0.7.2" hashbrown = "0.1.8" wasmer-clif-backend = { path = "lib/clif-backend" } -wasmer-dynasm-backend = { path = "lib/dynasm-backend", optional = true } +wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } +wasmer-middleware-common = { path = "lib/middleware-common" } wasmer-runtime = { path = "lib/runtime" } wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true } wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } +wasmer-wasi = { path = "lib/wasi", optional = true } [workspace] -members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"] +members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi", "lib/middleware-common", "examples/plugin-for-example"] [build-dependencies] wabt = "0.7.2" glob = "0.2.11" +rustc_version = "0.2.3" [features] -default = ["fast-tests"] -debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] +default = ["fast-tests", "wasi"] +debug = ["wasmer-runtime-core/debug"] +extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] -llvm = ["wasmer-llvm-backend"] -dynasm = ["wasmer-dynasm-backend"] +"backend:llvm" = ["wasmer-llvm-backend"] +"backend:singlepass" = ["wasmer-singlepass-backend"] +wasi = ["wasmer-wasi"] vfs = ["wasmer-runtime-abi"] + +[[example]] +name = "plugin" +crate-type = ["bin"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..02ea6c413 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM circleci/rust:1.33.0-stretch as wasmer-build-env +RUN sudo apt-get update && \ + sudo apt-get install -y --no-install-recommends \ + cmake \ + && sudo rm -rf /var/lib/apt/lists/* +RUN curl -SL https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz \ + | tar -xJC /home/circleci +ENV LLVM_SYS_70_PREFIX /home/circleci/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/ + +FROM wasmer-build-env AS wasmer-debug-env +RUN sudo apt-get update && \ + sudo apt-get install -y --no-install-recommends \ + valgrind \ + && sudo rm -rf /var/lib/apt/lists/* + +FROM wasmer-build-env AS wasmer-build +WORKDIR /home/circleci/wasmer +COPY . /home/circleci/wasmer +RUN sudo chmod -R 777 . +RUN cargo build --release + +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 diff --git a/LICENSE b/LICENSE index 7e40523b0..079740dd3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2019 Syrus Akbary +MIT License + +Copyright (c) 2019 Wasmer, Inc. and its affiliates. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 19ce942d1..2a1bfa138 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ integration-tests: release echo "Running Integration Tests" ./integration_tests/lua/test.sh ./integration_tests/nginx/test.sh + ./integration_tests/cowsay/test.sh lint: cargo fmt --all -- --check @@ -32,32 +33,59 @@ lint: precommit: lint test +build-install: + mkdir -p ./install/bin + cp ./wapm-cli/target/release/wapm ./install/bin/ + cp ./target/release/wasmer ./install/bin/ + tar -C ./install -zcvf wasmer.tar.gz bin/wapm bin/wasmer + +# For installing the contents locally +do-install: + tar -C ~/.wasmer -zxvf wasmer.tar.gz + test: # We use one thread so the emscripten stdouts doesn't collide - cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-dynasm-backend -- $(runargs) + cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend -- $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) cargo test --manifest-path lib/spectests/Cargo.toml --features clif cargo test --manifest-path lib/spectests/Cargo.toml --features llvm cargo build -p wasmer-runtime-c-api cargo test -p wasmer-runtime-c-api -- --nocapture -test-nightly: - cargo test --manifest-path lib/spectests/Cargo.toml --features dynasm +test-singlepass: + cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass -test-emscripten: - cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs) +test-emscripten-llvm: cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs) -test-emscripten-nightly: - cargo test --manifest-path lib/emscripten/Cargo.toml --features dynasm -- --test-threads=1 $(runargs) +test-emscripten-clif: + cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs) + +test-emscripten-singlepass: + cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs) + +singlepass-debug-release: + cargo +nightly build --features "backend:singlepass debug" --release + +singlepass-release: + cargo +nightly build --features "backend:singlepass" --release + +singlepass-build: + cargo +nightly build --features "backend:singlepass debug" release: # If you are in OS-X, you will need mingw-w64 for cross compiling to windows # brew install mingw-w64 cargo build --release +production-release: + cargo build --release --features backend:singlepass,backend:llvm + debug-release: cargo build --release --features "debug" +extra-debug-release: + cargo build --release --features "extra-debug" + publish-release: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/ diff --git a/README.md b/README.md index 2a97592ed..b1a4ccfa2 100644 --- a/README.md +++ b/README.md @@ -26,23 +26,53 @@ Install Wasmer with: curl https://get.wasmer.io -sSfL | sh ``` -_**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_ +Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨: + +* [**Rust**](https://github.com/wasmerio/wasmer-rust-example) +* [**C/C++**](https://github.com/wasmerio/wasmer-c-api) +* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm) +* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm) +* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm) ### Usage Wasmer can execute both the standard binary format (`.wasm`) and the text format defined by the WebAssembly reference interpreter (`.wat`). -Once installed, you will be able to run any WebAssembly files (_including nginx and Lua!_): +Once installed, you will be able to run any WebAssembly files (_including Lua, PHP, SQLite and nginx!_): ```sh # Run Lua wasmer run examples/lua.wasm +# Run PHP +wasmer run examples/php.wasm + +# Run SQLite +wasmer run examples/sqlite.wasm + # Run nginx wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf ``` +#### With WAPM + +Installing Wasmer through `wasmer.io` includes +[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager. + +Wapm allows you to easily download, run, and distribute WebAssembly binaries. + +```sh +# Install cowsay globally +wapm install -g cowsay + +# Run cowsay +wapm run cowsay "Hello, world!" +``` + +For more information about wapm, check out the [website](https://www.wapm.io) +and this [example program](https://github.com/wapm-packages/rust-wasi-example). + ## Code Structure Wasmer is structured into different directories: @@ -70,6 +100,7 @@ Please select your operating system: - [macOS](#macos) - [Debian-based Linuxes](#debian-based-linuxes) +- [FreeBSD](#freebsd) - [Microsoft Windows](#windows-msvc) #### macOS @@ -89,7 +120,13 @@ sudo port install cmake #### Debian-based Linuxes ```sh -sudo apt install cmake +sudo apt install cmake pkg-config libssl-dev +``` + +#### FreeBSD + +```sh +pkg install cmake ``` #### Windows (MSVC) @@ -164,7 +201,7 @@ 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)_ -- [ ] Support WASI _(in the works)_ +- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0) - [ ] Support Emscripten calls _(in the works)_ - [ ] Support Rust ABI calls - [ ] Support Go ABI calls diff --git a/binary-name.sh b/binary-name.sh index 98a3a8497..4ac8ef8e3 100755 --- a/binary-name.sh +++ b/binary-name.sh @@ -23,8 +23,8 @@ initOS() { darwin) OS='darwin';; linux) OS='linux';; freebsd) OS='freebsd';; - mingw*) OS='windows';; - msys*) OS='windows';; + # mingw*) OS='windows';; + # msys*) OS='windows';; *) echo "OS ${OS} is not supported by this installation script"; exit 1;; esac } @@ -34,11 +34,11 @@ initArch initOS # determine install directory if required -BINARY="wasmer-${OS}-${ARCH}" - +BINARY="wasmer-${OS}-${ARCH}.tar.gz" + # add .exe if on windows -if [ "$OS" = "windows" ]; then - BINARY="$BINARY.exe" -fi +# if [ "$OS" = "windows" ]; then +# BINARY="$BINARY.exe" +# fi echo "${BINARY}" diff --git a/bors.toml b/bors.toml index c56141cd5..3ccbdc008 100644 --- a/bors.toml +++ b/bors.toml @@ -2,8 +2,9 @@ status = [ "ci/circleci: lint", "ci/circleci: test", "ci/circleci: test-macos", + "ci/circleci: test-rust-nightly", "continuous-integration/appveyor/branch" ] required_approvals = 1 timeout_sec = 900 -delete_merged_branches = true \ No newline at end of file +delete_merged_branches = true diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 000000000..2086284f7 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,39 @@ +# Dockerfile Documentation +The `Dockerfile` included in the project root directory could be used for development purposes or to build a small image containing the `wasmer` executable. + +The `wasmer-build-env` stage in the Dockerfile contains the dependencies needed to compile Wasmer including LLVM. + +The `wasmer-debug-env` stage adds the `valgrind` profiling tool to the `wasmer-build-env` stage. + +The `wasmer-build` stage in the Dockerfile will copy the current directory, assuming the build context is the `wasmer` project, and build the project using `cargo build --release`. + +The `wasmer` stage will copy the resulting `wasmer` executable from the `wasmer-build` stage into a new base image to create a smaller image containing `wasmer`. + +## Example Usages + +### Wasmer image +1. From the `wasmer` project directory, build the image: +`docker build -t wasmer --target=wasmer .` + +2. List options: +`docker run wasmer --help` + +3. Mount a directory, and run an example wasm file: +`docker run -v /Users/admin/Documents/wasmer-workspace:/root/wasmer-workspace wasmer run /root/wasmer-workspace/examples/hello.wasm` + +### Profiling +1. Build `wasmer-debug-env`: + `docker build --tag=wasmer-debug-env --target wasmer-debug-env .` + +2. Mount a directory from the host and run interactively: + `docker run -it -v /Users/admin/Documents/wasmer-workspace:/home/circleci/wasmer-workspace wasmer-debug-env /bin/bash` + +3. Inside the container, build `wasmer` and run profiling tool: +``` +cd /home/circleci/wasmer-workspace/wasmer` +cargo build +valgrind --tool=callgrind --dump-instr=yes --collect-jumps=yes --simulate-cache=yes target/debug/wasmer run test.wasm +``` + +The `callgrind.out` can be viewed with the `qcachegrind` tool on Mac OS (`brew install qcachegrind`). + diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..5abb6eaee --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +# WebAssembly Examples + +In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime: + +* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)] +* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)] +* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)] +* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)] +* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)] diff --git a/examples/cowsay.wasm b/examples/cowsay.wasm new file mode 100755 index 000000000..4ff43df2c Binary files /dev/null and b/examples/cowsay.wasm differ diff --git a/examples/exit.wat b/examples/exit.wat new file mode 100644 index 000000000..b2f832cb3 --- /dev/null +++ b/examples/exit.wat @@ -0,0 +1,11 @@ +(module + (import "wasi_unstable" "proc_exit" (func $proc_exit (param i32))) + (export "_start" (func $_start)) + + (memory 10) + (export "memory" (memory 0)) + + (func $_start + (call $proc_exit (i32.const 7)) + ) +) diff --git a/examples/lua.wasm b/examples/lua.wasm index 1a424ae53..c0e65b22a 100644 Binary files a/examples/lua.wasm and b/examples/lua.wasm differ diff --git a/examples/php.wasm b/examples/php.wasm new file mode 100644 index 000000000..36c2325b9 Binary files /dev/null and b/examples/php.wasm differ diff --git a/examples/plugin-for-example.wasm b/examples/plugin-for-example.wasm new file mode 100755 index 000000000..4754287ab Binary files /dev/null and b/examples/plugin-for-example.wasm differ diff --git a/examples/plugin-for-example/Cargo.toml b/examples/plugin-for-example/Cargo.toml new file mode 100644 index 000000000..28d234910 --- /dev/null +++ b/examples/plugin-for-example/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] diff --git a/examples/plugin-for-example/README.md b/examples/plugin-for-example/README.md new file mode 100644 index 000000000..42bc3ac26 --- /dev/null +++ b/examples/plugin-for-example/README.md @@ -0,0 +1,43 @@ +# WASI plugin example + +In this example we extend the imports of Wasmer's WASI ABI to demonstrate how custom plugins work. + +See the `wasmer/examples/plugin.rs` file for the source code of the host system. + +## Compiling +_Attention Windows users: WASI target only works with the `nightly-x86_64-pc-windows-gnu` toolchain._ +``` +# Install an up to date version of Rust nightly +# Add the target +rustup target add wasm32-unknown-wasi +# build it +cargo +nightly build --release --target=wasm32-unknown-wasi +# copy it to examples folder +cp ../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm ../ +``` + +## Running +``` +# Go back to top level Wasmer dir +cd .. +# Run the example +cargo run --example plugin +``` + +## Inspecting the plugin +``` +# Install wabt via wapm; installed globally with the `g` flag +wapm install -g wabt +# Turn the binary WASM file in to a readable WAT text file +wapm run wasm2wat examples/plugin-for-example.wasm +``` + +At the top of the file we can see which functions this plugin expects. Most are covered by WASI, but we handle the rest. + +## Explanation + +In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allowing our program to rely on Wasmer's implementation of the syscalls defined by WASI as well as our own that we made. This allows us to use the full power of an existing ABI, like WASI, and give it super-powers for our specific use case. + +Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24). + +[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/ diff --git a/examples/plugin-for-example/src/main.rs b/examples/plugin-for-example/src/main.rs new file mode 100644 index 000000000..9ee284aab --- /dev/null +++ b/examples/plugin-for-example/src/main.rs @@ -0,0 +1,12 @@ +extern "C" { + fn it_works() -> i32; +} + +#[no_mangle] +pub fn plugin_entrypoint(n: i32) -> i32 { + println!("Hello from inside WASI"); + let result = unsafe { it_works() }; + result + n +} + +pub fn main() {} diff --git a/examples/plugin-for-example/wapm.toml b/examples/plugin-for-example/wapm.toml new file mode 100644 index 000000000..7e2e261a7 --- /dev/null +++ b/examples/plugin-for-example/wapm.toml @@ -0,0 +1,12 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +description = "A plugin for our example system" +readme = "README.md" +repository = "https://github.com/wasmerio/wasmer/examples/plugin-for-example" +license = "MIT" + +[[module]] +name = "plugin-for-example" +source = "../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm" +abi = "none" \ No newline at end of file diff --git a/examples/plugin.rs b/examples/plugin.rs new file mode 100644 index 000000000..dbb06fe61 --- /dev/null +++ b/examples/plugin.rs @@ -0,0 +1,38 @@ +use wasmer_runtime::{func, imports, instantiate}; +use wasmer_runtime_core::vm::Ctx; +use wasmer_wasi::generate_import_object; + +static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm"; + +fn it_works(_ctx: &mut Ctx) -> i32 { + println!("Hello from outside WASI"); + 5 +} + +fn main() { + // Load the plugin data + let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!( + "Could not read in WASM plugin at {}", + PLUGIN_LOCATION + )); + + // WASI imports + let mut base_imports = generate_import_object(vec![], vec![], vec![]); + // env is the default namespace for extern functions + let custom_imports = imports! { + "env" => { + "it_works" => func!(it_works), + }, + }; + // The WASI imports object contains all required import functions for a WASI module to run. + // Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run. + base_imports.extend(custom_imports); + let instance = + instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module"); + + // get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32 + let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap(); + // call the "entry_point" function in WebAssembly with the number "2" as the i32 argument + let result = entry_point.call(2).expect("failed to execute plugin"); + println!("result: {}", result); +} diff --git a/examples/single_pass_tests/loop.wat b/examples/single_pass_tests/loop.wat index dfdc1b1d5..4d7c7eac8 100644 --- a/examples/single_pass_tests/loop.wat +++ b/examples/single_pass_tests/loop.wat @@ -1,5 +1,5 @@ (module - (func $main (export "main") (result i32) + (func $main (export "main") (local $count i32) (local $sum i32) (loop (result i32) @@ -7,10 +7,11 @@ (set_local $sum (i32.add (get_local $sum) (get_local $count))) (i32.sub (i32.const 1) (i32.eq (get_local $count) - (i32.const 100000) + (i32.const 50000) )) (br_if 0) (get_local $sum) ) + (if (i32.ne (i32.const 1250025000)) (unreachable)) ) ) diff --git a/sqlite.wasm b/examples/sqlite.wasm similarity index 100% rename from sqlite.wasm rename to examples/sqlite.wasm diff --git a/install.sh b/install.sh index 0b7da4ca2..8d4e1e9d2 100755 --- a/install.sh +++ b/install.sh @@ -32,6 +32,7 @@ white="\033[37m" bold="\e[1m" dim="\e[2m" +# Warning: Remove this on the public repo RELEASES_URL="https://github.com/wasmerio/wasmer/releases" wasmer_download_json() { @@ -129,11 +130,11 @@ wasmer_detect_profile() { wasmer_link() { printf "$cyan> Adding to bash profile...$reset\n" WASMER_PROFILE="$(wasmer_detect_profile)" - LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" - SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" + LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" + SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n" # We create the wasmer.sh file - printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" + printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasmer.sh" if [ -z "${WASMER_PROFILE-}" ] ; then printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n" @@ -155,13 +156,15 @@ wasmer_link() { echo "If this isn't the profile of your current shell then please add the following to your correct profile:" printf "$LOAD_STR$reset\n" - version=`$HOME/.wasmer/bin/wasmer --version` || ( + version=`$INSTALL_DIRECTORY/bin/wasmer --version` || ( printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n" exit 1; ) - printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $HOME/.wasmer/wasmer.sh$reset\n" - printf "\nOtherwise, wasmer will be available the next time you open the terminal.\n" + printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n" + printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n" + echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`." + echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)" fi } @@ -254,7 +257,7 @@ wasmer_install() { " fi -# if [ -d "$HOME/.wasmer" ]; then +# if [ -d "$INSTALL_DIRECTORY" ]; then # if which wasmer; then # local latest_url # local specified_version @@ -283,15 +286,14 @@ wasmer_install() { # exit 0 # else # printf "$yellow> $wasmer_alt_version is already installed, Specified version: $specified_version.$reset\n" -# rm -rf "$HOME/.wasmer" +# rm -rf "$INSTALL_DIRECTORY" # fi # else -# printf "$red> $HOME/.wasmer already exists, possibly from a past Wasmer install.$reset\n" -# printf "$red> Remove it (rm -rf $HOME/.wasmer) and run this script again.$reset\n" +# printf "$red> $INSTALL_DIRECTORY already exists, possibly from a past Wasmer install.$reset\n" +# printf "$red> Remove it (rm -rf $INSTALL_DIRECTORY) and run this script again.$reset\n" # exit 0 # fi # fi - wasmer_download # $1 $2 wasmer_link wasmer_reset @@ -369,12 +371,12 @@ wasmer_download() { WASMER=INSTALL_DIRECTORY # assemble expected release artifact name - BINARY="wasmer-${OS}-${ARCH}" + BINARY="wasmer-${OS}-${ARCH}.tar.gz" # add .exe if on windows - if [ "$OS" = "windows" ]; then - BINARY="$BINARY.exe" - fi + # if [ "$OS" = "windows" ]; then + # BINARY="$BINARY.exe" + # fi # if WASMER_RELEASE_TAG was not provided, assume latest if [ -z "$WASMER_RELEASE_TAG" ]; then @@ -417,9 +419,6 @@ wasmer_download() { printf "\033[K\n\033[1A" # printf "\033[1A$cyan> Downloaded$reset\033[K\n" # echo "Setting executable permissions." - chmod +x "$DOWNLOAD_FILE" - - INSTALL_NAME="wasmer" # windows not supported yet # if [ "$OS" = "windows" ]; then @@ -428,8 +427,9 @@ wasmer_download() { # echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME" - mkdir -p $INSTALL_DIRECTORY/bin - mv "$DOWNLOAD_FILE" "$INSTALL_DIRECTORY/bin/$INSTALL_NAME" + mkdir -p $INSTALL_DIRECTORY + # Untar the wasmer contents in the install directory + tar -C $INSTALL_DIRECTORY -zxvf $DOWNLOAD_FILE } wasmer_verify_or_quit() { diff --git a/integration_tests/cowsay/README.md b/integration_tests/cowsay/README.md new file mode 100644 index 000000000..f44e6e680 --- /dev/null +++ b/integration_tests/cowsay/README.md @@ -0,0 +1,9 @@ +# `cowsay` integration test + + +This starts Wasmer with the Cowsay WASI Wasm file. The test makes assertions on +the output of Wasmer. Run test with: + +```bash +./integration_tests/cowsay/test.sh +``` diff --git a/integration_tests/cowsay/test.sh b/integration_tests/cowsay/test.sh new file mode 100755 index 000000000..20c46a259 --- /dev/null +++ b/integration_tests/cowsay/test.sh @@ -0,0 +1,14 @@ +#! /bin/bash + +nohup ./target/release/wasmer run examples/cowsay.wasm --disable-cache -- "hello integration" + +if grep "hello integration" ./nohup.out +then + echo "cowsay wasi integration test succeeded" + rm ./nohup.out + exit 0 +else + echo "cowsay wasi integration test failed" + rm ./nohup.out + exit -1 +fi diff --git a/lib/README.md b/lib/README.md index 4c0008ee5..83a335588 100644 --- a/lib/README.md +++ b/lib/README.md @@ -22,7 +22,8 @@ The integration builds on the Wasmer runtime and allow us to run WebAssembly fil Wasmer intends to support different integrations: -- [emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm). +- [WASI](./wasi): run WebAssembly files with the [WASI ABI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/). +- [Emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm). - Go ABI: _we will work on this soon! Want to give us a hand? ✋_ - Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_ @@ -33,6 +34,6 @@ to tune the codegen properties (compile speed, performance, etc) to best fit the Currently, we support multiple backends for compiling WebAssembly to machine code: -- [dynasm-backend](./dynasm-backend/): Dynasm backend - super fast compilation, slower runtime speed +- [singlepass-backend](./singlepass-backend/): Single pass backend - super fast compilation, slower runtime speed - [clif-backend](./clif-backend/): Cranelift backend - slower compilation, normal runtime speed - [llvm-backend](./llvm-backend/): LLVM backend - slow compilation, native runtime speed diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 1f08b8b1e..cbfbfb44a 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.2.0" +version = "0.4.1" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,14 +8,14 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } -cranelift-native = "0.26.0" -cranelift-codegen = "0.26.0" -cranelift-entity = "0.26.0" -cranelift-wasm = "0.26.0" +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +cranelift-native = "0.30.0" +cranelift-codegen = "0.30.0" +cranelift-entity = "0.30.0" +cranelift-wasm = "0.30.0" hashbrown = "0.1" -target-lexicon = "0.2.0" -wasmparser = "0.23.0" +target-lexicon = "0.3.0" +wasmparser = "0.29.2" byteorder = "1" nix = "0.13.0" libc = "0.2.49" @@ -33,7 +33,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.2.0" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" } [features] debug = ["wasmer-runtime-core/debug"] diff --git a/lib/clif-backend/README.md b/lib/clif-backend/README.md new file mode 100644 index 000000000..eb0c17e8c --- /dev/null +++ b/lib/clif-backend/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer Cranelift backend + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate represents the Cranelift backend. diff --git a/lib/clif-backend/src/cache.rs b/lib/clif-backend/src/cache.rs index 5af708703..b4f647372 100644 --- a/lib/clif-backend/src/cache.rs +++ b/lib/clif-backend/src/cache.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use wasmer_runtime_core::{ backend::{sys::Memory, CacheGen}, cache::{Artifact, Error}, - module::{ModuleInfo, ModuleInner}, + module::ModuleInfo, structures::Map, types::{LocalFuncIndex, SigIndex}, }; @@ -27,18 +27,12 @@ impl CacheGenerator { } impl CacheGen for CacheGenerator { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), Error> { - let info = Box::new(module.info.clone()); - + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), Error> { // Clone the memory to a new location. This could take a long time, // depending on the throughput of your memcpy implementation. let compiled_code = (*self.memory).clone(); Ok(( - info, self.backend_cache.into_backend_data()?.into_boxed_slice(), compiled_code, )) diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index d09da6812..50fbe5dbf 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + mod cache; mod func_env; mod libcalls; diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 521918185..ad0576e59 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -73,16 +73,15 @@ impl Module { handler_data.clone(), )?; - let protected_caller = Caller::new(&self.info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info: self.info, @@ -103,16 +102,15 @@ impl Module { ) .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?; - let protected_caller = Caller::new(&info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info, @@ -151,8 +149,8 @@ convert_clif_to_runtime_index![ (SignatureIndex: SigIndex), ]; -impl<'a> From> for FuncSig { - fn from(signature: Converter<&'a ir::Signature>) -> Self { +impl From> for FuncSig { + fn from(signature: Converter) -> Self { FuncSig::new( signature .0 diff --git a/lib/clif-backend/src/module_env.rs b/lib/clif-backend/src/module_env.rs index 7c6cd6731..7cf1dfaff 100644 --- a/lib/clif-backend/src/module_env.rs +++ b/lib/clif-backend/src/module_env.rs @@ -50,6 +50,20 @@ impl<'module, 'isa> ModuleEnv<'module, 'isa> { Ok(self.func_bodies) } + + /// Return the global for the given global index. + pub fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global { + &self.globals[Converter(global_index).into()] + } + + /// Return the signature index for the given function index. + pub fn get_func_type( + &self, + func_index: cranelift_wasm::FuncIndex, + ) -> cranelift_wasm::SignatureIndex { + let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()]; + Converter(sig_index).into() + } } impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> { @@ -59,16 +73,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> } /// Declares a function signature to the environment. - fn declare_signature(&mut self, sig: &ir::Signature) { + fn declare_signature(&mut self, sig: ir::Signature) { self.signatures.push(sig.clone()); self.module.info.signatures.push(Converter(sig).into()); } - /// Return the signature with the given index. - fn get_signature(&self, clif_sig_index: cranelift_wasm::SignatureIndex) -> &ir::Signature { - &self.signatures[Converter(clif_sig_index).into()] - } - /// Declares a function import to the environment. fn declare_func_import( &mut self, @@ -92,11 +101,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> }); } - /// Return the number of imported funcs. - fn get_num_func_imports(&self) -> usize { - self.module.info.imported_functions.len() - } - /// Declares the type (signature) of a local function in the module. fn declare_func_type(&mut self, clif_sig_index: cranelift_wasm::SignatureIndex) { // We convert the cranelift signature index to @@ -106,15 +110,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> self.module.info.func_assoc.push(sig_index); } - /// Return the signature index for the given function index. - fn get_func_type( - &self, - func_index: cranelift_wasm::FuncIndex, - ) -> cranelift_wasm::SignatureIndex { - let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()]; - Converter(sig_index).into() - } - /// Declares a global to the environment. fn declare_global(&mut self, global: cranelift_wasm::Global) { let desc = GlobalDescriptor { @@ -180,11 +175,6 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> self.globals.push(global); } - /// Return the global for the given global index. - fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global { - &self.globals[Converter(global_index).into()] - } - /// Declares a table to the environment. fn declare_table(&mut self, table: cranelift_wasm::Table) { use cranelift_wasm::TableElementType; @@ -238,7 +228,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> table_index: cranelift_wasm::TableIndex, base: Option, offset: usize, - elements: Vec, + elements: Box<[cranelift_wasm::FuncIndex]>, ) { // Convert Cranelift GlobalIndex to wamser GlobalIndex // let base = base.map(|index| WasmerGlobalIndex::new(index.index())); @@ -376,7 +366,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> } /// Provides the contents of a function body. - fn define_function_body(&mut self, body_bytes: &'data [u8]) -> cranelift_wasm::WasmResult<()> { + fn define_function_body( + &mut self, + body_bytes: &'data [u8], + body_offset: usize, + ) -> cranelift_wasm::WasmResult<()> { let mut func_translator = FuncTranslator::new(); let func_body = { @@ -390,7 +384,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let mut func = ir::Function::with_name_signature(name, sig); - func_translator.translate(body_bytes, &mut func, &mut func_env)?; + func_translator.translate(body_bytes, body_offset, &mut func, &mut func_env)?; #[cfg(feature = "debug")] { @@ -530,7 +524,10 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> .special_param(ir::ArgumentPurpose::VMContext) .expect("missing vmctx parameter"); - let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64); + let func_index = pos.ins().iconst( + ir::types::I32, + func_index.index() as i64 + self.module.info.imported_functions.len() as i64, + ); pos.ins().call(start_debug, &[vmctx, func_index]); diff --git a/lib/clif-backend/src/relocation.rs b/lib/clif-backend/src/relocation.rs index 92ef0485a..50a032095 100644 --- a/lib/clif-backend/src/relocation.rs +++ b/lib/clif-backend/src/relocation.rs @@ -224,6 +224,7 @@ pub enum TrapCode { IntegerDivisionByZero, BadConversionToInteger, Interrupt, + UnreachableCodeReached, User(u16), } @@ -297,6 +298,7 @@ impl binemit::TrapSink for LocalTrapSink { ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero, ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger, ir::TrapCode::Interrupt => TrapCode::Interrupt, + ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached, ir::TrapCode::User(x) => TrapCode::User(x), }; diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index e2706b6e1..a55bf56d7 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -21,7 +21,6 @@ use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::{ self, backend::{ - self, sys::{Memory, Protect}, SigRegistry, }, @@ -357,13 +356,8 @@ pub struct FuncResolver { pub(crate) memory: Arc, } -// Implements FuncResolver trait. -impl backend::FuncResolver for FuncResolver { - fn get( - &self, - _module: &wasmer_runtime_core::module::ModuleInner, - index: LocalFuncIndex, - ) -> Option> { +impl FuncResolver { + pub fn lookup(&self, index: LocalFuncIndex) -> Option> { lookup_func(&self.map, &self.memory, index) } } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 04b97e832..3facce2ad 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,15 +1,14 @@ use crate::relocation::{TrapData, TrapSink}; +use crate::resolver::FuncResolver; use crate::trampoline::Trampolines; -use hashbrown::HashSet; use libc::c_void; -use std::{any::Any, cell::Cell, sync::Arc}; +use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::{ProtectedCaller, Token, UserTrapper}, - error::RuntimeResult, - export::Context, - module::{ExportIndex, ModuleInfo, ModuleInner}, - types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value}, - vm::{self, ImportBacking}, + backend::RunnableModule, + module::ModuleInfo, + typed_func::{Wasm, WasmTrapInfo}, + types::{LocalFuncIndex, SigIndex}, + vm, }; #[cfg(unix)] @@ -28,164 +27,89 @@ thread_local! { pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } -pub struct Trapper; - -impl UserTrapper for Trapper { - unsafe fn do_early_trap(&self, data: Box) -> ! { - TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); - trigger_trap() - } +pub enum CallProtError { + Trap(WasmTrapInfo), + Error(Box), } pub struct Caller { - func_export_set: HashSet, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, } impl Caller { pub fn new( - module: &ModuleInfo, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, ) -> Self { - let mut func_export_set = HashSet::new(); - for export_index in module.exports.values() { - if let ExportIndex::Func(func_index) = export_index { - func_export_set.insert(*func_index); - } - } - if let Some(start_func_index) = module.start_func { - func_export_set.insert(start_func_index); - } - Self { - func_export_set, handler_data, trampolines, + resolver, } } } -impl ProtectedCaller for Caller { - fn call( - &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - let (func_ptr, ctx, signature, sig_index) = - get_func_from_index(&module, import_backing, func_index); +impl RunnableModule for Caller { + fn get_func(&self, _: &ModuleInfo, func_index: LocalFuncIndex) -> Option> { + self.resolver.lookup(func_index) + } - let vmctx_ptr = match ctx { - Context::External(external_vmctx) => external_vmctx, - Context::Internal => vmctx, - }; + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + unsafe extern "C" fn invoke( + trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), + ctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + invoke_env: Option>, + ) -> bool { + let handler_data = &*invoke_env.unwrap().cast().as_ptr(); - assert!(self.func_export_set.contains(&func_index)); + #[cfg(not(target_os = "windows"))] + let res = call_protected(handler_data, || { + // Leap of faith. + trampoline(ctx, func, args, rets); + }); - assert!( - signature.returns().len() <= 1, - "multi-value returns not yet supported" - ); + // the trampoline is called from C on windows + #[cfg(target_os = "windows")] + let res = call_protected(handler_data, trampoline, ctx, func, args, rets); - assert!( - signature.check_param_value_types(params), - "incorrect signature" - ); - - let param_vec: Vec = params - .iter() - .map(|val| match val { - Value::I32(x) => *x as u64, - Value::I64(x) => *x as u64, - Value::F32(x) => x.to_bits() as u64, - Value::F64(x) => x.to_bits(), - }) - .collect(); - - let mut return_vec = vec![0; signature.returns().len()]; + match res { + Err(err) => { + match err { + CallProtError::Trap(info) => *trap_info = info, + CallProtError::Error(data) => *user_error = Some(data), + } + false + } + Ok(()) => true, + } + } let trampoline = self .trampolines .lookup(sig_index) .expect("that trampoline doesn't exist"); - #[cfg(not(target_os = "windows"))] - call_protected(&self.handler_data, || unsafe { - // Leap of faith. - trampoline( - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - ); - })?; - - // the trampoline is called from C on windows - #[cfg(target_os = "windows")] - call_protected( - &self.handler_data, - trampoline, - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - )?; - - Ok(return_vec - .iter() - .zip(signature.returns().iter()) - .map(|(&x, ty)| match ty { - Type::I32 => Value::I32(x as i32), - Type::I64 => Value::I64(x as i64), - Type::F32 => Value::F32(f32::from_bits(x as u32)), - Type::F64 => Value::F64(f64::from_bits(x as u64)), - }) - .collect()) - } - - fn get_early_trapper(&self) -> Box { - Box::new(Trapper) - } -} - -fn get_func_from_index<'a>( - module: &'a ModuleInner, - import_backing: &ImportBacking, - func_index: FuncIndex, -) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) { - let sig_index = *module - .info - .func_assoc - .get(func_index) - .expect("broken invariant, incorrect func index"); - - let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { - LocalOrImport::Local(local_func_index) => ( - module - .func_resolver - .get(&module, local_func_index) - .expect("broken invariant, func resolver not synced with module.exports") - .cast() - .as_ptr() as *const _, - Context::Internal, - ), - LocalOrImport::Import(imported_func_index) => { - let imported_func = import_backing.imported_func(imported_func_index); - ( - imported_func.func as *const _, - Context::External(imported_func.vmctx), + Some(unsafe { + Wasm::from_raw_parts( + trampoline, + invoke, + Some(NonNull::from(&self.handler_data).cast()), ) - } - }; + }) + } - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) + unsafe fn do_early_trap(&self, data: Box) -> ! { + TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); + trigger_trap() + } } unsafe impl Send for HandlerData {} diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index feaf4e2e2..2f12713fa 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -1,5 +1,5 @@ //! Installing signal handlers allows us to handle traps and out-of-bounds memory -//! accesses that occur when runniing webassembly. +//! accesses that occur when running WebAssembly. //! //! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 //! @@ -10,7 +10,7 @@ //! unless you have memory unsafety elsewhere in your code. //! use crate::relocation::{TrapCode, TrapData}; -use crate::signal::HandlerData; +use crate::signal::{CallProtError, HandlerData}; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, @@ -18,7 +18,7 @@ use nix::sys::signal::{ use std::cell::{Cell, UnsafeCell}; use std::ptr; use std::sync::Once; -use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; +use wasmer_runtime_core::typed_func::WasmTrapInfo; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -62,7 +62,10 @@ pub unsafe fn trigger_trap() -> ! { longjmp(jmp_buf as *mut c_void, 0) } -pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult { +pub fn call_protected( + handler_data: &HandlerData, + f: impl FnOnce() -> T, +) -> Result { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let prev_jmp_buf = *jmp_buf; @@ -76,7 +79,7 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R *jmp_buf = prev_jmp_buf; if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(RuntimeError::Panic { data }) + Err(CallProtError::Error(data)) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); @@ -85,33 +88,18 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R srcloc: _, }) = handler_data.lookup(inst_ptr) { - Err(match Signal::from_c_int(signum) { + Err(CallProtError::Trap(match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { - TrapCode::BadSignature => RuntimeError::Trap { - msg: "incorrect call_indirect signature".into(), - }, - TrapCode::IndirectCallToNull => RuntimeError::Trap { - msg: "indirect call to null".into(), - }, - TrapCode::HeapOutOfBounds => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, - TrapCode::TableOutOfBounds => RuntimeError::Trap { - msg: "table out-of-bounds access".into(), - }, - _ => RuntimeError::Trap { - msg: "unknown trap".into(), - }, - }, - Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, - Ok(SIGFPE) => RuntimeError::Trap { - msg: "illegal arithmetic operation".into(), + TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature, + TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB, + TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds, + TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB, + _ => WasmTrapInfo::Unknown, }, + Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds, + Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic, _ => unimplemented!(), - } - .into()) + })) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -119,13 +107,11 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R Ok(SIGSEGV) => "segmentation violation", Ok(SIGBUS) => "bus error", Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", + _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Trap { - msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), - } - .into()) + let s = format!("unknown trap at {:p} - {}", faulting_addr, signal); + Err(CallProtError::Error(Box::new(s))) } } } else { diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index 99145869e..d755cd575 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -1,20 +1,24 @@ use crate::relocation::{TrapCode, TrapData}; -use crate::signal::HandlerData; +use crate::signal::{CallProtError, HandlerData}; use crate::trampoline::Trampoline; use std::cell::Cell; use std::ffi::c_void; -use std::ptr; -use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; +use std::ptr::{self, NonNull}; +use wasmer_runtime_core::typed_func::WasmTrapInfo; use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Func; use wasmer_win_exception_handler::CallProtectedData; pub use wasmer_win_exception_handler::_call_protected; use winapi::shared::minwindef::DWORD; use winapi::um::minwinbase::{ - EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, + EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, + EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, - EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION, - EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW, + EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, + EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, + EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, + EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, + EXCEPTION_STACK_OVERFLOW, }; thread_local! { @@ -25,10 +29,10 @@ pub fn call_protected( handler_data: &HandlerData, trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, -) -> RuntimeResult<()> { +) -> Result<(), CallProtError> { // TODO: trap early // user code error // if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { @@ -42,7 +46,7 @@ pub fn call_protected( } let CallProtectedData { - code: signum, + code, exception_address, instruction_pointer, } = result.unwrap_err(); @@ -52,40 +56,24 @@ pub fn call_protected( srcloc: _, }) = handler_data.lookup(instruction_pointer as _) { - Err(match signum as DWORD { - EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, + Err(CallProtError::Trap(match code as DWORD { + EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds, EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode { - TrapCode::BadSignature => RuntimeError::Trap { - msg: "incorrect call_indirect signature".into(), - }, - TrapCode::IndirectCallToNull => RuntimeError::Trap { - msg: "indirect call to null".into(), - }, - TrapCode::HeapOutOfBounds => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, - TrapCode::TableOutOfBounds => RuntimeError::Trap { - msg: "table out-of-bounds access".into(), - }, - _ => RuntimeError::Trap { - msg: "unknown trap".into(), - }, + TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature, + TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB, + TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds, + TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB, + TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable, + _ => WasmTrapInfo::Unknown, }, - EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap { - msg: "stack overflow trap".into(), - }, - EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap { - msg: "illegal arithmetic operation".into(), - }, - _ => RuntimeError::Trap { - msg: "unknown trap".into(), - }, - } - .into()) + EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown, + EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => { + WasmTrapInfo::IllegalArithmetic + } + _ => WasmTrapInfo::Unknown, + })) } else { - let signal = match signum as DWORD { + let signal = match code as DWORD { EXCEPTION_FLT_DENORMAL_OPERAND | EXCEPTION_FLT_DIVIDE_BY_ZERO | EXCEPTION_FLT_INEXACT_RESULT @@ -95,13 +83,28 @@ pub fn call_protected( | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", EXCEPTION_ACCESS_VIOLATION => "segmentation violation", - _ => "unkown trapped signal", + EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment", + EXCEPTION_BREAKPOINT => "breakpoint", + EXCEPTION_SINGLE_STEP => "single step", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded", + EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero", + EXCEPTION_INT_OVERFLOW => "int overflow", + EXCEPTION_PRIV_INSTRUCTION => "priv instruction", + EXCEPTION_IN_PAGE_ERROR => "in page error", + EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception", + EXCEPTION_STACK_OVERFLOW => "stack overflow", + EXCEPTION_GUARD_PAGE => "guard page", + EXCEPTION_INVALID_HANDLE => "invalid handle", + EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock", + _ => "unknown exception code", }; - Err(RuntimeError::Trap { - msg: format!("unknown trap at {} - {}", exception_address, signal).into(), - } - .into()) + let s = format!( + "unhandled trap at {:x} - code #{:x}: {}", + exception_address, code, signal, + ); + + Err(CallProtError::Error(Box::new(s))) } } diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index b20109ef0..09ee4cf74 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -6,8 +6,7 @@ use cranelift_codegen::{ isa, Context, }; use hashbrown::HashMap; -use std::ffi::c_void; -use std::{iter, mem}; +use std::{iter, mem, ptr::NonNull}; use wasmer_runtime_core::{ backend::sys::{Memory, Protect}, module::{ExportIndex, ModuleInfo}, @@ -23,8 +22,7 @@ impl RelocSink for NullRelocSink { fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {} } -pub type Trampoline = - unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void; +pub type Trampoline = unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64); pub struct Trampolines { memory: Memory, diff --git a/lib/dynasm-backend/src/codegen.rs b/lib/dynasm-backend/src/codegen.rs deleted file mode 100644 index 798dea114..000000000 --- a/lib/dynasm-backend/src/codegen.rs +++ /dev/null @@ -1,33 +0,0 @@ -use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller}, - module::ModuleInfo, - structures::Map, - types::{FuncIndex, FuncSig, SigIndex}, -}; -use wasmparser::{Operator, Type as WpType}; - -pub trait ModuleCodeGenerator { - fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError>; - fn next_function(&mut self) -> Result<&mut FCG, CodegenError>; - fn finalize(self, module_info: &ModuleInfo) -> Result<(PC, FR), CodegenError>; - fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError>; - fn feed_function_signatures( - &mut self, - assoc: Map, - ) -> Result<(), CodegenError>; - fn feed_import_function(&mut self) -> Result<(), CodegenError>; -} - -pub trait FunctionCodeGenerator { - fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError>; - fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError>; - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError>; - fn begin_body(&mut self) -> Result<(), CodegenError>; - fn feed_opcode(&mut self, op: Operator, module_info: &ModuleInfo) -> Result<(), CodegenError>; - fn finalize(&mut self) -> Result<(), CodegenError>; -} - -#[derive(Debug)] -pub struct CodegenError { - pub message: &'static str, -} diff --git a/lib/dynasm-backend/src/codegen_x64.rs b/lib/dynasm-backend/src/codegen_x64.rs deleted file mode 100644 index ef056a695..000000000 --- a/lib/dynasm-backend/src/codegen_x64.rs +++ /dev/null @@ -1,5261 +0,0 @@ -#![allow(clippy::forget_copy)] // Used by dynasm. - -use super::codegen::*; -use super::stack::{ - ControlFrame, ControlStack, IfElseState, ScratchRegister, ValueInfo, ValueLocation, ValueStack, -}; -use crate::protect_unix; -use byteorder::{ByteOrder, LittleEndian}; -use dynasmrt::{ - x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer, -}; -use std::cell::RefCell; -use std::ptr::NonNull; -use std::{any::Any, collections::HashMap, sync::Arc}; -use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller, Token, UserTrapper}, - error::{RuntimeError, RuntimeResult}, - memory::MemoryType, - module::{ModuleInfo, ModuleInner}, - structures::{Map, TypedIndex}, - types::{ - FuncIndex, FuncSig, ImportedMemoryIndex, LocalFuncIndex, LocalGlobalIndex, - LocalMemoryIndex, LocalOrImport, MemoryIndex, SigIndex, Type, Value, - }, - units::Pages, - vm::{self, ImportBacking, LocalGlobal, LocalMemory, LocalTable}, -}; -use wasmparser::{Operator, Type as WpType}; - -thread_local! { - static CURRENT_EXECUTION_CONTEXT: RefCell> = RefCell::new(Vec::new()); -} - -lazy_static! { - static ref CALL_WASM: unsafe extern "C" fn( - params: *const u8, - params_len: usize, - target: *const u8, - memory_base: *mut u8, - memory_size_pages: usize, - vmctx: *mut vm::Ctx - ) -> i64 = { - let mut assembler = Assembler::new().unwrap(); - let offset = assembler.offset(); - dynasm!( - assembler - ; push rbx - ; push r12 - ; push r13 - ; push r14 - ; push r15 - - ; mov r15, rcx // memory_base - - // Use the upper 16 bits of r15 to store memory size (in pages). This can support memory size up to 4GB. - // Wasmer currently only runs in usermode so here we assume the upper 17 bits of memory base address are all zero. - // FIXME: Change this if want to use this backend in kernel mode. - ; shl r8, 48 - ; or r15, r8 - - ; mov r14, r9 // vmctx - ; lea rax, [>after_call] - ; push rax - ; push rbp - ; mov rbp, rsp - ; sub rsp, rsi // params_len - ; mov rcx, 0 - ; mov r8, rsp - ; _loop: - ; cmp rsi, 0 - ; je >_loop_end - ; mov rax, [rdi] - ; mov [r8], rax - ; add r8, 8 - ; add rdi, 8 - ; sub rsi, 8 - ; jmp <_loop - ; _loop_end: - ; jmp rdx - ; after_call: - ; pop r15 - ; pop r14 - ; pop r13 - ; pop r12 - ; pop rbx - ; ret - ); - let buf = assembler.finalize().unwrap(); - let ret = unsafe { ::std::mem::transmute(buf.ptr(offset)) }; - ::std::mem::forget(buf); - ret - }; - - static ref CONSTRUCT_STACK_AND_CALL_NATIVE: unsafe extern "C" fn (stack_top: *mut u8, stack_base: *mut u8, ctx: *mut vm::Ctx, target: *const vm::Func) -> u64 = { - let mut assembler = Assembler::new().unwrap(); - let offset = assembler.offset(); - dynasm!( - assembler - ; push r15 - ; push r14 - ; push r13 - ; push r12 - ; push r11 - ; push rbp - ; mov rbp, rsp - - ; mov r15, rdi - ; mov r14, rsi - ; mov r13, rdx - ; mov r12, rcx - - ; mov rdi, r13 // ctx - - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov rsi, [r14] - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov rdx, [r14] - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov rcx, [r14] - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov r8, [r14] - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov r9, [r14] - ; sub r14, 8 - ; cmp r14, r15 - ; jb >stack_ready - - ; mov rax, r14 - ; sub rax, r15 - ; sub rsp, rax - ; sub rsp, 8 - ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 - ; and rsp, rax - ; mov rax, rsp - ; loop_begin: - ; mov r11, [r14] - ; mov [rax], r11 - ; sub r14, 8 - ; add rax, 8 - ; cmp r14, r15 - ; jb >stack_ready - ; jmp Register { - use self::Register::*; - match sr.raw_id() { - 0 => RDI, - 1 => RSI, - 2 => RDX, - 3 => RCX, - 4 => R8, - 5 => R9, - 6 => R10, - 7 => R11, - 8 => RBX, - 9 => R12, - // 10 => R13, // R13 is reserved as temporary register. - // 11 => R14, // R14 is reserved for vmctx. - // 12 => R15, // R15 is reserved for memory base pointer. - _ => unreachable!(), - } - } - - pub fn is_used(&self, stack: &ValueStack) -> bool { - for val in &stack.values { - match val.location { - ValueLocation::Register(x) => { - if Register::from_scratch_reg(x) == *self { - return true; - } - } - ValueLocation::Stack => break, - } - } - - false - } -} - -#[allow(dead_code)] -pub struct NativeTrampolines { - memory_size_dynamic_local: DynamicLabel, - memory_size_static_local: DynamicLabel, - memory_size_shared_local: DynamicLabel, - memory_size_dynamic_import: DynamicLabel, - memory_size_static_import: DynamicLabel, - memory_size_shared_import: DynamicLabel, - memory_grow_dynamic_local: DynamicLabel, - memory_grow_static_local: DynamicLabel, - memory_grow_shared_local: DynamicLabel, - memory_grow_dynamic_import: DynamicLabel, - memory_grow_static_import: DynamicLabel, - memory_grow_shared_import: DynamicLabel, -} - -pub struct X64ModuleCodeGenerator { - functions: Vec, - signatures: Option>>, - function_signatures: Option>>, - function_labels: Option)>>, - assembler: Option, - native_trampolines: Arc, - func_import_count: usize, -} - -pub struct X64FunctionCode { - signatures: Arc>, - function_signatures: Arc>, - native_trampolines: Arc, - - begin_offset: AssemblyOffset, - assembler: Option, - function_labels: Option)>>, - br_table_data: Option>>, - returns: Vec, - locals: Vec, - num_params: usize, - current_stack_offset: usize, - value_stack: ValueStack, - control_stack: Option, - unreachable_depth: usize, -} - -enum FuncPtrInner {} -#[repr(transparent)] -#[derive(Copy, Clone, Debug)] -struct FuncPtr(*const FuncPtrInner); -unsafe impl Send for FuncPtr {} -unsafe impl Sync for FuncPtr {} - -pub struct X64ExecutionContext { - code: ExecutableBuffer, - functions: Vec, - signatures: Arc>, - function_signatures: Arc>, - function_pointers: Vec, - _br_table_data: Vec>, - func_import_count: usize, -} - -pub struct X64RuntimeResolver { - _code: ExecutableBuffer, - local_pointers: Vec, -} - -impl X64ExecutionContext { - fn get_runtime_resolver( - &self, - module_info: &ModuleInfo, - ) -> Result { - let mut assembler = Assembler::new().unwrap(); - let mut offsets: Vec = vec![]; - - for i in self.func_import_count..self.function_pointers.len() { - offsets.push(assembler.offset()); - X64FunctionCode::emit_managed_call_trampoline( - &mut assembler, - module_info, - self.function_pointers[i], - self.signatures[self.function_signatures[FuncIndex::new(i)]] - .params() - .len(), - )?; - } - - let code = assembler.finalize().unwrap(); - let local_pointers: Vec = - offsets.iter().map(|x| FuncPtr(code.ptr(*x) as _)).collect(); - - Ok(X64RuntimeResolver { - _code: code, - local_pointers: local_pointers, - }) - } -} - -impl FuncResolver for X64RuntimeResolver { - fn get( - &self, - _module: &ModuleInner, - _local_func_index: LocalFuncIndex, - ) -> Option> { - NonNull::new(self.local_pointers[_local_func_index.index() as usize].0 as *mut vm::Func) - } -} - -impl ProtectedCaller for X64ExecutionContext { - fn call( - &self, - _module: &ModuleInner, - _func_index: FuncIndex, - _params: &[Value], - _import_backing: &ImportBacking, - _vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - let index = _func_index.index() - self.func_import_count; - let ptr = self.code.ptr(self.functions[index].begin_offset); - let return_ty = self.functions[index].returns.last().cloned(); - - if self.functions[index].num_params != _params.len() { - return Err(RuntimeError::Trap { - msg: "param count mismatch".into(), - }); - } - - let f = &self.functions[index]; - let total_size = f.num_params * 8; - - if f.num_params > 0 && f.locals[f.num_params - 1].stack_offset != total_size { - panic!("internal error: inconsistent stack layout"); - } - - let mut param_buf: Vec = vec![0; total_size]; - for i in 0..f.num_params { - let local = &f.locals[i]; - let buf = &mut param_buf[total_size - local.stack_offset..]; - let size = get_size_of_type(&local.ty).unwrap(); - - if is_dword(size) { - match _params[i] { - Value::I32(x) => LittleEndian::write_u32(buf, x as u32), - Value::F32(x) => LittleEndian::write_u32(buf, f32::to_bits(x)), - _ => { - return Err(RuntimeError::Trap { - msg: "signature mismatch".into(), - }); - } - } - } else { - match _params[i] { - Value::I64(x) => LittleEndian::write_u64(buf, x as u64), - Value::F64(x) => LittleEndian::write_u64(buf, f64::to_bits(x)), - _ => { - return Err(RuntimeError::Trap { - msg: "signature mismatch".into(), - }); - } - } - } - } - - let (memory_base, memory_size): (*mut u8, usize) = if _module.info.memories.len() > 0 { - if _module.info.memories.len() != 1 || _module.info.imported_memories.len() != 0 { - return Err(RuntimeError::Trap { - msg: "only one linear memory is supported".into(), - }); - } - unsafe { - let vmctx = _vmctx as *mut vm::InternalCtx; - ((**(*vmctx).memories).base, (**(*vmctx).memories).bound) - } - } else if _module.info.imported_memories.len() > 0 { - if _module.info.memories.len() != 0 || _module.info.imported_memories.len() != 1 { - return Err(RuntimeError::Trap { - msg: "only one linear memory is supported".into(), - }); - } - unsafe { - let vmctx = _vmctx as *mut vm::InternalCtx; - ( - (**(*vmctx).imported_memories).base, - (**(*vmctx).imported_memories).bound, - ) - } - } else { - (::std::ptr::null_mut(), 0) - }; - //println!("MEMORY = {:?}", memory_base); - - CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().push(self)); - - let ret = unsafe { - protect_unix::call_protected(|| { - CALL_WASM( - param_buf.as_ptr(), - param_buf.len(), - ptr, - memory_base, - memory_size.wrapping_shr(16), - _vmctx, - ) - }) - }; - - CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().pop().unwrap()); - - let ret = ret?; - - Ok(if let Some(ty) = return_ty { - vec![match ty { - WpType::I32 => Value::I32(ret as i32), - WpType::I64 => Value::I64(ret), - WpType::F32 => Value::F32(f32::from_bits(ret as i32 as u32)), - WpType::F64 => Value::F64(f64::from_bits(ret as u64)), - _ => unreachable!(), - }] - } else { - vec![] - }) - } - - fn get_early_trapper(&self) -> Box { - pub struct Trapper; - - impl UserTrapper for Trapper { - unsafe fn do_early_trap(&self, _data: Box) -> ! { - panic!("do_early_trap"); - } - } - - Box::new(Trapper) - } -} - -#[derive(Copy, Clone, Debug)] -struct Local { - ty: WpType, - stack_offset: usize, -} - -impl X64ModuleCodeGenerator { - pub fn new() -> X64ModuleCodeGenerator { - let mut assembler = Assembler::new().unwrap(); - let nt = NativeTrampolines { - memory_size_dynamic_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::DynamicLocal, - 0usize, - ), - memory_size_static_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::StaticLocal, - 0usize, - ), - memory_size_shared_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::SharedLocal, - 0usize, - ), - memory_size_dynamic_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::DynamicImport, - 0usize, - ), - memory_size_static_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::StaticImport, - 0usize, - ), - memory_size_shared_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_size, - MemoryKind::SharedImport, - 0usize, - ), - memory_grow_dynamic_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::DynamicLocal, - 0usize, - ), - memory_grow_static_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::StaticLocal, - 0usize, - ), - memory_grow_shared_local: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::SharedLocal, - 0usize, - ), - memory_grow_dynamic_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::DynamicImport, - 0usize, - ), - memory_grow_static_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::StaticImport, - 0usize, - ), - memory_grow_shared_import: X64FunctionCode::emit_native_call_trampoline( - &mut assembler, - _memory_grow, - MemoryKind::SharedImport, - 0usize, - ), - }; - - X64ModuleCodeGenerator { - functions: vec![], - signatures: None, - function_signatures: None, - function_labels: Some(HashMap::new()), - assembler: Some(assembler), - native_trampolines: Arc::new(nt), - func_import_count: 0, - } - } -} - -impl ModuleCodeGenerator - for X64ModuleCodeGenerator -{ - fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { - Ok(()) - } - - fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> { - let (mut assembler, mut function_labels, br_table_data) = match self.functions.last_mut() { - Some(x) => ( - x.assembler.take().unwrap(), - x.function_labels.take().unwrap(), - x.br_table_data.take().unwrap(), - ), - None => ( - self.assembler.take().unwrap(), - self.function_labels.take().unwrap(), - vec![], - ), - }; - let begin_offset = assembler.offset(); - let begin_label_info = function_labels - .entry(self.functions.len() + self.func_import_count) - .or_insert_with(|| (assembler.new_dynamic_label(), None)); - - begin_label_info.1 = Some(begin_offset); - let begin_label = begin_label_info.0; - - dynasm!( - assembler - ; => begin_label - //; int 3 - ); - let code = X64FunctionCode { - signatures: self.signatures.as_ref().unwrap().clone(), - function_signatures: self.function_signatures.as_ref().unwrap().clone(), - native_trampolines: self.native_trampolines.clone(), - - begin_offset: begin_offset, - assembler: Some(assembler), - function_labels: Some(function_labels), - br_table_data: Some(br_table_data), - returns: vec![], - locals: vec![], - num_params: 0, - current_stack_offset: 0, - value_stack: ValueStack::new(4), // FIXME: Use of R8 and above registers generates incorrect assembly. - control_stack: None, - unreachable_depth: 0, - }; - self.functions.push(code); - Ok(self.functions.last_mut().unwrap()) - } - - fn finalize( - mut self, - module_info: &ModuleInfo, - ) -> Result<(X64ExecutionContext, X64RuntimeResolver), CodegenError> { - let (assembler, mut br_table_data) = match self.functions.last_mut() { - Some(x) => (x.assembler.take().unwrap(), x.br_table_data.take().unwrap()), - None => { - return Err(CodegenError { - message: "no function", - }); - } - }; - let output = assembler.finalize().unwrap(); - - for table in &mut br_table_data { - for entry in table { - *entry = output.ptr(AssemblyOffset(*entry)) as usize; - } - } - - let function_labels = if let Some(x) = self.functions.last() { - x.function_labels.as_ref().unwrap() - } else { - self.function_labels.as_ref().unwrap() - }; - let mut out_labels: Vec = vec![]; - - for i in 0..function_labels.len() { - let (_, offset) = match function_labels.get(&i) { - Some(x) => x, - None => { - return Err(CodegenError { - message: "label not found", - }); - } - }; - let offset = match offset { - Some(x) => x, - None => { - return Err(CodegenError { - message: "offset is none", - }); - } - }; - out_labels.push(FuncPtr(output.ptr(*offset) as _)); - } - - let ctx = X64ExecutionContext { - code: output, - functions: self.functions, - _br_table_data: br_table_data, - func_import_count: self.func_import_count, - signatures: match self.signatures { - Some(x) => x, - None => { - return Err(CodegenError { - message: "no signatures", - }); - } - }, - function_pointers: out_labels, - function_signatures: match self.function_signatures { - Some(x) => x, - None => { - return Err(CodegenError { - message: "no function signatures", - }); - } - }, - }; - let resolver = ctx.get_runtime_resolver(module_info)?; - - Ok((ctx, resolver)) - } - - fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { - self.signatures = Some(Arc::new(signatures)); - Ok(()) - } - - fn feed_function_signatures( - &mut self, - assoc: Map, - ) -> Result<(), CodegenError> { - self.function_signatures = Some(Arc::new(assoc)); - Ok(()) - } - - fn feed_import_function(&mut self) -> Result<(), CodegenError> { - let labels = match self.function_labels.as_mut() { - Some(x) => x, - None => { - return Err(CodegenError { - message: "got function import after code", - }); - } - }; - let id = labels.len(); - - let offset = self.assembler.as_mut().unwrap().offset(); - - let label = X64FunctionCode::emit_native_call_trampoline( - self.assembler.as_mut().unwrap(), - invoke_import, - 0, - id, - ); - labels.insert(id, (label, Some(offset))); - - self.func_import_count += 1; - - Ok(()) - } -} - -impl X64FunctionCode { - fn gen_rt_pop(assembler: &mut Assembler, info: &ValueInfo) -> Result<(), CodegenError> { - match info.location { - ValueLocation::Register(_) => {} - ValueLocation::Stack => { - dynasm!( - assembler - ; add rsp, 8 - ); - } - } - Ok(()) - } - - fn emit_reinterpret( - value_stack: &mut ValueStack, - in_ty: WpType, - out_ty: WpType, - ) -> Result<(), CodegenError> { - let val = value_stack.pop()?; - if val.ty != in_ty { - return Err(CodegenError { - message: "reinterpret type mismatch", - }); - } - value_stack.push(out_ty); - Ok(()) - } - - /// Emits a unary operator. - fn emit_unop( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - in_ty: WpType, - out_ty: WpType, - ) -> Result<(), CodegenError> { - let a = value_stack.pop()?; - if a.ty != in_ty { - return Err(CodegenError { - message: "unop(i32) type mismatch", - }); - } - value_stack.push(out_ty); - - match a.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - f(assembler, value_stack, reg); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov rax, [rsp] - ); - f(assembler, value_stack, Register::RAX); - dynasm!( - assembler - ; mov [rsp], rax - ); - } - } - - Ok(()) - } - - fn emit_unop_i32( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - ) -> Result<(), CodegenError> { - Self::emit_unop(assembler, value_stack, f, WpType::I32, WpType::I32) - } - - fn emit_unop_i64( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - ) -> Result<(), CodegenError> { - Self::emit_unop(assembler, value_stack, f, WpType::I64, WpType::I64) - } - - /// Emits a binary operator. - /// - /// Guarantees that the first Register parameter to callback `f` will never be `Register::RAX`. - fn emit_binop( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - in_ty: WpType, - out_ty: WpType, - ) -> Result<(), CodegenError> { - let (a, b) = value_stack.pop2()?; - if a.ty != in_ty || b.ty != in_ty { - return Err(CodegenError { - message: "binop(i32) type mismatch", - }); - } - value_stack.push(out_ty); - - if a.location.is_register() && b.location.is_register() { - // output is in a_reg. - f( - assembler, - value_stack, - Register::from_scratch_reg(a.location.get_register()?), - Register::from_scratch_reg(b.location.get_register()?), - ); - } else if a.location.is_register() { - dynasm!( - assembler - ; pop rax - ); - f( - assembler, - value_stack, - Register::from_scratch_reg(a.location.get_register()?), - Register::RAX, - ); - } else if b.location.is_register() { - unreachable!(); - } else { - dynasm!( - assembler - ; push rcx - ; mov rcx, [rsp + 16] - ; mov rax, [rsp + 8] - ); - f(assembler, value_stack, Register::RCX, Register::RAX); - dynasm!( - assembler - ; mov [rsp + 16], rcx - ; pop rcx - ; add rsp, 8 - ); - } - - Ok(()) - } - - fn emit_binop_i32( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - ) -> Result<(), CodegenError> { - Self::emit_binop(assembler, value_stack, f, WpType::I32, WpType::I32) - } - - fn emit_binop_i64( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - ) -> Result<(), CodegenError> { - Self::emit_binop(assembler, value_stack, f, WpType::I64, WpType::I64) - } - - fn emit_shift( - assembler: &mut Assembler, - value_stack: &ValueStack, - left: Register, - right: Register, - f: F, - ) { - let rcx_used = Register::RCX.is_used(value_stack); - if rcx_used { - dynasm!( - assembler - ; push rcx - ); - } - dynasm!( - assembler - ; mov rcx, Rq(right as u8) - ); - f(assembler, left); - if rcx_used { - dynasm!( - assembler - ; pop rcx - ); - } - } - - fn emit_div_i32( - assembler: &mut Assembler, - value_stack: &ValueStack, - left: Register, - right: Register, - signed: bool, - out: Register, - ) { - let dx_save = - Register::RDX.is_used(value_stack) && left != Register::RDX && right != Register::RDX; - if dx_save { - dynasm!( - assembler - ; push rdx - ); - } - - dynasm!( - assembler - ; push r15 - ; mov r15d, Rd(right as u8) - ; mov eax, Rd(left as u8) - ); - if signed { - dynasm!( - assembler - ; cdq - ; idiv r15d - ); - } else { - dynasm!( - assembler - ; xor edx, edx - ; div r15d - ); - } - dynasm!( - assembler - ; mov Rd(left as u8), Rd(out as u8) - ; pop r15 - ); - - if dx_save { - dynasm!( - assembler - ; pop rdx - ); - } - } - - fn emit_div_i64( - assembler: &mut Assembler, - value_stack: &ValueStack, - left: Register, - right: Register, - signed: bool, - out: Register, - ) { - let dx_save = - Register::RDX.is_used(value_stack) && left != Register::RDX && right != Register::RDX; - if dx_save { - dynasm!( - assembler - ; push rdx - ); - } - - dynasm!( - assembler - ; push r15 - ; mov r15, Rq(right as u8) - ; mov rax, Rq(left as u8) - ); - if signed { - dynasm!( - assembler - ; cqo - ; idiv r15 - ); - } else { - dynasm!( - assembler - ; xor rdx, rdx - ; div r15 - ); - } - dynasm!( - assembler - ; mov Rq(left as u8), Rq(out as u8) - ; pop r15 - ); - - if dx_save { - dynasm!( - assembler - ; pop rdx - ); - } - } - - fn emit_cmp_i32( - assembler: &mut Assembler, - left: Register, - right: Register, - f: F, - ) { - dynasm!( - assembler - ; cmp Rd(left as u8), Rd(right as u8) - ); - f(assembler); - dynasm!( - assembler - ; xor Rd(left as u8), Rd(left as u8) - ; jmp >label_end - ; label_true: - ; mov Rd(left as u8), 1 - ; label_end: - ); - } - - fn emit_cmp_i64( - assembler: &mut Assembler, - left: Register, - right: Register, - f: F, - ) { - dynasm!( - assembler - ; cmp Rq(left as u8), Rq(right as u8) - ); - f(assembler); - dynasm!( - assembler - ; xor Rq(left as u8), Rq(left as u8) - ; jmp >label_end - ; label_true: - ; mov Rq(left as u8), 1 - ; label_end: - ); - } - - fn emit_peek_into_ax( - assembler: &mut Assembler, - value_stack: &ValueStack, - ) -> Result { - let val = match value_stack.values.last() { - Some(x) => *x, - None => { - return Err(CodegenError { - message: "no value", - }); - } - }; - match val.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov rax, Rq(reg as u8) - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov rax, [rsp] - ); - } - } - - Ok(val.ty) - } - - fn emit_pop_into_reg( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - target: Register, - ) -> Result { - let val = value_stack.pop()?; - match val.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rq(target as u8), Rq(reg as u8) - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; pop Rq(target as u8) - ); - } - } - - Ok(val.ty) - } - - fn emit_pop_into_ax( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - ) -> Result { - Self::emit_pop_into_reg(assembler, value_stack, Register::RAX) - } - - fn emit_push_from_reg( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - ty: WpType, - source: Register, - ) -> Result<(), CodegenError> { - let loc = value_stack.push(ty); - match loc { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rq(reg as u8), Rq(source as u8) - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; push Rq(source as u8) - ); - } - } - - Ok(()) - } - - fn emit_push_from_ax( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - ty: WpType, - ) -> Result<(), CodegenError> { - Self::emit_push_from_reg(assembler, value_stack, ty, Register::RAX) - } - - fn emit_leave_frame( - assembler: &mut Assembler, - frame: &ControlFrame, - value_stack: &mut ValueStack, - peek: bool, - ) -> Result<(), CodegenError> { - let ret_ty = match frame.returns.len() { - 1 => Some(frame.returns[0]), - 0 => None, - _ => { - return Err(CodegenError { - message: "more than one block returns are not yet supported", - }); - } - }; - - if value_stack.values.len() < frame.value_stack_depth_before + frame.returns.len() { - return Err(CodegenError { - message: "value stack underflow", - }); - } - - if let Some(_) = ret_ty { - if value_stack.values.iter().last().map(|x| x.ty) != ret_ty { - return Err(CodegenError { - message: "value type != return type", - }); - } - if peek { - Self::emit_peek_into_ax(assembler, value_stack)?; - } else { - Self::emit_pop_into_ax(assembler, value_stack)?; - } - } - - Ok(()) - } - - fn emit_else( - assembler: &mut Assembler, - control_stack: &mut ControlStack, - value_stack: &mut ValueStack, - was_unreachable: bool, - ) -> Result<(), CodegenError> { - let frame = match control_stack.frames.last_mut() { - Some(x) => x, - None => { - return Err(CodegenError { - message: "no frame (else)", - }); - } - }; - - if !was_unreachable { - Self::emit_leave_frame(assembler, frame, value_stack, false)?; - if value_stack.values.len() != frame.value_stack_depth_before { - return Err(CodegenError { - message: "value_stack.values.len() != frame.value_stack_depth_before", - }); - } - } else { - // No need to actually unwind the stack here. - value_stack.reset_depth(frame.value_stack_depth_before); - } - - match frame.if_else { - IfElseState::If(label) => { - dynasm!( - assembler - ; jmp =>frame.label - ; => label - ); - frame.if_else = IfElseState::Else; - } - _ => { - return Err(CodegenError { - message: "unexpected if else state", - }); - } - } - - Ok(()) - } - - fn emit_block_end( - assembler: &mut Assembler, - control_stack: &mut ControlStack, - value_stack: &mut ValueStack, - was_unreachable: bool, - ) -> Result<(), CodegenError> { - let frame = match control_stack.frames.pop() { - Some(x) => x, - None => { - return Err(CodegenError { - message: "no frame (block end)", - }); - } - }; - - if !was_unreachable { - Self::emit_leave_frame(assembler, &frame, value_stack, false)?; - if value_stack.values.len() != frame.value_stack_depth_before { - return Err(CodegenError { - message: "value_stack.values.len() != frame.value_stack_depth_before", - }); - } - } else { - // No need to actually unwind the stack here. - value_stack.reset_depth(frame.value_stack_depth_before); - } - - if !frame.loop_like { - match frame.if_else { - IfElseState::None | IfElseState::Else => { - dynasm!( - assembler - ; => frame.label - ); - } - IfElseState::If(label) => { - dynasm!( - assembler - ; => frame.label - ; => label - ); - - if frame.returns.len() != 0 { - return Err(CodegenError { - message: "if without else, with non-empty returns", - }); - } - } - } - } - - if frame.returns.len() == 1 { - let loc = value_stack.push(frame.returns[0]); - match loc { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rq(reg as u8), rax - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; push rax - ); - } - } - } - - Ok(()) - } - - fn emit_jmp( - assembler: &mut Assembler, - control_stack: &ControlStack, - value_stack: &mut ValueStack, - relative_frame_offset: usize, - ) -> Result<(), CodegenError> { - let frame = if relative_frame_offset >= control_stack.frames.len() { - return Err(CodegenError { - message: "jmp offset out of bounds", - }); - } else { - &control_stack.frames[control_stack.frames.len() - 1 - relative_frame_offset] - }; - - if !frame.loop_like { - Self::emit_leave_frame(assembler, frame, value_stack, true)?; - } - - let mut sp_diff: usize = 0; - - for i in 0..value_stack.values.len() - frame.value_stack_depth_before { - let vi = value_stack.values[value_stack.values.len() - 1 - i]; - if vi.location == ValueLocation::Stack { - sp_diff += 8 - } else { - break; - } - } - - dynasm!( - assembler - ; add rsp, sp_diff as i32 - ; jmp =>frame.label - ); - - Ok(()) - } - - fn emit_return( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - returns: &Vec, - ) -> Result<(), CodegenError> { - match returns.len() { - 0 => {} - 1 => { - if value_stack.values.iter().last().map(|x| x.ty) != Some(returns[0]) { - return Err(CodegenError { - message: "self.value_stack.last().cloned() != Some(self.returns[0])", - }); - } - Self::emit_pop_into_ax(assembler, value_stack)?; - } - _ => { - return Err(CodegenError { - message: "multiple return values is not yet supported", - }); - } - } - - dynasm!( - assembler - ; mov rsp, rbp - ; pop rbp - ; ret - ); - - Ok(()) - } - - fn emit_update_memory_from_ctx( - assembler: &mut Assembler, - info: &ModuleInfo, - ) -> Result<(), CodegenError> { - if info.memories.len() > 0 { - if info.memories.len() != 1 || info.imported_memories.len() != 0 { - return Err(CodegenError { - message: "only one linear memory is supported", - }); - } - dynasm!( - assembler - ; mov r15, r14 => vm::InternalCtx.memories - ); - } else if info.imported_memories.len() > 0 { - if info.memories.len() != 0 || info.imported_memories.len() != 1 { - return Err(CodegenError { - message: "only one linear memory is supported", - }); - } - dynasm!( - assembler - ; mov r15, r14 => vm::InternalCtx.imported_memories - ); - } else { - return Ok(()); - }; - - dynasm!( - assembler - ; mov r15, [r15] - ; mov r13, r15 => LocalMemory.bound - ; shr r13, 16 // 65536 bytes per page - ; shl r13, 48 - ; mov r15, r15 => LocalMemory.base - ; or r15, r13 - ); - Ok(()) - } - - fn emit_managed_call_trampoline( - assembler: &mut Assembler, - info: &ModuleInfo, - target: FuncPtr, - num_params: usize, - ) -> Result<(), CodegenError> { - dynasm!( - assembler - ; push rbp - ; mov rbp, rsp - ); - - for i in 0..num_params { - match i { - i if i < 5 => { - let reg = match i { - 0 => Register::RSI, - 1 => Register::RDX, - 2 => Register::RCX, - 3 => Register::R8, - 4 => Register::R9, - _ => unreachable!(), - }; - dynasm!( - assembler - ; push Rq(reg as u8) - ); - } - i => { - let offset = (i - 5) * 8; - dynasm!( - assembler - ; mov rax, [rbp + (16 + offset) as i32] - ; push rax - ); - } - } - } - - dynasm!( - assembler - ; mov r9, rdi // vmctx - ; mov rdx, QWORD target.0 as usize as i64 - ; mov rsi, QWORD (num_params * 8) as i64 - ; mov rdi, rsp - ); - - let has_memory = if info.memories.len() > 0 { - if info.memories.len() != 1 || info.imported_memories.len() != 0 { - return Err(CodegenError { - message: "only one linear memory is supported", - }); - } - dynasm!( - assembler - ; mov rcx, r9 => vm::InternalCtx.memories - ); - true - } else if info.imported_memories.len() > 0 { - if info.memories.len() != 0 || info.imported_memories.len() != 1 { - return Err(CodegenError { - message: "only one linear memory is supported", - }); - } - dynasm!( - assembler - ; mov rcx, r9 => vm::InternalCtx.imported_memories - ); - true - } else { - false - }; - - if has_memory { - dynasm!( - assembler - ; mov rcx, [rcx] - ; mov r8, rcx => LocalMemory.bound - ; shr r8, 16 // 65536 bytes per page - ; mov rcx, rcx => LocalMemory.base - ); - } else { - dynasm!( - assembler - ; mov rcx, 0 - ); - } - - dynasm!( - assembler - ; mov rax, QWORD *CALL_WASM as usize as i64 - ; call rax - ; mov rsp, rbp - ; pop rbp - ; ret - ); - - Ok(()) - } - - fn emit_f32_int_conv_check( - assembler: &mut Assembler, - reg: Register, - lower_bound: f32, - upper_bound: f32, - ) { - let lower_bound = f32::to_bits(lower_bound); - let upper_bound = f32::to_bits(upper_bound); - - dynasm!( - assembler - ; movq xmm5, r15 - - // underflow - ; movd xmm1, Rd(reg as u8) - ; mov r15d, lower_bound as i32 - ; movd xmm2, r15d - ; vcmpltss xmm0, xmm1, xmm2 - ; movd r15d, xmm0 - ; cmp r15d, 1 - ; je >trap - - // overflow - ; mov r15d, upper_bound as i32 - ; movd xmm2, r15d - ; vcmpgtss xmm0, xmm1, xmm2 - ; movd r15d, xmm0 - ; cmp r15d, 1 - ; je >trap - - // NaN - ; vcmpeqss xmm0, xmm1, xmm1 - ; movd r15d, xmm0 - ; cmp r15d, 0 - ; je >trap - - ; movq r15, xmm5 - ; jmp >ok - - ; trap: - ; ud2 - - ; ok: - ); - } - - fn emit_f64_int_conv_check( - assembler: &mut Assembler, - reg: Register, - lower_bound: f64, - upper_bound: f64, - ) { - let lower_bound = f64::to_bits(lower_bound); - let upper_bound = f64::to_bits(upper_bound); - - dynasm!( - assembler - ; movq xmm5, r15 - - // underflow - ; movq xmm1, Rq(reg as u8) - ; mov r15, QWORD lower_bound as i64 - ; movq xmm2, r15 - ; vcmpltsd xmm0, xmm1, xmm2 - ; movd r15d, xmm0 - ; cmp r15d, 1 - ; je >trap - - // overflow - ; mov r15, QWORD upper_bound as i64 - ; movq xmm2, r15 - ; vcmpgtsd xmm0, xmm1, xmm2 - ; movd r15d, xmm0 - ; cmp r15d, 1 - ; je >trap - - // NaN - ; vcmpeqsd xmm0, xmm1, xmm1 - ; movd r15d, xmm0 - ; cmp r15d, 0 - ; je >trap - - ; movq r15, xmm5 - ; jmp >ok - - ; trap: - ; ud2 - - ; ok: - ); - } - - fn emit_native_call_trampoline( - assembler: &mut Assembler, - target: unsafe extern "C" fn( - ctx1: A, - ctx2: B, - stack_top: *mut u8, - stack_base: *mut u8, - vmctx: *mut vm::Ctx, - memory_base: *mut u8, - ) -> u64, - ctx1: A, - ctx2: B, - ) -> DynamicLabel { - let label = assembler.new_dynamic_label(); - - dynasm!( - assembler - ; =>label - ); - - // FIXME: Check at compile time. - assert_eq!(::std::mem::size_of::(), ::std::mem::size_of::()); - assert_eq!(::std::mem::size_of::(), ::std::mem::size_of::()); - - dynasm!( - assembler - ; mov rdi, QWORD unsafe { ::std::mem::transmute_copy::(&ctx1) } - ; mov rsi, QWORD unsafe { ::std::mem::transmute_copy::(&ctx2) } - ; mov rdx, rsp - ; mov rcx, rbp - ; mov r8, r14 // vmctx - ; mov r9, r15 // memory_base - ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 - ; and rsp, rax - ; mov rax, QWORD target as i64 - ; call rax - ; mov rsp, rbp - ; pop rbp - ; ret - ); - - label - } - - fn emit_call_raw( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - target: DynamicLabel, - params: &[WpType], - returns: &[WpType], - ) -> Result<(), CodegenError> { - let total_size: usize = params.len() * 8; - - if params.len() > value_stack.values.len() { - return Err(CodegenError { - message: "value stack underflow in call", - }); - } - - let mut saved_regs: Vec = Vec::new(); - - for v in &value_stack.values[0..value_stack.values.len() - params.len()] { - match v.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; push Rq(reg as u8) - ); - saved_regs.push(reg); - } - ValueLocation::Stack => break, - } - } - - dynasm!( - assembler - ; lea rax, [>after_call] // TODO: Is this correct? - ; push rax - ; push rbp - ); - - if total_size != 0 { - dynasm!( - assembler - ; sub rsp, total_size as i32 - ); - } - - let mut offset: usize = 0; - let mut caller_stack_offset: usize = 0; - for ty in params.iter().rev() { - let val = value_stack.pop()?; - if val.ty != *ty { - return Err(CodegenError { - message: "value type mismatch in call", - }); - } - - match val.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov [rsp + offset as i32], Rq(reg as u8) - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov rax, [rsp + (total_size + 16 + saved_regs.len() * 8 + caller_stack_offset) as i32] - ; mov [rsp + offset as i32], rax - ); - caller_stack_offset += 8; - } - } - - offset += 8; - } - - assert_eq!(offset, total_size); - - dynasm!( - assembler - ; mov rbp, rsp - ); - if total_size != 0 { - dynasm!( - assembler - ; add rbp, total_size as i32 - ); - } - dynasm!( - assembler - ; jmp =>target - ; after_call: - ); - - for reg in saved_regs.iter().rev() { - dynasm!( - assembler - ; pop Rq(*reg as u8) - ); - } - - if caller_stack_offset != 0 { - dynasm!( - assembler - ; add rsp, caller_stack_offset as i32 - ); - } - - match returns.len() { - 0 => {} - 1 => { - Self::emit_push_from_ax(assembler, value_stack, returns[0])?; - } - _ => { - return Err(CodegenError { - message: "more than 1 function returns are not supported", - }); - } - } - - Ok(()) - } - - fn emit_memory_bound_check_if_needed( - assembler: &mut Assembler, - module_info: &ModuleInfo, - offset_reg: Register, - value_size: usize, - ) { - let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) { - LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index], - LocalOrImport::Import(import_mem_index) => { - &module_info.imported_memories[import_mem_index].1 - } - }; - let need_check = match mem_desc.memory_type() { - MemoryType::Dynamic => true, - MemoryType::Static | MemoryType::SharedStatic => false, - }; - if need_check { - dynasm!( - assembler - ; movq xmm5, r14 - ; lea r14, [Rq(offset_reg as u8) + value_size as i32] // overflow isn't possible since offset_reg contains a 32-bit value. - - ; mov r13, r15 - ; shr r13, 48 - ; shl r13, 16 - ; cmp r14, r13 - ; ja >out_of_bounds - ; jmp >ok - - ; out_of_bounds: - ; ud2 - ; ok: - ; movq r14, xmm5 - ); - } - } - - fn emit_memory_load( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - out_ty: WpType, - module_info: &ModuleInfo, - read_size: usize, - ) -> Result<(), CodegenError> { - let addr_info = value_stack.pop()?; - let out_loc = value_stack.push(out_ty); - - if addr_info.ty != WpType::I32 { - return Err(CodegenError { - message: "memory address must be i32", - }); - } - - assert_eq!(out_loc, addr_info.location); - - match addr_info.location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rd(reg as u8), Rd(reg as u8) - ); - Self::emit_memory_bound_check_if_needed(assembler, module_info, reg, read_size); - dynasm!( - assembler - ; add Rq(reg as u8), r15 - ; shl Rq(reg as u8), 16 - ; shr Rq(reg as u8), 16 - ); - f(assembler, reg); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; pop rax - ; mov eax, eax - ); - Self::emit_memory_bound_check_if_needed( - assembler, - module_info, - Register::RAX, - read_size, - ); - dynasm!( - assembler - ; add rax, r15 - ; shl rax, 16 - ; shr rax, 16 - ); - f(assembler, Register::RAX); - dynasm!( - assembler - ; push rax - ) - } - } - Ok(()) - } - - fn emit_memory_store( - assembler: &mut Assembler, - value_stack: &mut ValueStack, - f: F, - value_ty: WpType, - module_info: &ModuleInfo, - write_size: usize, - ) -> Result<(), CodegenError> { - let value_info = value_stack.pop()?; - let addr_info = value_stack.pop()?; - - if addr_info.ty != WpType::I32 { - return Err(CodegenError { - message: "memory address must be i32", - }); - } - - if value_info.ty != value_ty { - return Err(CodegenError { - message: "value type mismatch in memory store", - }); - } - - match value_info.location { - ValueLocation::Register(x) => { - let value_reg = Register::from_scratch_reg(x); - let addr_reg = - Register::from_scratch_reg(addr_info.location.get_register().unwrap()); // must be a register - dynasm!( - assembler - ; mov Rd(addr_reg as u8), Rd(addr_reg as u8) - ); - Self::emit_memory_bound_check_if_needed( - assembler, - module_info, - addr_reg, - write_size, - ); - dynasm!( - assembler - ; add Rq(addr_reg as u8), r15 - ; shl Rq(addr_reg as u8), 16 - ; shr Rq(addr_reg as u8), 16 - ); - f(assembler, addr_reg, value_reg); - } - ValueLocation::Stack => { - match addr_info.location { - ValueLocation::Register(x) => { - let addr_reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rd(addr_reg as u8), Rd(addr_reg as u8) - ); - Self::emit_memory_bound_check_if_needed( - assembler, - module_info, - addr_reg, - write_size, - ); - dynasm!( - assembler - ; add Rq(addr_reg as u8), r15 - ; shl Rq(addr_reg as u8), 16 - ; shr Rq(addr_reg as u8), 16 - ; pop rax - ); - f(assembler, addr_reg, Register::RAX); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov [rsp - 8], rcx // red zone - ; pop rax // value - ; pop rcx // address - ); - dynasm!( - assembler - ; mov ecx, ecx - ); - Self::emit_memory_bound_check_if_needed( - assembler, - module_info, - Register::RCX, - write_size, - ); - dynasm!( - assembler - ; add rcx, r15 - ; shl rcx, 16 - ; shr rcx, 16 - ); - f(assembler, Register::RCX, Register::RAX); - dynasm!( - assembler - ; mov rcx, [rsp - 24] - ); - } - } - } - } - Ok(()) - } -} - -impl FunctionCodeGenerator for X64FunctionCode { - fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> { - self.returns.push(ty); - Ok(()) - } - - /// Stack layout of a call frame: - /// - Return address - /// - Old RBP - /// - Params in reversed order, caller initialized - /// - Locals in reversed order, callee initialized - fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError> { - self.current_stack_offset += 8; - self.locals.push(Local { - ty: ty, - stack_offset: self.current_stack_offset, - }); - - self.num_params += 1; - - Ok(()) - } - - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { - let assembler = self.assembler.as_mut().unwrap(); - let size = get_size_of_type(&ty)?; - - if is_dword(size) { - for _ in 0..n { - // FIXME: check range of n - self.current_stack_offset += 4; - self.locals.push(Local { - ty: ty, - stack_offset: self.current_stack_offset, - }); - dynasm!( - assembler - ; sub rsp, 4 - ; mov DWORD [rsp], 0 - ); - } - if n % 2 == 1 { - self.current_stack_offset += 4; - dynasm!( - assembler - ; sub rsp, 4 - ); - } - } else { - for _ in 0..n { - // FIXME: check range of n - self.current_stack_offset += 8; - self.locals.push(Local { - ty: ty, - stack_offset: self.current_stack_offset, - }); - dynasm!( - assembler - ; push 0 - ); - } - } - Ok(()) - } - fn begin_body(&mut self) -> Result<(), CodegenError> { - self.control_stack = Some(ControlStack::new( - self.assembler.as_mut().unwrap().new_dynamic_label(), - self.returns.clone(), - )); - Ok(()) - } - fn feed_opcode(&mut self, op: Operator, module_info: &ModuleInfo) -> Result<(), CodegenError> { - let was_unreachable; - - if self.unreachable_depth > 0 { - was_unreachable = true; - match op { - Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => { - self.unreachable_depth += 1; - } - Operator::End => { - self.unreachable_depth -= 1; - } - Operator::Else => { - // We are in a reachable true branch - if self.unreachable_depth == 1 { - if let Some(IfElseState::If(_)) = self - .control_stack - .as_ref() - .unwrap() - .frames - .last() - .map(|x| x.if_else) - { - self.unreachable_depth -= 1; - } - } - } - _ => {} - } - if self.unreachable_depth > 0 { - return Ok(()); - } - } else { - was_unreachable = false; - } - - let assembler = self.assembler.as_mut().unwrap(); - - match op { - Operator::GetGlobal { global_index } => { - let mut global_index = global_index as usize; - if global_index < module_info.imported_globals.len() { - dynasm!( - assembler - ; mov rax, r14 => vm::InternalCtx.imported_globals - ); - } else { - global_index -= module_info.imported_globals.len(); - if global_index >= module_info.globals.len() { - return Err(CodegenError { - message: "global out of bounds", - }); - } - dynasm!( - assembler - ; mov rax, r14 => vm::InternalCtx.globals - ); - } - - dynasm!( - assembler - ; mov rax, [rax + (global_index as i32) * 8] - ; mov rax, rax => LocalGlobal.data - ); - Self::emit_push_from_ax( - assembler, - &mut self.value_stack, - type_to_wp_type( - module_info.globals[LocalGlobalIndex::new(global_index)] - .desc - .ty, - ), - )?; - } - Operator::SetGlobal { global_index } => { - let ty = Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; - - let mut global_index = global_index as usize; - if global_index < module_info.imported_globals.len() { - dynasm!( - assembler - ; push rbx - ; mov rbx, r14 => vm::InternalCtx.imported_globals - ); - } else { - global_index -= module_info.imported_globals.len(); - if global_index >= module_info.globals.len() { - return Err(CodegenError { - message: "global out of bounds", - }); - } - dynasm!( - assembler - ; push rbx - ; mov rbx, r14 => vm::InternalCtx.globals - ); - } - - if ty - != type_to_wp_type( - module_info.globals[LocalGlobalIndex::new(global_index)] - .desc - .ty, - ) - { - return Err(CodegenError { - message: "type mismatch in SetGlobal", - }); - } - dynasm!( - assembler - ; mov rbx, [rbx + (global_index as i32) * 8] - ; mov rbx => LocalGlobal.data, rax - ; pop rbx - ); - } - Operator::GetLocal { local_index } => { - let local_index = local_index as usize; - if local_index >= self.locals.len() { - return Err(CodegenError { - message: "local out of bounds", - }); - } - let local = self.locals[local_index]; - let location = self.value_stack.push(local.ty); - let size = get_size_of_type(&local.ty)?; - - match location { - ValueLocation::Register(id) => { - if is_dword(size) { - dynasm!( - assembler - ; mov Rd(Register::from_scratch_reg(id) as u8), [rbp - (local.stack_offset as i32)] - ); - } else { - dynasm!( - assembler - ; mov Rq(Register::from_scratch_reg(id) as u8), [rbp - (local.stack_offset as i32)] - ); - } - } - ValueLocation::Stack => { - if is_dword(size) { - dynasm!( - assembler - ; mov eax, [rbp - (local.stack_offset as i32)] - ; push rax - ); - } else { - dynasm!( - assembler - ; mov rax, [rbp - (local.stack_offset as i32)] - ; push rax - ); - } - } - } - } - Operator::SetLocal { local_index } => { - let local_index = local_index as usize; - if local_index >= self.locals.len() { - return Err(CodegenError { - message: "local out of bounds", - }); - } - let local = self.locals[local_index]; - let ty = Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; - if ty != local.ty { - return Err(CodegenError { - message: "SetLocal type mismatch", - }); - } - - if is_dword(get_size_of_type(&ty)?) { - dynasm!( - assembler - ; mov [rbp - (local.stack_offset as i32)], eax - ); - } else { - dynasm!( - assembler - ; mov [rbp - (local.stack_offset as i32)], rax - ); - } - } - Operator::TeeLocal { local_index } => { - let local_index = local_index as usize; - if local_index >= self.locals.len() { - return Err(CodegenError { - message: "local out of bounds", - }); - } - let local = self.locals[local_index]; - let ty = Self::emit_peek_into_ax(assembler, &self.value_stack)?; - if ty != local.ty { - return Err(CodegenError { - message: "TeeLocal type mismatch", - }); - } - - if is_dword(get_size_of_type(&ty)?) { - dynasm!( - assembler - ; mov [rbp - (local.stack_offset as i32)], eax - ); - } else { - dynasm!( - assembler - ; mov [rbp - (local.stack_offset as i32)], rax - ); - } - } - Operator::I32Const { value } => { - let location = self.value_stack.push(WpType::I32); - match location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rd(reg as u8), value - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; push value - ); - } - } - } - Operator::I32Add => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; add Rd(left as u8), Rd(right as u8) - ) - }, - )?; - } - Operator::I32Sub => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; sub Rd(left as u8), Rd(right as u8) - ) - }, - )?; - } - Operator::I32Mul => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; imul Rd(left as u8), Rd(right as u8) - ) - }, - )?; - } - Operator::I32DivU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i32( - assembler, - value_stack, - left, - right, - false, - Register::RAX, - ); - }, - )?; - } - Operator::I32DivS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i32( - assembler, - value_stack, - left, - right, - true, - Register::RAX, - ); - }, - )?; - } - Operator::I32RemU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i32( - assembler, - value_stack, - left, - right, - false, - Register::RDX, - ); - }, - )?; - } - Operator::I32RemS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i32( - assembler, - value_stack, - left, - right, - true, - Register::RDX, - ); - }, - )?; - } - Operator::I32And => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; and Rd(left as u8), Rd(right as u8) - ); - }, - )?; - } - Operator::I32Or => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; or Rd(left as u8), Rd(right as u8) - ); - }, - )?; - } - Operator::I32Xor => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; xor Rd(left as u8), Rd(right as u8) - ); - }, - )?; - } - Operator::I32Eq => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; cmp Rd(left as u8), Rd(right as u8) - ; lahf - ; shr ax, 14 - ; and eax, 1 - ; mov Rd(left as u8), eax - ); - }, - )?; - } - Operator::I32Ne => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; cmp Rd(left as u8), Rd(right as u8) - ; lahf - ; shr ax, 14 - ; and eax, 1 - ; xor eax, 1 - ; mov Rd(left as u8), eax - ); - }, - )?; - } - Operator::I32Eqz => { - Self::emit_unop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cmp Rd(reg as u8), 0 - ; lahf - ; shr ax, 14 - ; and eax, 1 - ); - if reg != Register::RAX { - dynasm!( - assembler - ; mov Rd(reg as u8), eax - ); - } - }, - )?; - } - Operator::I32Clz => { - Self::emit_unop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; lzcnt Rd(reg as u8), Rd(reg as u8) - ); - }, - )?; - } - Operator::I32Ctz => { - Self::emit_unop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; tzcnt Rd(reg as u8), Rd(reg as u8) - ); - }, - )?; - } - Operator::I32Popcnt => { - Self::emit_unop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; popcnt Rd(reg as u8), Rd(reg as u8) - ); - }, - )?; - } - Operator::I32Shl => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; shl Rd(left as u8), cl - ) - }); - }, - )?; - } - Operator::I32ShrU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; shr Rd(left as u8), cl - ) - }); - }, - )?; - } - Operator::I32ShrS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; sar Rd(left as u8), cl - ) - }); - }, - )?; - } - Operator::I32Rotl => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; rol Rd(left as u8), cl - ) - }); - }, - )?; - } - Operator::I32Rotr => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; ror Rd(left as u8), cl - ) - }); - }, - )?; - } - // Comparison operators. - // https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow - // TODO: Is reading flag register directly faster? - Operator::I32LtS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jl >label_true - ); - }); - }, - )?; - } - Operator::I32LeS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jle >label_true - ); - }); - }, - )?; - } - Operator::I32GtS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jg >label_true - ); - }); - }, - )?; - } - Operator::I32GeS => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jge >label_true - ); - }); - }, - )?; - } - Operator::I32LtU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jb >label_true - ); - }); - }, - )?; - } - Operator::I32LeU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jbe >label_true - ); - }); - }, - )?; - } - Operator::I32GtU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; ja >label_true - ); - }); - }, - )?; - } - Operator::I32GeU => { - Self::emit_binop_i32( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i32(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jae >label_true - ); - }); - }, - )?; - } - Operator::I64Const { value } => { - let location = self.value_stack.push(WpType::I64); - match location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rq(reg as u8), QWORD value - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov rax, QWORD value - ; push rax - ); - } - } - } - Operator::I64Add => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; add Rq(left as u8), Rq(right as u8) - ) - }, - )?; - } - Operator::I64Sub => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; sub Rq(left as u8), Rq(right as u8) - ) - }, - )?; - } - Operator::I64Mul => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; imul Rq(left as u8), Rq(right as u8) - ) - }, - )?; - } - Operator::I64DivU => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i64( - assembler, - value_stack, - left, - right, - false, - Register::RAX, - ); - }, - )?; - } - Operator::I64DivS => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i64( - assembler, - value_stack, - left, - right, - true, - Register::RAX, - ); - }, - )?; - } - Operator::I64RemU => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i64( - assembler, - value_stack, - left, - right, - false, - Register::RDX, - ); - }, - )?; - } - Operator::I64RemS => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_div_i64( - assembler, - value_stack, - left, - right, - true, - Register::RDX, - ); - }, - )?; - } - Operator::I64And => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; and Rq(left as u8), Rq(right as u8) - ); - }, - )?; - } - Operator::I64Or => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; or Rq(left as u8), Rq(right as u8) - ); - }, - )?; - } - Operator::I64Xor => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; xor Rq(left as u8), Rq(right as u8) - ); - }, - )?; - } - Operator::I64Eq => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; cmp Rq(left as u8), Rq(right as u8) - ; lahf - ; shr ax, 14 - ; and eax, 1 - ; mov Rd(left as u8), eax - ); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64Ne => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; cmp Rq(left as u8), Rq(right as u8) - ; lahf - ; shr ax, 14 - ; and eax, 1 - ; xor eax, 1 - ; mov Rd(left as u8), eax - ); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64Eqz => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cmp Rq(reg as u8), 0 - ; lahf - ; shr ax, 14 - ; and eax, 1 - ); - if reg != Register::RAX { - dynasm!( - assembler - ; mov Rd(reg as u8), eax - ); - } - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64Clz => { - Self::emit_unop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; lzcnt Rq(reg as u8), Rq(reg as u8) - ); - }, - )?; - } - Operator::I64Ctz => { - Self::emit_unop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; tzcnt Rq(reg as u8), Rq(reg as u8) - ); - }, - )?; - } - Operator::I64Popcnt => { - Self::emit_unop_i64( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; popcnt Rq(reg as u8), Rq(reg as u8) - ); - }, - )?; - } - Operator::I64Shl => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; shl Rq(left as u8), cl - ) - }); - }, - )?; - } - Operator::I64ShrU => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; shr Rq(left as u8), cl - ) - }); - }, - )?; - } - Operator::I64ShrS => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; sar Rq(left as u8), cl - ) - }); - }, - )?; - } - Operator::I64Rotl => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; rol Rq(left as u8), cl - ) - }); - }, - )?; - } - Operator::I64Rotr => { - Self::emit_binop_i64( - assembler, - &mut self.value_stack, - |assembler, value_stack, left, right| { - Self::emit_shift(assembler, value_stack, left, right, |assembler, left| { - dynasm!( - assembler - ; ror Rq(left as u8), cl - ) - }); - }, - )?; - } - // Comparison operators. - // https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow - // TODO: Is reading flag register directly faster? - Operator::I64LtS => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jl >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64LeS => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jle >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64GtS => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jg >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64GeS => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jge >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64LtU => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jb >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64LeU => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jbe >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64GtU => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; ja >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64GeU => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - Self::emit_cmp_i64(assembler, left, right, |assembler| { - dynasm!( - assembler - ; jae >label_true - ); - }); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::I64ExtendSI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movsx Rq(reg as u8), Rd(reg as u8) - ); - }, - WpType::I32, - WpType::I64, - )?; - } - Operator::I64ExtendUI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |_assembler, _value_stack, _reg| { - // FIXME: Is it correct to do nothing here? - }, - WpType::I32, - WpType::I64, - )?; - } - Operator::I32WrapI64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), Rd(reg as u8) // clear upper 32 bits - ); - }, - WpType::I64, - WpType::I32, - )?; - } - Operator::Block { ty } => { - self.control_stack - .as_mut() - .unwrap() - .frames - .push(ControlFrame { - label: assembler.new_dynamic_label(), - loop_like: false, - if_else: IfElseState::None, - returns: match ty { - WpType::EmptyBlockType => vec![], - _ => vec![ty], - }, - value_stack_depth_before: self.value_stack.values.len(), - }); - } - Operator::Unreachable => { - dynasm!( - assembler - ; ud2 - ); - self.unreachable_depth = 1; - } - Operator::Drop => { - let info = self.value_stack.pop()?; - Self::gen_rt_pop(assembler, &info)?; - } - Operator::Return => { - Self::emit_return(assembler, &mut self.value_stack, &self.returns)?; - self.unreachable_depth = 1; - } - Operator::Call { function_index } => { - let function_index = function_index as usize; - let label = self - .function_labels - .as_mut() - .unwrap() - .entry(function_index) - .or_insert_with(|| (assembler.new_dynamic_label(), None)) - .0; - let sig_index = match self.function_signatures.get(FuncIndex::new(function_index)) { - Some(x) => *x, - None => { - return Err(CodegenError { - message: "signature not found", - }); - } - }; - let sig = match self.signatures.get(sig_index) { - Some(x) => x, - None => { - return Err(CodegenError { - message: "signature does not exist", - }); - } - }; - let param_types: Vec = - sig.params().iter().cloned().map(type_to_wp_type).collect(); - let return_types: Vec = - sig.returns().iter().cloned().map(type_to_wp_type).collect(); - Self::emit_call_raw( - assembler, - &mut self.value_stack, - label, - ¶m_types, - &return_types, - )?; - } - Operator::CallIndirect { index, table_index } => { - if table_index != 0 { - return Err(CodegenError { - message: "only one table is supported", - }); - } - let local_or_import = if module_info.tables.len() > 0 { - if module_info.tables.len() != 1 || module_info.imported_tables.len() != 0 { - return Err(CodegenError { - message: "only one table is supported", - }); - } - CallIndirectLocalOrImport::Local - } else if module_info.imported_tables.len() > 0 { - if module_info.tables.len() != 0 || module_info.imported_tables.len() != 1 { - return Err(CodegenError { - message: "only one table is supported", - }); - } - CallIndirectLocalOrImport::Import - } else { - return Err(CodegenError { - message: "no tables", - }); - }; - let sig_index = SigIndex::new(index as usize); - let sig = match self.signatures.get(sig_index) { - Some(x) => x, - None => { - return Err(CodegenError { - message: "signature does not exist", - }); - } - }; - let mut param_types: Vec = - sig.params().iter().cloned().map(type_to_wp_type).collect(); - let return_types: Vec = - sig.returns().iter().cloned().map(type_to_wp_type).collect(); - param_types.push(WpType::I32); // element index - - dynasm!( - assembler - ; jmp >after_trampoline - ); - - let trampoline_label = Self::emit_native_call_trampoline( - assembler, - call_indirect, - index as usize, - local_or_import, - ); - - dynasm!( - assembler - ; after_trampoline: - ); - - Self::emit_call_raw( - assembler, - &mut self.value_stack, - trampoline_label, - ¶m_types, - &return_types, - )?; - } - Operator::End => { - if self.control_stack.as_ref().unwrap().frames.len() == 1 { - let frame = self.control_stack.as_mut().unwrap().frames.pop().unwrap(); - - if !was_unreachable { - Self::emit_leave_frame(assembler, &frame, &mut self.value_stack, false)?; - } else { - self.value_stack.reset_depth(0); - } - - dynasm!( - assembler - ; =>frame.label - ); - } else { - Self::emit_block_end( - assembler, - self.control_stack.as_mut().unwrap(), - &mut self.value_stack, - was_unreachable, - )?; - } - } - Operator::Loop { ty } => { - let label = assembler.new_dynamic_label(); - self.control_stack - .as_mut() - .unwrap() - .frames - .push(ControlFrame { - label: label, - loop_like: true, - if_else: IfElseState::None, - returns: match ty { - WpType::EmptyBlockType => vec![], - _ => vec![ty], - }, - value_stack_depth_before: self.value_stack.values.len(), - }); - dynasm!( - assembler - ; =>label - ); - } - Operator::If { ty } => { - let label_end = assembler.new_dynamic_label(); - let label_else = assembler.new_dynamic_label(); - - Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; // TODO: typeck? - - self.control_stack - .as_mut() - .unwrap() - .frames - .push(ControlFrame { - label: label_end, - loop_like: false, - if_else: IfElseState::If(label_else), - returns: match ty { - WpType::EmptyBlockType => vec![], - _ => vec![ty], - }, - value_stack_depth_before: self.value_stack.values.len(), - }); - dynasm!( - assembler - ; cmp eax, 0 - ; je =>label_else - ); - } - Operator::Else => { - Self::emit_else( - assembler, - self.control_stack.as_mut().unwrap(), - &mut self.value_stack, - was_unreachable, - )?; - } - Operator::Select => { - Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; - let v_b = self.value_stack.pop()?; - let v_a = self.value_stack.pop()?; - - if v_b.ty != v_a.ty { - return Err(CodegenError { - message: "select: type mismatch", - }); - } - - dynasm!( - assembler - ; cmp eax, 0 - ); - match v_b.location { - ValueLocation::Stack => { - dynasm!( - assembler - ; cmove rax, [rsp] - ; add rsp, 8 - ); - } - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; cmove rax, Rq(reg as u8) - ); - } - } - match v_a.location { - ValueLocation::Stack => { - dynasm!( - assembler - ; cmovne rax, [rsp] - ; add rsp, 8 - ); - } - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; cmovne rax, Rq(reg as u8) - ); - } - } - - Self::emit_push_from_ax(assembler, &mut self.value_stack, v_a.ty)?; - } - Operator::Br { relative_depth } => { - Self::emit_jmp( - assembler, - self.control_stack.as_ref().unwrap(), - &mut self.value_stack, - relative_depth as usize, - )?; - self.unreachable_depth = 1; - } - Operator::BrIf { relative_depth } => { - let no_br_label = assembler.new_dynamic_label(); - Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; // TODO: typeck? - dynasm!( - assembler - ; cmp eax, 0 - ; je =>no_br_label - ); - Self::emit_jmp( - assembler, - self.control_stack.as_ref().unwrap(), - &mut self.value_stack, - relative_depth as usize, - )?; - dynasm!( - assembler - ; =>no_br_label - ); - } - Operator::BrTable { table } => { - let (targets, default_target) = match table.read_table() { - Ok(x) => x, - Err(_) => { - return Err(CodegenError { - message: "cannot read br table", - }); - } - }; - let cond_ty = Self::emit_pop_into_ax(assembler, &mut self.value_stack)?; - if cond_ty != WpType::I32 { - return Err(CodegenError { - message: "expecting i32 for BrTable condition", - }); - } - let mut table = vec![0usize; targets.len()]; - dynasm!( - assembler - ; cmp eax, targets.len() as i32 - ; jae >default_br - ; shl rax, 3 - ; push rcx - ; mov rcx, QWORD table.as_ptr() as usize as i64 - ; add rax, rcx - ; pop rcx - ; mov rax, [rax] // assuming upper 32 bits of rax are zeroed - ; jmp rax - ); - for (i, target) in targets.iter().enumerate() { - let AssemblyOffset(offset) = assembler.offset(); - table[i] = offset; - Self::emit_jmp( - assembler, - self.control_stack.as_ref().unwrap(), - &mut self.value_stack, - *target as usize, - )?; // This does not actually modify value_stack. - } - dynasm!( - assembler - ; default_br: - ); - Self::emit_jmp( - assembler, - self.control_stack.as_ref().unwrap(), - &mut self.value_stack, - default_target as usize, - )?; - self.br_table_data.as_mut().unwrap().push(table); - self.unreachable_depth = 1; - } - Operator::I32Load { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I32, - module_info, - 4, - )?; - } - Operator::I32Load8U { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movzx Rd(reg as u8), BYTE [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I32, - module_info, - 1, - )?; - } - Operator::I32Load8S { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movsx Rd(reg as u8), BYTE [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I32, - module_info, - 1, - )?; - } - Operator::I32Load16U { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movzx Rd(reg as u8), WORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I32, - module_info, - 2, - )?; - } - Operator::I32Load16S { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movsx Rd(reg as u8), WORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I32, - module_info, - 2, - )?; - } - Operator::I32Store { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rd(value_reg as u8) - ); - }, - WpType::I32, - module_info, - 4, - )?; - } - Operator::I32Store8 { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rb(value_reg as u8) - ); - }, - WpType::I32, - module_info, - 1, - )?; - } - Operator::I32Store16 { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rw(value_reg as u8) - ); - }, - WpType::I32, - module_info, - 2, - )?; - } - Operator::I64Load { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; mov Rq(reg as u8), [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 8, - )?; - } - Operator::I64Load8U { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movzx Rq(reg as u8), BYTE [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 1, - )?; - } - Operator::I64Load8S { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movsx Rq(reg as u8), BYTE [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 1, - )?; - } - Operator::I64Load16U { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movzx Rq(reg as u8), WORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 2, - )?; - } - Operator::I64Load16S { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movsx Rq(reg as u8), WORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 2, - )?; - } - Operator::I64Load32U { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), DWORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 4, - )?; - } - Operator::I64Load32S { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; movsx Rq(reg as u8), DWORD [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::I64, - module_info, - 4, - )?; - } - Operator::I64Store { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rq(value_reg as u8) - ); - }, - WpType::I64, - module_info, - 8, - )?; - } - Operator::I64Store8 { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rb(value_reg as u8) - ); - }, - WpType::I64, - module_info, - 1, - )?; - } - Operator::I64Store16 { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rw(value_reg as u8) - ); - }, - WpType::I64, - module_info, - 2, - )?; - } - Operator::I64Store32 { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rd(value_reg as u8) - ); - }, - WpType::I64, - module_info, - 4, - )?; - } - Operator::F32Const { value } => { - let location = self.value_stack.push(WpType::F32); - match location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rd(reg as u8), value.bits() as i32 - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; push value.bits() as i32 - ); - } - } - } - Operator::F64Const { value } => { - let location = self.value_stack.push(WpType::F64); - match location { - ValueLocation::Register(x) => { - let reg = Register::from_scratch_reg(x); - dynasm!( - assembler - ; mov Rq(reg as u8), QWORD value.bits() as i64 - ); - } - ValueLocation::Stack => { - dynasm!( - assembler - ; mov rax, QWORD value.bits() as i64 - ; push rax - ); - } - } - } - Operator::F32Load { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::F32, - module_info, - 4, - )?; - } - Operator::F32Store { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rd(value_reg as u8) - ); - }, - WpType::F32, - module_info, - 4, - )?; - } - Operator::F64Load { memarg } => { - Self::emit_memory_load( - assembler, - &mut self.value_stack, - |assembler, reg| { - dynasm!( - assembler - ; mov Rq(reg as u8), [Rq(reg as u8) + memarg.offset as i32] - ); - }, - WpType::F64, - module_info, - 8, - )?; - } - Operator::F64Store { memarg } => { - Self::emit_memory_store( - assembler, - &mut self.value_stack, - |assembler, addr_reg, value_reg| { - dynasm!( - assembler - ; mov [Rq(addr_reg as u8) + memarg.offset as i32], Rq(value_reg as u8) - ); - }, - WpType::F64, - module_info, - 8, - )?; - } - Operator::I32ReinterpretF32 => { - Self::emit_reinterpret(&mut self.value_stack, WpType::F32, WpType::I32)?; - } - Operator::F32ReinterpretI32 => { - Self::emit_reinterpret(&mut self.value_stack, WpType::I32, WpType::F32)?; - } - Operator::I64ReinterpretF64 => { - Self::emit_reinterpret(&mut self.value_stack, WpType::F64, WpType::I64)?; - } - Operator::F64ReinterpretI64 => { - Self::emit_reinterpret(&mut self.value_stack, WpType::I64, WpType::F64)?; - } - Operator::F32ConvertSI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cvtsi2ss xmm1, Rd(reg as u8) - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::I32, - WpType::F32, - )?; - } - Operator::F32ConvertUI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), Rd(reg as u8) // clear upper 32 bits - ; cvtsi2ss xmm1, Rq(reg as u8) - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::I32, - WpType::F32, - )?; - } - Operator::F32ConvertSI64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cvtsi2ss xmm1, Rq(reg as u8) - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::I64, - WpType::F32, - )?; - } - /* - 0: 48 85 ff test %rdi,%rdi - 3: 78 0b js 10 - 5: c4 e1 fb 2a c7 vcvtsi2sd %rdi,%xmm0,%xmm0 - a: c3 retq - b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) - 10: 48 89 f8 mov %rdi,%rax - 13: 83 e7 01 and $0x1,%edi - 16: 48 d1 e8 shr %rax - 19: 48 09 f8 or %rdi,%rax - 1c: c4 e1 fb 2a c0 vcvtsi2sd %rax,%xmm0,%xmm0 - 21: c5 fb 58 c0 vaddsd %xmm0,%xmm0,%xmm0 - 25: c3 retq - */ - Operator::F32ConvertUI64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; test Rq(reg as u8), Rq(reg as u8) - ; js >do_convert - ; cvtsi2ss xmm1, Rq(reg as u8) - ; movd Rd(reg as u8), xmm1 - ; jmp >end_convert - ; do_convert: - ; movq xmm5, r15 - ; mov r15, Rq(reg as u8) - ; and r15, 1 - ; shr Rq(reg as u8), 1 - ; or Rq(reg as u8), r15 - ; cvtsi2ss xmm1, Rq(reg as u8) - ; addss xmm1, xmm1 - ; movq r15, xmm5 - ; movd Rd(reg as u8), xmm1 - ; end_convert: - ); - }, - WpType::I64, - WpType::F32, - )?; - } - Operator::F64ConvertSI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cvtsi2sd xmm1, Rd(reg as u8) - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::I32, - WpType::F64, - )?; - } - Operator::F64ConvertUI32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; mov Rd(reg as u8), Rd(reg as u8) // clear upper 32 bits - ; cvtsi2sd xmm1, Rq(reg as u8) - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::I32, - WpType::F64, - )?; - } - Operator::F64ConvertSI64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; cvtsi2sd xmm1, Rq(reg as u8) - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::I64, - WpType::F64, - )?; - } - Operator::F64ConvertUI64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; test Rq(reg as u8), Rq(reg as u8) - ; js >do_convert - ; cvtsi2sd xmm1, Rq(reg as u8) - ; movq Rq(reg as u8), xmm1 - ; jmp >end_convert - ; do_convert: - ; movq xmm5, r15 - ; mov r15, Rq(reg as u8) - ; and r15, 1 - ; shr Rq(reg as u8), 1 - ; or Rq(reg as u8), r15 - ; cvtsi2sd xmm1, Rq(reg as u8) - ; addsd xmm1, xmm1 - ; movq r15, xmm5 - ; movq Rq(reg as u8), xmm1 - ; end_convert: - ); - }, - WpType::I64, - WpType::F64, - )?; - } - Operator::F64PromoteF32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; cvtss2sd xmm1, xmm1 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F64, - )?; - } - Operator::F32DemoteF64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; cvtsd2ss xmm1, xmm1 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F32, - )?; - } - Operator::F32Add => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; addss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Sub => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; subss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Mul => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; mulss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Div => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; divss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Max => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; maxss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Min => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; minss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Eq => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; cmpeqss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Ne => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; cmpneqss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Gt => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; vcmpgtss xmm1, xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Ge => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; vcmpgess xmm1, xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Lt => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; cmpltss xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Le => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; cmpless xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::F32Copysign => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movd xmm1, Rd(left as u8) - ; movd xmm2, Rd(right as u8) - ; mov eax, 0x7fffffffu32 as i32 - ; movd xmm3, eax - ; pand xmm1, xmm3 - ; mov eax, 0x80000000u32 as i32 - ; movd xmm3, eax - ; pand xmm2, xmm3 - ; por xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Sqrt => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; sqrtss xmm1, xmm1 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Abs => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; and Rd(reg as u8), 0x7fffffffu32 as i32 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Neg => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; btc Rd(reg as u8), 31 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Nearest => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; roundss xmm1, xmm1, 0 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Floor => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; roundss xmm1, xmm1, 1 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Ceil => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; roundss xmm1, xmm1, 2 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::F32Trunc => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; roundss xmm1, xmm1, 3 - ; movd Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::F32, - )?; - } - Operator::I32TruncUF32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f32_int_conv_check(assembler, reg, -1.0, 4294967296.0); - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; cvttss2si Rq(reg as u8), xmm1 - ; mov Rd(reg as u8), Rd(reg as u8) - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::I32TruncSF32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f32_int_conv_check(assembler, reg, -2147483904.0, 2147483648.0); - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; cvttss2si Rd(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::I32, - )?; - } - Operator::I64TruncUF32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f32_int_conv_check(assembler, reg, -1.0, 18446744073709551616.0); - /* - LCPI0_0: - .long 1593835520 ## float 9.22337203E+18 - - movss LCPI0_0(%rip), %xmm1 ## xmm1 = mem[0],zero,zero,zero - movaps %xmm0, %xmm2 - subss %xmm1, %xmm2 - cvttss2si %xmm2, %rax - movabsq $-9223372036854775808, %rcx ## imm = 0x8000000000000000 - xorq %rax, %rcx - cvttss2si %xmm0, %rax - ucomiss %xmm1, %xmm0 - cmovaeq %rcx, %rax - */ - dynasm!( - assembler - ; 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 - ); - }, - WpType::F32, - WpType::I64, - )?; - } - Operator::I64TruncSF32 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f32_int_conv_check( - assembler, - reg, - -9223373136366403584.0, - 9223372036854775808.0, - ); - dynasm!( - assembler - ; movd xmm1, Rd(reg as u8) - ; cvttss2si Rq(reg as u8), xmm1 - ); - }, - WpType::F32, - WpType::I64, - )?; - } - Operator::F64Add => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; addsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Sub => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; subsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Mul => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; mulsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Div => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; divsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Max => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; maxsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Min => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; minsd xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Eq => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; cmpeqsd xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Ne => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; cmpneqsd xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Gt => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; vcmpgtsd xmm1, xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Ge => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; vcmpgesd xmm1, xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Lt => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; cmpltsd xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Le => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; cmplesd xmm1, xmm2 - ; movd Rd(left as u8), xmm1 - ; and Rd(left as u8), 1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::F64Copysign => { - Self::emit_binop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, left, right| { - dynasm!( - assembler - ; movq xmm1, Rq(left as u8) - ; movq xmm2, Rq(right as u8) - ; mov rax, QWORD 0x7fffffffffffffffu64 as i64 - ; movq xmm3, rax - ; pand xmm1, xmm3 - ; mov rax, QWORD 0x8000000000000000u64 as i64 - ; movq xmm3, rax - ; pand xmm2, xmm3 - ; por xmm1, xmm2 - ; movq Rq(left as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Sqrt => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; sqrtsd xmm1, xmm1 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Abs => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; mov rax, QWORD 0x7fffffffffffffff - ; movq xmm2, rax - ; pand xmm1, xmm2 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Neg => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; btc Rq(reg as u8), 63 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Nearest => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; roundsd xmm1, xmm1, 0 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Floor => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; roundsd xmm1, xmm1, 1 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Ceil => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; roundsd xmm1, xmm1, 2 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::F64Trunc => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; roundsd xmm1, xmm1, 3 - ; movq Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::F64, - )?; - } - Operator::I32TruncUF64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f64_int_conv_check(assembler, reg, -1.0, 4294967296.0); - - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; cvttsd2si Rq(reg as u8), xmm1 - ; mov Rd(reg as u8), Rd(reg as u8) - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::I32TruncSF64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f64_int_conv_check(assembler, reg, -2147483649.0, 2147483648.0); - - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; cvttsd2si Rd(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::I32, - )?; - } - Operator::I64TruncUF64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f64_int_conv_check(assembler, reg, -1.0, 18446744073709551616.0); - - /* - LCPI0_0: - .quad 4890909195324358656 ## double 9.2233720368547758E+18 - - movsd LCPI0_0(%rip), %xmm1 ## xmm1 = mem[0],zero - movapd %xmm0, %xmm2 - subsd %xmm1, %xmm2 - cvttsd2si %xmm2, %rax - movabsq $-9223372036854775808, %rcx ## imm = 0x8000000000000000 - xorq %rax, %rcx - cvttsd2si %xmm0, %rax - ucomisd %xmm1, %xmm0 - cmovaeq %rcx, %rax - */ - - dynasm!( - assembler - ; movq xmm5, r15 - ; mov r15, QWORD 4890909195324358656u64 as i64 //double 9.2233720368547758E+18 - ; movq xmm1, r15 - ; movq xmm2, Rq(reg as u8) - ; movq xmm3, Rq(reg as u8) - ; subsd xmm2, xmm1 - ; cvttsd2si Rq(reg as u8), xmm2 - ; mov r15, QWORD 0x8000000000000000u64 as i64 - ; xor r15, Rq(reg as u8) - ; cvttsd2si Rq(reg as u8), xmm3 - ; ucomisd xmm3, xmm1 - ; cmovae Rq(reg as u8), r15 - ; movq r15, xmm5 - ); - }, - WpType::F64, - WpType::I64, - )?; - } - Operator::I64TruncSF64 => { - Self::emit_unop( - assembler, - &mut self.value_stack, - |assembler, _value_stack, reg| { - Self::emit_f64_int_conv_check( - assembler, - reg, - -9223372036854777856.0, - 9223372036854775808.0, - ); - - dynasm!( - assembler - ; movq xmm1, Rq(reg as u8) - ; cvttsd2si Rq(reg as u8), xmm1 - ); - }, - WpType::F64, - WpType::I64, - )?; - } - Operator::Nop => {} - Operator::MemorySize { reserved } => { - let memory_index = MemoryIndex::new(reserved as usize); - let label = match memory_index.local_or_import(module_info) { - LocalOrImport::Local(local_mem_index) => { - let mem_desc = &module_info.memories[local_mem_index]; - match mem_desc.memory_type() { - MemoryType::Dynamic => { - self.native_trampolines.memory_size_dynamic_local - } - MemoryType::Static => self.native_trampolines.memory_size_static_local, - MemoryType::SharedStatic => { - self.native_trampolines.memory_size_shared_local - } - } - } - LocalOrImport::Import(import_mem_index) => { - let mem_desc = &module_info.imported_memories[import_mem_index].1; - match mem_desc.memory_type() { - MemoryType::Dynamic => { - self.native_trampolines.memory_size_dynamic_import - } - MemoryType::Static => self.native_trampolines.memory_size_static_import, - MemoryType::SharedStatic => { - self.native_trampolines.memory_size_shared_import - } - } - } - }; - Self::emit_call_raw(assembler, &mut self.value_stack, label, &[], &[WpType::I32])?; - } - Operator::MemoryGrow { reserved } => { - let memory_index = MemoryIndex::new(reserved as usize); - let label = match memory_index.local_or_import(module_info) { - LocalOrImport::Local(local_mem_index) => { - let mem_desc = &module_info.memories[local_mem_index]; - match mem_desc.memory_type() { - MemoryType::Dynamic => { - self.native_trampolines.memory_grow_dynamic_local - } - MemoryType::Static => self.native_trampolines.memory_grow_static_local, - MemoryType::SharedStatic => { - self.native_trampolines.memory_grow_shared_local - } - } - } - LocalOrImport::Import(import_mem_index) => { - let mem_desc = &module_info.imported_memories[import_mem_index].1; - match mem_desc.memory_type() { - MemoryType::Dynamic => { - self.native_trampolines.memory_grow_dynamic_import - } - MemoryType::Static => self.native_trampolines.memory_grow_static_import, - MemoryType::SharedStatic => { - self.native_trampolines.memory_grow_shared_import - } - } - } - }; - Self::emit_call_raw( - assembler, - &mut self.value_stack, - label, - &[WpType::I32], - &[WpType::I32], - )?; - Self::emit_update_memory_from_ctx(assembler, module_info)?; - } - _ => { - panic!("{:?}", op); - } - } - Ok(()) - } - - fn finalize(&mut self) -> Result<(), CodegenError> { - let assembler = self.assembler.as_mut().unwrap(); - - dynasm!( - assembler - ; mov rsp, rbp - ; pop rbp - ; ret - ); - - if self.value_stack.values.len() != 0 - || self.control_stack.as_ref().unwrap().frames.len() != 0 - { - return Err(CodegenError { - message: "control/value stack not empty at end of function", - }); - } - - Ok(()) - } -} - -fn get_size_of_type(ty: &WpType) -> Result { - match *ty { - WpType::I32 | WpType::F32 => Ok(4), - WpType::I64 | WpType::F64 => Ok(8), - _ => Err(CodegenError { - message: "unknown type", - }), - } -} - -fn is_dword(n: usize) -> bool { - n == 4 -} - -fn type_to_wp_type(ty: Type) -> WpType { - match ty { - Type::I32 => WpType::I32, - Type::I64 => WpType::I64, - Type::F32 => WpType::F32, - Type::F64 => WpType::F64, - } -} - -unsafe extern "C" fn invoke_import( - _unused: usize, - import_id: usize, - stack_top: *mut u8, - stack_base: *mut u8, - _vmctx: *mut vm::Ctx, - _memory_base: *mut u8, -) -> u64 { - let vmctx: &mut vm::InternalCtx = &mut *(_vmctx as *mut vm::InternalCtx); - let import = (*vmctx.imported_funcs.offset(import_id as isize)).func; - - CONSTRUCT_STACK_AND_CALL_NATIVE(stack_top, stack_base, _vmctx, import) -} - -#[repr(u64)] -#[derive(Copy, Clone, Debug)] -enum CallIndirectLocalOrImport { - Local, - Import, -} - -#[allow(clippy::cast_ptr_alignment)] -unsafe extern "C" fn call_indirect( - sig_index: usize, - local_or_import: CallIndirectLocalOrImport, - mut stack_top: *mut u8, - stack_base: *mut u8, - vmctx: *mut vm::Ctx, - _memory_base: *mut u8, -) -> u64 { - let elem_index = *(stack_top as *mut u32) as usize; - stack_top = stack_top.offset(8); - assert!(stack_top as usize <= stack_base as usize); - - let table: &LocalTable = match local_or_import { - CallIndirectLocalOrImport::Local => &*(*(*(vmctx as *mut vm::InternalCtx)).tables), - CallIndirectLocalOrImport::Import => { - &*(*(*(vmctx as *mut vm::InternalCtx)).imported_tables) - } - }; - if elem_index >= table.count as usize { - eprintln!("element index out of bounds"); - protect_unix::trigger_trap(); - } - let anyfunc = &*(table.base as *mut vm::Anyfunc).offset(elem_index as isize); - let dynamic_sigindex = *(*(vmctx as *mut vm::InternalCtx)) - .dynamic_sigindices - .offset(sig_index as isize); - - if anyfunc.func.is_null() { - eprintln!("null anyfunc"); - protect_unix::trigger_trap(); - } - - if anyfunc.sig_id.0 != dynamic_sigindex.0 { - eprintln!("signature mismatch"); - protect_unix::trigger_trap(); - } - - CONSTRUCT_STACK_AND_CALL_NATIVE(stack_top, stack_base, anyfunc.ctx, anyfunc.func) -} - -#[repr(u64)] -#[derive(Copy, Clone, Debug)] -enum MemoryKind { - DynamicLocal, - StaticLocal, - SharedLocal, - DynamicImport, - StaticImport, - SharedImport, -} - -unsafe extern "C" fn _memory_size( - op: MemoryKind, - index: usize, - _stack_top: *mut u8, - _stack_base: *mut u8, - vmctx: *mut vm::Ctx, - _memory_base: *mut u8, -) -> u64 { - use wasmer_runtime_core::vmcalls; - let ret = match op { - MemoryKind::DynamicLocal => { - vmcalls::local_dynamic_memory_size(&*vmctx, LocalMemoryIndex::new(index)) - } - MemoryKind::StaticLocal => { - vmcalls::local_static_memory_size(&*vmctx, LocalMemoryIndex::new(index)) - } - MemoryKind::SharedLocal => unreachable!(), - MemoryKind::DynamicImport => { - vmcalls::imported_dynamic_memory_size(&*vmctx, ImportedMemoryIndex::new(index)) - } - MemoryKind::StaticImport => { - vmcalls::imported_static_memory_size(&*vmctx, ImportedMemoryIndex::new(index)) - } - MemoryKind::SharedImport => unreachable!(), - }; - ret.0 as u32 as u64 -} - -#[allow(clippy::cast_ptr_alignment)] -unsafe extern "C" fn _memory_grow( - op: MemoryKind, - index: usize, - stack_top: *mut u8, - stack_base: *mut u8, - vmctx: *mut vm::Ctx, - _memory_base: *mut u8, -) -> u64 { - use wasmer_runtime_core::vmcalls; - assert_eq!(stack_base as usize - stack_top as usize, 8); - let pages = Pages(*(stack_top as *mut u32)); - let ret = match op { - MemoryKind::DynamicLocal => { - vmcalls::local_dynamic_memory_grow(&mut *vmctx, LocalMemoryIndex::new(index), pages) - } - MemoryKind::StaticLocal => { - vmcalls::local_static_memory_grow(&mut *vmctx, LocalMemoryIndex::new(index), pages) - } - MemoryKind::SharedLocal => unreachable!(), - MemoryKind::DynamicImport => vmcalls::imported_dynamic_memory_grow( - &mut *vmctx, - ImportedMemoryIndex::new(index), - pages, - ), - MemoryKind::StaticImport => vmcalls::imported_static_memory_grow( - &mut *vmctx, - ImportedMemoryIndex::new(index), - pages, - ), - MemoryKind::SharedImport => unreachable!(), - }; - ret as u32 as u64 -} diff --git a/lib/dynasm-backend/src/lib.rs b/lib/dynasm-backend/src/lib.rs deleted file mode 100644 index 5d1aa5f96..000000000 --- a/lib/dynasm-backend/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -#![feature(proc_macro_hygiene)] - -#[cfg(not(any( - all(target_os = "macos", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "x86_64"), -)))] -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; - -#[macro_use] -extern crate dynasm; - -#[macro_use] -extern crate lazy_static; - -extern crate byteorder; - -mod codegen; -mod codegen_x64; -mod parse; -mod protect_unix; -mod stack; - -use crate::codegen::{CodegenError, ModuleCodeGenerator}; -use crate::parse::LoadError; -use wasmer_runtime_core::{ - backend::{sys::Memory, Backend, CacheGen, Compiler, CompilerConfig, Token}, - cache::{Artifact, Error as CacheError}, - error::{CompileError, CompileResult}, - module::{ModuleInfo, ModuleInner}, -}; - -struct Placeholder; -impl CacheGen for Placeholder { - fn generate_cache( - &self, - _module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { - Err(CacheError::Unknown( - "the dynasm backend doesn't support caching yet".to_string(), - )) - } -} - -pub struct SinglePassCompiler {} -impl SinglePassCompiler { - pub fn new() -> Self { - Self {} - } -} - -impl Compiler for SinglePassCompiler { - fn compile( - &self, - wasm: &[u8], - compiler_config: CompilerConfig, - _: Token, - ) -> CompileResult { - let mut mcg = codegen_x64::X64ModuleCodeGenerator::new(); - let info = parse::read_module(wasm, Backend::Dynasm, &mut mcg, &compiler_config)?; - let (ec, resolver) = mcg.finalize(&info)?; - Ok(ModuleInner { - cache_gen: Box::new(Placeholder), - func_resolver: Box::new(resolver), - protected_caller: Box::new(ec), - info: info, - }) - } - - unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - Err(CacheError::Unknown( - "the dynasm backend doesn't support caching yet".to_string(), - )) - } -} - -impl From for CompileError { - fn from(other: CodegenError) -> CompileError { - CompileError::InternalError { - msg: other.message.into(), - } - } -} - -impl From for CompileError { - fn from(other: LoadError) -> CompileError { - CompileError::InternalError { - msg: format!("{:?}", other), - } - } -} diff --git a/lib/dynasm-backend/src/parse.rs b/lib/dynasm-backend/src/parse.rs deleted file mode 100644 index b198ea4c5..000000000 --- a/lib/dynasm-backend/src/parse.rs +++ /dev/null @@ -1,447 +0,0 @@ -use crate::codegen::{CodegenError, FunctionCodeGenerator, ModuleCodeGenerator}; -use hashbrown::HashMap; -use wasmer_runtime_core::{ - backend::{Backend, CompilerConfig, FuncResolver, ProtectedCaller}, - module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, - }, - structures::{Map, TypedIndex}, - types::{ - ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, - ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, - TableIndex, Type, Value, - }, - units::Pages, -}; -use wasmparser::{ - BinaryReaderError, Data, DataKind, Element, ElementKind, Export, ExternalKind, FuncType, - Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, SectionCode, Type as WpType, - WasmDecoder, -}; - -#[derive(Debug)] -pub enum LoadError { - Parse(BinaryReaderError), - Codegen(CodegenError), -} - -impl From for LoadError { - fn from(other: BinaryReaderError) -> LoadError { - LoadError::Parse(other) - } -} - -impl From for LoadError { - fn from(other: CodegenError) -> LoadError { - LoadError::Codegen(other) - } -} - -fn validate(bytes: &[u8]) -> Result<(), LoadError> { - let mut parser = wasmparser::ValidatingParser::new( - bytes, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: false, - enable_reference_types: false, - enable_simd: false, - enable_bulk_memory: false, - }, - mutable_global_imports: false, - }), - ); - - loop { - let state = parser.read(); - match *state { - wasmparser::ParserState::EndWasm => break Ok(()), - wasmparser::ParserState::Error(err) => Err(LoadError::Parse(err))?, - _ => {} - } - } -} - -pub fn read_module< - MCG: ModuleCodeGenerator, - FCG: FunctionCodeGenerator, - PC: ProtectedCaller, - FR: FuncResolver, ->( - wasm: &[u8], - backend: Backend, - mcg: &mut MCG, - compiler_config: &CompilerConfig, -) -> Result { - validate(wasm)?; - let mut info = ModuleInfo { - memories: Map::new(), - globals: Map::new(), - tables: Map::new(), - - imported_functions: Map::new(), - imported_memories: Map::new(), - imported_tables: Map::new(), - imported_globals: Map::new(), - - exports: Default::default(), - - data_initializers: Vec::new(), - elem_initializers: Vec::new(), - - start_func: None, - - func_assoc: Map::new(), - signatures: Map::new(), - backend: backend, - - namespace_table: StringTable::new(), - name_table: StringTable::new(), - - em_symbol_map: compiler_config.symbol_map.clone(), - - custom_sections: HashMap::new(), - }; - - let mut reader = ModuleReader::new(wasm)?; - - loop { - if reader.eof() { - return Ok(info); - } - - let section = reader.read()?; - - match section.code { - SectionCode::Type => { - let type_reader = section.get_type_section_reader()?; - - for ty in type_reader { - let ty = ty?; - info.signatures.push(func_type_to_func_sig(ty)?); - } - - mcg.feed_signatures(info.signatures.clone())?; - } - SectionCode::Import => { - let import_reader = section.get_import_section_reader()?; - let mut namespace_builder = StringTableBuilder::new(); - let mut name_builder = StringTableBuilder::new(); - - for import in import_reader { - let Import { module, field, ty } = import?; - - let namespace_index = namespace_builder.register(module); - let name_index = name_builder.register(field); - let import_name = ImportName { - namespace_index, - name_index, - }; - - match ty { - ImportSectionEntryType::Function(sigindex) => { - let sigindex = SigIndex::new(sigindex as usize); - info.imported_functions.push(import_name); - info.func_assoc.push(sigindex); - mcg.feed_import_function()?; - } - ImportSectionEntryType::Table(table_ty) => { - assert_eq!(table_ty.element_type, WpType::AnyFunc); - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.imported_tables.push((import_name, table_desc)); - } - ImportSectionEntryType::Memory(memory_ty) => { - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - info.imported_memories.push((import_name, mem_desc)); - } - ImportSectionEntryType::Global(global_ty) => { - let global_desc = GlobalDescriptor { - mutable: global_ty.mutable, - ty: wp_type_to_type(global_ty.content_type)?, - }; - info.imported_globals.push((import_name, global_desc)); - } - } - } - - info.namespace_table = namespace_builder.finish(); - info.name_table = name_builder.finish(); - } - SectionCode::Function => { - let func_decl_reader = section.get_function_section_reader()?; - - for sigindex in func_decl_reader { - let sigindex = sigindex?; - - let sigindex = SigIndex::new(sigindex as usize); - info.func_assoc.push(sigindex); - } - - mcg.feed_function_signatures(info.func_assoc.clone())?; - } - SectionCode::Table => { - let table_decl_reader = section.get_table_section_reader()?; - - for table_ty in table_decl_reader { - let table_ty = table_ty?; - - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.tables.push(table_desc); - } - } - SectionCode::Memory => { - let mem_decl_reader = section.get_memory_section_reader()?; - - for memory_ty in mem_decl_reader { - let memory_ty = memory_ty?; - - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - - info.memories.push(mem_desc); - } - } - SectionCode::Global => { - let global_decl_reader = section.get_global_section_reader()?; - - for global in global_decl_reader { - let global = global?; - - let desc = GlobalDescriptor { - mutable: global.ty.mutable, - ty: wp_type_to_type(global.ty.content_type)?, - }; - - let global_init = GlobalInit { - desc, - init: eval_init_expr(&global.init_expr)?, - }; - - info.globals.push(global_init); - } - } - SectionCode::Export => { - let export_reader = section.get_export_section_reader()?; - - for export in export_reader { - let Export { field, kind, index } = export?; - - let export_index = match kind { - ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), - ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), - ExternalKind::Memory => { - ExportIndex::Memory(MemoryIndex::new(index as usize)) - } - ExternalKind::Global => { - ExportIndex::Global(GlobalIndex::new(index as usize)) - } - }; - - info.exports.insert(field.to_string(), export_index); - } - } - SectionCode::Start => { - let start_index = section.get_start_section_content()?; - - info.start_func = Some(FuncIndex::new(start_index as usize)); - } - SectionCode::Element => { - let element_reader = section.get_element_section_reader()?; - - for element in element_reader { - let Element { kind, items } = element?; - - match kind { - ElementKind::Active { - table_index, - init_expr, - } => { - let table_index = TableIndex::new(table_index as usize); - let base = eval_init_expr(&init_expr)?; - let items_reader = items.get_items_reader()?; - - let elements: Vec<_> = items_reader - .into_iter() - .map(|res| res.map(|index| FuncIndex::new(index as usize))) - .collect::>()?; - - let table_init = TableInitializer { - table_index, - base, - elements, - }; - - info.elem_initializers.push(table_init); - } - ElementKind::Passive(_ty) => { - return Err(BinaryReaderError { - message: "passive tables are not yet supported", - offset: -1isize as usize, - } - .into()); - } - } - } - } - SectionCode::Code => { - let mut code_reader = section.get_code_section_reader()?; - if code_reader.get_count() as usize > info.func_assoc.len() { - return Err(BinaryReaderError { - message: "code_reader.get_count() > info.func_assoc.len()", - offset: ::std::usize::MAX, - } - .into()); - } - mcg.check_precondition(&info)?; - for i in 0..code_reader.get_count() { - let item = code_reader.read()?; - let fcg = mcg.next_function()?; - let sig = info - .signatures - .get( - *info - .func_assoc - .get(FuncIndex::new(i as usize + info.imported_functions.len())) - .unwrap(), - ) - .unwrap(); - for ret in sig.returns() { - fcg.feed_return(type_to_wp_type(*ret))?; - } - for param in sig.params() { - fcg.feed_param(type_to_wp_type(*param))?; - } - for local in item.get_locals_reader()? { - let (count, ty) = local?; - fcg.feed_local(ty, count as usize)?; - } - fcg.begin_body()?; - for op in item.get_operators_reader()? { - let op = op?; - fcg.feed_opcode(op, &info)?; - } - fcg.finalize()?; - } - } - SectionCode::Data => { - let data_reader = section.get_data_section_reader()?; - - for data in data_reader { - let Data { kind, data } = data?; - - match kind { - DataKind::Active { - memory_index, - init_expr, - } => { - let memory_index = MemoryIndex::new(memory_index as usize); - let base = eval_init_expr(&init_expr)?; - - let data_init = DataInitializer { - memory_index, - base, - data: data.to_vec(), - }; - - info.data_initializers.push(data_init); - } - DataKind::Passive => { - return Err(BinaryReaderError { - message: "passive memories are not yet supported", - offset: -1isize as usize, - } - .into()); - } - } - } - } - SectionCode::DataCount => {} - SectionCode::Custom { .. } => {} - } - } -} - -pub fn wp_type_to_type(ty: WpType) -> Result { - Ok(match ty { - WpType::I32 => Type::I32, - WpType::I64 => Type::I64, - WpType::F32 => Type::F32, - WpType::F64 => Type::F64, - WpType::V128 => { - return Err(BinaryReaderError { - message: "the wasmer llvm backend does not yet support the simd extension", - offset: -1isize as usize, - }); - } - _ => panic!("broken invariant, invalid type"), - }) -} - -pub fn type_to_wp_type(ty: Type) -> WpType { - match ty { - Type::I32 => WpType::I32, - Type::I64 => WpType::I64, - Type::F32 => WpType::F32, - Type::F64 => WpType::F64, - } -} - -fn func_type_to_func_sig(func_ty: FuncType) -> Result { - assert_eq!(func_ty.form, WpType::Func); - - Ok(FuncSig::new( - func_ty - .params - .iter() - .cloned() - .map(wp_type_to_type) - .collect::, _>>()?, - func_ty - .returns - .iter() - .cloned() - .map(wp_type_to_type) - .collect::, _>>()?, - )) -} - -fn eval_init_expr(expr: &InitExpr) -> Result { - let mut reader = expr.get_operators_reader(); - let (op, offset) = reader.read_with_offset()?; - Ok(match op { - Operator::GetGlobal { global_index } => { - Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) - } - Operator::I32Const { value } => Initializer::Const(Value::I32(value)), - Operator::I64Const { value } => Initializer::Const(Value::I64(value)), - Operator::F32Const { value } => { - Initializer::Const(Value::F32(f32::from_bits(value.bits()))) - } - Operator::F64Const { value } => { - Initializer::Const(Value::F64(f64::from_bits(value.bits()))) - } - _ => { - return Err(BinaryReaderError { - message: "init expr evaluation failed: unsupported opcode", - offset, - }); - } - }) -} diff --git a/lib/dynasm-backend/src/stack.rs b/lib/dynasm-backend/src/stack.rs deleted file mode 100644 index d237b05b1..000000000 --- a/lib/dynasm-backend/src/stack.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::codegen::CodegenError; -use dynasmrt::DynamicLabel; -use wasmparser::Type as WpType; - -/*#[repr(u8)] -#[derive(Copy, Clone, Debug)] -pub enum RegisterName { - RDI, - RSI, - RDX, - RCX, - R8, - R9, - R10, - R11, - RBX, - R12, - R13, - R14, - R15, - Invalid, -}*/ - -#[derive(Debug, Copy, Clone)] -pub enum IfElseState { - None, - If(DynamicLabel), - Else, -} - -#[derive(Debug)] -pub struct ControlFrame { - pub label: DynamicLabel, - pub loop_like: bool, - pub if_else: IfElseState, - pub returns: Vec, - pub value_stack_depth_before: usize, -} - -#[derive(Debug)] -pub struct ControlStack { - pub frames: Vec, -} - -#[derive(Debug)] -pub struct ValueStack { - pub num_regs: u8, - pub values: Vec, -} - -#[derive(Copy, Clone, Debug)] -pub struct ValueInfo { - pub ty: WpType, - pub location: ValueLocation, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ValueLocation { - Register(ScratchRegister), - Stack, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct ScratchRegister(u8); - -impl ScratchRegister { - pub fn raw_id(&self) -> u8 { - self.0 - } -} - -impl ValueLocation { - pub fn is_register(&self) -> bool { - if let ValueLocation::Register(_) = *self { - true - } else { - false - } - } - - pub fn get_register(&self) -> Result { - if let ValueLocation::Register(id) = *self { - Ok(id) - } else { - Err(CodegenError { - message: "not a register location", - }) - } - } -} - -impl ValueStack { - pub fn new(num_regs: u8) -> ValueStack { - ValueStack { - num_regs: num_regs, - values: vec![], - } - } - - fn next_location(&self, loc: &ValueLocation) -> ValueLocation { - match *loc { - ValueLocation::Register(ScratchRegister(x)) => { - if x >= self.num_regs - 1 { - ValueLocation::Stack - } else { - ValueLocation::Register(ScratchRegister(x + 1)) - } - } - ValueLocation::Stack => ValueLocation::Stack, - } - } - - pub fn push(&mut self, ty: WpType) -> ValueLocation { - let loc = self - .values - .last() - .map(|x| self.next_location(&x.location)) - .unwrap_or(ValueLocation::Register(ScratchRegister(0))); - self.values.push(ValueInfo { - ty: ty, - location: loc, - }); - loc - } - - pub fn pop(&mut self) -> Result { - match self.values.pop() { - Some(x) => Ok(x), - None => Err(CodegenError { - message: "no value on top of stack", - }), - } - } - - pub fn pop2(&mut self) -> Result<(ValueInfo, ValueInfo), CodegenError> { - if self.values.len() < 2 { - Err(CodegenError { - message: "less than 2 values on top of stack", - }) - } else { - let v2 = self.values.pop().unwrap(); - let v1 = self.values.pop().unwrap(); - Ok((v1, v2)) - } - } - - pub fn reset_depth(&mut self, target_depth: usize) { - self.values.truncate(target_depth); - } -} - -impl ControlStack { - pub fn new(label: DynamicLabel, returns: Vec) -> ControlStack { - ControlStack { - frames: vec![ControlFrame { - label: label, - loop_like: false, - if_else: IfElseState::None, - returns: returns, - value_stack_depth_before: 0, - }], - } - } -} diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 2823b1c4f..f89f96ca6 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,14 +9,14 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } lazy_static = "1.2.0" libc = "0.2.49" byteorder = "1" time = "0.1.41" -wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } -wasmer-dynasm-backend = { path = "../dynasm-backend", version = "0.1.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0", optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" @@ -30,4 +30,5 @@ glob = "0.2.11" [features] clif = [] llvm = ["wasmer-llvm-backend"] -dynasm = ["wasmer-dynasm-backend"] \ No newline at end of file +singlepass = ["wasmer-singlepass-backend"] +debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] diff --git a/lib/emscripten/build/emtests.rs b/lib/emscripten/build/emtests.rs index 09c4419bf..4a543683d 100644 --- a/lib/emscripten/build/emtests.rs +++ b/lib/emscripten/build/emtests.rs @@ -46,26 +46,42 @@ pub fn compile(file: &str, ignores: &Vec) -> Option { output_path.set_extension("js"); let output_str = output_path.to_str().unwrap(); - // Compile to wasm - let _wasm_compilation = Command::new("emcc") - .arg(file) - .arg("-s") - .arg("WASM=1") - .arg("-o") - .arg(output_str) - .output() - .expect("failed to execute process"); + let wasm_file_metadata = { + let mut wasm_file_path = PathBuf::from(file); + wasm_file_path.set_extension("wasm"); + if let Ok(wasm_file) = File::open(wasm_file_path) { + Some(wasm_file.metadata().unwrap()) + } else { + None + } + }; - // panic!("{:?}", wasm_compilation); - // if output.stderr { - // panic!("{}", output.stderr); - // } - // Remove js file + let real_file = File::open(file).unwrap(); + let file_metadata = real_file.metadata().unwrap(); + if wasm_file_metadata.is_none() + || file_metadata.modified().unwrap() >= wasm_file_metadata.unwrap().modified().unwrap() + { + // Compile to wasm + let _wasm_compilation = Command::new("emcc") + .arg(file) + .arg("-s") + .arg("WASM=1") + .arg("-o") + .arg(output_str) + .output() + .expect("failed to execute process"); - if Path::new(output_str).is_file() { - fs::remove_file(output_str).unwrap(); - } else { - println!("Output JS not found: {}", output_str); + // panic!("{:?}", wasm_compilation); + // if output.stderr { + // panic!("{}", output.stderr); + // } + // Remove js file + + if Path::new(output_str).is_file() { + fs::remove_file(output_str).unwrap(); + } else { + println!("Output JS not found: {}", output_str); + } } let mut output_path = PathBuf::from(file); diff --git a/lib/emscripten/emtests/hello.cpp b/lib/emscripten/emtests/hello.cpp new file mode 100644 index 000000000..fd90a65b3 --- /dev/null +++ b/lib/emscripten/emtests/hello.cpp @@ -0,0 +1,4 @@ +#include +int main() { + std::cout << "hello world\n"; +} diff --git a/lib/emscripten/emtests/hello.out b/lib/emscripten/emtests/hello.out new file mode 100644 index 000000000..9a71f81a4 --- /dev/null +++ b/lib/emscripten/emtests/hello.out @@ -0,0 +1,2 @@ +hello world + diff --git a/lib/emscripten/emtests/hello.wasm b/lib/emscripten/emtests/hello.wasm new file mode 100644 index 000000000..4e9541665 Binary files /dev/null and b/lib/emscripten/emtests/hello.wasm differ diff --git a/lib/emscripten/emtests/ignores.txt b/lib/emscripten/emtests/ignores.txt index 5033a51c3..16ff1a2a7 100644 --- a/lib/emscripten/emtests/ignores.txt +++ b/lib/emscripten/emtests/ignores.txt @@ -30,16 +30,7 @@ test_i64 test_i64_7z test_i64_varargs test_llvm_intrinsics -test_longjmp2 -test_longjmp3 -test_longjmp4 -test_longjmp test_longjmp_exc -test_longjmp_funcptr -test_longjmp_repeat -test_longjmp_stacked -test_longjmp_throw -test_longjmp_unwind test_lower_intrinsics test_main_thread_async_em_asm test_mainenv @@ -71,4 +62,47 @@ test_wprintf test_std_cout_new test_strptime_reentrant test_gmtime -test_time_c \ No newline at end of file +test_time_c +test_execvp +test_nl_types +test_phiundef +test_pipe +test_printf_2 +test_printf_more +test_regex +test_relocatable_void_function +test_rounding +test_set_align +test_sintvars +test_sizeof +test_sscanf +test_sscanf_3 +test_sscanf_4 +test_sscanf_5 +test_sscanf_6 +test_sscanf_caps +test_sscanf_float +test_sscanf_n +test_strcasecmp +test_strcmp_uni +test_strndup +test_strstr +test_strtod +test_strtok +test_strtol_bin +test_strtol_dec +test_strtol_hex +test_strtol_oct +test_strtoll_bin +test_strtoll_dec +test_strtoll_hex +test_strtoll_oct +test_struct_varargs +test_transtrcase +test_trickystring +test_unary_literal +test_vfs +test_vprintf +test_vsnprintf +test_write_stdout_fileno +test_zerodiv diff --git a/lib/emscripten/src/bitwise.rs b/lib/emscripten/src/bitwise.rs index aa7fbab32..6868c402a 100644 --- a/lib/emscripten/src/bitwise.rs +++ b/lib/emscripten/src/bitwise.rs @@ -1,8 +1,9 @@ +use crate::emscripten_target; use wasmer_runtime_core::vm::Ctx; ///emscripten: _llvm_bswap_i64 pub fn _llvm_bswap_i64(_ctx: &mut Ctx, _low: i32, high: i32) -> i32 { debug!("emscripten::_llvm_bswap_i64"); - // setTempRet0(low.swap_bytes) + emscripten_target::setTempRet0(_ctx, _low.swap_bytes()); high.swap_bytes() } diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs index 5e6b927e8..88073bc32 100644 --- a/lib/emscripten/src/emscripten_target.rs +++ b/lib/emscripten/src/emscripten_target.rs @@ -5,85 +5,25 @@ use crate::env::get_emscripten_data; use libc::getdtablesize; use wasmer_runtime_core::vm::Ctx; -pub fn setTempRet0(_ctx: &mut Ctx, _a: i32) { - debug!("emscripten::setTempRet0"); +pub fn setTempRet0(ctx: &mut Ctx, val: i32) { + debug!("emscripten::setTempRet0: {}", val); + get_emscripten_data(ctx).temp_ret_0 = val; } -pub fn getTempRet0(_ctx: &mut Ctx) -> i32 { + +pub fn getTempRet0(ctx: &mut Ctx) -> i32 { debug!("emscripten::getTempRet0"); + get_emscripten_data(ctx).temp_ret_0 +} + +pub fn _atexit(_ctx: &mut Ctx, _func: i32) -> i32 { + debug!("emscripten::_atexit"); + // TODO: implement atexit properly + // __ATEXIT__.unshift({ + // func: func, + // arg: arg + // }); 0 } -pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 { - debug!("emscripten::invoke_i"); - if let Some(dyn_call_i) = &get_emscripten_data(ctx).dyn_call_i { - dyn_call_i.call(index).unwrap() - } else { - panic!("dyn_call_i is set to None"); - } -} -pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { - debug!("emscripten::invoke_ii"); - if let Some(dyn_call_ii) = &get_emscripten_data(ctx).dyn_call_ii { - dyn_call_ii.call(index, a1).unwrap() - } else { - panic!("dyn_call_ii is set to None"); - } -} -pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 { - debug!("emscripten::invoke_iii"); - if let Some(dyn_call_iii) = &get_emscripten_data(ctx).dyn_call_iii { - dyn_call_iii.call(index, a1, a2).unwrap() - } else { - panic!("dyn_call_iii is set to None"); - } -} -pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { - debug!("emscripten::invoke_iiii"); - if let Some(dyn_call_iiii) = &get_emscripten_data(ctx).dyn_call_iiii { - dyn_call_iiii.call(index, a1, a2, a3).unwrap() - } else { - panic!("dyn_call_iiii is set to None"); - } -} -pub fn invoke_v(ctx: &mut Ctx, index: i32) { - debug!("emscripten::invoke_v"); - if let Some(dyn_call_v) = &get_emscripten_data(ctx).dyn_call_v { - dyn_call_v.call(index).unwrap(); - } else { - panic!("dyn_call_v is set to None"); - } -} -pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) { - debug!("emscripten::invoke_vi"); - if let Some(dyn_call_vi) = &get_emscripten_data(ctx).dyn_call_vi { - dyn_call_vi.call(index, a1).unwrap(); - } else { - panic!("dyn_call_vi is set to None"); - } -} -pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { - debug!("emscripten::invoke_vii"); - if let Some(dyn_call_vii) = &get_emscripten_data(ctx).dyn_call_vii { - dyn_call_vii.call(index, a1, a2).unwrap(); - } else { - panic!("dyn_call_vii is set to None"); - } -} -pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { - debug!("emscripten::invoke_viii"); - if let Some(dyn_call_viii) = &get_emscripten_data(ctx).dyn_call_viii { - dyn_call_viii.call(index, a1, a2, a3).unwrap(); - } else { - panic!("dyn_call_viii is set to None"); - } -} -pub fn invoke_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) { - debug!("emscripten::invoke_viiii"); - if let Some(dyn_call_viiii) = &get_emscripten_data(ctx).dyn_call_viiii { - dyn_call_viiii.call(index, a1, a2, a3, a4).unwrap(); - } else { - panic!("dyn_call_viiii is set to None"); - } -} pub fn __Unwind_Backtrace(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::__Unwind_Backtrace"); 0 @@ -114,14 +54,65 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_dladdr"); 0 } +pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_attr_init({})", _a); + 0 +} +pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_attr_destroy"); + 0 +} +pub fn _pthread_attr_getstack( + _ctx: &mut Ctx, + _stackaddr: i32, + _stacksize: i32, + _other: i32, +) -> i32 { + debug!( + "emscripten::_pthread_attr_getstack({}, {}, {})", + _stackaddr, _stacksize, _other + ); + // TODO: Translate from Emscripten + // HEAP32[stackaddr >> 2] = STACK_BASE; + // HEAP32[stacksize >> 2] = TOTAL_STACK; + 0 +} pub fn _pthread_cond_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_cond_destroy"); 0 } +pub fn _pthread_cond_timedwait(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32) -> i32 { + debug!("emscripten::_pthread_cond_timedwait"); + 0 +} +pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_getspecific"); + 0 +} +pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 { + debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr); + 0 +} +pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_setspecific"); + 0 +} +pub fn _pthread_once(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_once"); + 0 +} +pub fn _pthread_key_create(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_key_create"); + 0 +} pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 { debug!("emscripten::_pthread_create"); 0 } +pub fn _pthread_detach(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_detach"); + 0 +} pub fn _pthread_join(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_join"); 0 @@ -225,29 +216,96 @@ pub fn _getloadavg(_ctx: &mut Ctx, _loadavg: i32, _nelem: i32) -> i32 { debug!("emscripten::getloadavg"); 0 } + +// Invoke functions +// They save the stack to allow unwinding + +// Macro definitions +macro_rules! invoke { + ($ctx: ident, $name:ident, $( $arg:ident ),*) => {{ + let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed"); + let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*); + match result { + Ok(v) => v, + Err(_e) => { + get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed"); + // TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error + // JS version is: if (e !== e+0 && e !== 'longjmp') throw e; + get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed"); + 0 as _ + } + } + }}; +} +macro_rules! invoke_no_return { + ($ctx: ident, $name:ident, $( $arg:ident ),*) => {{ + let sp = get_emscripten_data($ctx).stack_save.as_ref().expect("stack_save is None").call().expect("stack_save call failed"); + let result = get_emscripten_data($ctx).$name.as_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*); + match result { + Ok(v) => v, + Err(_e) => { + get_emscripten_data($ctx).stack_restore.as_ref().expect("stack_restore is None").call(sp).expect("stack_restore call failed"); + // TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error + // JS version is: if (e !== e+0 && e !== 'longjmp') throw e; + get_emscripten_data($ctx).set_threw.as_ref().expect("set_threw is None").call(1, 0).expect("set_threw call failed"); + } + } + }}; +} + +// Invoke functions +pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 { + debug!("emscripten::invoke_i"); + invoke!(ctx, dyn_call_i, index) +} +pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { + debug!("emscripten::invoke_ii"); + invoke!(ctx, dyn_call_ii, index, a1) +} +pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 { + debug!("emscripten::invoke_iii"); + invoke!(ctx, dyn_call_iii, index, a1, a2) +} +pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { + debug!("emscripten::invoke_iiii"); + invoke!(ctx, dyn_call_iiii, index, a1, a2, a3) +} +pub fn invoke_iifi(ctx: &mut Ctx, index: i32, a1: i32, a2: f64, a3: i32) -> i32 { + debug!("emscripten::invoke_iifi"); + invoke!(ctx, dyn_call_iifi, index, a1, a2, a3) +} +pub fn invoke_v(ctx: &mut Ctx, index: i32) { + debug!("emscripten::invoke_v"); + invoke_no_return!(ctx, dyn_call_v, index); +} +pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) { + debug!("emscripten::invoke_vi"); + invoke_no_return!(ctx, dyn_call_vi, index, a1); +} +pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { + debug!("emscripten::invoke_vii"); + invoke_no_return!(ctx, dyn_call_vii, index, a1, a2); +} + +pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { + debug!("emscripten::invoke_viii"); + invoke_no_return!(ctx, dyn_call_viii, index, a1, a2, a3); +} +pub fn invoke_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) { + debug!("emscripten::invoke_viiii"); + invoke_no_return!(ctx, dyn_call_viiii, index, a1, a2, a3, a4); +} pub fn invoke_dii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> f64 { debug!("emscripten::invoke_dii"); - if let Some(dyn_call_dii) = &get_emscripten_data(ctx).dyn_call_dii { - dyn_call_dii.call(index, a1, a2).unwrap() - } else { - panic!("dyn_call_dii is set to None"); - } + invoke!(ctx, dyn_call_dii, index, a1, a2) } pub fn invoke_diiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> f64 { debug!("emscripten::invoke_diiii"); - if let Some(dyn_call_diiii) = &get_emscripten_data(ctx).dyn_call_diiii { - dyn_call_diiii.call(index, a1, a2, a3, a4).unwrap() - } else { - panic!("dyn_call_diiii is set to None"); - } + invoke!(ctx, dyn_call_diiii, index, a1, a2, a3, a4) } pub fn invoke_iiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 { debug!("emscripten::invoke_iiiii"); - if let Some(dyn_call_iiiii) = &get_emscripten_data(ctx).dyn_call_iiiii { - dyn_call_iiiii.call(index, a1, a2, a3, a4).unwrap() - } else { - panic!("dyn_call_iiiii is set to None"); - } + invoke!(ctx, dyn_call_iiiii, index, a1, a2, a3, a4) } pub fn invoke_iiiiii( ctx: &mut Ctx, @@ -259,11 +317,7 @@ pub fn invoke_iiiiii( a5: i32, ) -> i32 { debug!("emscripten::invoke_iiiiii"); - if let Some(dyn_call_iiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiii { - dyn_call_iiiiii.call(index, a1, a2, a3, a4, a5).unwrap() - } else { - panic!("dyn_call_iiiiii is set to None"); - } + invoke!(ctx, dyn_call_iiiiii, index, a1, a2, a3, a4, a5) } pub fn invoke_iiiiiii( ctx: &mut Ctx, @@ -276,13 +330,7 @@ pub fn invoke_iiiiiii( a6: i32, ) -> i32 { debug!("emscripten::invoke_iiiiiii"); - if let Some(dyn_call_iiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiii { - dyn_call_iiiiiii - .call(index, a1, a2, a3, a4, a5, a6) - .unwrap() - } else { - panic!("dyn_call_iiiiiii is set to None"); - } + invoke!(ctx, dyn_call_iiiiiii, index, a1, a2, a3, a4, a5, a6) } pub fn invoke_iiiiiiii( ctx: &mut Ctx, @@ -296,13 +344,34 @@ pub fn invoke_iiiiiiii( a7: i32, ) -> i32 { debug!("emscripten::invoke_iiiiiiii"); - if let Some(dyn_call_iiiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiiii { - dyn_call_iiiiiiii - .call(index, a1, a2, a3, a4, a5, a6, a7) - .unwrap() - } else { - panic!("dyn_call_iiiiiiii is set to None"); - } + invoke!(ctx, dyn_call_iiiiiiii, index, a1, a2, a3, a4, a5, a6, a7) +} +pub fn invoke_iiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiii"); + invoke!( + ctx, + dyn_call_iiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8 + ) } pub fn invoke_iiiiiiiiii( ctx: &mut Ctx, @@ -318,29 +387,59 @@ pub fn invoke_iiiiiiiiii( a9: i32, ) -> i32 { debug!("emscripten::invoke_iiiiiiiiii"); - if let Some(dyn_call_iiiiiiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiiiiiii { - dyn_call_iiiiiiiiii - .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9) - .unwrap() - } else { - panic!("dyn_call_iiiiiiiiii is set to None"); - } + invoke!( + ctx, + dyn_call_iiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9 + ) +} +pub fn invoke_iiiiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, + a10: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiiiii"); + invoke!( + ctx, + dyn_call_iiiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ) } pub fn invoke_vd(ctx: &mut Ctx, index: i32, a1: f64) { debug!("emscripten::invoke_vd"); - if let Some(dyn_call_vd) = &get_emscripten_data(ctx).dyn_call_vd { - dyn_call_vd.call(index, a1).unwrap(); - } else { - panic!("dyn_call_vd is set to None"); - } + invoke_no_return!(ctx, dyn_call_vd, index, a1) } pub fn invoke_viiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { debug!("emscripten::invoke_viiiii"); - if let Some(dyn_call_viiiii) = &get_emscripten_data(ctx).dyn_call_viiiii { - dyn_call_viiiii.call(index, a1, a2, a3, a4, a5).unwrap(); - } else { - panic!("dyn_call_viiiii is set to None"); - } + invoke_no_return!(ctx, dyn_call_viiiii, index, a1, a2, a3, a4, a5) } pub fn invoke_viiiiii( ctx: &mut Ctx, @@ -353,13 +452,7 @@ pub fn invoke_viiiiii( a6: i32, ) { debug!("emscripten::invoke_viiiiii"); - if let Some(dyn_call_viiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiii { - dyn_call_viiiiii - .call(index, a1, a2, a3, a4, a5, a6) - .unwrap(); - } else { - panic!("dyn_call_viiiiii is set to None"); - } + invoke_no_return!(ctx, dyn_call_viiiiii, index, a1, a2, a3, a4, a5, a6) } pub fn invoke_viiiiiii( ctx: &mut Ctx, @@ -373,13 +466,7 @@ pub fn invoke_viiiiiii( a7: i32, ) { debug!("emscripten::invoke_viiiiiii"); - if let Some(dyn_call_viiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiii { - dyn_call_viiiiiii - .call(index, a1, a2, a3, a4, a5, a6, a7) - .unwrap(); - } else { - panic!("dyn_call_viiiiiii is set to None"); - } + invoke_no_return!(ctx, dyn_call_viiiiiii, index, a1, a2, a3, a4, a5, a6, a7) } pub fn invoke_viiiiiiii( ctx: &mut Ctx, @@ -394,13 +481,19 @@ pub fn invoke_viiiiiiii( a8: i32, ) { debug!("emscripten::invoke_viiiiiiii"); - if let Some(dyn_call_viiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiii { - dyn_call_viiiiiiii - .call(index, a1, a2, a3, a4, a5, a6, a7, a8) - .unwrap(); - } else { - panic!("dyn_call_viiiiiiii is set to None"); - } + invoke_no_return!( + ctx, + dyn_call_viiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8 + ) } pub fn invoke_viiiiiiiii( ctx: &mut Ctx, @@ -416,21 +509,75 @@ pub fn invoke_viiiiiiiii( a9: i32, ) { debug!("emscripten::invoke_viiiiiiiii"); - if let Some(dyn_call_viiiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiiii { - dyn_call_viiiiiiiii - .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9) - .unwrap(); - } else { - panic!("dyn_call_viiiiiiiii is set to None"); - } + invoke_no_return!( + ctx, + dyn_call_viiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9 + ) } +pub fn invoke_viiiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, + a10: i32, +) { + debug!("emscripten::invoke_viiiiiiiiii"); + invoke_no_return!( + ctx, + dyn_call_viiiiiiiiii, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ) +} + +pub fn invoke_iij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { + debug!("emscripten::invoke_iij"); + invoke!(ctx, dyn_call_iij, index, a1, a2, a3) +} + pub fn invoke_iiji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 { debug!("emscripten::invoke_iiji"); - if let Some(dyn_call_iiji) = &get_emscripten_data(ctx).dyn_call_iiji { - dyn_call_iiji.call(index, a1, a2, a3, a4).unwrap() - } else { - panic!("dyn_call_iiji is set to None"); - } + invoke!(ctx, dyn_call_iiji, index, a1, a2, a3, a4) +} + +pub fn invoke_iiijj( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) -> i32 { + debug!("emscripten::invoke_iiijj"); + invoke!(ctx, dyn_call_iiijj, index, a1, a2, a3, a4, a5, a6) } pub fn invoke_j(ctx: &mut Ctx, index: i32) -> i32 { debug!("emscripten::invoke_j"); @@ -448,6 +595,15 @@ pub fn invoke_ji(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { panic!("dyn_call_ji is set to None"); } } +pub fn invoke_jii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 { + debug!("emscripten::invoke_jii"); + if let Some(dyn_call_jii) = &get_emscripten_data(ctx).dyn_call_jii { + dyn_call_jii.call(index, a1, a2).unwrap() + } else { + panic!("dyn_call_jii is set to None"); + } +} + pub fn invoke_jij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { debug!("emscripten::invoke_jij"); if let Some(dyn_call_jij) = &get_emscripten_data(ctx).dyn_call_jij { @@ -571,6 +727,18 @@ pub fn invoke_viijj( panic!("dyn_call_viijj is set to None"); } } +pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { + debug!("emscripten::invoke_vj"); + if let Some(dyn_call_vj) = &get_emscripten_data(ctx).dyn_call_vj { + dyn_call_vj.call(index, a1, a2).unwrap(); + } else { + panic!("dyn_call_vj is set to None"); + } +} +pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_vjji"); + invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5) +} pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { debug!("emscripten::invoke_vij"); if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij { @@ -612,11 +780,42 @@ pub fn invoke_vijj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32 panic!("dyn_call_vijj is set to None"); } } +pub fn invoke_viid(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64) { + debug!("emscripten::invoke_viid"); + invoke_no_return!(ctx, dyn_call_viid, index, a1, a2, a3); +} pub fn invoke_viidii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: f64, a4: i32, a5: i32) { debug!("emscripten::invoke_viidii"); - if let Some(dyn_call_viidii) = &get_emscripten_data(ctx).dyn_call_viidii { - dyn_call_viidii.call(index, a1, a2, a3, a4, a5).unwrap(); - } else { - panic!("dyn_call_viidii is set to None"); - } + invoke_no_return!(ctx, dyn_call_viidii, index, a1, a2, a3, a4, a5); +} +pub fn invoke_viidddddddd( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: f64, + a4: f64, + a5: f64, + a6: f64, + a7: f64, + a8: f64, + a9: f64, + a10: f64, +) { + debug!("emscripten::invoke_viidddddddd"); + invoke_no_return!( + ctx, + dyn_call_viidddddddd, + index, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10 + ); } diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 662d4ba94..897fb38f0 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -47,27 +47,64 @@ pub fn _getpagesize(_ctx: &mut Ctx) -> u32 { 16384 } +pub fn _times(ctx: &mut Ctx, buffer: u32) -> u32 { + if buffer != 0 { + call_memset(ctx, buffer, 0, 16); + } + 0 +} + #[allow(clippy::cast_ptr_alignment)] pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) { debug!("emscripten::___build_environment {}", environ); const MAX_ENV_VALUES: u32 = 64; const TOTAL_ENV_SIZE: u32 = 1024; let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int; - unsafe { + let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe { let (pool_offset, _pool_slice): (u32, &mut [u8]) = allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32); let (env_offset, _env_slice): (u32, &mut [u8]) = allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32); let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int; - let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int; + let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8; *env_ptr = pool_offset as i32; *environment = env_offset as i32; - // *env_ptr = 0; + (pool_offset, env_ptr, pool_ptr) }; - // unsafe { - // *env_ptr = 0; - // }; + + // *env_ptr = 0; + let default_vars = vec![ + ["USER", "web_user"], + ["LOGNAME", "web_user"], + ["PATH", "/"], + ["PWD", "/"], + ["HOME", "/home/web_user"], + ["LANG", "C.UTF-8"], + ["_", "thisProgram"], + ]; + let mut strings = vec![]; + let mut total_size = 0; + for [key, val] in &default_vars { + let line = key.to_string() + "=" + val; + total_size += line.len(); + strings.push(line); + } + if total_size as u32 > TOTAL_ENV_SIZE { + panic!("Environment size exceeded TOTAL_ENV_SIZE!"); + } + unsafe { + for (i, s) in strings.iter().enumerate() { + for (j, c) in s.chars().enumerate() { + debug_assert!(c < u8::max_value() as char); + *pool_ptr.add(j) = c as u8; + } + *env_ptr.add(i * 4) = pool_offset as i32; + pool_offset += s.len() as u32 + 1; + pool_ptr = pool_ptr.add(s.len() + 1); + } + *env_ptr.add(strings.len() * 4) = 0; + } } pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) { diff --git a/lib/emscripten/src/exception.rs b/lib/emscripten/src/exception.rs index b0aab78dd..f37c015e5 100644 --- a/lib/emscripten/src/exception.rs +++ b/lib/emscripten/src/exception.rs @@ -14,3 +14,23 @@ pub fn ___cxa_throw(ctx: &mut Ctx, _ptr: u32, _ty: u32, _destructor: u32) { debug!("emscripten::___cxa_throw"); _abort(ctx); } + +pub fn ___cxa_begin_catch(_ctx: &mut Ctx, _exception_object_ptr: u32) -> i32 { + debug!("emscripten::___cxa_begin_catch"); + -1 +} + +pub fn ___cxa_end_catch(_ctx: &mut Ctx) { + debug!("emscripten::___cxa_end_catch"); +} + +pub fn ___cxa_uncaught_exception(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::___cxa_uncaught_exception"); + -1 +} + +pub fn ___cxa_pure_virtual(_ctx: &mut Ctx) { + debug!("emscripten::___cxa_pure_virtual"); + // ABORT = true + panic!("Pure virtual function called!"); +} diff --git a/lib/emscripten/src/io/mod.rs b/lib/emscripten/src/io/mod.rs index ceee3bdc1..6666cd5af 100644 --- a/lib/emscripten/src/io/mod.rs +++ b/lib/emscripten/src/io/mod.rs @@ -25,21 +25,35 @@ pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 { } /// sigdelset -pub fn sigdelset(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { +pub fn sigdelset(ctx: &mut Ctx, set: i32, signum: i32) -> i32 { debug!("emscripten::sigdelset"); - unimplemented!() + let memory = ctx.memory(0); + #[allow(clippy::cast_ptr_alignment)] + let ptr = emscripten_memory_pointer!(memory, set) as *mut i32; + + unsafe { *ptr = *ptr & !(1 << (signum - 1)) } + + 0 } /// sigfillset -pub fn sigfillset(_ctx: &mut Ctx, _one: i32) -> i32 { +pub fn sigfillset(ctx: &mut Ctx, set: i32) -> i32 { debug!("emscripten::sigfillset"); - unimplemented!() + let memory = ctx.memory(0); + #[allow(clippy::cast_ptr_alignment)] + let ptr = emscripten_memory_pointer!(memory, set) as *mut i32; + + unsafe { + *ptr = -1; + } + + 0 } /// tzset pub fn tzset(_ctx: &mut Ctx) { - debug!("emscripten::tzset"); - unimplemented!() + debug!("emscripten::tzset - stub"); + //unimplemented!() } /// strptime diff --git a/lib/emscripten/src/jmp.rs b/lib/emscripten/src/jmp.rs index c5b54f02b..1d9691bc1 100644 --- a/lib/emscripten/src/jmp.rs +++ b/lib/emscripten/src/jmp.rs @@ -1,43 +1,61 @@ use super::env::get_emscripten_data; -use libc::{c_int, c_void}; -use std::cell::UnsafeCell; +use super::process::abort_with_message; +use libc::c_int; +// use std::cell::UnsafeCell; use wasmer_runtime_core::vm::Ctx; /// setjmp -pub fn __setjmp(ctx: &mut Ctx, env_addr: u32) -> c_int { +pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int { debug!("emscripten::__setjmp (setjmp)"); - unsafe { - // Rather than using the env as the holder of the jump buffer pointer, - // we use the environment address to store the index relative to jumps - // so the address of the jump it's outside the wasm memory itself. - let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; - // We create the jump buffer outside of the wasm memory - let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]); - let jumps = &mut get_emscripten_data(ctx).jumps; - let result = setjmp(jump_buf.get() as _); - // We set the jump index to be the last 3value of jumps - *jump_index = jumps.len() as _; - // We hold the reference of the jump buffer - jumps.push(jump_buf); - result - } + abort_with_message(ctx, "missing function: _setjmp"); + unreachable!() + // unsafe { + // // Rather than using the env as the holder of the jump buffer pointer, + // // we use the environment address to store the index relative to jumps + // // so the address of the jump it's outside the wasm memory itself. + // let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; + // // We create the jump buffer outside of the wasm memory + // let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]); + // let jumps = &mut get_emscripten_data(ctx).jumps; + // let result = setjmp(jump_buf.get() as _); + // // We set the jump index to be the last 3value of jumps + // *jump_index = jumps.len() as _; + // // We hold the reference of the jump buffer + // jumps.push(jump_buf); + // result + // } } /// longjmp #[allow(unreachable_code)] -pub fn __longjmp(ctx: &mut Ctx, env_addr: u32, val: c_int) { +pub fn __longjmp(ctx: &mut Ctx, _env_addr: u32, _val: c_int) { debug!("emscripten::__longjmp (longmp)"); - unsafe { - // We retrieve the jump index from the env address - let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; - let jumps = &mut get_emscripten_data(ctx).jumps; - // We get the real jump buffer from the jumps vector, using the retrieved index - let jump_buf = &jumps[*jump_index as usize]; - longjmp(jump_buf.get() as _, val) - }; + abort_with_message(ctx, "missing function: _longjmp"); + // unsafe { + // // We retrieve the jump index from the env address + // let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8; + // let jumps = &mut get_emscripten_data(ctx).jumps; + // // We get the real jump buffer from the jumps vector, using the retrieved index + // let jump_buf = &jumps[*jump_index as usize]; + // longjmp(jump_buf.get() as _, val) + // }; } -extern "C" { - fn setjmp(env: *mut c_void) -> c_int; - fn longjmp(env: *mut c_void, val: c_int) -> !; +/// _longjmp +// This function differs from the js implementation, it should return Result<(), &'static str> +pub fn _longjmp(ctx: &mut Ctx, env_addr: i32, val: c_int) -> Result<(), ()> { + let val = if val == 0 { 1 } else { val }; + get_emscripten_data(ctx) + .set_threw + .as_ref() + .expect("set_threw is None") + .call(env_addr, val) + .expect("set_threw failed to call"); + // TODO: return Err("longjmp") + Err(()) } + +// extern "C" { +// fn setjmp(env: *mut c_void) -> c_int; +// fn longjmp(env: *mut c_void, val: c_int) -> !; +// } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 4d41cc40d..6b0340513 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; @@ -50,7 +52,7 @@ mod varargs; pub use self::storage::{align_memory, static_alloc}; pub use self::utils::{ - allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, + allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, get_emscripten_metadata, get_emscripten_table_size, is_emscripten_module, }; @@ -83,6 +85,7 @@ pub struct EmscriptenData<'a> { pub dyn_call_ii: Option>, pub dyn_call_iii: Option>, pub dyn_call_iiii: Option>, + pub dyn_call_iifi: Option>, pub dyn_call_v: Option>, pub dyn_call_vi: Option>, pub dyn_call_vii: Option>, @@ -96,17 +99,25 @@ pub struct EmscriptenData<'a> { pub dyn_call_iiiiii: Option>, pub dyn_call_iiiiiii: Option>, pub dyn_call_iiiiiiii: Option>, + pub dyn_call_iiiiiiiii: Option>, pub dyn_call_iiiiiiiiii: Option>, + pub dyn_call_iiiiiiiiiii: + Option>, pub dyn_call_vd: Option>, pub dyn_call_viiiii: Option>, pub dyn_call_viiiiii: Option>, pub dyn_call_viiiiiii: Option>, pub dyn_call_viiiiiiii: Option>, pub dyn_call_viiiiiiiii: Option>, + pub dyn_call_viiiiiiiiii: + Option>, + pub dyn_call_iij: Option>, pub dyn_call_iiji: Option>, + pub dyn_call_iiijj: Option>, pub dyn_call_j: Option>, pub dyn_call_ji: Option>, + pub dyn_call_jii: Option>, pub dyn_call_jij: Option>, pub dyn_call_jjj: Option>, pub dyn_call_viiij: Option>, @@ -117,22 +128,28 @@ pub struct EmscriptenData<'a> { pub dyn_call_viiji: Option>, pub dyn_call_viijiii: Option>, pub dyn_call_viijj: Option>, + pub dyn_call_vj: Option>, + pub dyn_call_vjji: Option>, pub dyn_call_vij: Option>, pub dyn_call_viji: Option>, pub dyn_call_vijiii: Option>, pub dyn_call_vijj: Option>, + pub dyn_call_viid: Option>, pub dyn_call_viidii: Option>, + pub dyn_call_viidddddddd: + Option>, + pub temp_ret_0: i32, + + pub stack_save: Option>, + pub stack_restore: Option>, + pub set_threw: Option>, } impl<'a> EmscriptenData<'a> { pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> { let malloc = instance.func("_malloc").unwrap(); let free = instance.func("_free").unwrap(); - let memalign = if let Ok(func) = instance.func("_memalign") { - Some(func) - } else { - None - }; + let memalign = instance.func("_memalign").ok(); let memset = instance.func("_memset").unwrap(); let stack_alloc = instance.func("stackAlloc").unwrap(); @@ -140,6 +157,7 @@ impl<'a> EmscriptenData<'a> { let dyn_call_ii = instance.func("dynCall_ii").ok(); let dyn_call_iii = instance.func("dynCall_iii").ok(); let dyn_call_iiii = instance.func("dynCall_iiii").ok(); + let dyn_call_iifi = instance.func("dynCall_iifi").ok(); let dyn_call_v = instance.func("dynCall_v").ok(); let dyn_call_vi = instance.func("dynCall_vi").ok(); let dyn_call_vii = instance.func("dynCall_vii").ok(); @@ -153,16 +171,22 @@ impl<'a> EmscriptenData<'a> { let dyn_call_iiiiii = instance.func("dynCall_iiiiii").ok(); let dyn_call_iiiiiii = instance.func("dynCall_iiiiiii").ok(); let dyn_call_iiiiiiii = instance.func("dynCall_iiiiiiii").ok(); + let dyn_call_iiiiiiiii = instance.func("dynCall_iiiiiiiii").ok(); let dyn_call_iiiiiiiiii = instance.func("dynCall_iiiiiiiiii").ok(); + let dyn_call_iiiiiiiiiii = instance.func("dynCall_iiiiiiiiiii").ok(); let dyn_call_vd = instance.func("dynCall_vd").ok(); let dyn_call_viiiii = instance.func("dynCall_viiiii").ok(); let dyn_call_viiiiii = instance.func("dynCall_viiiiii").ok(); let dyn_call_viiiiiii = instance.func("dynCall_viiiiiii").ok(); let dyn_call_viiiiiiii = instance.func("dynCall_viiiiiiii").ok(); let dyn_call_viiiiiiiii = instance.func("dynCall_viiiiiiiii").ok(); + let dyn_call_viiiiiiiiii = instance.func("dynCall_viiiiiiiiii").ok(); + let dyn_call_iij = instance.func("dynCall_iij").ok(); let dyn_call_iiji = instance.func("dynCall_iiji").ok(); + let dyn_call_iiijj = instance.func("dynCall_iiijj").ok(); let dyn_call_j = instance.func("dynCall_j").ok(); let dyn_call_ji = instance.func("dynCall_ji").ok(); + let dyn_call_jii = instance.func("dynCall_jii").ok(); let dyn_call_jij = instance.func("dynCall_jij").ok(); let dyn_call_jjj = instance.func("dynCall_jjj").ok(); let dyn_call_viiij = instance.func("dynCall_viiij").ok(); @@ -172,11 +196,19 @@ impl<'a> EmscriptenData<'a> { let dyn_call_viiji = instance.func("dynCall_viiji").ok(); let dyn_call_viijiii = instance.func("dynCall_viijiii").ok(); let dyn_call_viijj = instance.func("dynCall_viijj").ok(); + let dyn_call_vj = instance.func("dynCall_vj").ok(); + let dyn_call_vjji = instance.func("dynCall_vjji").ok(); let dyn_call_vij = instance.func("dynCall_vij").ok(); let dyn_call_viji = instance.func("dynCall_viji").ok(); let dyn_call_vijiii = instance.func("dynCall_vijiii").ok(); let dyn_call_vijj = instance.func("dynCall_vijj").ok(); + let dyn_call_viid = instance.func("dynCall_viid").ok(); let dyn_call_viidii = instance.func("dynCall_viidii").ok(); + let dyn_call_viidddddddd = instance.func("dynCall_viidddddddd").ok(); + + let stack_save = instance.func("stackSave").ok(); + let stack_restore = instance.func("stackRestore").ok(); + let set_threw = instance.func("_setThrew").ok(); EmscriptenData { malloc, @@ -189,6 +221,7 @@ impl<'a> EmscriptenData<'a> { dyn_call_ii, dyn_call_iii, dyn_call_iiii, + dyn_call_iifi, dyn_call_v, dyn_call_vi, dyn_call_vii, @@ -202,16 +235,22 @@ impl<'a> EmscriptenData<'a> { dyn_call_iiiiii, dyn_call_iiiiiii, dyn_call_iiiiiiii, + dyn_call_iiiiiiiii, dyn_call_iiiiiiiiii, + dyn_call_iiiiiiiiiii, dyn_call_vd, dyn_call_viiiii, dyn_call_viiiiii, dyn_call_viiiiiii, dyn_call_viiiiiiii, dyn_call_viiiiiiiii, + dyn_call_viiiiiiiiii, + dyn_call_iij, dyn_call_iiji, + dyn_call_iiijj, dyn_call_j, dyn_call_ji, + dyn_call_jii, dyn_call_jij, dyn_call_jjj, dyn_call_viiij, @@ -221,11 +260,20 @@ impl<'a> EmscriptenData<'a> { dyn_call_viiji, dyn_call_viijiii, dyn_call_viijj, + dyn_call_vj, + dyn_call_vjji, dyn_call_vij, dyn_call_viji, dyn_call_vijiii, dyn_call_vijj, + dyn_call_viid, dyn_call_viidii, + dyn_call_viidddddddd, + temp_ret_0: 0, + + stack_save, + stack_restore, + set_threw, } } } @@ -235,63 +283,76 @@ pub fn run_emscripten_instance( instance: &mut Instance, path: &str, args: Vec<&str>, + entrypoint: Option, ) -> CallResult<()> { let mut data = EmscriptenData::new(instance); let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; + // ATINIT + // (used by C++) + if let Ok(_func) = instance.dyn_func("globalCtors") { + instance.call("globalCtors", &[])?; + } + if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { instance.call("___emscripten_environ_constructor", &[])?; } // println!("running emscripten instance"); - let main_func = instance.dyn_func("_main")?; - let num_params = main_func.signature().params().len(); - let _result = match num_params { - 2 => { - let (argc, argv) = store_module_arguments(instance.context_mut(), path, args); - instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; - } - 0 => { - instance.call("_main", &[])?; - } - _ => panic!( - "The emscripten main function has received an incorrect number of params {}", - num_params - ), - }; + if let Some(ep) = entrypoint { + debug!("Running entry point: {}", &ep); + let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 }; + //let (argc, argv) = store_module_arguments(instance.context_mut(), args); + instance.call(&ep, &[Value::I32(arg as i32)])?; + } else { + let main_func = instance.dyn_func("_main")?; + let num_params = main_func.signature().params().len(); + let _result = match num_params { + 2 => { + let mut new_args = vec![path]; + new_args.extend(args); + let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); + instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; + } + 0 => { + instance.call("_main", &[])?; + } + _ => panic!( + "The emscripten main function has received an incorrect number of params {}", + num_params + ), + }; + } - // TODO atinit and atexit for emscripten + // TODO atexit for emscripten // println!("{:?}", data); Ok(()) } -fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) { +fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) { let argc = args.len() + 1; let mut args_slice = vec![0; argc]; - args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 }; - for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) { + for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) { *slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 }; } let (argv_offset, argv_slice): (_, &mut [u32]) = - unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) }; + unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) }; assert!(!argv_slice.is_empty()); for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) { *slot = *arg } argv_slice[argc] = 0; - (argc as u32, argv_offset) + (argc as u32 - 1, argv_offset) } pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) { let dynamictop_ptr = globals.dynamictop_ptr; - let stack_max = globals.stack_max; - - let dynamic_base = align_memory(stack_max); + let dynamic_base = globals.dynamic_base; memory.view::()[(dynamictop_ptr / 4) as usize].set(dynamic_base); } @@ -302,6 +363,7 @@ pub struct EmscriptenGlobalsData { stacktop: u32, stack_max: u32, dynamictop_ptr: u32, + dynamic_base: u32, memory_base: u32, table_base: u32, temp_double_ptr: u32, @@ -371,7 +433,14 @@ impl EmscriptenGlobals { let temp_double_ptr = static_top; static_top += 16; - let dynamictop_ptr = static_alloc(&mut static_top, 4); + let (dynamic_base, dynamictop_ptr) = + get_emscripten_metadata(&module).unwrap_or_else(|| { + let dynamictop_ptr = static_alloc(&mut static_top, 4); + ( + align_memory(align_memory(static_top) + TOTAL_STACK), + dynamictop_ptr, + ) + }); let stacktop = align_memory(static_top); let stack_max = stacktop + TOTAL_STACK; @@ -381,6 +450,7 @@ impl EmscriptenGlobals { stacktop, stack_max, dynamictop_ptr, + dynamic_base, memory_base, table_base, temp_double_ptr, @@ -476,6 +546,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_getpagesize" => func!(crate::env::_getpagesize), "_sysconf" => func!(crate::env::_sysconf), "_getaddrinfo" => func!(crate::env::_getaddrinfo), + "_times" => func!(crate::env::_times), // Syscalls "___syscall1" => func!(crate::syscalls::___syscall1), @@ -505,8 +576,8 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___syscall77" => func!(crate::syscalls::___syscall77), "___syscall83" => func!(crate::syscalls::___syscall83), "___syscall85" => func!(crate::syscalls::___syscall85), - "___syscall91" => func!(crate::syscalls::___syscall191), - "___syscall94" => func!(crate::syscalls::___syscall194), + "___syscall91" => func!(crate::syscalls::___syscall91), + "___syscall94" => func!(crate::syscalls::___syscall94), "___syscall97" => func!(crate::syscalls::___syscall97), "___syscall102" => func!(crate::syscalls::___syscall102), "___syscall110" => func!(crate::syscalls::___syscall110), @@ -543,6 +614,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___syscall272" => func!(crate::syscalls::___syscall272), "___syscall295" => func!(crate::syscalls::___syscall295), "___syscall300" => func!(crate::syscalls::___syscall300), + "___syscall320" => func!(crate::syscalls::___syscall320), "___syscall324" => func!(crate::syscalls::___syscall324), "___syscall330" => func!(crate::syscalls::___syscall330), "___syscall334" => func!(crate::syscalls::___syscall334), @@ -562,8 +634,10 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_kill" => func!(crate::process::_kill), "_llvm_stackrestore" => func!(crate::process::_llvm_stackrestore), "_llvm_stacksave" => func!(crate::process::_llvm_stacksave), + "_llvm_eh_typeid_for" => func!(crate::process::_llvm_eh_typeid_for), "_raise" => func!(crate::process::_raise), "_sem_init" => func!(crate::process::_sem_init), + "_sem_destroy" => func!(crate::process::_sem_destroy), "_sem_post" => func!(crate::process::_sem_post), "_sem_wait" => func!(crate::process::_sem_wait), "_getgrent" => func!(crate::process::_getgrent), @@ -591,12 +665,19 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_emscripten_get_heap_size" => func!(crate::memory::_emscripten_get_heap_size), "_emscripten_resize_heap" => func!(crate::memory::_emscripten_resize_heap), "enlargeMemory" => func!(crate::memory::enlarge_memory), + "segfault" => func!(crate::memory::segfault), + "alignfault" => func!(crate::memory::alignfault), + "ftfault" => func!(crate::memory::ftfault), "getTotalMemory" => func!(crate::memory::get_total_memory), "___map_file" => func!(crate::memory::___map_file), // Exception "___cxa_allocate_exception" => func!(crate::exception::___cxa_allocate_exception), "___cxa_throw" => func!(crate::exception::___cxa_throw), + "___cxa_begin_catch" => func!(crate::exception::___cxa_begin_catch), + "___cxa_end_catch" => func!(crate::exception::___cxa_end_catch), + "___cxa_uncaught_exception" => func!(crate::exception::___cxa_uncaught_exception), + "___cxa_pure_virtual" => func!(crate::exception::___cxa_pure_virtual), // Time "_gettimeofday" => func!(crate::time::_gettimeofday), @@ -609,6 +690,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_localtime" => func!(crate::time::_localtime), "_time" => func!(crate::time::_time), "_strftime" => func!(crate::time::_strftime), + "_strftime_l" => func!(crate::time::_strftime_l), "_localtime_r" => func!(crate::time::_localtime_r), "_gmtime_r" => func!(crate::time::_gmtime_r), "_mktime" => func!(crate::time::_mktime), @@ -622,12 +704,16 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_llvm_log2_f32" => func!(crate::math::_llvm_log2_f64), "_llvm_sin_f64" => func!(crate::math::_llvm_sin_f64), "_llvm_cos_f64" => func!(crate::math::_llvm_cos_f64), + "_llvm_exp2_f32" => func!(crate::math::_llvm_exp2_f32), + "_llvm_exp2_f64" => func!(crate::math::_llvm_exp2_f64), + "_llvm_trunc_f64" => func!(crate::math::_llvm_trunc_f64), "_emscripten_random" => func!(crate::math::_emscripten_random), // Jump "__setjmp" => func!(crate::jmp::__setjmp), "__longjmp" => func!(crate::jmp::__longjmp), - "_longjmp" => func!(crate::jmp::__longjmp), + "_longjmp" => func!(crate::jmp::_longjmp), + "_emscripten_longjmp" => func!(crate::jmp::_longjmp), // Bitwise "_llvm_bswap_i64" => func!(crate::bitwise::_llvm_bswap_i64), @@ -639,14 +725,18 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_dlsym" => func!(crate::linking::_dlsym), // wasm32-unknown-emscripten + "_atexit" => func!(crate::emscripten_target::_atexit), "setTempRet0" => func!(crate::emscripten_target::setTempRet0), "getTempRet0" => func!(crate::emscripten_target::getTempRet0), "invoke_i" => func!(crate::emscripten_target::invoke_i), "invoke_ii" => func!(crate::emscripten_target::invoke_ii), "invoke_iii" => func!(crate::emscripten_target::invoke_iii), "invoke_iiii" => func!(crate::emscripten_target::invoke_iiii), + "invoke_iifi" => func!(crate::emscripten_target::invoke_iifi), "invoke_v" => func!(crate::emscripten_target::invoke_v), "invoke_vi" => func!(crate::emscripten_target::invoke_vi), + "invoke_vj" => func!(crate::emscripten_target::invoke_vj), + "invoke_vjji" => func!(crate::emscripten_target::invoke_vjji), "invoke_vii" => func!(crate::emscripten_target::invoke_vii), "invoke_viii" => func!(crate::emscripten_target::invoke_viii), "invoke_viiii" => func!(crate::emscripten_target::invoke_viiii), @@ -659,10 +749,15 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___resumeException" => func!(crate::emscripten_target::___resumeException), "_dladdr" => func!(crate::emscripten_target::_dladdr), "_pthread_create" => func!(crate::emscripten_target::_pthread_create), + "_pthread_detach" => func!(crate::emscripten_target::_pthread_detach), "_pthread_join" => func!(crate::emscripten_target::_pthread_join), - "_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy), + "_pthread_attr_init" => func!(crate::emscripten_target::_pthread_attr_init), + "_pthread_attr_destroy" => func!(crate::emscripten_target::_pthread_attr_destroy), + "_pthread_attr_getstack" => func!(crate::emscripten_target::_pthread_attr_getstack), "_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init), + "_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy), "_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal), + "_pthread_cond_timedwait" => func!(crate::emscripten_target::_pthread_cond_timedwait), "_pthread_cond_wait" => func!(crate::emscripten_target::_pthread_cond_wait), "_pthread_condattr_destroy" => func!(crate::emscripten_target::_pthread_condattr_destroy), "_pthread_condattr_init" => func!(crate::emscripten_target::_pthread_condattr_init), @@ -675,6 +770,11 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_pthread_rwlock_rdlock" => func!(crate::emscripten_target::_pthread_rwlock_rdlock), "_pthread_rwlock_unlock" => func!(crate::emscripten_target::_pthread_rwlock_unlock), "_pthread_setcancelstate" => func!(crate::emscripten_target::_pthread_setcancelstate), + "_pthread_getspecific" => func!(crate::emscripten_target::_pthread_getspecific), + "_pthread_getattr_np" => func!(crate::emscripten_target::_pthread_getattr_np), + "_pthread_setspecific" => func!(crate::emscripten_target::_pthread_setspecific), + "_pthread_once" => func!(crate::emscripten_target::_pthread_once), + "_pthread_key_create" => func!(crate::emscripten_target::_pthread_key_create), "___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0), "_getdtablesize" => func!(crate::emscripten_target::_getdtablesize), "_gethostbyaddr" => func!(crate::emscripten_target::_gethostbyaddr), @@ -686,16 +786,23 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "invoke_iiiiii" => func!(crate::emscripten_target::invoke_iiiiii), "invoke_iiiiiii" => func!(crate::emscripten_target::invoke_iiiiiii), "invoke_iiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiii), + "invoke_iiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiii), "invoke_iiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiii), + "invoke_iiiiiiiiiii" => func!(crate::emscripten_target::invoke_iiiiiiiiiii), "invoke_vd" => func!(crate::emscripten_target::invoke_vd), "invoke_viiiii" => func!(crate::emscripten_target::invoke_viiiii), "invoke_viiiiii" => func!(crate::emscripten_target::invoke_viiiiii), "invoke_viiiiiii" => func!(crate::emscripten_target::invoke_viiiiiii), "invoke_viiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiii), "invoke_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii), + "invoke_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii), + "invoke_viiiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiiii), + "invoke_iij" => func!(crate::emscripten_target::invoke_iij), "invoke_iiji" => func!(crate::emscripten_target::invoke_iiji), + "invoke_iiijj" => func!(crate::emscripten_target::invoke_iiijj), "invoke_j" => func!(crate::emscripten_target::invoke_j), "invoke_ji" => func!(crate::emscripten_target::invoke_ji), + "invoke_jii" => func!(crate::emscripten_target::invoke_jii), "invoke_jij" => func!(crate::emscripten_target::invoke_jij), "invoke_jjj" => func!(crate::emscripten_target::invoke_jjj), "invoke_viiij" => func!(crate::emscripten_target::invoke_viiij), @@ -709,7 +816,9 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "invoke_viji" => func!(crate::emscripten_target::invoke_viji), "invoke_vijiii" => func!(crate::emscripten_target::invoke_vijiii), "invoke_vijj" => func!(crate::emscripten_target::invoke_vijj), + "invoke_viid" => func!(crate::emscripten_target::invoke_viid), "invoke_viidii" => func!(crate::emscripten_target::invoke_viidii), + "invoke_viidddddddd" => func!(crate::emscripten_target::invoke_viidddddddd), }; for null_func_name in globals.null_func_names.iter() { @@ -729,6 +838,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject }, "asm2wasm" => { "f64-rem" => func!(crate::math::f64_rem), + "f64-to-int" => func!(crate::math::f64_to_int), }, }; diff --git a/lib/emscripten/src/math.rs b/lib/emscripten/src/math.rs index c73580d77..3256b49c5 100644 --- a/lib/emscripten/src/math.rs +++ b/lib/emscripten/src/math.rs @@ -34,12 +34,27 @@ pub fn _llvm_log2_f32(_ctx: &mut Ctx, _value: f64) -> f64 { -1.0 } +pub fn _llvm_exp2_f32(_ctx: &mut Ctx, value: f32) -> f32 { + debug!("emscripten::_llvm_exp2_f32"); + 2f32.powf(value) +} + +pub fn _llvm_exp2_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_exp2_f64"); + 2f64.powf(value) +} + +pub fn _llvm_trunc_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_trunc_f64"); + value.trunc() +} + pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 { debug!("emscripten::_emscripten_random"); -1.0 } -// emscripten: f64-rem +// emscripten: asm2wasm.f64-rem pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { debug!("emscripten::f64-rem"); x % y @@ -59,3 +74,9 @@ pub fn exp(_ctx: &mut Ctx, value: f64) -> f64 { pub fn log(_ctx: &mut Ctx, value: f64) -> f64 { value.ln() } + +// emscripten: asm2wasm.f64-to-int +pub fn f64_to_int(_ctx: &mut Ctx, value: f64) -> i32 { + debug!("emscripten::f64_to_int {}", value); + value as i32 +} diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index 02877a011..0f1cb1f19 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -1,6 +1,9 @@ use super::process::abort_with_message; use libc::{c_int, c_void, memcpy, size_t}; -use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::{ + units::{Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE}, + vm::Ctx, +}; /// emscripten: _emscripten_memcpy_big pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u32 { @@ -17,17 +20,49 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u } /// emscripten: _emscripten_get_heap_size -pub fn _emscripten_get_heap_size(_ctx: &mut Ctx) -> u32 { - debug!("emscripten::_emscripten_get_heap_size",); - // TODO: Fix implementation - 16_777_216 +pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 { + debug!("emscripten::_emscripten_get_heap_size"); + let result = ctx.memory(0).size().bytes().0 as u32; + debug!("=> {}", result); + + result +} + +// From emscripten implementation +fn align_up(mut val: usize, multiple: usize) -> usize { + if val % multiple > 0 { + val += multiple - val % multiple; + } + val } /// emscripten: _emscripten_resize_heap -pub fn _emscripten_resize_heap(_ctx: &mut Ctx, _requested_size: u32) -> u32 { - debug!("emscripten::_emscripten_resize_heap {}", _requested_size); - // TODO: Fix implementation - 0 +/// Note: this function only allows growing the size of heap +pub fn _emscripten_resize_heap(ctx: &mut Ctx, requested_size: u32) -> u32 { + debug!("emscripten::_emscripten_resize_heap {}", requested_size); + let current_memory_pages = ctx.memory(0).size(); + let current_memory = current_memory_pages.bytes().0 as u32; + + // implementation from emscripten + let mut new_size = usize::max(current_memory as usize, WASM_MIN_PAGES * WASM_PAGE_SIZE); + while new_size < requested_size as usize { + if new_size <= 0x2000_0000 { + new_size = align_up(new_size * 2, WASM_PAGE_SIZE); + } else { + new_size = usize::min( + align_up((3 * new_size + 0x8000_0000) / 4, WASM_PAGE_SIZE), + WASM_PAGE_SIZE * WASM_MAX_PAGES, + ); + } + } + + let amount_to_grow = (new_size - current_memory as usize) / WASM_PAGE_SIZE; + if let Ok(_pages_allocated) = ctx.memory(0).grow(Pages(amount_to_grow as u32)) { + debug!("{} pages allocated", _pages_allocated.0); + 1 + } else { + 0 + } } /// emscripten: getTotalMemory @@ -35,7 +70,7 @@ pub fn get_total_memory(_ctx: &mut Ctx) -> u32 { debug!("emscripten::get_total_memory"); // instance.memories[0].current_pages() // TODO: Fix implementation - 16_777_216 + _ctx.memory(0).size().bytes().0 as u32 } /// emscripten: enlargeMemory @@ -63,6 +98,24 @@ pub fn abort_on_cannot_grow_memory_old(ctx: &mut Ctx) -> u32 { 0 } +/// emscripten: segfault +pub fn segfault(ctx: &mut Ctx) { + debug!("emscripten::segfault"); + abort_with_message(ctx, "segmentation fault"); +} + +/// emscripten: alignfault +pub fn alignfault(ctx: &mut Ctx) { + debug!("emscripten::alignfault"); + abort_with_message(ctx, "alignment fault"); +} + +/// emscripten: ftfault +pub fn ftfault(ctx: &mut Ctx) { + debug!("emscripten::ftfault"); + abort_with_message(ctx, "Function table mask error"); +} + /// emscripten: ___map_file pub fn ___map_file(_ctx: &mut Ctx, _one: u32, _two: u32) -> c_int { debug!("emscripten::___map_file"); diff --git a/lib/emscripten/src/process.rs b/lib/emscripten/src/process.rs index ce47502e7..538ed528f 100644 --- a/lib/emscripten/src/process.rs +++ b/lib/emscripten/src/process.rs @@ -82,8 +82,13 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 { } pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { - debug!("emscripten::_sem_init"); - -1 + debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three); + 0 +} + +pub fn _sem_destroy(_ctx: &mut Ctx, _one: i32) -> i32 { + debug!("emscripten::_sem_destroy"); + 0 } pub fn _sem_post(_ctx: &mut Ctx, _one: i32) -> i32 { @@ -150,11 +155,16 @@ pub fn _llvm_trap(ctx: &mut Ctx) { abort_with_message(ctx, "abort!"); } +pub fn _llvm_eh_typeid_for(_ctx: &mut Ctx, _type_info_addr: u32) -> i32 { + debug!("emscripten::_llvm_eh_typeid_for"); + -1 +} + pub fn _system(_ctx: &mut Ctx, _one: i32) -> c_int { debug!("emscripten::_system"); // TODO: May need to change this Em impl to a working version eprintln!("Can't call external programs"); - return EAGAIN; + EAGAIN } pub fn _popen(_ctx: &mut Ctx, _one: i32, _two: i32) -> c_int { diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 36a1b0c7c..5654a1299 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -31,12 +31,13 @@ use libc::{ off_t, // open, read, + rename, + // sockaddr_in, // readv, rmdir, // writev, stat, write, - // sockaddr_in, }; use wasmer_runtime_core::vm::Ctx; @@ -118,9 +119,21 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { unsafe { getpid() } } -pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall38"); - -1 +// rename +pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall38 (rename)"); + let old_path_addr: u32 = varargs.get(ctx); + let new_path_addr: u32 = varargs.get(ctx); + let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8; + let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8; + let result = unsafe { rename(old_path, new_path) }; + debug!( + "=> old_path: {}, new_path: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() }, + result + ); + result } // rmdir @@ -194,8 +207,8 @@ pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall91"); - -1 + debug!("emscripten::___syscall91 - stub"); + 0 } pub fn ___syscall97(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -246,12 +259,21 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in if fd == -1 { let ptr = env::call_memalign(ctx, 16384, len); if ptr == 0 { - return -1; + // ENOMEM + return -12; } + let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8; env::call_memset(ctx, ptr, 0, len); - ptr as _ + for i in 0..(len as usize) { + unsafe { + assert_eq!(*real_ptr.add(i), 0); + } + } + debug!("=> ptr: {}", ptr); + return ptr as i32; } else { - -1 + // return ENODEV + return -19; } } @@ -461,6 +483,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// utimensat +pub fn ___syscall320(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall320 (utimensat), {}", _which); + 0 +} + pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall334"); -1 diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index c8fa04101..ec8b98f3f 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -1,4 +1,6 @@ use crate::varargs::VarArgs; +#[cfg(target_os = "macos")] +use libc::size_t; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html use libc::{ @@ -53,10 +55,10 @@ use libc::{ sendto, setpgid, setsockopt, - size_t, sockaddr, socket, socklen_t, + stat, symlink, uid_t, uname, @@ -73,6 +75,7 @@ use libc::{ }; use wasmer_runtime_core::vm::Ctx; +use crate::utils; #[allow(unused_imports)] use std::io::Error; use std::mem; @@ -88,7 +91,7 @@ extern "C" { } #[cfg(not(target_os = "macos"))] -use libc::{fallocate, fdatasync, ftruncate64, lstat64, madvise, wait4}; +use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4}; // Another conditional constant for name resolution: Macos et iOS use // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. @@ -249,8 +252,9 @@ pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; let result = unsafe { access(path, amode) }; debug!( - "=> path: {}, result: {}", + "=> path: {}, amode: {}, result: {}", unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + amode, result ); result @@ -352,8 +356,13 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall54 (ioctl) {}", _which); let fd: i32 = varargs.get(ctx); let request: u32 = varargs.get(ctx); - debug!("fd: {}, op: {}", fd, request); + debug!("=> fd: {}, op: {}", fd, request); // Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html + // let argp: u32 = varargs.get(ctx); + // let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void; + // let ret = unsafe { ioctl(fd, request as _, argp_ptr) }; + // debug!("=> {}", ret); + // ret match request as _ { 21537 => { // FIONBIO @@ -763,19 +772,28 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall196 (lstat64) {}", _which); let path_ptr: c_int = varargs.get(ctx); - let buf_ptr: c_int = varargs.get(ctx); - let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const c_char; - let buf = emscripten_memory_pointer!(ctx.memory(0), buf_ptr) as *mut c_void; - let result = unsafe { lstat64(path, buf as _) }; - debug!( - "=> path: {}, buf: {} = fd: {}\npath: {}\nlast os error: {}", - path_ptr, - buf_ptr, - result, - unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, - Error::last_os_error(), - ); - result + let buf_ptr: u32 = varargs.get(ctx); + let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; + unsafe { + let mut stat: stat = std::mem::zeroed(); + + #[cfg(target_os = "macos")] + let stat_ptr = &mut stat as *mut stat as *mut c_void; + #[cfg(not(target_os = "macos"))] + let stat_ptr = &mut stat as *mut stat; + + #[cfg(target_os = "macos")] + let ret = lstat64(path, stat_ptr); + #[cfg(not(target_os = "macos"))] + let ret = lstat(path, stat_ptr); + + debug!("ret: {}", ret); + if ret != 0 { + return ret; + } + utils::copy_stat_into_wasm(ctx, buf_ptr, &stat); + } + 0 } /// fallocate diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 28a60ec34..32e7a61c6 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -28,7 +28,9 @@ use wasmer_runtime_core::vm::Ctx; use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME}; #[cfg(target_os = "macos")] -use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; +use libc::CLOCK_REALTIME; +#[cfg(target_os = "macos")] +const CLOCK_MONOTONIC: clockid_t = 1; #[cfg(target_os = "macos")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; @@ -298,15 +300,77 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 { /// emscripten: _strftime pub fn _strftime( - _ctx: &mut Ctx, - _s_ptr: c_int, - _maxsize: u32, - _format_ptr: c_int, - _tm_ptr: c_int, + ctx: &mut Ctx, + s_ptr: c_int, + maxsize: u32, + format_ptr: c_int, + tm_ptr: c_int, ) -> i32 { debug!( "emscripten::_strftime {} {} {} {}", - _s_ptr, _maxsize, _format_ptr, _tm_ptr + s_ptr, maxsize, format_ptr, tm_ptr ); - 0 + + #[allow(clippy::cast_ptr_alignment)] + let s = emscripten_memory_pointer!(ctx.memory(0), s_ptr) as *mut c_char; + #[allow(clippy::cast_ptr_alignment)] + let format = emscripten_memory_pointer!(ctx.memory(0), format_ptr) as *const c_char; + #[allow(clippy::cast_ptr_alignment)] + let tm = emscripten_memory_pointer!(ctx.memory(0), tm_ptr) as *const guest_tm; + + let format_string = unsafe { std::ffi::CStr::from_ptr(format).to_str().unwrap() }; + + debug!("=> format_string: {:?}", format_string); + + let tm = unsafe { &*tm }; + + let rust_tm = ::time::Tm { + tm_sec: tm.tm_sec, + tm_min: tm.tm_min, + tm_hour: tm.tm_hour, + tm_mday: tm.tm_mday, + tm_mon: tm.tm_mon, + tm_year: tm.tm_year, + tm_wday: tm.tm_wday, + tm_yday: tm.tm_yday, + tm_isdst: tm.tm_isdst, + tm_utcoff: tm.tm_gmtoff, + tm_nsec: 0, + }; + + let result_str = match ::time::strftime(format_string, &rust_tm) { + Ok(res_string) => res_string, + // TODO: maybe match on e in Err(e) and return different values if required + _ => return 0, + }; + + // pad for null? + let bytes = result_str.chars().count(); + if bytes as u32 > maxsize { + 0 + } else { + // write output string + for (i, c) in result_str.chars().enumerate() { + unsafe { *s.add(i) = c as c_char }; + } + // null terminate? + bytes as i32 + } +} + +/// emscripten: _strftime_l +pub fn _strftime_l( + ctx: &mut Ctx, + s_ptr: c_int, + maxsize: u32, + format_ptr: c_int, + tm_ptr: c_int, + _last: c_int, +) -> i32 { + debug!( + "emscripten::_strftime_l {} {} {} {}", + s_ptr, maxsize, format_ptr, tm_ptr + ); + + _strftime(ctx, s_ptr, maxsize, format_ptr, tm_ptr) } diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 5dcfd4e11..2ad5b9a40 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -1,5 +1,6 @@ use super::env; use super::env::get_emscripten_data; +use crate::storage::align_memory; use libc::stat; use std::ffi::CStr; use std::mem::size_of; @@ -39,6 +40,43 @@ pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option) { (memory.minimum, memory.maximum) } +/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1` +/// Assumes values start from the end in this order: +/// Last export: Dynamic Base +/// Second-to-Last export: Dynamic top pointer +pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> { + let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?; + let snd_max_idx = &module + .info() + .globals + .iter() + .map(|(k, _)| k) + .filter(|k| k != max_idx) + .max()?; + + use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32}; + if let ( + GlobalInit { + init: Const(I32(dynamic_base)), + .. + }, + GlobalInit { + init: Const(I32(dynamictop_ptr)), + .. + }, + ) = ( + &module.info().globals[*max_idx], + &module.info().globals[*snd_max_idx], + ) { + Some(( + align_memory(*dynamic_base as u32 - 32), + align_memory(*dynamictop_ptr as u32 - 32), + )) + } else { + None + } +} + pub unsafe fn write_to_buf(ctx: &mut Ctx, string: *const c_char, buf: u32, max: u32) -> u32 { let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_char; @@ -125,7 +163,7 @@ pub struct GuestStat { st_atime: u64, st_mtime: u64, st_ctime: u64, - st_ino: u64, + st_ino: u32, } #[allow(clippy::cast_ptr_alignment)] @@ -186,13 +224,13 @@ mod tests { LLVMCompiler::new() } - #[cfg(feature = "dynasm")] + #[cfg(feature = "singlepass")] fn get_compiler() -> impl Compiler { - use wasmer_dynasm_backend::SinglePassCompiler; + use wasmer_singlepass_backend::SinglePassCompiler; SinglePassCompiler::new() } - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))] + #[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; diff --git a/lib/emscripten/src/varargs.rs b/lib/emscripten/src/varargs.rs index cd9073cb9..a6a1fe46b 100644 --- a/lib/emscripten/src/varargs.rs +++ b/lib/emscripten/src/varargs.rs @@ -1,8 +1,5 @@ use std::mem; -use wasmer_runtime_core::{ - types::{Type, WasmExternType}, - vm::Ctx, -}; +use wasmer_runtime_core::{types::WasmExternType, vm::Ctx}; #[repr(transparent)] #[derive(Copy, Clone)] @@ -19,5 +16,12 @@ impl VarArgs { } unsafe impl WasmExternType for VarArgs { - const TYPE: Type = Type::I32; + type Native = i32; + + fn to_native(self) -> Self::Native { + self.pointer as _ + } + fn from_native(n: Self::Native) -> Self { + Self { pointer: n as u32 } + } } diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 1289fe741..c2e304e32 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -20,13 +20,13 @@ macro_rules! assert_emscripten_output { LLVMCompiler::new() } - #[cfg(feature = "dynasm")] + #[cfg(feature = "singlepass")] fn get_compiler() -> impl Compiler { - use wasmer_dynasm_backend::SinglePassCompiler; + use wasmer_singlepass_backend::SinglePassCompiler; SinglePassCompiler::new() } - #[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))] + #[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; @@ -53,6 +53,7 @@ macro_rules! assert_emscripten_output { &mut instance, $name, $args, + None, ).expect("run_emscripten_instance finishes"); let output = capturer.end().unwrap().0; @@ -67,36 +68,36 @@ macro_rules! assert_emscripten_output { }}; } -pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) { - use wasmer_clif_backend::CraneliftCompiler; - use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals}; +// pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) { +// use wasmer_clif_backend::CraneliftCompiler; +// use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals}; - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); +// let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) +// .expect("WASM can't be compiled"); - let mut emscripten_globals = EmscriptenGlobals::new(&module); - let import_object = generate_emscripten_env(&mut emscripten_globals); - let mut instance = module - .instantiate(&import_object) - .map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err)) - .unwrap(); +// let mut emscripten_globals = EmscriptenGlobals::new(&module); +// let import_object = generate_emscripten_env(&mut emscripten_globals); +// let mut instance = module +// .instantiate(&import_object) +// .map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err)) +// .unwrap(); - let capturer = StdioCapturer::new(); +// let capturer = StdioCapturer::new(); - wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) - .expect("run_emscripten_instance finishes"); +// wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) +// .expect("run_emscripten_instance finishes"); - let raw_output_string = capturer.end().unwrap().0; +// let raw_output_string = capturer.end().unwrap().0; - // trim the strings to avoid cross-platform line ending and white space issues - let output = raw_output_string.trim(); - let expected_output = raw_expected_str.trim(); +// // trim the strings to avoid cross-platform line ending and white space issues +// let output = raw_output_string.trim(); +// let expected_output = raw_expected_str.trim(); - let contains_output = output.contains(expected_output); +// let contains_output = output.contains(expected_output); - assert!( - contains_output, - "Output: `{}` does not contain expected output: `{}`", - output, expected_output - ); -} +// assert!( +// contains_output, +// "Output: `{}` does not contain expected output: `{}`", +// output, expected_output +// ); +// } diff --git a/lib/emscripten/tests/emtests/mod.rs b/lib/emscripten/tests/emtests/mod.rs index 89a66bf2b..645351227 100644 --- a/lib/emscripten/tests/emtests/mod.rs +++ b/lib/emscripten/tests/emtests/mod.rs @@ -176,6 +176,7 @@ mod test_unary_literal; mod test_utf; mod test_varargs; mod test_varargs_multi; +mod test_vfs; mod test_vprintf; mod test_vsnprintf; mod test_wprintf; diff --git a/lib/emscripten/tests/emtests/test_execvp.rs b/lib/emscripten/tests/emtests/test_execvp.rs index c9b4b5113..085e41d9a 100644 --- a/lib/emscripten/tests/emtests/test_execvp.rs +++ b/lib/emscripten/tests/emtests/test_execvp.rs @@ -1,17 +1,10 @@ #[test] -fn test_execvp() { - #[cfg(not(target_os = "windows"))] +#[ignore] +fn test_test_execvp() { assert_emscripten_output!( "../../emtests/test_execvp.wasm", "test_execvp", vec![], "../../emtests/test_execvp.out" ); - #[cfg(target_os = "windows")] - assert_emscripten_output!( - "../../emtests/test_execvp_windows.wasm", - "test_execvp", - vec![], - "../../emtests/test_execvp.out" - ); } diff --git a/lib/emscripten/tests/emtests/test_getcwd.rs b/lib/emscripten/tests/emtests/test_getcwd.rs index ec770b505..98723df87 100644 --- a/lib/emscripten/tests/emtests/test_getcwd.rs +++ b/lib/emscripten/tests/emtests/test_getcwd.rs @@ -1,8 +1,8 @@ #[test] -fn test_getcwd() { +fn test_test_getcwd() { assert_emscripten_output!( "../../emtests/test_getcwd.wasm", - "getcwd", + "test_getcwd", vec![], "../../emtests/test_getcwd.out" ); diff --git a/lib/emscripten/tests/emtests/test_longjmp.rs b/lib/emscripten/tests/emtests/test_longjmp.rs index c125f5d21..90aaba8ff 100644 --- a/lib/emscripten/tests/emtests/test_longjmp.rs +++ b/lib/emscripten/tests/emtests/test_longjmp.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp() { assert_emscripten_output!( "../../emtests/test_longjmp.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp2.rs b/lib/emscripten/tests/emtests/test_longjmp2.rs index 44989e08f..3ad2c66c6 100644 --- a/lib/emscripten/tests/emtests/test_longjmp2.rs +++ b/lib/emscripten/tests/emtests/test_longjmp2.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp2() { assert_emscripten_output!( "../../emtests/test_longjmp2.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp3.rs b/lib/emscripten/tests/emtests/test_longjmp3.rs index 4ce0e2e6f..65923c8ed 100644 --- a/lib/emscripten/tests/emtests/test_longjmp3.rs +++ b/lib/emscripten/tests/emtests/test_longjmp3.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp3() { assert_emscripten_output!( "../../emtests/test_longjmp3.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp4.rs b/lib/emscripten/tests/emtests/test_longjmp4.rs index f3c53ea56..3eabe4099 100644 --- a/lib/emscripten/tests/emtests/test_longjmp4.rs +++ b/lib/emscripten/tests/emtests/test_longjmp4.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp4() { assert_emscripten_output!( "../../emtests/test_longjmp4.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs b/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs index a40316558..85a69d8ed 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_funcptr.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_funcptr() { assert_emscripten_output!( "../../emtests/test_longjmp_funcptr.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_repeat.rs b/lib/emscripten/tests/emtests/test_longjmp_repeat.rs index 6fbc65527..5d2357db7 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_repeat.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_repeat.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_repeat() { assert_emscripten_output!( "../../emtests/test_longjmp_repeat.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_stacked.rs b/lib/emscripten/tests/emtests/test_longjmp_stacked.rs index b145e6da4..e5a69fb17 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_stacked.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_stacked.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_stacked() { assert_emscripten_output!( "../../emtests/test_longjmp_stacked.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_throw.rs b/lib/emscripten/tests/emtests/test_longjmp_throw.rs index 5f97462ad..877c620ef 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_throw.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_throw.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_throw() { assert_emscripten_output!( "../../emtests/test_longjmp_throw.wasm", diff --git a/lib/emscripten/tests/emtests/test_longjmp_unwind.rs b/lib/emscripten/tests/emtests/test_longjmp_unwind.rs index eec134135..f6e921396 100644 --- a/lib/emscripten/tests/emtests/test_longjmp_unwind.rs +++ b/lib/emscripten/tests/emtests/test_longjmp_unwind.rs @@ -1,5 +1,4 @@ #[test] -#[ignore] fn test_test_longjmp_unwind() { assert_emscripten_output!( "../../emtests/test_longjmp_unwind.wasm", diff --git a/lib/emscripten/tests/emtests/test_nl_types.rs b/lib/emscripten/tests/emtests/test_nl_types.rs index 101ee4894..68b81c562 100644 --- a/lib/emscripten/tests/emtests/test_nl_types.rs +++ b/lib/emscripten/tests/emtests/test_nl_types.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_nl_types() { assert_emscripten_output!( "../../emtests/test_nl_types.wasm", diff --git a/lib/emscripten/tests/emtests/test_phiundef.rs b/lib/emscripten/tests/emtests/test_phiundef.rs index fbe4203f7..e3cba2c0f 100644 --- a/lib/emscripten/tests/emtests/test_phiundef.rs +++ b/lib/emscripten/tests/emtests/test_phiundef.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_phiundef() { assert_emscripten_output!( "../../emtests/test_phiundef.wasm", diff --git a/lib/emscripten/tests/emtests/test_pipe.rs b/lib/emscripten/tests/emtests/test_pipe.rs index 9ffe0c828..e7caee28b 100644 --- a/lib/emscripten/tests/emtests/test_pipe.rs +++ b/lib/emscripten/tests/emtests/test_pipe.rs @@ -1,8 +1,10 @@ -use crate::emtests::_common::assert_emscripten_output; - #[test] -fn test_pipe() { - let wasm_bytes = include_bytes!("../../emtests/test_pipe.wasm"); - let expected_str = include_str!("../../emtests/test_pipe.out"); - assert_emscripten_output(wasm_bytes, expected_str); +#[ignore] +fn test_test_pipe() { + assert_emscripten_output!( + "../../emtests/test_pipe.wasm", + "test_pipe", + vec![], + "../../emtests/test_pipe.out" + ); } diff --git a/lib/emscripten/tests/emtests/test_printf_2.rs b/lib/emscripten/tests/emtests/test_printf_2.rs index 62151b8a4..ac15139bf 100644 --- a/lib/emscripten/tests/emtests/test_printf_2.rs +++ b/lib/emscripten/tests/emtests/test_printf_2.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_printf_2() { assert_emscripten_output!( "../../emtests/test_printf_2.wasm", diff --git a/lib/emscripten/tests/emtests/test_printf_more.rs b/lib/emscripten/tests/emtests/test_printf_more.rs index 6dc34233f..b1e4fc00f 100644 --- a/lib/emscripten/tests/emtests/test_printf_more.rs +++ b/lib/emscripten/tests/emtests/test_printf_more.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_printf_more() { assert_emscripten_output!( "../../emtests/test_printf_more.wasm", diff --git a/lib/emscripten/tests/emtests/test_regex.rs b/lib/emscripten/tests/emtests/test_regex.rs index 03ab62295..ae65d8ef9 100644 --- a/lib/emscripten/tests/emtests/test_regex.rs +++ b/lib/emscripten/tests/emtests/test_regex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_regex() { assert_emscripten_output!( "../../emtests/test_regex.wasm", diff --git a/lib/emscripten/tests/emtests/test_relocatable_void_function.rs b/lib/emscripten/tests/emtests/test_relocatable_void_function.rs index 7cdd382b8..77dda77ba 100644 --- a/lib/emscripten/tests/emtests/test_relocatable_void_function.rs +++ b/lib/emscripten/tests/emtests/test_relocatable_void_function.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_relocatable_void_function() { assert_emscripten_output!( "../../emtests/test_relocatable_void_function.wasm", diff --git a/lib/emscripten/tests/emtests/test_rounding.rs b/lib/emscripten/tests/emtests/test_rounding.rs index defd8df6f..cbe0a8ff5 100644 --- a/lib/emscripten/tests/emtests/test_rounding.rs +++ b/lib/emscripten/tests/emtests/test_rounding.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_rounding() { assert_emscripten_output!( "../../emtests/test_rounding.wasm", diff --git a/lib/emscripten/tests/emtests/test_set_align.rs b/lib/emscripten/tests/emtests/test_set_align.rs index 039d7f0c8..47aa7c3dd 100644 --- a/lib/emscripten/tests/emtests/test_set_align.rs +++ b/lib/emscripten/tests/emtests/test_set_align.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_set_align() { assert_emscripten_output!( "../../emtests/test_set_align.wasm", diff --git a/lib/emscripten/tests/emtests/test_sintvars.rs b/lib/emscripten/tests/emtests/test_sintvars.rs index cf506e10c..f74daa8b1 100644 --- a/lib/emscripten/tests/emtests/test_sintvars.rs +++ b/lib/emscripten/tests/emtests/test_sintvars.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sintvars() { assert_emscripten_output!( "../../emtests/test_sintvars.wasm", diff --git a/lib/emscripten/tests/emtests/test_sizeof.rs b/lib/emscripten/tests/emtests/test_sizeof.rs index 516f0c58f..53e8cf19f 100644 --- a/lib/emscripten/tests/emtests/test_sizeof.rs +++ b/lib/emscripten/tests/emtests/test_sizeof.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sizeof() { assert_emscripten_output!( "../../emtests/test_sizeof.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf.rs b/lib/emscripten/tests/emtests/test_sscanf.rs index 72353b9db..e3072841f 100644 --- a/lib/emscripten/tests/emtests/test_sscanf.rs +++ b/lib/emscripten/tests/emtests/test_sscanf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf() { assert_emscripten_output!( "../../emtests/test_sscanf.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_3.rs b/lib/emscripten/tests/emtests/test_sscanf_3.rs index 816045fd5..c331db881 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_3.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_3.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_3() { assert_emscripten_output!( "../../emtests/test_sscanf_3.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_4.rs b/lib/emscripten/tests/emtests/test_sscanf_4.rs index d859d1767..a9a959f38 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_4.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_4.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_4() { assert_emscripten_output!( "../../emtests/test_sscanf_4.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_5.rs b/lib/emscripten/tests/emtests/test_sscanf_5.rs index 22d4d5315..05801407f 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_5.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_5.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_5() { assert_emscripten_output!( "../../emtests/test_sscanf_5.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_6.rs b/lib/emscripten/tests/emtests/test_sscanf_6.rs index 52be26949..d9771ca13 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_6.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_6.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_6() { assert_emscripten_output!( "../../emtests/test_sscanf_6.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_caps.rs b/lib/emscripten/tests/emtests/test_sscanf_caps.rs index 5f2d64b90..f88ec7e76 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_caps.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_caps.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_caps() { assert_emscripten_output!( "../../emtests/test_sscanf_caps.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_float.rs b/lib/emscripten/tests/emtests/test_sscanf_float.rs index 39d3b5cba..f98c0b2d0 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_float.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_float.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_float() { assert_emscripten_output!( "../../emtests/test_sscanf_float.wasm", diff --git a/lib/emscripten/tests/emtests/test_sscanf_n.rs b/lib/emscripten/tests/emtests/test_sscanf_n.rs index 57f90b142..596c70307 100644 --- a/lib/emscripten/tests/emtests/test_sscanf_n.rs +++ b/lib/emscripten/tests/emtests/test_sscanf_n.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_sscanf_n() { assert_emscripten_output!( "../../emtests/test_sscanf_n.wasm", diff --git a/lib/emscripten/tests/emtests/test_strcasecmp.rs b/lib/emscripten/tests/emtests/test_strcasecmp.rs index 294017872..4c0a9ed9e 100644 --- a/lib/emscripten/tests/emtests/test_strcasecmp.rs +++ b/lib/emscripten/tests/emtests/test_strcasecmp.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strcasecmp() { assert_emscripten_output!( "../../emtests/test_strcasecmp.wasm", diff --git a/lib/emscripten/tests/emtests/test_strcmp_uni.rs b/lib/emscripten/tests/emtests/test_strcmp_uni.rs index e729c00ae..5691e4f57 100644 --- a/lib/emscripten/tests/emtests/test_strcmp_uni.rs +++ b/lib/emscripten/tests/emtests/test_strcmp_uni.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strcmp_uni() { assert_emscripten_output!( "../../emtests/test_strcmp_uni.wasm", diff --git a/lib/emscripten/tests/emtests/test_strndup.rs b/lib/emscripten/tests/emtests/test_strndup.rs index 62ac406e9..7fdb25bbe 100644 --- a/lib/emscripten/tests/emtests/test_strndup.rs +++ b/lib/emscripten/tests/emtests/test_strndup.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strndup() { assert_emscripten_output!( "../../emtests/test_strndup.wasm", diff --git a/lib/emscripten/tests/emtests/test_strstr.rs b/lib/emscripten/tests/emtests/test_strstr.rs index 080d3bbda..86af149e8 100644 --- a/lib/emscripten/tests/emtests/test_strstr.rs +++ b/lib/emscripten/tests/emtests/test_strstr.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strstr() { assert_emscripten_output!( "../../emtests/test_strstr.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtod.rs b/lib/emscripten/tests/emtests/test_strtod.rs index cab755982..52bb1987e 100644 --- a/lib/emscripten/tests/emtests/test_strtod.rs +++ b/lib/emscripten/tests/emtests/test_strtod.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtod() { assert_emscripten_output!( "../../emtests/test_strtod.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtok.rs b/lib/emscripten/tests/emtests/test_strtok.rs index 1399ef683..cef228e85 100644 --- a/lib/emscripten/tests/emtests/test_strtok.rs +++ b/lib/emscripten/tests/emtests/test_strtok.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtok() { assert_emscripten_output!( "../../emtests/test_strtok.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_bin.rs b/lib/emscripten/tests/emtests/test_strtol_bin.rs index fd847c186..48f459556 100644 --- a/lib/emscripten/tests/emtests/test_strtol_bin.rs +++ b/lib/emscripten/tests/emtests/test_strtol_bin.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_bin() { assert_emscripten_output!( "../../emtests/test_strtol_bin.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_dec.rs b/lib/emscripten/tests/emtests/test_strtol_dec.rs index a485bfa97..7a3a6e89a 100644 --- a/lib/emscripten/tests/emtests/test_strtol_dec.rs +++ b/lib/emscripten/tests/emtests/test_strtol_dec.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_dec() { assert_emscripten_output!( "../../emtests/test_strtol_dec.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_hex.rs b/lib/emscripten/tests/emtests/test_strtol_hex.rs index 57f108259..257e14b35 100644 --- a/lib/emscripten/tests/emtests/test_strtol_hex.rs +++ b/lib/emscripten/tests/emtests/test_strtol_hex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_hex() { assert_emscripten_output!( "../../emtests/test_strtol_hex.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtol_oct.rs b/lib/emscripten/tests/emtests/test_strtol_oct.rs index 9e4340f04..ba9fa1a78 100644 --- a/lib/emscripten/tests/emtests/test_strtol_oct.rs +++ b/lib/emscripten/tests/emtests/test_strtol_oct.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtol_oct() { assert_emscripten_output!( "../../emtests/test_strtol_oct.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_bin.rs b/lib/emscripten/tests/emtests/test_strtoll_bin.rs index 1276f0c85..0e067ef72 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_bin.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_bin.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_bin() { assert_emscripten_output!( "../../emtests/test_strtoll_bin.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_dec.rs b/lib/emscripten/tests/emtests/test_strtoll_dec.rs index 6e902d0ed..a073eb05d 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_dec.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_dec.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_dec() { assert_emscripten_output!( "../../emtests/test_strtoll_dec.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_hex.rs b/lib/emscripten/tests/emtests/test_strtoll_hex.rs index cc98e0fd0..8bb61fa1b 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_hex.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_hex.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_hex() { assert_emscripten_output!( "../../emtests/test_strtoll_hex.wasm", diff --git a/lib/emscripten/tests/emtests/test_strtoll_oct.rs b/lib/emscripten/tests/emtests/test_strtoll_oct.rs index 13ed7f5b2..8a96615d6 100644 --- a/lib/emscripten/tests/emtests/test_strtoll_oct.rs +++ b/lib/emscripten/tests/emtests/test_strtoll_oct.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_strtoll_oct() { assert_emscripten_output!( "../../emtests/test_strtoll_oct.wasm", diff --git a/lib/emscripten/tests/emtests/test_struct_varargs.rs b/lib/emscripten/tests/emtests/test_struct_varargs.rs index 71f66595b..211164a29 100644 --- a/lib/emscripten/tests/emtests/test_struct_varargs.rs +++ b/lib/emscripten/tests/emtests/test_struct_varargs.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_struct_varargs() { assert_emscripten_output!( "../../emtests/test_struct_varargs.wasm", diff --git a/lib/emscripten/tests/emtests/test_transtrcase.rs b/lib/emscripten/tests/emtests/test_transtrcase.rs index f9d169287..d0c7079c9 100644 --- a/lib/emscripten/tests/emtests/test_transtrcase.rs +++ b/lib/emscripten/tests/emtests/test_transtrcase.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_transtrcase() { assert_emscripten_output!( "../../emtests/test_transtrcase.wasm", diff --git a/lib/emscripten/tests/emtests/test_trickystring.rs b/lib/emscripten/tests/emtests/test_trickystring.rs index cf5698528..f04295c92 100644 --- a/lib/emscripten/tests/emtests/test_trickystring.rs +++ b/lib/emscripten/tests/emtests/test_trickystring.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_trickystring() { assert_emscripten_output!( "../../emtests/test_trickystring.wasm", diff --git a/lib/emscripten/tests/emtests/test_unary_literal.rs b/lib/emscripten/tests/emtests/test_unary_literal.rs index 06f61b92c..fc58fa288 100644 --- a/lib/emscripten/tests/emtests/test_unary_literal.rs +++ b/lib/emscripten/tests/emtests/test_unary_literal.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_unary_literal() { assert_emscripten_output!( "../../emtests/test_unary_literal.wasm", diff --git a/lib/emscripten/tests/emtests/test_vfs.rs b/lib/emscripten/tests/emtests/test_vfs.rs index 11acd4616..afd339042 100644 --- a/lib/emscripten/tests/emtests/test_vfs.rs +++ b/lib/emscripten/tests/emtests/test_vfs.rs @@ -1,8 +1,10 @@ -use crate::emtests::_common::assert_emscripten_output; - #[test] -fn test_vfs() { - let wasm_bytes = include_bytes!("../../emtests/test_vfs_bundle.wasm"); - let expected_str = include_str!("../../emtests/test_vfs.out"); - assert_emscripten_output(wasm_bytes, expected_str); +#[ignore] +fn test_test_vfs() { + assert_emscripten_output!( + "../../emtests/test_vfs.wasm", + "test_vfs", + vec![], + "../../emtests/test_vfs.out" + ); } diff --git a/lib/emscripten/tests/emtests/test_vprintf.rs b/lib/emscripten/tests/emtests/test_vprintf.rs index 1a5cdb142..18fa63018 100644 --- a/lib/emscripten/tests/emtests/test_vprintf.rs +++ b/lib/emscripten/tests/emtests/test_vprintf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_vprintf() { assert_emscripten_output!( "../../emtests/test_vprintf.wasm", diff --git a/lib/emscripten/tests/emtests/test_vsnprintf.rs b/lib/emscripten/tests/emtests/test_vsnprintf.rs index c5cbbf091..f11254464 100644 --- a/lib/emscripten/tests/emtests/test_vsnprintf.rs +++ b/lib/emscripten/tests/emtests/test_vsnprintf.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_vsnprintf() { assert_emscripten_output!( "../../emtests/test_vsnprintf.wasm", diff --git a/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs b/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs index df3da00a5..debdc6530 100644 --- a/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs +++ b/lib/emscripten/tests/emtests/test_write_stdout_fileno.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_write_stdout_fileno() { assert_emscripten_output!( "../../emtests/test_write_stdout_fileno.wasm", diff --git a/lib/emscripten/tests/emtests/test_zerodiv.rs b/lib/emscripten/tests/emtests/test_zerodiv.rs index 86712f6b5..df68359d3 100644 --- a/lib/emscripten/tests/emtests/test_zerodiv.rs +++ b/lib/emscripten/tests/emtests/test_zerodiv.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn test_test_zerodiv() { assert_emscripten_output!( "../../emtests/test_zerodiv.wasm", diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 8be5aaa32..fabf70cef 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wasmer-llvm-backend" -version = "0.1.0" +version = "0.4.1" authors = ["Lachlan Sneff "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } -wasmparser = "0.28.0" +wasmparser = "0.29.2" hashbrown = "0.1.8" smallvec = "0.6.8" goblin = "0.0.20" @@ -27,4 +27,4 @@ wabt = "0.7.4" [features] debug = ["wasmer-runtime-core/debug"] -disasm = ["capstone"] \ No newline at end of file +disasm = ["capstone"] diff --git a/lib/llvm-backend/README.md b/lib/llvm-backend/README.md new file mode 100644 index 000000000..f53a0cb31 --- /dev/null +++ b/lib/llvm-backend/README.md @@ -0,0 +1,31 @@ + + +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer LLVM backend + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate represents the LLVM backend. diff --git a/lib/llvm-backend/build.rs b/lib/llvm-backend/build.rs index ed96cae9d..ab59748b1 100644 --- a/lib/llvm-backend/build.rs +++ b/lib/llvm-backend/build.rs @@ -213,6 +213,8 @@ fn main() { println!("cargo:rustc-link-lib=static=llvm-backend"); println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=cpp/object_loader.cpp"); + println!("cargo:rerun-if-changed=cpp/object_loader.hh"); // Enable "nightly" cfg if the current compiler is nightly. if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly { diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index cef7c7214..28bea6354 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -175,21 +175,26 @@ WasmModule::WasmModule( callbacks_t callbacks ) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) { - object_file = llvm::cantFail(llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( + + + if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( llvm::StringRef((const char *)object_start, object_size), "object" - ))); + ))) { + object_file = cantFail(std::move(created_object_file)); + SymbolLookup symbol_resolver(callbacks); + runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); - SymbolLookup symbol_resolver(callbacks); - runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); + runtime_dyld->setProcessAllSections(true); - runtime_dyld->setProcessAllSections(true); + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); - runtime_dyld->loadObject(*object_file); - runtime_dyld->finalizeWithMemoryManagerLocking(); - - if (runtime_dyld->hasError()) { - std::cout << "RuntimeDyld error: " << (std::string)runtime_dyld->getErrorString() << std::endl; - abort(); + if (runtime_dyld->hasError()) { + _init_failed = true; + return; + } + } else { + _init_failed = true; } } diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index d22acb919..63630a479 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -5,14 +5,16 @@ #include #include -typedef enum { +typedef enum +{ PROTECT_NONE, PROTECT_READ, PROTECT_READ_WRITE, PROTECT_READ_EXECUTE, } mem_protect_t; -typedef enum { +typedef enum +{ RESULT_OK, RESULT_ALLOCATE_FAILURE, RESULT_PROTECT_FAILURE, @@ -20,16 +22,17 @@ typedef enum { RESULT_OBJECT_LOAD_FAILURE, } result_t; -typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t** ptr_out, size_t* size_out); -typedef result_t (*protect_memory_t)(uint8_t* ptr, size_t size, mem_protect_t protect); -typedef result_t (*dealloc_memory_t)(uint8_t* ptr, size_t size); -typedef uintptr_t (*lookup_vm_symbol_t)(const char* name_ptr, size_t length); +typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t **ptr_out, size_t *size_out); +typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size, mem_protect_t protect); +typedef result_t (*dealloc_memory_t)(uint8_t *ptr, size_t size); +typedef uintptr_t (*lookup_vm_symbol_t)(const char *name_ptr, size_t length); typedef void (*fde_visitor_t)(uint8_t *fde); typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size, fde_visitor_t visitor); -typedef void (*trampoline_t)(void*, void*, void*, void*); +typedef void (*trampoline_t)(void *, void *, void *, void *); -typedef struct { +typedef struct +{ /* Memory management. */ alloc_memory_t alloc_memory; protect_memory_t protect_memory; @@ -40,32 +43,45 @@ typedef struct { visit_fde_t visit_fde; } callbacks_t; -struct WasmException { -public: +typedef struct +{ + size_t data, vtable; +} box_any_t; + +struct WasmException +{ + public: virtual std::string description() const noexcept = 0; }; -struct UncatchableException : WasmException { -public: - virtual std::string description() const noexcept override { +struct UncatchableException : WasmException +{ + public: + virtual std::string description() const noexcept override + { return "Uncatchable exception"; } }; -struct UserException : UncatchableException { -public: - UserException(std::string msg) : msg(msg) {} +struct UserException : UncatchableException +{ + public: + UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {} - virtual std::string description() const noexcept override { - return std::string("user exception: ") + msg; + virtual std::string description() const noexcept override + { + return "user exception"; } -private: - std::string msg; + + // The parts of a `Box`. + box_any_t error_data; }; -struct WasmTrap : UncatchableException { -public: - enum Type { +struct WasmTrap : UncatchableException +{ + public: + enum Type + { Unreachable = 0, IncorrectCallIndirectSignature = 1, MemoryOutOfBounds = 2, @@ -76,49 +92,54 @@ public: WasmTrap(Type type) : type(type) {} - virtual std::string description() const noexcept override { + virtual std::string description() const noexcept override + { std::ostringstream ss; ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n'; - + return ss.str(); } Type type; -private: - friend std::ostream& operator<<(std::ostream& out, const Type& ty) { - switch (ty) { - case Type::Unreachable: - out << "unreachable"; - break; - case Type::IncorrectCallIndirectSignature: - out << "incorrect call_indirect signature"; - break; - case Type::MemoryOutOfBounds: - out << "memory access out-of-bounds"; - break; - case Type::CallIndirectOOB: - out << "call_indirect out-of-bounds"; - break; - case Type::IllegalArithmetic: - out << "illegal arithmetic operation"; - break; - case Type::Unknown: - default: - out << "unknown"; - break; + private: + friend std::ostream &operator<<(std::ostream &out, const Type &ty) + { + switch (ty) + { + case Type::Unreachable: + out << "unreachable"; + break; + case Type::IncorrectCallIndirectSignature: + out << "incorrect call_indirect signature"; + break; + case Type::MemoryOutOfBounds: + out << "memory access out-of-bounds"; + break; + case Type::CallIndirectOOB: + out << "call_indirect out-of-bounds"; + break; + case Type::IllegalArithmetic: + out << "illegal arithmetic operation"; + break; + case Type::Unknown: + default: + out << "unknown"; + break; } return out; } }; -struct CatchableException : WasmException { -public: +struct CatchableException : WasmException +{ + public: CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {} - virtual std::string description() const noexcept override { + virtual std::string description() const noexcept override + { return "catchable exception"; } @@ -126,25 +147,33 @@ public: uint64_t values[1]; }; -struct WasmModule { -public: +struct WasmModule +{ + public: WasmModule( const uint8_t *object_start, size_t object_size, - callbacks_t callbacks - ); + callbacks_t callbacks); void *get_func(llvm::StringRef name) const; -private: + + bool _init_failed = false; + private: std::unique_ptr memory_manager; std::unique_ptr object_file; std::unique_ptr runtime_dyld; }; -extern "C" { - result_t module_load(const uint8_t* mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule** module_out) { +extern "C" +{ + result_t module_load(const uint8_t *mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule **module_out) + { *module_out = new WasmModule(mem_ptr, mem_size, callbacks); + if ((*module_out)->_init_failed) { + return RESULT_OBJECT_LOAD_FAILURE; + } + return RESULT_OK; } @@ -152,34 +181,56 @@ extern "C" { throw WasmTrap(ty); } - void module_delete(WasmModule* module) { + void module_delete(WasmModule *module) + { delete module; } + // Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust + // side. + [[noreturn]] void throw_any(size_t data, size_t vtable) { + throw UserException(data, vtable); + } + bool invoke_trampoline( trampoline_t trampoline, - void* ctx, - void* func, - void* params, - void* results, - WasmTrap::Type* trap_out - ) throw() { - try { + void *ctx, + void *func, + void *params, + void *results, + WasmTrap::Type *trap_out, + box_any_t *user_error, + void *invoke_env) throw() + { + try + { trampoline(ctx, func, params, results); return true; - } catch(const WasmTrap& e) { + } + catch (const WasmTrap &e) + { *trap_out = e.type; return false; - } catch(const WasmException& e) { + } + catch (const UserException &e) + { + *user_error = e.error_data; + return false; + } + catch (const WasmException &e) + { *trap_out = WasmTrap::Type::Unknown; return false; - } catch (...) { + } + catch (...) + { *trap_out = WasmTrap::Type::Unknown; return false; } } - void* get_func_symbol(WasmModule* module, const char* name) { + void *get_func_symbol(WasmModule *module, const char *name) + { return module->get_func(llvm::StringRef(name)); } } \ No newline at end of file diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 742069d48..7a8c6c64a 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -11,24 +11,24 @@ use libc::{ }; use std::{ any::Any, - ffi::CString, + ffi::{c_void, CString}, mem, + ops::Deref, ptr::{self, NonNull}, slice, str, - sync::Once, + sync::{Arc, Once}, }; use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller, Token, UserTrapper}, - error::{RuntimeError, RuntimeResult}, - export::Context, - module::{ModuleInfo, ModuleInner}, - structures::TypedIndex, - types::{ - FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, - Value, + backend::{ + sys::{Memory, Protect}, + CacheGen, RunnableModule, }, - vm::{self, ImportBacking}, - vmcalls, + cache::Error as CacheError, + module::ModuleInfo, + structures::TypedIndex, + typed_func::{Wasm, WasmTrapInfo}, + types::{LocalFuncIndex, SigIndex}, + vm, vmcalls, }; #[repr(C)] @@ -57,16 +57,6 @@ enum LLVMResult { OBJECT_LOAD_FAILURE, } -#[repr(C)] -enum WasmTrapType { - Unreachable = 0, - IncorrectCallIndirectSignature = 1, - MemoryOutOfBounds = 2, - CallIndirectOOB = 3, - IllegalArithmetic = 4, - Unknown, -} - #[repr(C)] struct Callbacks { alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult, @@ -89,13 +79,22 @@ extern "C" { fn throw_trap(ty: i32); + /// This should be the same as spliting up the fat pointer into two arguments, + /// but this is cleaner, I think? + #[cfg_attr(nightly, unwind(allowed))] + #[allow(improper_ctypes)] + fn throw_any(data: *mut dyn Any) -> !; + + #[allow(improper_ctypes)] fn invoke_trampoline( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64), + trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), vmctx_ptr: *mut vm::Ctx, - func_ptr: *const vm::Func, + func_ptr: NonNull, params: *const u64, results: *mut u64, - trap_out: *mut WasmTrapType, + trap_out: *mut WasmTrapInfo, + user_error: *mut Option>, + invoke_env: Option>, ) -> bool; } @@ -210,17 +209,32 @@ fn get_callbacks() -> Callbacks { } } +pub enum Buffer { + LlvmMemory(MemoryBuffer), + Memory(Memory), +} + +impl Deref for Buffer { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + Buffer::LlvmMemory(mem_buffer) => mem_buffer.as_slice(), + Buffer::Memory(memory) => unsafe { memory.as_slice() }, + } + } +} + unsafe impl Send for LLVMBackend {} unsafe impl Sync for LLVMBackend {} pub struct LLVMBackend { module: *mut LLVMModule, #[allow(dead_code)] - memory_buffer: MemoryBuffer, + buffer: Arc, } impl LLVMBackend { - pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) { + pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) { Target::initialize_x86(&InitializationConfig { asm_parser: true, asm_printer: true, @@ -269,16 +283,55 @@ impl LLVMBackend { panic!("failed to load object") } + let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer)); + ( Self { module, - memory_buffer, + buffer: Arc::clone(&buffer), }, - LLVMProtectedCaller { module }, + LLVMCache { buffer }, ) } - pub fn get_func( + pub unsafe fn from_buffer(memory: Memory) -> Result<(Self, LLVMCache), String> { + let callbacks = get_callbacks(); + let mut module: *mut LLVMModule = ptr::null_mut(); + + let slice = memory.as_slice(); + + let res = module_load(slice.as_ptr(), slice.len(), callbacks, &mut module); + + if res != LLVMResult::OK { + return Err("failed to load object".to_string()); + } + + static SIGNAL_HANDLER_INSTALLED: Once = Once::new(); + + SIGNAL_HANDLER_INSTALLED.call_once(|| { + crate::platform::install_signal_handler(); + }); + + let buffer = Arc::new(Buffer::Memory(memory)); + + Ok(( + Self { + module, + buffer: Arc::clone(&buffer), + }, + LLVMCache { buffer }, + )) + } +} + +impl Drop for LLVMBackend { + fn drop(&mut self) { + unsafe { module_delete(self.module) } + } +} + +impl RunnableModule for LLVMBackend { + fn get_func( &self, info: &ModuleInfo, local_func_index: LocalFuncIndex, @@ -295,74 +348,14 @@ impl LLVMBackend { NonNull::new(ptr as _) } -} -impl Drop for LLVMBackend { - fn drop(&mut self) { - unsafe { module_delete(self.module) } - } -} - -impl FuncResolver for LLVMBackend { - fn get( - &self, - module: &ModuleInner, - local_func_index: LocalFuncIndex, - ) -> Option> { - self.get_func(&module.info, local_func_index) - } -} - -struct Placeholder; - -unsafe impl Send for LLVMProtectedCaller {} -unsafe impl Sync for LLVMProtectedCaller {} - -pub struct LLVMProtectedCaller { - module: *mut LLVMModule, -} - -impl ProtectedCaller for LLVMProtectedCaller { - fn call( - &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - let (func_ptr, ctx, signature, sig_index) = - get_func_from_index(&module, import_backing, func_index); - - let vmctx_ptr = match ctx { - Context::External(external_vmctx) => external_vmctx, - Context::Internal => vmctx, - }; - - assert!( - signature.returns().len() <= 1, - "multi-value returns not yet supported" - ); - - assert!( - signature.check_param_value_types(params), - "incorrect signature" - ); - - let param_vec: Vec = params - .iter() - .map(|val| match val { - Value::I32(x) => *x as u64, - Value::I64(x) => *x as u64, - Value::F32(x) => x.to_bits() as u64, - Value::F64(x) => x.to_bits(), - }) - .collect(); - - let mut return_vec = vec![0; signature.returns().len()]; - - let trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) = unsafe { + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + let trampoline: unsafe extern "C" fn( + *mut vm::Ctx, + NonNull, + *const u64, + *mut u64, + ) = unsafe { let name = if cfg!(target_os = "macos") { format!("_trmp{}", sig_index.index()) } else { @@ -376,99 +369,34 @@ impl ProtectedCaller for LLVMProtectedCaller { mem::transmute(symbol) }; - let mut trap_out = WasmTrapType::Unknown; - - // Here we go. - let success = unsafe { - invoke_trampoline( - trampoline, - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - &mut trap_out, - ) - }; - - if success { - Ok(return_vec - .iter() - .zip(signature.returns().iter()) - .map(|(&x, ty)| match ty { - Type::I32 => Value::I32(x as i32), - Type::I64 => Value::I64(x as i64), - Type::F32 => Value::F32(f32::from_bits(x as u32)), - Type::F64 => Value::F64(f64::from_bits(x as u64)), - }) - .collect()) - } else { - Err(match trap_out { - WasmTrapType::Unreachable => RuntimeError::Trap { - msg: "unreachable".into(), - }, - WasmTrapType::IncorrectCallIndirectSignature => RuntimeError::Trap { - msg: "uncorrect call_indirect signature".into(), - }, - WasmTrapType::MemoryOutOfBounds => RuntimeError::Trap { - msg: "memory out-of-bounds access".into(), - }, - WasmTrapType::CallIndirectOOB => RuntimeError::Trap { - msg: "call_indirect out-of-bounds".into(), - }, - WasmTrapType::IllegalArithmetic => RuntimeError::Trap { - msg: "illegal arithmetic operation".into(), - }, - WasmTrapType::Unknown => RuntimeError::Trap { - msg: "unknown trap".into(), - }, - }) - } + Some(unsafe { Wasm::from_raw_parts(trampoline, invoke_trampoline, None) }) } - fn get_early_trapper(&self) -> Box { - Box::new(Placeholder) + unsafe fn do_early_trap(&self, data: Box) -> ! { + throw_any(Box::leak(data)) } } -impl UserTrapper for Placeholder { - unsafe fn do_early_trap(&self, _data: Box) -> ! { - unimplemented!("do early trap") - } +unsafe impl Send for LLVMCache {} +unsafe impl Sync for LLVMCache {} + +pub struct LLVMCache { + buffer: Arc, } -fn get_func_from_index<'a>( - module: &'a ModuleInner, - import_backing: &ImportBacking, - func_index: FuncIndex, -) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) { - let sig_index = *module - .info - .func_assoc - .get(func_index) - .expect("broken invariant, incorrect func index"); +impl CacheGen for LLVMCache { + 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 (func_ptr, ctx) = match func_index.local_or_import(&module.info) { - LocalOrImport::Local(local_func_index) => ( - module - .func_resolver - .get(&module, local_func_index) - .expect("broken invariant, func resolver not synced with module.exports") - .cast() - .as_ptr() as *const _, - Context::Internal, - ), - LocalOrImport::Import(imported_func_index) => { - let imported_func = import_backing.imported_func(imported_func_index); - ( - imported_func.func as *const _, - Context::External(imported_func.vmctx), - ) + let buffer = self.buffer.deref(); + + unsafe { + memory.as_slice_mut()[..buffer.len()].copy_from_slice(buffer); } - }; - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) + Ok(([].as_ref().into(), memory)) + } } #[cfg(feature = "disasm")] diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 173262b67..028207a8c 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -3,24 +3,26 @@ use inkwell::{ context::Context, module::{Linkage, Module}, passes::PassManager, - types::{BasicType, BasicTypeEnum, FunctionType, IntType, PointerType}, + types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, values::{BasicValue, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue}, AddressSpace, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; +use std::sync::Arc; use wasmer_runtime_core::{ + backend::{Backend, CacheGen, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, memory::MemoryType, - module::{ExportIndex, ModuleInfo}, - structures::{Map, SliceMap, TypedIndex}, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, types::{ - FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, + FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, }; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, -}; +use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; +use crate::backend::LLVMBackend; use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; use crate::read_info::type_to_type; use crate::state::{ControlFrame, IfElseState, State}; @@ -58,144 +60,353 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } } -pub fn parse_function_bodies( - info: &ModuleInfo, - code_reader: CodeSectionReader, -) -> Result<(Module, Intrinsics), BinaryReaderError> { - let context = Context::create(); - let module = context.create_module("module"); - let builder = context.create_builder(); - - let intrinsics = Intrinsics::declare(&module, &context); - - let personality_func = module.add_function( - "__gxx_personality_v0", - intrinsics.i32_ty.fn_type(&[], false), - Some(Linkage::External), - ); - - let signatures: Map = info - .signatures - .iter() - .map(|(_, sig)| func_sig_to_llvm(&context, &intrinsics, sig)) - .collect(); - let functions: Map = info - .func_assoc - .iter() - .skip(info.imported_functions.len()) - .map(|(func_index, &sig_index)| { - let func = module.add_function( - &format!("fn{}", func_index.index()), - signatures[sig_index], - Some(Linkage::External), - ); - func.set_personality_function(personality_func); - func - }) - .collect(); - - for (local_func_index, body) in code_reader.into_iter().enumerate() { - let body = body?; - - let locals_reader = body.get_locals_reader()?; - let op_reader = body.get_operators_reader()?; - - parse_function( - &context, - &module, - &builder, - &intrinsics, - info, - &signatures, - &functions, - LocalFuncIndex::new(local_func_index), - locals_reader, - op_reader, - ) - .map_err(|e| BinaryReaderError { - message: e.message, - offset: local_func_index, - })?; - } - - // module.print_to_stderr(); - - generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics); - - let pass_manager = PassManager::create_for_module(); - // pass_manager.add_verifier_pass(); - pass_manager.add_function_inlining_pass(); - pass_manager.add_promote_memory_to_register_pass(); - pass_manager.add_cfg_simplification_pass(); - // pass_manager.add_instruction_combining_pass(); - pass_manager.add_aggressive_inst_combiner_pass(); - pass_manager.add_merged_load_store_motion_pass(); - // pass_manager.add_sccp_pass(); - // pass_manager.add_gvn_pass(); - pass_manager.add_new_gvn_pass(); - pass_manager.add_aggressive_dce_pass(); - pass_manager.run_on_module(&module); - - // module.print_to_stderr(); - - Ok((module, intrinsics)) -} - -fn parse_function( - context: &Context, - module: &Module, +fn trap_if_not_representatable_as_int( builder: &Builder, intrinsics: &Intrinsics, - info: &ModuleInfo, - signatures: &SliceMap, - functions: &SliceMap, - func_index: LocalFuncIndex, - locals_reader: LocalsReader, - op_reader: OperatorsReader, -) -> Result<(), BinaryReaderError> { - let sig_index = info.func_assoc[func_index.convert_up(info)]; - let func_sig = &info.signatures[sig_index]; - let llvm_sig = &signatures[sig_index]; + context: &Context, + function: &FunctionValue, + lower_bounds: f64, + upper_bound: f64, + value: FloatValue, +) { + enum FloatSize { + Bits32, + Bits64, + } - let function = functions[func_index]; - let mut state = State::new(); - let entry_block = context.append_basic_block(&function, "entry"); + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_block"); - let return_block = context.append_basic_block(&function, "return"); - builder.position_at_end(&return_block); + let float_ty = value.get_type(); + let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { + (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) + } else if float_ty == intrinsics.f64_ty { + (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) + } else { + unreachable!() + }; - let phis: SmallVec<[PhiValue; 1]> = func_sig - .returns() - .iter() - .map(|&wasmer_ty| type_to_llvm(intrinsics, wasmer_ty)) - .map(|ty| builder.build_phi(ty, &state.var_name())) - .collect(); + let (exponent, invalid_exponent) = { + let float_bits = { + let space = builder.build_alloca(int_ty, "space"); + let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); + builder.build_store(float_ptr, value); + builder.build_load(space, "float_bits").into_int_value() + }; - state.push_block(return_block, phis); - builder.position_at_end(&entry_block); + let (shift_amount, exponent_mask, invalid_exponent) = match float_size { + FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), + FloatSize::Bits64 => ( + 52, + 0b0111111111110000000000000000000000000000000000000000000000000000, + 0b11111111111, + ), + }; - let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); - locals.extend( - function - .get_param_iter() - .skip(1) - .enumerate() - .map(|(index, param)| { - let ty = param.get_type(); + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, + ) + }; - let alloca = builder.build_alloca(ty, &format!("local{}", index)); - builder.build_store(alloca, param); - alloca - }), + let is_invalid_float = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + exponent, + int_ty.const_int(invalid_exponent, false), + "is_not_normal", + ), + builder.build_or( + builder.build_float_compare( + FloatPredicate::ULT, + value, + float_ty.const_float(lower_bounds), + "less_than_lower_bounds", + ), + builder.build_float_compare( + FloatPredicate::UGT, + value, + float_ty.const_float(upper_bound), + "greater_than_upper_bounds", + ), + "float_not_in_bounds", + ), + "is_invalid_float", ); - let param_len = locals.len(); + let is_invalid_float = builder + .build_call( + intrinsics.expect_i1, + &[ + is_invalid_float.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "is_invalid_float_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); - let mut local_idx = 0; - for (index, local) in locals_reader.into_iter().enumerate() { - let (count, ty) = local?; + builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); + builder.position_at_end(&failure_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); +} + +fn trap_if_zero_or_overflow( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + left: IntValue, + right: IntValue, +) { + let int_type = left.get_type(); + + let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { + let min_value = int_type.const_int(i32::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); + (min_value, neg_one_value) + } else if int_type == intrinsics.i64_ty { + let min_value = int_type.const_int(i64::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i64 as u64, false); + (min_value, neg_one_value) + } else { + unreachable!() + }; + + let should_trap = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + right, + int_type.const_int(0, false), + "divisor_is_zero", + ), + builder.build_and( + builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), + builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), + "div_will_overflow", + ), + "div_should_trap", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .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"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn trap_if_zero( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + value: IntValue, +) { + let int_type = value.get_type(); + let should_trap = builder.build_int_compare( + IntPredicate::EQ, + value, + int_type.const_int(0, false), + "divisor_is_zero", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .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"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn resolve_memory_ptr( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + state: &mut State, + ctx: &mut CtxType, + memarg: &MemoryImmediate, + ptr_ty: PointerType, +) -> Result { + // Ignore alignment hint for the time being. + let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); + let var_offset_i32 = state.pop1()?.into_int_value(); + let var_offset = + builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); + let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); + let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics); + + let mem_base_int = match memory_cache { + MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + } => { + let base = builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(); + let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); + + let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); + + let base_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + effective_offset, + bounds, + "base_in_bounds", + ); + + let base_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + base_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "base_in_bounds_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .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"); + builder.build_conditional_branch( + base_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_memory_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + base_as_int + } + MemoryCache::Static { + base_ptr, + bounds: _, + } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), + }; + + let effective_address_int = + builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); + Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: String, +} + +pub struct LLVMModuleCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + functions: Vec, + signatures: Map, + signatures_raw: Map, + function_signatures: Option>>, + func_import_count: usize, + personality_func: FunctionValue, + module: Module, +} + +pub struct LLVMFunctionCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + state: State, + function: FunctionValue, + func_sig: FuncSig, + signatures: Map, + locals: Vec, // Contains params and locals + num_params: usize, + ctx: Option>, + unreachable_depth: usize, +} + +impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { + fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + let param_len = self.num_params; + + let mut local_idx = 0; + // let (count, ty) = local?; + let count = n; let wasmer_ty = type_to_type(ty)?; + + let intrinsics = self.intrinsics.as_ref().unwrap(); let ty = type_to_llvm(intrinsics, wasmer_ty); let default_value = match wasmer_ty { @@ -205,51 +416,90 @@ fn parse_function( Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), }; + let builder = self.builder.as_ref().unwrap(); + for _ in 0..count { let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); builder.build_store(alloca, default_value); - locals.push(alloca); + self.locals.push(alloca); local_idx += 1; } + Ok(()) } - let start_of_code_block = context.append_basic_block(&function, "start_of_code"); - let entry_end_inst = builder.build_unconditional_branch(&start_of_code_block); - builder.position_at_end(&start_of_code_block); + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let start_of_code_block = self + .context + .as_ref() + .unwrap() + .append_basic_block(&self.function, "start_of_code"); + let entry_end_inst = self + .builder + .as_ref() + .unwrap() + .build_unconditional_branch(&start_of_code_block); + self.builder + .as_ref() + .unwrap() + .position_at_end(&start_of_code_block); - let cache_builder = context.create_builder(); - cache_builder.position_before(&entry_end_inst); - let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); - let mut unreachable_depth = 0; + let cache_builder = self.context.as_ref().unwrap().create_builder(); + 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); + + self.ctx = Some(ctx); + Ok(()) + } + + fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let op = match event { + Event::Wasm(x) => x, + Event::Internal(_x) => { + return Ok(()); + } + }; + + let mut state = &mut self.state; + let builder = self.builder.as_ref().unwrap(); + let context = self.context.as_ref().unwrap(); + let function = self.function; + let intrinsics = self.intrinsics.as_ref().unwrap(); + let locals = &self.locals; + let info = module_info; + let signatures = &self.signatures; + let mut ctx = self.ctx.as_mut().unwrap(); - for op in op_reader { - let op = op?; if !state.reachable { - match op { + match *op { Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { - unreachable_depth += 1; - continue; + self.unreachable_depth += 1; + return Ok(()); } Operator::Else => { - if unreachable_depth != 0 { - continue; + if self.unreachable_depth != 0 { + return Ok(()); } } Operator::End => { - if unreachable_depth != 0 { - unreachable_depth -= 1; - continue; + if self.unreachable_depth != 0 { + self.unreachable_depth -= 1; + return Ok(()); } } _ => { - continue; + return Ok(()); } } } - match op { + match *op { /*************************** * Control Flow instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions @@ -383,7 +633,12 @@ fn parse_function( .iter() .enumerate() .map(|(case_index, &depth)| { - let frame = state.frame_at_depth(depth)?; + let frame_result: Result<&ControlFrame, BinaryReaderError> = + state.frame_at_depth(depth); + let frame = match frame_result { + Ok(v) => v, + Err(e) => return Err(e), + }; let case_index_literal = context.i32_type().const_int(case_index as u64, false); @@ -397,7 +652,8 @@ fn parse_function( builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); - state.popn(args.len())?; + let args_len = args.len(); + state.popn(args_len)?; state.reachable = false; } Operator::If { ty } => { @@ -490,7 +746,6 @@ fn parse_function( if let ControlFrame::IfElse { if_else, next, - phis, if_else_state, .. } = &frame @@ -620,7 +875,7 @@ fn parse_function( Operator::GetGlobal { global_index } => { let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Const { value } => { state.push1(value); @@ -634,7 +889,7 @@ fn parse_function( Operator::SetGlobal { global_index } => { let value = state.pop1()?; let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Mut { ptr_to_value } => { builder.build_store(ptr_to_value, value); @@ -670,12 +925,14 @@ fn parse_function( .map(|v| *v) .collect(); - let func_ptr = ctx.local_func(local_func_index, llvm_sig); + let func_ptr = + ctx.local_func(local_func_index, llvm_sig, intrinsics, builder); builder.build_call(func_ptr, ¶ms, &state.var_name()) } LocalOrImport::Import(import_func_index) => { - let (func_ptr_untyped, ctx_ptr) = ctx.imported_func(import_func_index); + let (func_ptr_untyped, ctx_ptr) = + ctx.imported_func(import_func_index, intrinsics); let params: Vec<_> = [ctx_ptr.as_basic_value_enum()] .iter() .chain(state.peekn(func_sig.params().len())?.iter()) @@ -714,8 +971,9 @@ fn parse_function( } Operator::CallIndirect { index, table_index } => { let sig_index = SigIndex::new(index as usize); - let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index); - let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize)); + let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index, intrinsics); + let (table_base, table_bound) = + ctx.table(TableIndex::new(table_index as usize), intrinsics, builder); let func_index = state.pop1()?.into_int_value(); // We assume the table has the `anyfunc` element type. @@ -866,7 +1124,7 @@ fn parse_function( let value = call_site.try_as_basic_value().left().unwrap(); state.push1(value); } - returns @ _ => unimplemented!("multi-value returns"), + _ => unimplemented!("multi-value returns"), } } @@ -1732,7 +1990,7 @@ fn parse_function( * Load and Store instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions ***************************/ - Operator::I32Load { memarg } => { + Operator::I32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1746,7 +2004,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::I64Load { memarg } => { + Operator::I64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1760,7 +2018,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F32Load { memarg } => { + Operator::F32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1774,7 +2032,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F64Load { memarg } => { + Operator::F64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1789,7 +2047,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store { memarg } => { + Operator::I32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1803,7 +2061,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::I64Store { memarg } => { + Operator::I64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1817,7 +2075,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F32Store { memarg } => { + Operator::F32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1831,7 +2089,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F64Store { memarg } => { + Operator::F64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1846,7 +2104,7 @@ fn parse_function( builder.build_store(effective_address, value); } - Operator::I32Load8S { memarg } => { + Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1864,7 +2122,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16S { memarg } => { + Operator::I32Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1882,7 +2140,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8S { memarg } => { + Operator::I64Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1900,7 +2158,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16S { memarg } => { + Operator::I64Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1918,7 +2176,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32S { memarg } => { + Operator::I64Load32S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1937,7 +2195,7 @@ fn parse_function( state.push1(result); } - Operator::I32Load8U { memarg } => { + Operator::I32Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1955,7 +2213,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16U { memarg } => { + Operator::I32Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1973,7 +2231,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8U { memarg } => { + Operator::I64Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1991,7 +2249,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16U { memarg } => { + Operator::I64Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2009,7 +2267,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32U { memarg } => { + Operator::I64Load32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2028,7 +2286,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + Operator::I32Store8 { ref memarg } | Operator::I64Store8 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2044,7 +2302,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + Operator::I32Store16 { ref memarg } | Operator::I64Store16 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2060,7 +2318,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I64Store32 { memarg } => { + Operator::I64Store32 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2143,324 +2401,239 @@ fn parse_function( ); state.push1(result.try_as_basic_value().left().unwrap()); } - op @ _ => { + _ => { unimplemented!("{:?}", op); } } + + Ok(()) } - let results = state.popn_save(func_sig.returns().len())?; + fn finalize(&mut self) -> Result<(), CodegenError> { + let results = self.state.popn_save(self.func_sig.returns().len())?; - match results.as_slice() { - [] => { - builder.build_return(None); - } - [one_value] => { - builder.build_return(Some(one_value)); - } - returns @ _ => { - // let struct_ty = llvm_sig.get_return_type().as_struct_type(); - // let ret_struct = struct_ty.const_zero(); - unimplemented!("multi-value returns not yet implemented") + match results.as_slice() { + [] => { + self.builder.as_ref().unwrap().build_return(None); + } + [one_value] => { + self.builder.as_ref().unwrap().build_return(Some(one_value)); + } + _ => unimplemented!("multi-value returns not yet implemented"), } + Ok(()) } - - Ok(()) } -fn trap_if_not_representatable_as_int( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - lower_bounds: f64, - upper_bound: f64, - value: FloatValue, -) { - enum FloatSize { - Bits32, - Bits64, +impl From for CodegenError { + fn from(other: BinaryReaderError) -> CodegenError { + CodegenError { + message: format!("{:?}", other), + } } +} - let failure_block = context.append_basic_block(function, "conversion_failure_block"); - let continue_block = context.append_basic_block(function, "conversion_success_block"); +impl ModuleCodeGenerator + for LLVMModuleCodeGenerator +{ + fn new() -> LLVMModuleCodeGenerator { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); - let float_ty = value.get_type(); - let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { - (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) - } else if float_ty == intrinsics.f64_ty { - (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) - } else { - unreachable!() - }; + let intrinsics = Intrinsics::declare(&module, &context); - let (exponent, invalid_exponent) = { - let float_bits = { - let space = builder.build_alloca(int_ty, "space"); - let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); - builder.build_store(float_ptr, value); - builder.build_load(space, "float_bits").into_int_value() - }; - - let (shift_amount, exponent_mask, invalid_exponent) = match float_size { - FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), - FloatSize::Bits64 => ( - 52, - 0b0111111111110000000000000000000000000000000000000000000000000000, - 0b11111111111, - ), - }; - - let masked = builder.build_and( - float_bits, - int_ty.const_int(exponent_mask, false), - "masked_bits", + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), ); - ( - builder.build_right_shift( - float_bits, - int_ty.const_int(shift_amount, false), - false, - "exponent", - ), - invalid_exponent, - ) - }; + let signatures = Map::new(); - let is_invalid_float = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - exponent, - int_ty.const_int(invalid_exponent, false), - "is_not_normal", - ), - builder.build_or( - builder.build_float_compare( - FloatPredicate::ULT, - value, - float_ty.const_float(lower_bounds), - "less_than_lower_bounds", - ), - builder.build_float_compare( - FloatPredicate::UGT, - value, - float_ty.const_float(upper_bound), - "greater_than_upper_bounds", - ), - "float_not_in_bounds", - ), - "is_invalid_float", - ); - - let is_invalid_float = builder - .build_call( - intrinsics.expect_i1, - &[ - is_invalid_float.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "is_invalid_float_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); - builder.position_at_end(&failure_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&continue_block); -} - -fn trap_if_zero_or_overflow( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - left: IntValue, - right: IntValue, -) { - let int_type = left.get_type(); - - let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { - let min_value = int_type.const_int(i32::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); - (min_value, neg_one_value) - } else if int_type == intrinsics.i64_ty { - let min_value = int_type.const_int(i64::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i64 as u64, false); - (min_value, neg_one_value) - } else { - unreachable!() - }; - - let should_trap = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - right, - int_type.const_int(0, false), - "divisor_is_zero", - ), - builder.build_and( - builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), - builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), - "div_will_overflow", - ), - "div_should_trap", - ); - - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .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"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} - -fn trap_if_zero( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - value: IntValue, -) { - let int_type = value.get_type(); - let should_trap = builder.build_int_compare( - IntPredicate::EQ, - value, - int_type.const_int(0, false), - "divisor_is_zero", - ); - - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .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"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} - -fn resolve_memory_ptr( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - state: &mut State, - ctx: &mut CtxType, - memarg: MemoryImmediate, - ptr_ty: PointerType, -) -> Result { - // Ignore alignment hint for the time being. - let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); - let var_offset_i32 = state.pop1()?.into_int_value(); - let var_offset = - builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); - let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); - let memory_cache = ctx.memory(MemoryIndex::new(0)); - - let mem_base_int = match memory_cache { - MemoryCache::Dynamic { - ptr_to_base_ptr, - ptr_to_bounds, - } => { - let base = builder - .build_load(ptr_to_base_ptr, "base") - .into_pointer_value(); - let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); - - let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); - - let base_in_bounds = builder.build_int_compare( - IntPredicate::ULT, - effective_offset, - bounds, - "base_in_bounds", - ); - - let base_in_bounds = builder - .build_call( - intrinsics.expect_i1, - &[ - base_in_bounds.as_basic_value_enum(), - intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), - ], - "base_in_bounds_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .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"); - builder.build_conditional_branch( - base_in_bounds, - &in_bounds_continue_block, - ¬_in_bounds_block, - ); - builder.position_at_end(¬_in_bounds_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_memory_oob], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&in_bounds_continue_block); - - base_as_int + LLVMModuleCodeGenerator { + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + module, + functions: vec![], + signatures, + signatures_raw: Map::new(), + function_signatures: None, + func_import_count: 0, + personality_func, } - MemoryCache::Static { - base_ptr, - bounds: _, - } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), - }; + } - let effective_address_int = - builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); - Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) + fn backend_id() -> Backend { + Backend::LLVM + } + + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut LLVMFunctionCodeGenerator, 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) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + + let sig_id = self.function_signatures.as_ref().unwrap() + [FuncIndex::new(self.func_import_count + self.functions.len())]; + let func_sig = self.signatures_raw[sig_id].clone(); + + let function = self.module.add_function( + &format!("fn{}", self.func_import_count + self.functions.len()), + self.signatures[sig_id], + Some(Linkage::External), + ); + function.set_personality_function(self.personality_func); + + let mut state = State::new(); + let entry_block = context.append_basic_block(&function, "entry"); + + let return_block = context.append_basic_block(&function, "return"); + builder.position_at_end(&return_block); + + let phis: SmallVec<[PhiValue; 1]> = func_sig + .returns() + .iter() + .map(|&wasmer_ty| type_to_llvm(&intrinsics, wasmer_ty)) + .map(|ty| builder.build_phi(ty, &state.var_name())) + .collect(); + + state.push_block(return_block, phis); + builder.position_at_end(&entry_block); + + let mut locals = Vec::new(); + locals.extend( + function + .get_param_iter() + .skip(1) + .enumerate() + .map(|(index, param)| { + let ty = param.get_type(); + + let alloca = builder.build_alloca(ty, &format!("local{}", index)); + builder.build_store(alloca, param); + alloca + }), + ); + let num_params = locals.len(); + + let code = LLVMFunctionCodeGenerator { + state, + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + function, + func_sig: func_sig, + locals, + signatures: self.signatures.clone(), + num_params, + ctx: None, + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize( + mut self, + module_info: &ModuleInfo, + ) -> Result<(LLVMBackend, Box), CodegenError> { + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + self.context = Some(context); + self.builder = Some(builder); + self.intrinsics = Some(intrinsics); + + generate_trampolines( + module_info, + &self.signatures, + &self.module, + self.context.as_ref().unwrap(), + self.builder.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + ); + + let pass_manager = PassManager::create_for_module(); + if cfg!(test) { + pass_manager.add_verifier_pass(); + } + pass_manager.add_function_inlining_pass(); + pass_manager.add_promote_memory_to_register_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_aggressive_inst_combiner_pass(); + pass_manager.add_merged_load_store_motion_pass(); + pass_manager.add_new_gvn_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.run_on_module(&self.module); + + // self.module.print_to_stderr(); + + let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap()); + Ok((backend, Box::new(cache_gen))) + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = signatures + .iter() + .map(|(_, sig)| { + func_sig_to_llvm( + self.context.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + sig, + ) + }) + .collect(); + self.signatures_raw = signatures.clone(); + Ok(()) + } + + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + self.func_import_count += 1; + Ok(()) + } + + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; + + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), + + info, + }) + } } diff --git a/lib/llvm-backend/src/example.rs b/lib/llvm-backend/src/example.rs deleted file mode 100644 index 5ce3c9a5f..000000000 --- a/lib/llvm-backend/src/example.rs +++ /dev/null @@ -1,61 +0,0 @@ -use inkwell::OptimizationLevel; -use inkwell::builder::Builder; -use inkwell::context::Context; -use inkwell::execution_engine::{ExecutionEngine, JitFunction}; -use inkwell::module::Module; -use inkwell::targets::{InitializationConfig, Target}; -use std::error::Error; - -/// Convenience type alias for the `sum` function. -/// -/// Calling this is innately `unsafe` because there's no guarantee it doesn't -/// do `unsafe` operations internally. -type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; - -#[test] -fn test_sum() -> Result<(), Box> { - let context = Context::create(); - let module = context.create_module("sum"); - let builder = context.create_builder(); - let execution_engine = module.create_jit_execution_engine(OptimizationLevel::Aggressive)?; - - let sum = jit_compile_sum(&context, &module, &builder, &execution_engine) - .ok_or("Unable to JIT compile `sum`")?; - - let x = 1u64; - let y = 2u64; - let z = 3u64; - - unsafe { - println!("{} + {} + {} = {}", x, y, z, sum.call(x, y, z)); - assert_eq!(sum.call(x, y, z), x + y + z); - } - - Ok(()) -} - -fn jit_compile_sum( - context: &Context, - module: &Module, - builder: &Builder, - execution_engine: &ExecutionEngine, -) -> Option> { - let i64_type = context.i64_type(); - let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); - - let function = module.add_function("sum", fn_type, None); - let basic_block = context.append_basic_block(&function, "entry"); - - builder.position_at_end(&basic_block); - - let x = function.get_nth_param(0)?.into_int_value(); - let y = function.get_nth_param(1)?.into_int_value(); - let z = function.get_nth_param(2)?.into_int_value(); - - let sum = builder.build_int_add(x, y, "sum"); - let sum = builder.build_int_add(sum, z, "sum"); - - builder.build_return(Some(&sum)); - - unsafe { execution_engine.get_function("sum").ok() } -} diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index ba1e3abd3..6250fd4fd 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -4,10 +4,7 @@ use inkwell::{ context::Context, module::Module, types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType}, - values::{ - BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionValue, IntValue, - PointerValue, - }, + values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue}, AddressSpace, }; use std::marker::PhantomData; @@ -117,7 +114,6 @@ pub struct Intrinsics { pub throw_trap: FunctionValue, - ctx_ty: StructType, pub ctx_ptr_ty: PointerType, } @@ -370,38 +366,9 @@ impl Intrinsics { void_ty.fn_type(&[i32_ty_basic], false), None, ), - ctx_ty, ctx_ptr_ty, } } - - pub fn ctx<'a>( - &'a self, - info: &'a ModuleInfo, - builder: &'a Builder, - func_value: &'a FunctionValue, - cache_builder: Builder, - ) -> CtxType<'a> { - CtxType { - ctx_ty: self.ctx_ty, - ctx_ptr_ty: self.ctx_ptr_ty, - - ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), - - builder, - intrinsics: self, - info, - cache_builder, - - cached_memories: HashMap::new(), - cached_tables: HashMap::new(), - cached_sigindices: HashMap::new(), - cached_globals: HashMap::new(), - cached_imported_functions: HashMap::new(), - - _phantom: PhantomData, - } - } } #[derive(Clone, Copy)] @@ -435,13 +402,8 @@ struct ImportedFuncCache { } pub struct CtxType<'a> { - ctx_ty: StructType, - ctx_ptr_ty: PointerType, - ctx_ptr_value: PointerValue, - builder: &'a Builder, - intrinsics: &'a Intrinsics, info: &'a ModuleInfo, cache_builder: Builder, @@ -455,17 +417,36 @@ pub struct CtxType<'a> { } impl<'a> CtxType<'a> { + pub fn new( + info: &'a ModuleInfo, + func_value: &'a FunctionValue, + cache_builder: Builder, + ) -> CtxType<'a> { + CtxType { + ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), + + info, + cache_builder, + + cached_memories: HashMap::new(), + cached_tables: HashMap::new(), + cached_sigindices: HashMap::new(), + cached_globals: HashMap::new(), + cached_imported_functions: HashMap::new(), + + _phantom: PhantomData, + } + } + pub fn basic(&self) -> BasicValueEnum { self.ctx_ptr_value.as_basic_value_enum() } - pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache { - let (cached_memories, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache { + let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -526,13 +507,16 @@ impl<'a> CtxType<'a> { }) } - pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { - let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn table( + &mut self, + index: TableIndex, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> (PointerValue, IntValue) { + let (cached_tables, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_tables, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -587,43 +571,39 @@ impl<'a> CtxType<'a> { ) } - pub fn local_func(&mut self, index: LocalFuncIndex, fn_ty: FunctionType) -> PointerValue { - let local_func_array_ptr_ptr = unsafe { - self.builder - .build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") - }; - let local_func_array_ptr = self - .builder + pub fn local_func( + &mut self, + index: LocalFuncIndex, + fn_ty: FunctionType, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> PointerValue { + let local_func_array_ptr_ptr = + unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") }; + let local_func_array_ptr = builder .build_load(local_func_array_ptr_ptr, "local_func_array_ptr") .into_pointer_value(); let local_func_ptr_ptr = unsafe { - self.builder.build_in_bounds_gep( + builder.build_in_bounds_gep( local_func_array_ptr, - &[self - .intrinsics - .i32_ty - .const_int(index.index() as u64, false)], + &[intrinsics.i32_ty.const_int(index.index() as u64, false)], "local_func_ptr_ptr", ) }; - let local_func_ptr = self - .builder + let local_func_ptr = builder .build_load(local_func_ptr_ptr, "local_func_ptr") .into_pointer_value(); - self.builder.build_pointer_cast( + builder.build_pointer_cast( local_func_ptr, fn_ty.ptr_type(AddressSpace::Generic), "local_func_ptr", ) } - pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue { - let (cached_sigindices, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue { + let (cached_sigindices, ctx_ptr_value, cache_builder) = ( &mut self.cached_sigindices, - self.builder, - self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -650,13 +630,11 @@ impl<'a> CtxType<'a> { }) } - pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { - let (cached_globals, builder, ctx_ptr_value, info, intrinsics, cache_builder) = ( + pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache { + let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, - self.builder, self.ctx_ptr_value, self.info, - self.intrinsics, &self.cache_builder, ); @@ -727,12 +705,14 @@ impl<'a> CtxType<'a> { }) } - pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { - let (cached_imported_functions, builder, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn imported_func( + &mut self, + index: ImportedFuncIndex, + intrinsics: &Intrinsics, + ) -> (PointerValue, PointerValue) { + let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( &mut self.cached_imported_functions, - self.builder, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -770,38 +750,4 @@ impl<'a> CtxType<'a> { (imported_func_cache.func_ptr, imported_func_cache.ctx_ptr) } - - pub fn build_trap(&self) { - self.builder.build_call(self.intrinsics.trap, &[], "trap"); - } } - -// pub struct Ctx { -// /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. -// pub(crate) memories: *mut *mut LocalMemory, - -// /// A pointer to an array of locally-defined tables, indexed by `TableIndex`. -// pub(crate) tables: *mut *mut LocalTable, - -// /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. -// pub(crate) globals: *mut *mut LocalGlobal, - -// /// A pointer to an array of imported memories, indexed by `MemoryIndex, -// pub(crate) imported_memories: *mut *mut LocalMemory, - -// /// A pointer to an array of imported tables, indexed by `TableIndex`. -// pub(crate) imported_tables: *mut *mut LocalTable, - -// /// A pointer to an array of imported globals, indexed by `GlobalIndex`. -// pub(crate) imported_globals: *mut *mut LocalGlobal, - -// /// A pointer to an array of imported functions, indexed by `FuncIndex`. -// pub(crate) imported_funcs: *mut ImportedFunc, - -// local_backing: *mut LocalBacking, -// import_backing: *mut ImportBacking, -// module: *const ModuleInner, - -// pub data: *mut c_void, -// pub data_finalizer: Option, -// } diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index ee4e02bbc..6d8816af8 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -1,18 +1,6 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![cfg_attr(nightly, feature(unwind_attributes))] -use inkwell::{ - execution_engine::JitFunction, - targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, - OptimizationLevel, -}; -use wasmer_runtime_core::{ - backend::{Compiler, CompilerConfig, Token}, - cache::{Artifact, Error as CacheError}, - error::CompileError, - module::ModuleInner, -}; -use wasmparser::{self, WasmDecoder}; - mod backend; mod code; mod intrinsics; @@ -21,124 +9,11 @@ mod read_info; mod state; mod trampolines; -pub struct LLVMCompiler { - _private: (), -} +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; -impl LLVMCompiler { - pub fn new() -> Self { - Self { _private: () } - } -} - -impl Compiler for LLVMCompiler { - fn compile( - &self, - wasm: &[u8], - compiler_config: CompilerConfig, - _: Token, - ) -> Result { - validate(wasm)?; - - let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics); - - // Create placeholder values here. - let cache_gen = { - use wasmer_runtime_core::backend::{ - sys::Memory, CacheGen, ProtectedCaller, UserTrapper, - }; - use wasmer_runtime_core::cache::Error as CacheError; - use wasmer_runtime_core::error::RuntimeResult; - use wasmer_runtime_core::module::ModuleInfo; - use wasmer_runtime_core::types::{FuncIndex, Value}; - use wasmer_runtime_core::vm; - struct Placeholder; - impl CacheGen for Placeholder { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { - unimplemented!() - } - } - - Box::new(Placeholder) - }; - - Ok(ModuleInner { - func_resolver: Box::new(backend), - protected_caller: Box::new(protected_caller), - cache_gen, - - info, - }) - } - - unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - unimplemented!("the llvm backend doesn't support caching yet") - } -} - -fn validate(bytes: &[u8]) -> Result<(), CompileError> { - let mut parser = wasmparser::ValidatingParser::new( - bytes, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: false, - enable_reference_types: false, - enable_simd: false, - enable_bulk_memory: false, - }, - mutable_global_imports: false, - }), - ); - - loop { - let state = parser.read(); - match *state { - wasmparser::ParserState::EndWasm => break Ok(()), - wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError { - msg: err.message.to_string(), - })?, - _ => {} - } - } -} - -#[test] -fn test_read_module() { - use std::mem::transmute; - use wabt::wat2wasm; - use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex, vm, vmcalls}; - // let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8]; - let wat = r#" - (module - (type $t0 (func (param i32) (result i32))) - (type $t1 (func (result i32))) - (memory 1) - (global $g0 (mut i32) (i32.const 0)) - (func $foo (type $t0) (param i32) (result i32) - get_local 0 - )) - "#; - let wasm = wat2wasm(wat).unwrap(); - - let (info, code_reader) = read_info::read_module(&wasm, Default::default()).unwrap(); - - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, _caller) = backend::LLVMBackend::new(module, intrinsics); - - let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); - - println!("func_ptr: {:p}", func_ptr.as_ptr()); - - unsafe { - let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr); - let result = func(0 as _, 42); - println!("result: {}", result); - } -} +pub type LLVMCompiler = SimpleStreamingCompilerGen< + code::LLVMModuleCodeGenerator, + code::LLVMFunctionCodeGenerator, + backend::LLVMBackend, + code::CodegenError, +>; diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs index 2267ab129..d16384179 100644 --- a/lib/llvm-backend/src/platform/unix.rs +++ b/lib/llvm-backend/src/platform/unix.rs @@ -1,7 +1,5 @@ use libc::{c_void, siginfo_t}; -use nix::sys::signal::{ - sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, -}; +use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV}; /// `__register_frame` and `__deregister_frame` on macos take a single fde as an /// argument, so we need to parse the fde table here. @@ -36,7 +34,7 @@ pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut } #[cfg(not(target_os = "macos"))] -pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { +pub unsafe fn visit_fde(addr: *mut u8, _size: usize, visitor: extern "C" fn(*mut u8)) { visitor(addr); } @@ -51,24 +49,22 @@ pub unsafe fn install_signal_handler() { SaFlags::SA_ONSTACK | SaFlags::SA_SIGINFO, SigSet::empty(), ); - // sigaction(SIGFPE, &sa).unwrap(); - // sigaction(SIGILL, &sa).unwrap(); sigaction(SIGSEGV, &sa).unwrap(); sigaction(SIGBUS, &sa).unwrap(); } #[cfg_attr(nightly, unwind(allowed))] extern "C" fn signal_trap_handler( - signum: ::nix::libc::c_int, - siginfo: *mut siginfo_t, - ucontext: *mut c_void, + _signum: ::nix::libc::c_int, + _siginfo: *mut siginfo_t, + _ucontext: *mut c_void, ) { unsafe { - /// Apparently, we can unwind from arbitary instructions, as long - /// as we don't need to catch the exception inside the function that - /// was interrupted. - /// - /// This works on macos, not sure about linux. + // Apparently, we can unwind from arbitary instructions, as long + // as we don't need to catch the exception inside the function that + // was interrupted. + // + // This works on macos, not sure about linux. throw_trap(2); } } diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs index 5ebfa5868..572469ce2 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -1,289 +1,5 @@ -use wasmer_runtime_core::{ - backend::{Backend, CompilerConfig}, - module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, - }, - structures::{Map, TypedIndex}, - types::{ - ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, - ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, - TableIndex, Type, Value, - }, - units::Pages, -}; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, - ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, - SectionCode, Type as WpType, -}; - -use hashbrown::HashMap; - -pub fn read_module( - wasm: &[u8], - compiler_config: CompilerConfig, -) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { - let mut info = ModuleInfo { - memories: Map::new(), - globals: Map::new(), - tables: Map::new(), - - imported_functions: Map::new(), - imported_memories: Map::new(), - imported_tables: Map::new(), - imported_globals: Map::new(), - - exports: Default::default(), - - data_initializers: Vec::new(), - elem_initializers: Vec::new(), - - start_func: None, - - func_assoc: Map::new(), - signatures: Map::new(), - backend: Backend::LLVM, - - namespace_table: StringTable::new(), - name_table: StringTable::new(), - - em_symbol_map: compiler_config.symbol_map.clone(), - - custom_sections: HashMap::new(), - }; - - let mut reader = ModuleReader::new(wasm)?; - let mut code_reader = None; - - loop { - if reader.eof() { - return Ok((info, code_reader.unwrap())); - } - - let section = reader.read()?; - - match section.code { - SectionCode::Type => { - let type_reader = section.get_type_section_reader()?; - - for ty in type_reader { - let ty = ty?; - info.signatures.push(func_type_to_func_sig(ty)?); - } - } - SectionCode::Import => { - let import_reader = section.get_import_section_reader()?; - let mut namespace_builder = StringTableBuilder::new(); - let mut name_builder = StringTableBuilder::new(); - - for import in import_reader { - let Import { module, field, ty } = import?; - - let namespace_index = namespace_builder.register(module); - let name_index = name_builder.register(field); - let import_name = ImportName { - namespace_index, - name_index, - }; - - match ty { - ImportSectionEntryType::Function(sigindex) => { - let sigindex = SigIndex::new(sigindex as usize); - info.imported_functions.push(import_name); - info.func_assoc.push(sigindex); - } - ImportSectionEntryType::Table(table_ty) => { - assert_eq!(table_ty.element_type, WpType::AnyFunc); - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.imported_tables.push((import_name, table_desc)); - } - ImportSectionEntryType::Memory(memory_ty) => { - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - info.imported_memories.push((import_name, mem_desc)); - } - ImportSectionEntryType::Global(global_ty) => { - let global_desc = GlobalDescriptor { - mutable: global_ty.mutable, - ty: type_to_type(global_ty.content_type)?, - }; - info.imported_globals.push((import_name, global_desc)); - } - } - } - - info.namespace_table = namespace_builder.finish(); - info.name_table = name_builder.finish(); - } - SectionCode::Function => { - let func_decl_reader = section.get_function_section_reader()?; - - for sigindex in func_decl_reader { - let sigindex = sigindex?; - - let sigindex = SigIndex::new(sigindex as usize); - info.func_assoc.push(sigindex); - } - } - SectionCode::Table => { - let table_decl_reader = section.get_table_section_reader()?; - - for table_ty in table_decl_reader { - let table_ty = table_ty?; - - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.tables.push(table_desc); - } - } - SectionCode::Memory => { - let mem_decl_reader = section.get_memory_section_reader()?; - - for memory_ty in mem_decl_reader { - let memory_ty = memory_ty?; - - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - - info.memories.push(mem_desc); - } - } - SectionCode::Global => { - let global_decl_reader = section.get_global_section_reader()?; - - for global in global_decl_reader { - let global = global?; - - let desc = GlobalDescriptor { - mutable: global.ty.mutable, - ty: type_to_type(global.ty.content_type)?, - }; - - let global_init = GlobalInit { - desc, - init: eval_init_expr(&global.init_expr)?, - }; - - info.globals.push(global_init); - } - } - SectionCode::Export => { - let export_reader = section.get_export_section_reader()?; - - for export in export_reader { - let Export { field, kind, index } = export?; - - let export_index = match kind { - ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), - ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), - ExternalKind::Memory => { - ExportIndex::Memory(MemoryIndex::new(index as usize)) - } - ExternalKind::Global => { - ExportIndex::Global(GlobalIndex::new(index as usize)) - } - }; - - info.exports.insert(field.to_string(), export_index); - } - } - SectionCode::Start => { - let start_index = section.get_start_section_content()?; - - info.start_func = Some(FuncIndex::new(start_index as usize)); - } - SectionCode::Element => { - let element_reader = section.get_element_section_reader()?; - - for element in element_reader { - let Element { kind, items } = element?; - - match kind { - ElementKind::Active { - table_index, - init_expr, - } => { - let table_index = TableIndex::new(table_index as usize); - let base = eval_init_expr(&init_expr)?; - let items_reader = items.get_items_reader()?; - - let elements: Vec<_> = items_reader - .into_iter() - .map(|res| res.map(|index| FuncIndex::new(index as usize))) - .collect::>()?; - - let table_init = TableInitializer { - table_index, - base, - elements, - }; - - info.elem_initializers.push(table_init); - } - ElementKind::Passive(_ty) => { - return Err(BinaryReaderError { - message: "passive tables are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::Code => { - code_reader = Some(section.get_code_section_reader()?); - } - SectionCode::Data => { - let data_reader = section.get_data_section_reader()?; - - for data in data_reader { - let Data { kind, data } = data?; - - match kind { - DataKind::Active { - memory_index, - init_expr, - } => { - let memory_index = MemoryIndex::new(memory_index as usize); - let base = eval_init_expr(&init_expr)?; - - let data_init = DataInitializer { - memory_index, - base, - data: data.to_vec(), - }; - - info.data_initializers.push(data_init); - } - DataKind::Passive => { - return Err(BinaryReaderError { - message: "passive memories are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::DataCount => {} - SectionCode::Custom { .. } => {} - } - } -} +use wasmer_runtime_core::types::Type; +use wasmparser::{BinaryReaderError, Type as WpType}; pub fn type_to_type(ty: WpType) -> Result { Ok(match ty { @@ -305,46 +21,3 @@ pub fn type_to_type(ty: WpType) -> Result { } }) } - -fn func_type_to_func_sig(func_ty: FuncType) -> Result { - assert_eq!(func_ty.form, WpType::Func); - - Ok(FuncSig::new( - func_ty - .params - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - func_ty - .returns - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - )) -} - -fn eval_init_expr(expr: &InitExpr) -> Result { - let mut reader = expr.get_operators_reader(); - let (op, offset) = reader.read_with_offset()?; - Ok(match op { - Operator::GetGlobal { global_index } => { - Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) - } - Operator::I32Const { value } => Initializer::Const(Value::I32(value)), - Operator::I64Const { value } => Initializer::Const(Value::I64(value)), - Operator::F32Const { value } => { - Initializer::Const(Value::F32(f32::from_bits(value.bits()))) - } - Operator::F64Const { value } => { - Initializer::Const(Value::F64(f64::from_bits(value.bits()))) - } - _ => { - return Err(BinaryReaderError { - message: "init expr evaluation failed: unsupported opcode", - offset, - }); - } - }) -} diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs index 4619cb990..23677618b 100644 --- a/lib/llvm-backend/src/trampolines.rs +++ b/lib/llvm-backend/src/trampolines.rs @@ -3,10 +3,9 @@ use inkwell::{ builder::Builder, context::Context, module::{Linkage, Module}, - passes::PassManager, - types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, - values::{BasicValue, FunctionValue, PhiValue, PointerValue}, - AddressSpace, FloatPredicate, IntPredicate, + types::{BasicType, FunctionType}, + values::FunctionValue, + AddressSpace, }; use wasmer_runtime_core::{ module::ModuleInfo, @@ -43,20 +42,12 @@ pub fn generate_trampolines( Some(Linkage::External), ); - generate_trampoline( - trampoline_func, - func_type, - sig, - context, - builder, - intrinsics, - ); + generate_trampoline(trampoline_func, sig, context, builder, intrinsics); } } fn generate_trampoline( trampoline_func: FunctionValue, - sig_type: FunctionType, func_sig: &FuncSig, context: &Context, builder: &Builder, diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml new file mode 100644 index 000000000..57a42577c --- /dev/null +++ b/lib/middleware-common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wasmer-middleware-common" +version = "0.4.1" +repository = "https://github.com/wasmerio/wasmer" +description = "Wasmer runtime common middlewares" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs new file mode 100644 index 000000000..9c47d7d7b --- /dev/null +++ b/lib/middleware-common/src/call_trace.rs @@ -0,0 +1,27 @@ +use wasmer_runtime_core::{ + codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, + module::ModuleInfo, +}; + +pub struct CallTrace; + +impl FunctionMiddleware for CallTrace { + 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(id)) => sink.push(Event::Internal( + InternalEvent::Breakpoint(Box::new(move |_| { + eprintln!("func ({})", id); + })), + )), + _ => {} + } + sink.push(op); + Ok(()) + } +} diff --git a/lib/middleware-common/src/lib.rs b/lib/middleware-common/src/lib.rs new file mode 100644 index 000000000..e09d9ee03 --- /dev/null +++ b/lib/middleware-common/src/lib.rs @@ -0,0 +1,3 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + +pub mod call_trace; diff --git a/lib/runtime-abi/Cargo.toml b/lib/runtime-abi/Cargo.toml index 19384647d..f8b540e4b 100644 --- a/lib/runtime-abi/Cargo.toml +++ b/lib/runtime-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-abi" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -13,7 +13,7 @@ wasmer-runtime-core = { path = "../runtime-core" } hashbrown = "0.1" failure = "0.1" tar = "0.4" -wasmparser = "0.23.0" +wasmparser = "0.29.2" zstd = "0.4" [target.'cfg(unix)'.dependencies.zbox] diff --git a/lib/runtime-abi/src/lib.rs b/lib/runtime-abi/src/lib.rs index 2f0ee8c51..d1c9e2326 100644 --- a/lib/runtime-abi/src/lib.rs +++ b/lib/runtime-abi/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[cfg(not(target_os = "windows"))] #[macro_use] extern crate failure; diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index c0f5cdef2..de93c42a5 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.2.1" +version = "0.4.1" description = "Wasmer C API library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -16,15 +16,15 @@ libc = "0.2" [dependencies.wasmer-runtime] path = "../runtime" -version = "0.2.1" +version = "0.4.1" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.2.1" +version = "0.4.1" [features] debug = ["wasmer-runtime/debug"] llvm = ["wasmer-runtime/llvm"] [build-dependencies] -cbindgen = "0.8" \ No newline at end of file +cbindgen = "0.8" diff --git a/lib/runtime-c-api/src/error.rs b/lib/runtime-c-api/src/error.rs index c5f07a704..1a9322df8 100644 --- a/lib/runtime-c-api/src/error.rs +++ b/lib/runtime-c-api/src/error.rs @@ -61,20 +61,20 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: return -1; } - let last_error = match take_last_error() { - Some(err) => err, + let error_message = match take_last_error() { + Some(err) => err.to_string(), None => return 0, }; - let error_message = last_error.to_string(); + let length = length as usize; - let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize); - - if error_message.len() >= buffer.len() { - // buffer to small for err message + if error_message.len() >= length { + // buffer is too small to hold the error message return -1; } + let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length); + ptr::copy_nonoverlapping( error_message.as_ptr(), buffer.as_mut_ptr(), @@ -85,7 +85,7 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: // accidentally read into garbage. buffer[error_message.len()] = 0; - error_message.len() as c_int + error_message.len() as c_int + 1 } #[derive(Debug)] diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index eda9976d6..054cd14e1 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -80,6 +80,7 @@ //! //! [wasmer_h]: ./wasmer.h //! [wasmer_hh]: ./wasmer.hh +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] extern crate wasmer_runtime; extern crate wasmer_runtime_core; diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c index 20332623e..8a7c2610e 100644 --- a/lib/runtime-c-api/tests/test-instantiate.c +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -46,7 +46,8 @@ int main() int error_len = wasmer_last_error_length(); printf("Error len: `%d`\n", error_len); char *error_str = malloc(error_len); - wasmer_last_error_message(error_str, error_len); + int error_result = wasmer_last_error_message(error_str, error_len); + assert(error_len == error_result); printf("Error str: `%s`\n", error_str); assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]")); free(error_str); diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 438643397..d6b735cff 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,13 +10,14 @@ edition = "2018" [dependencies] nix = "0.12.0" page_size = "0.4.1" -wasmparser = "0.23.0" +wasmparser = "0.29.2" parking_lot = "0.7.1" lazy_static = "1.2.0" indexmap = "1.0.2" errno = "0.2.4" libc = "0.2.49" hex = "0.3.2" +smallvec = "0.6.9" # Dependencies for caching. [dependencies.serde] @@ -45,6 +46,7 @@ field-offset = "0.1.1" [build-dependencies] blake2b_simd = "0.4.1" +rustc_version = "0.2.3" [features] debug = [] diff --git a/lib/runtime-core/README.md b/lib/runtime-core/README.md new file mode 100644 index 000000000..0bbb74379 --- /dev/null +++ b/lib/runtime-core/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer Runtime Core + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate represents the core of the runtime. diff --git a/lib/runtime-core/build.rs b/lib/runtime-core/build.rs index e91238bc6..38071e31c 100644 --- a/lib/runtime-core/build.rs +++ b/lib/runtime-core/build.rs @@ -23,4 +23,9 @@ fn main() { f_out .write_all(hash_string.as_bytes()) .expect("Could not write to file for wasmer hash value"); + + // Enable "nightly" cfg if the current compiler is nightly. + if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly { + println!("cargo:rustc-cfg=nightly"); + } } diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 94c5c87e3..d465cda58 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -1,9 +1,8 @@ use crate::{ - backing::ImportBacking, error::CompileResult, - error::RuntimeResult, module::ModuleInner, - types::{FuncIndex, LocalFuncIndex, Value}, + typed_func::Wasm, + types::{LocalFuncIndex, SigIndex}, vm, }; @@ -24,7 +23,7 @@ pub use crate::sig_registry::SigRegistry; #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Backend { Cranelift, - Dynasm, + Singlepass, LLVM, } @@ -66,55 +65,23 @@ pub trait Compiler { unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result; } -/// The functionality exposed by this trait is expected to be used -/// for calling functions exported by a webassembly module from -/// host code only. -pub trait ProtectedCaller: Send + Sync { - /// This calls the exported function designated by `local_func_index`. - /// Important to note, this supports calling imported functions that are - /// then exported. - /// - /// It's invalid to attempt to call a local function that isn't exported and - /// the implementation is expected to check for that. The implementation - /// is also expected to check for correct parameter types and correct - /// parameter number. - /// - /// The `returns` parameter is filled with dummy values when passed in and upon function - /// return, will be filled with the return values of the wasm function, as long as the - /// call completed successfully. - /// - /// The existance of the Token parameter ensures that this can only be called from - /// within the runtime crate. - fn call( +pub trait RunnableModule: Send + Sync { + /// This returns a pointer to the function designated by the `local_func_index` + /// parameter. + fn get_func( &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult>; + info: &ModuleInfo, + local_func_index: LocalFuncIndex, + ) -> Option>; - fn get_early_trapper(&self) -> Box; -} + /// A wasm trampoline contains the necesarry data to dynamically call an exported wasm function. + /// Given a particular signature index, we are returned a trampoline that is matched with that + /// signature and an invoke function that can call the trampoline. + fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; -pub trait UserTrapper { unsafe fn do_early_trap(&self, data: Box) -> !; } -pub trait FuncResolver: Send + Sync { - /// This returns a pointer to the function designated by the `local_func_index` - /// parameter. - fn get( - &self, - module: &ModuleInner, - local_func_index: LocalFuncIndex, - ) -> Option>; -} - pub trait CacheGen: Send + Sync { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError>; + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError>; } diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 48f2d2256..b3d529460 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -17,30 +17,31 @@ use crate::{ }; use std::slice; +/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance. +/// That is, local memories, tables, and globals (as well as some additional +/// data for the virtual call machinery). #[derive(Debug)] pub struct LocalBacking { + /// This is a map from the local resource index to actual memory, + /// table, and globals. pub(crate) memories: BoxedMap, pub(crate) tables: BoxedMap, pub(crate) globals: BoxedMap, + /// This own the memory containing the pointers to the local memories. + /// While simplifying implementation, this adds indirection and may hurt + /// performance, especially on cache-starved systems. pub(crate) vm_memories: BoxedMap, pub(crate) vm_tables: BoxedMap, pub(crate) vm_globals: BoxedMap, + /// The dynamic sigindices are used to efficiently support caching and + /// the `call_indirect` wasm instruction. This field (and local_functions + /// as well) are subject to change. pub(crate) dynamic_sigindices: BoxedMap, pub(crate) local_functions: BoxedMap, } -// impl LocalBacking { -// pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut Memory { -// &mut self.memories[local_memory_index] -// } - -// pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking { -// &mut self.tables[local_table_index] -// } -// } - impl LocalBacking { pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self { let mut memories = Self::generate_memories(module); @@ -72,8 +73,8 @@ impl LocalBacking { (0..module.info.func_assoc.len() - module.info.imported_functions.len()) .map(|index| { module - .func_resolver - .get(module, LocalFuncIndex::new(index)) + .runnable_module + .get_func(&module.info, LocalFuncIndex::new(index)) .unwrap() .as_ptr() as *const _ }) @@ -102,6 +103,9 @@ impl LocalBacking { memories.into_boxed_map() } + /// Initialize each locally-defined memory in the Module. + /// + /// This involves copying in the data initializers. fn finalize_memories( module: &ModuleInner, imports: &ImportBacking, @@ -174,6 +178,9 @@ impl LocalBacking { tables.into_boxed_map() } + /// This initializes all of the locally-defined tables in the Module, e.g. + /// putting all the table elements (function pointers) + /// in the right places. #[allow(clippy::cast_ptr_alignment)] fn finalize_tables( module: &ModuleInner, @@ -216,8 +223,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, @@ -255,8 +262,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs new file mode 100644 index 000000000..b61ce58bb --- /dev/null +++ b/lib/runtime-core/src/codegen.rs @@ -0,0 +1,264 @@ +use crate::{ + backend::RunnableModule, + backend::{Backend, CacheGen, Compiler, CompilerConfig, Token}, + cache::{Artifact, Error as CacheError}, + error::{CompileError, CompileResult}, + module::{ModuleInfo, ModuleInner}, + structures::Map, + types::{FuncIndex, FuncSig, SigIndex}, +}; +use smallvec::SmallVec; +use std::fmt; +use std::fmt::Debug; +use std::marker::PhantomData; +use wasmparser::{Operator, Type as WpType}; + +#[derive(Debug)] +pub enum Event<'a, 'b> { + Internal(InternalEvent), + Wasm(&'b Operator<'a>), +} + +pub enum InternalEvent { + FunctionBegin(u32), + FunctionEnd, + Breakpoint(Box), + SetInternal(u32), + GetInternal(u32), +} + +impl fmt::Debug for InternalEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"), + InternalEvent::FunctionEnd => write!(f, "FunctionEnd"), + InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"), + InternalEvent::SetInternal(_) => write!(f, "SetInternal"), + InternalEvent::GetInternal(_) => write!(f, "GetInternal"), + } + } +} + +pub struct BkptInfo {} + +pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { + fn new() -> Self; + fn backend_id() -> Backend; + fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>; + + /// Creates a new function and returns the function-scope code generator for it. + fn next_function(&mut self) -> Result<&mut FCG, E>; + fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box), E>; + fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; + + /// Sets function signatures. + fn feed_function_signatures(&mut self, assoc: Map) -> Result<(), E>; + + /// Adds an import function. + fn feed_import_function(&mut self) -> Result<(), E>; + + unsafe fn from_cache(cache: Artifact, _: Token) -> Result; +} + +pub struct StreamingCompiler< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, +> { + middleware_chain_generator: CGEN, + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, +} + +pub struct SimpleStreamingCompilerGen< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, +> { + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + > SimpleStreamingCompilerGen +{ + pub fn new() -> StreamingCompiler MiddlewareChain> { + StreamingCompiler::new(|| MiddlewareChain::new()) + } +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, + > StreamingCompiler +{ + pub fn new(chain_gen: CGEN) -> Self { + Self { + middleware_chain_generator: chain_gen, + _phantom_mcg: PhantomData, + _phantom_fcg: PhantomData, + _phantom_rm: PhantomData, + _phantom_e: PhantomData, + } + } +} + +impl< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule + 'static, + E: Debug, + CGEN: Fn() -> MiddlewareChain, + > Compiler for StreamingCompiler +{ + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { + let mut mcg = MCG::new(); + let mut chain = (self.middleware_chain_generator)(); + let info = crate::parse::read_module( + wasm, + MCG::backend_id(), + &mut mcg, + &mut chain, + &compiler_config, + )?; + let (exec_context, cache_gen) = + mcg.finalize(&info) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; + Ok(ModuleInner { + cache_gen, + runnable_module: Box::new(exec_context), + info, + }) + } + + unsafe fn from_cache( + &self, + artifact: Artifact, + token: Token, + ) -> Result { + MCG::from_cache(artifact, token) + } +} + +pub struct EventSink<'a, 'b> { + buffer: SmallVec<[Event<'a, 'b>; 2]>, +} + +impl<'a, 'b> EventSink<'a, 'b> { + pub fn push(&mut self, ev: Event<'a, 'b>) { + self.buffer.push(ev); + } +} + +pub struct MiddlewareChain { + chain: Vec>, +} + +impl MiddlewareChain { + pub fn new() -> MiddlewareChain { + MiddlewareChain { chain: vec![] } + } + + pub fn push(&mut self, m: M) { + self.chain.push(Box::new(m)); + } + + pub(crate) fn run>( + &mut self, + fcg: Option<&mut FCG>, + ev: Event, + module_info: &ModuleInfo, + ) -> Result<(), String> { + let mut sink = EventSink { + buffer: SmallVec::new(), + }; + sink.push(ev); + for m in &mut self.chain { + let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect(); + for ev in prev { + m.feed_event(ev, module_info, &mut sink)?; + } + } + if let Some(fcg) = fcg { + for ev in sink.buffer { + fcg.feed_event(ev, module_info) + .map_err(|x| format!("{:?}", x))?; + } + } + + Ok(()) + } +} + +pub trait FunctionMiddleware { + type Error: Debug; + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), Self::Error>; +} + +pub(crate) trait GenericFunctionMiddleware { + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), String>; +} + +impl> GenericFunctionMiddleware for T { + fn feed_event<'a, 'b: 'a>( + &mut self, + op: Event<'a, 'b>, + module_info: &ModuleInfo, + sink: &mut EventSink<'a, 'b>, + ) -> Result<(), String> { + ::feed_event(self, op, module_info, sink) + .map_err(|x| format!("{:?}", x)) + } +} + +/// The function-scope code generator trait. +pub trait FunctionCodeGenerator { + /// Sets the return type. + fn feed_return(&mut self, ty: WpType) -> Result<(), E>; + + /// Adds a parameter to the function. + fn feed_param(&mut self, ty: WpType) -> Result<(), E>; + + /// Adds `n` locals to the function. + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; + + /// Called before the first call to `feed_opcode`. + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; + + /// Called for each operator. + fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; + + /// Finalizes the function. + fn finalize(&mut self) -> Result<(), E>; +} diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 46fae102e..c93115d14 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,4 +1,4 @@ -use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value}; +use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; @@ -11,7 +11,7 @@ pub type ResolveResult = std::result::Result; pub type ParseResult = std::result::Result; /// This is returned when the chosen compiler is unable to -/// successfully compile the provided webassembly module into +/// successfully compile the provided WebAssembly module into /// a `Module`. /// /// Comparing two `CompileError`s always evaluates to false. @@ -114,16 +114,14 @@ impl std::fmt::Display for LinkError { impl std::error::Error for LinkError {} /// This is the error type returned when calling -/// a webassembly function. +/// a WebAssembly function. /// /// The main way to do this is `Instance.call`. /// /// Comparing two `RuntimeError`s always evaluates to false. -#[derive(Debug)] pub enum RuntimeError { Trap { msg: Box }, - Exception { data: Box<[Value]> }, - Panic { data: Box }, + Error { data: Box }, } impl PartialEq for RuntimeError { @@ -138,14 +136,25 @@ impl std::fmt::Display for RuntimeError { RuntimeError::Trap { ref msg } => { write!(f, "WebAssembly trap occured during runtime: {}", msg) } - RuntimeError::Exception { ref data } => { - write!(f, "Uncaught WebAssembly exception: {:?}", data) + RuntimeError::Error { data } => { + if let Some(s) = data.downcast_ref::() { + write!(f, "\"{}\"", s) + } else if let Some(s) = data.downcast_ref::<&str>() { + write!(f, "\"{}\"", s) + } else { + write!(f, "unknown error") + } } - RuntimeError::Panic { data: _ } => write!(f, "User-defined \"panic\""), } } } +impl std::fmt::Debug for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + impl std::error::Error for RuntimeError {} /// This error type is produced by resolving a wasm function @@ -197,7 +206,6 @@ impl std::error::Error for ResolveError {} /// be the `CallError::Runtime(RuntimeError)` variant. /// /// Comparing two `CallError`s always evaluates to false. -#[derive(Debug)] pub enum CallError { Resolve(ResolveError), Runtime(RuntimeError), @@ -218,6 +226,15 @@ impl std::fmt::Display for CallError { } } +impl std::fmt::Debug for CallError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + CallError::Resolve(resolve_err) => write!(f, "ResolveError: {:?}", resolve_err), + CallError::Runtime(runtime_err) => write!(f, "RuntimeError: {:?}", runtime_err), + } + } +} + impl std::error::Error for CallError {} /// This error type is produced when creating something, @@ -253,7 +270,7 @@ impl std::error::Error for CreationError {} /// The amalgamation of all errors that can occur /// during the compilation, instantiation, or execution -/// of a webassembly module. +/// of a WebAssembly module. /// /// Comparing two `Error`s always evaluates to false. #[derive(Debug)] diff --git a/lib/runtime-core/src/export.rs b/lib/runtime-core/src/export.rs index 0a2c09136..81e0eae92 100644 --- a/lib/runtime-core/src/export.rs +++ b/lib/runtime-core/src/export.rs @@ -40,13 +40,13 @@ impl FuncPointer { } pub struct ExportIter<'a> { - inner: &'a mut InstanceInner, + inner: &'a InstanceInner, iter: hash_map::Iter<'a, String, ExportIndex>, module: &'a ModuleInner, } impl<'a> ExportIter<'a> { - pub(crate) fn new(module: &'a ModuleInner, inner: &'a mut InstanceInner) -> Self { + pub(crate) fn new(module: &'a ModuleInner, inner: &'a InstanceInner) -> Self { Self { inner, iter: module.info.exports.iter(), diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 4c8401f06..14465deed 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -3,6 +3,7 @@ use hashbrown::{hash_map::Entry, HashMap}; use std::collections::VecDeque; use std::{ cell::{Ref, RefCell}, + ffi::c_void, rc::Rc, }; @@ -45,6 +46,7 @@ impl IsExport for Export { /// ``` pub struct ImportObject { map: Rc>>>, + state_creator: Option (*mut c_void, fn(*mut c_void))>>, } impl ImportObject { @@ -52,9 +54,24 @@ impl ImportObject { pub fn new() -> Self { Self { map: Rc::new(RefCell::new(HashMap::new())), + state_creator: None, } } + pub fn new_with_data(state_creator: F) -> Self + where + F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static, + { + Self { + map: Rc::new(RefCell::new(HashMap::new())), + state_creator: Some(Rc::new(state_creator)), + } + } + + pub(crate) fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> { + self.state_creator.as_ref().map(|state_gen| state_gen()) + } + /// Register anything that implements `LikeNamespace` as a namespace. /// /// # Usage: @@ -98,6 +115,7 @@ impl ImportObject { pub fn clone_ref(&self) -> Self { Self { map: Rc::clone(&self.map), + state_creator: self.state_creator.clone(), } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index ba1dcba37..d5f84acc2 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,19 +1,20 @@ use crate::{ - backend::Token, + backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallError, CallResult, ResolveError, ResolveResult, Result}, + error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError}, export::{Context, Export, ExportIter, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, memory::Memory, - module::{ExportIndex, Module, ModuleInner}, + module::{ExportIndex, Module, ModuleInfo, ModuleInner}, sig_registry::SigRegistry, table::Table, - typed_func::{Func, Safe, WasmTypeList}, - types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, + typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, + types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, vm, }; -use std::{mem, sync::Arc}; +use smallvec::{smallvec, SmallVec}; +use std::{mem, ptr::NonNull, sync::Arc}; pub(crate) struct InstanceInner { #[allow(dead_code)] @@ -63,7 +64,16 @@ impl Instance { // Initialize the vm::Ctx in-place after the backing // has been boxed. unsafe { - *inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module) + *inner.vmctx = match imports.call_state_creator() { + Some((data, dtor)) => vm::Ctx::new_with_data( + &mut inner.backing, + &mut inner.import_backing, + &module, + data, + dtor, + ), + None => vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module), + }; }; let instance = Instance { @@ -73,7 +83,45 @@ impl Instance { }; if let Some(start_index) = instance.module.info.start_func { - instance.call_with_index(start_index, &[])?; + // We know that the start function takes no arguments and returns no values. + // Therefore, we can call it without doing any signature checking, etc. + + let func_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(local_func_index) => instance + .module + .runnable_module + .get_func(&instance.module.info, local_func_index) + .unwrap(), + LocalOrImport::Import(import_func_index) => NonNull::new( + instance.inner.import_backing.vm_functions[import_func_index].func as *mut _, + ) + .unwrap(), + }; + + 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 + } + }; + + let sig_index = *instance + .module + .info + .func_assoc + .get(start_index) + .expect("broken invariant, incorrect func index"); + + let wasm_trampoline = instance + .module + .runnable_module + .get_trampoline(&instance.module.info, sig_index) + .expect("wasm trampoline"); + + let start_func: Func<(), (), Wasm> = + unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) }; + + start_func.call()?; } Ok(instance) @@ -98,7 +146,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn func(&self, name: &str) -> ResolveResult> + pub fn func(&self, name: &str) -> ResolveResult> where Args: WasmTypeList, Rets: WasmTypeList, @@ -136,20 +184,26 @@ impl Instance { } }; + let func_wasm_inner = self + .module + .runnable_module + .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 - .func_resolver - .get(&self.module, local_func_index) - .unwrap() - .as_ptr(), - LocalOrImport::Import(import_func_index) => { - self.inner.import_backing.vm_functions[import_func_index].func - } + .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 typed_func: Func = - unsafe { Func::new_from_ptr(func_ptr as _, ctx) }; + let typed_func: Func = + unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, ctx) }; Ok(typed_func) } else { @@ -208,7 +262,7 @@ impl Instance { } } - /// Call an exported webassembly function given the export name. + /// Call an exported WebAssembly function given the export name. /// Pass arguments by wrapping each one in the [`Value`] enum. /// The returned values are also each wrapped in a [`Value`]. /// @@ -216,7 +270,7 @@ impl Instance { /// /// # Note: /// This returns `CallResult>` in order to support - /// the future multi-value returns webassembly feature. + /// the future multi-value returns WebAssembly feature. /// /// # Usage: /// ``` @@ -230,7 +284,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn call(&self, name: &str, args: &[Value]) -> CallResult> { + pub fn call(&self, name: &str, params: &[Value]) -> CallResult> { let export_index = self.module .info @@ -249,7 +303,19 @@ impl Instance { .into()); }; - self.call_with_index(func_index, args) + let mut results = Vec::new(); + + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.inner.import_backing, + self.inner.vmctx, + func_index, + params, + &mut results, + )?; + + Ok(results) } /// Returns an immutable reference to the @@ -270,8 +336,8 @@ impl Instance { /// Returns an iterator over all of the items /// exported from this instance. - pub fn exports(&mut self) -> ExportIter { - ExportIter::new(&self.module, &mut self.inner) + pub fn exports(&self) -> ExportIter { + ExportIter::new(&self.module, &self.inner) } /// The module used to instantiate this Instance. @@ -280,45 +346,6 @@ impl Instance { } } -impl Instance { - fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult> { - let sig_index = *self - .module - .info - .func_assoc - .get(func_index) - .expect("broken invariant, incorrect func index"); - let signature = &self.module.info.signatures[sig_index]; - - if !signature.check_param_value_types(args) { - Err(ResolveError::Signature { - expected: signature.clone(), - found: args.iter().map(|val| val.ty()).collect(), - })? - } - - let vmctx = 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 - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, - func_index, - args, - &self.inner.import_backing, - vmctx, - token, - )?; - - Ok(returns) - } -} - impl InstanceInner { pub(crate) fn get_export_from_index( &self, @@ -367,8 +394,8 @@ impl InstanceInner { let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(&module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .expect("broken invariant, func resolver not synced with module.exports") .cast() .as_ptr() as *const _, @@ -437,6 +464,134 @@ impl LikeNamespace for Instance { } } +#[must_use] +fn call_func_with_index( + info: &ModuleInfo, + runnable: &dyn RunnableModule, + import_backing: &ImportBacking, + local_ctx: *mut vm::Ctx, + func_index: FuncIndex, + args: &[Value], + rets: &mut Vec, +) -> CallResult<()> { + rets.clear(); + + let sig_index = *info + .func_assoc + .get(func_index) + .expect("broken invariant, incorrect func index"); + + let signature = &info.signatures[sig_index]; + let num_results = signature.returns().len(); + rets.reserve(num_results); + + if !signature.check_param_value_types(args) { + Err(ResolveError::Signature { + expected: signature.clone(), + found: args.iter().map(|val| val.ty()).collect(), + })? + } + + let func_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + runnable.get_func(info, local_func_index).unwrap() + } + LocalOrImport::Import(import_func_index) => { + NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap() + } + }; + + 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 + } + }; + + let raw_args: SmallVec<[u64; 8]> = args + .iter() + .map(|v| match v { + Value::I32(i) => *i as u64, + Value::I64(i) => *i as u64, + Value::F32(f) => f.to_bits() as u64, + Value::F64(f) => f.to_bits(), + }) + .collect(); + + let Wasm { + trampoline, + invoke, + invoke_env, + } = runnable + .get_trampoline(info, sig_index) + .expect("wasm trampoline"); + + let run_wasm = |result_space: *mut u64| unsafe { + let mut trap_info = WasmTrapInfo::Unknown; + let mut user_error = None; + + let success = invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut trap_info, + &mut user_error, + invoke_env, + ); + + if success { + Ok(()) + } else { + if let Some(data) = user_error { + Err(RuntimeError::Error { data }) + } else { + Err(RuntimeError::Trap { + msg: trap_info.to_string().into(), + }) + } + } + }; + + let raw_to_value = |raw, ty| match ty { + Type::I32 => Value::I32(raw as i32), + Type::I64 => Value::I64(raw as i64), + Type::F32 => Value::F32(f32::from_bits(raw as u32)), + Type::F64 => Value::F64(f64::from_bits(raw)), + }; + + match signature.returns() { + &[] => { + run_wasm(0 as *mut u64)?; + Ok(()) + } + &[ty] => { + let mut result = 0u64; + + run_wasm(&mut result)?; + + rets.push(raw_to_value(result, ty)); + + Ok(()) + } + result_tys @ _ => { + let mut results: SmallVec<[u64; 8]> = smallvec![0; num_results]; + + run_wasm(results.as_mut_ptr())?; + + rets.extend( + results + .iter() + .zip(result_tys.iter()) + .map(|(&raw, &ty)| raw_to_value(raw, ty)), + ); + + Ok(()) + } + } +} + /// A representation of an exported WebAssembly function. pub struct DynFunc<'a> { pub(crate) signature: Arc, @@ -446,7 +601,7 @@ pub struct DynFunc<'a> { } impl<'a> DynFunc<'a> { - /// Call an exported webassembly function safely. + /// Call an exported WebAssembly function safely. /// /// Pass arguments by wrapping each one in the [`Value`] enum. /// The returned values are also each wrapped in a [`Value`]. @@ -455,7 +610,7 @@ impl<'a> DynFunc<'a> { /// /// # Note: /// This returns `CallResult>` in order to support - /// the future multi-value returns webassembly feature. + /// the future multi-value returns WebAssembly feature. /// /// # Usage: /// ``` @@ -469,32 +624,19 @@ impl<'a> DynFunc<'a> { /// # } /// ``` pub fn call(&self, params: &[Value]) -> CallResult> { - if !self.signature.check_param_value_types(params) { - Err(ResolveError::Signature { - expected: (*self.signature).clone(), - found: params.iter().map(|val| val.ty()).collect(), - })? - } + let mut results = Vec::new(); - let vmctx = match self.func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(_) => self.instance_inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.instance_inner.import_backing, + self.instance_inner.vmctx, self.func_index, params, - &self.instance_inner.import_backing, - vmctx, - token, + &mut results, )?; - Ok(returns) + Ok(results) } pub fn signature(&self) -> &FuncSig { @@ -505,8 +647,8 @@ impl<'a> DynFunc<'a> { match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module - .func_resolver - .get(self.module, local_func_index) + .runnable_module + .get_func(&self.module.info, local_func_index) .unwrap() .as_ptr(), LocalOrImport::Import(import_func_index) => { @@ -515,10 +657,3 @@ impl<'a> DynFunc<'a> { } } } - -#[doc(hidden)] -impl Instance { - pub fn memory_offset_addr(&self, _: u32, _: usize) -> *const u8 { - unimplemented!() - } -} diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 36bfcc221..da9144a2b 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,3 +1,6 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] +#![cfg_attr(nightly, feature(unwind_attributes))] + #[cfg(test)] #[macro_use] extern crate field_offset; @@ -12,6 +15,7 @@ pub mod backend; mod backing; pub mod cache; +pub mod codegen; pub mod error; pub mod export; pub mod global; @@ -19,11 +23,12 @@ pub mod import; pub mod instance; pub mod memory; pub mod module; +pub mod parse; mod sig_registry; pub mod structures; mod sys; pub mod table; -mod typed_func; +pub mod typed_func; pub mod types; pub mod units; pub mod vm; @@ -36,7 +41,7 @@ pub use self::error::Result; #[doc(inline)] pub use self::import::IsExport; #[doc(inline)] -pub use self::instance::Instance; +pub use self::instance::{DynFunc, Instance}; #[doc(inline)] pub use self::module::Module; #[doc(inline)] @@ -93,13 +98,18 @@ pub fn compile_with_config( /// WebAssembly specification. Returns `true` if validation /// succeeded, `false` if validation failed. pub fn validate(wasm: &[u8]) -> bool { + validate_and_report_errors(wasm).is_ok() +} + +/// The same as `validate` but with an Error message on failure +pub fn validate_and_report_errors(wasm: &[u8]) -> ::std::result::Result<(), String> { use wasmparser::WasmDecoder; let mut parser = wasmparser::ValidatingParser::new(wasm, None); loop { let state = parser.read(); match *state { - wasmparser::ParserState::EndWasm => break true, - wasmparser::ParserState::Error(_) => break false, + wasmparser::ParserState::EndWasm => break Ok(()), + wasmparser::ParserState::Error(e) => break Err(format!("{}", e)), _ => {} } } diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index e23ce1185..edda29366 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -1,8 +1,14 @@ #[macro_export] #[cfg(feature = "debug")] macro_rules! debug { - ($fmt:expr) => (println!(concat!("wasmer-runtime(:{})::", $fmt), line!())); - ($fmt:expr, $($arg:tt)*) => (println!(concat!("wasmer-runtime(:{})::", $fmt, "\n"), line!(), $($arg)*)); + ($fmt:expr) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt), { + let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time"); + format!("{}.{:03}", time.as_secs(), time.subsec_millis()) + }, line!())); + ($fmt:expr, $($arg:tt)*) => (println!(concat!("[{}] wasmer-runtime(:{}) ", $fmt, "\n"), { + let time = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).expect("Can't get time"); + format!("{}.{:03}", time.as_secs(), time.subsec_millis()) + }, line!(), $($arg)*)); } #[macro_export] @@ -38,6 +44,13 @@ macro_rules! func { /// }, /// }; /// +/// let imports_with_state = imports! { +/// || (0 as _, |_a| {}), +/// "env" => { +/// "foo" => func!(foo), +/// }, +/// }; +/// /// fn foo(_: &mut Ctx, n: i32) -> i32 { /// n /// } @@ -57,6 +70,21 @@ macro_rules! imports { import_object.register($ns_name, ns); })* + import_object + }}; + ($state_gen:expr, $( $ns_name:expr => $ns:tt, )* ) => {{ + use $crate::{ + import::{ImportObject, Namespace}, + }; + + let mut import_object = ImportObject::new_with_data($state_gen); + + $({ + let ns = $crate::__imports_internal!($ns); + + import_object.register($ns_name, ns); + })* + import_object }}; } diff --git a/lib/runtime-core/src/memory/atomic.rs b/lib/runtime-core/src/memory/atomic.rs index f4ed3d3d0..f14415def 100644 --- a/lib/runtime-core/src/memory/atomic.rs +++ b/lib/runtime-core/src/memory/atomic.rs @@ -52,6 +52,7 @@ macro_rules! intcast { } intcast! { u8 i8 u16 i16 u32 i32 u64 i64 } +#[repr(transparent)] pub struct Atomic { v: UnsafeCell>, } diff --git a/lib/runtime-core/src/memory/static_/unshared.rs b/lib/runtime-core/src/memory/static_/unshared.rs index b9e66de0a..602a02bac 100644 --- a/lib/runtime-core/src/memory/static_/unshared.rs +++ b/lib/runtime-core/src/memory/static_/unshared.rs @@ -11,7 +11,7 @@ use crate::{ /// This is an internal-only api. /// /// A static memory allocates 6GB of *virtual* memory when created -/// in order to allow the webassembly module to contain no bounds-checks. +/// in order to allow the WebAssembly module to contain no bounds-checks. /// /// Additionally, static memories stay at a single virtual address, so there is no need /// to reload its address on each use. diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index b0c33406e..1ffbc71fc 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,10 +1,9 @@ use crate::{ - backend::{Backend, FuncResolver, ProtectedCaller}, + backend::{Backend, RunnableModule}, cache::{Artifact, Error as CacheError}, error, import::ImportObject, structures::{Map, TypedIndex}, - typed_func::EARLY_TRAPPER, types::{ FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, @@ -22,9 +21,7 @@ use std::sync::Arc; /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { - pub func_resolver: Box, - pub protected_caller: Box, - + pub runnable_module: Box, pub cache_gen: Box, pub info: ModuleInfo, @@ -73,7 +70,7 @@ impl ModuleInfo { let len = reader.bytes_remaining(); let bytes = reader.read_bytes(len)?; let data = bytes.to_vec(); - let name = String::from_utf8_lossy(name).to_string(); + let name = name.to_string(); self.custom_sections.insert(name, data); } } @@ -94,10 +91,6 @@ pub struct Module { impl Module { pub(crate) fn new(inner: Arc) -> Self { - unsafe { - EARLY_TRAPPER - .with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper())); - } Module { inner } } @@ -128,8 +121,12 @@ impl Module { } pub fn cache(&self) -> Result { - let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?; - Ok(Artifact::from_parts(info, backend_metadata, code)) + let (backend_metadata, code) = self.inner.cache_gen.generate_cache()?; + Ok(Artifact::from_parts( + Box::new(self.inner.info.clone()), + backend_metadata, + code, + )) } pub fn info(&self) -> &ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs new file mode 100644 index 000000000..e7e80fc29 --- /dev/null +++ b/lib/runtime-core/src/parse.rs @@ -0,0 +1,426 @@ +use crate::codegen::*; +use crate::{ + backend::{Backend, CompilerConfig, RunnableModule}, + error::CompileError, + module::{ + DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, + TableInitializer, + }, + structures::{Map, TypedIndex}, + types::{ + ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, + ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, + TableIndex, Type, Value, + }, + units::Pages, +}; +use hashbrown::HashMap; +use std::fmt::Debug; +use wasmparser::{ + BinaryReaderError, ExternalKind, FuncType, ImportSectionEntryType, Operator, Type as WpType, + WasmDecoder, +}; + +#[derive(Debug)] +pub enum LoadError { + Parse(BinaryReaderError), + Codegen(String), +} + +impl From for CompileError { + fn from(other: LoadError) -> CompileError { + CompileError::InternalError { + msg: format!("{:?}", other), + } + } +} + +impl From for LoadError { + fn from(other: BinaryReaderError) -> LoadError { + LoadError::Parse(other) + } +} + +pub fn read_module< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule, + E: Debug, +>( + wasm: &[u8], + backend: Backend, + mcg: &mut MCG, + middlewares: &mut MiddlewareChain, + compiler_config: &CompilerConfig, +) -> Result { + let mut info = ModuleInfo { + memories: Map::new(), + globals: Map::new(), + tables: Map::new(), + + imported_functions: Map::new(), + imported_memories: Map::new(), + imported_tables: Map::new(), + imported_globals: Map::new(), + + exports: Default::default(), + + data_initializers: Vec::new(), + elem_initializers: Vec::new(), + + start_func: None, + + func_assoc: Map::new(), + signatures: Map::new(), + backend: backend, + + namespace_table: StringTable::new(), + name_table: StringTable::new(), + + em_symbol_map: compiler_config.symbol_map.clone(), + + custom_sections: HashMap::new(), + }; + + let mut parser = wasmparser::ValidatingParser::new( + wasm, + Some(wasmparser::ValidatingParserConfig { + operator_config: wasmparser::OperatorValidatorConfig { + enable_threads: false, + enable_reference_types: false, + enable_simd: false, + enable_bulk_memory: false, + }, + mutable_global_imports: false, + }), + ); + + let mut namespace_builder = Some(StringTableBuilder::new()); + let mut name_builder = Some(StringTableBuilder::new()); + let mut func_count: usize = ::std::usize::MAX; + + loop { + use wasmparser::ParserState; + let state = parser.read(); + match *state { + ParserState::EndWasm => break Ok(info), + ParserState::Error(err) => Err(LoadError::Parse(err))?, + ParserState::TypeSectionEntry(ref ty) => { + info.signatures.push(func_type_to_func_sig(ty)?); + } + ParserState::ImportSectionEntry { module, field, ty } => { + let namespace_index = namespace_builder.as_mut().unwrap().register(module); + let name_index = name_builder.as_mut().unwrap().register(field); + let import_name = ImportName { + namespace_index, + name_index, + }; + + match ty { + ImportSectionEntryType::Function(sigindex) => { + let sigindex = SigIndex::new(sigindex as usize); + info.imported_functions.push(import_name); + info.func_assoc.push(sigindex); + mcg.feed_import_function() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + ImportSectionEntryType::Table(table_ty) => { + assert_eq!(table_ty.element_type, WpType::AnyFunc); + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.imported_tables.push((import_name, table_desc)); + } + ImportSectionEntryType::Memory(memory_ty) => { + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + info.imported_memories.push((import_name, mem_desc)); + } + ImportSectionEntryType::Global(global_ty) => { + let global_desc = GlobalDescriptor { + mutable: global_ty.mutable, + ty: wp_type_to_type(global_ty.content_type)?, + }; + info.imported_globals.push((import_name, global_desc)); + } + } + } + ParserState::FunctionSectionEntry(sigindex) => { + let sigindex = SigIndex::new(sigindex as usize); + info.func_assoc.push(sigindex); + } + ParserState::TableSectionEntry(table_ty) => { + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.tables.push(table_desc); + } + ParserState::MemorySectionEntry(memory_ty) => { + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + + info.memories.push(mem_desc); + } + ParserState::ExportSectionEntry { field, kind, index } => { + let export_index = match kind { + ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), + ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), + ExternalKind::Memory => ExportIndex::Memory(MemoryIndex::new(index as usize)), + ExternalKind::Global => ExportIndex::Global(GlobalIndex::new(index as usize)), + }; + + info.exports.insert(field.to_string(), export_index); + } + ParserState::StartSectionEntry(start_index) => { + info.start_func = Some(FuncIndex::new(start_index as usize)); + } + ParserState::BeginFunctionBody { .. } => { + let id = func_count.wrapping_add(1); + func_count = id; + if func_count == 0 { + info.namespace_table = namespace_builder.take().unwrap().finish(); + info.name_table = name_builder.take().unwrap().finish(); + mcg.feed_signatures(info.signatures.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.feed_function_signatures(info.func_assoc.clone()) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + mcg.check_precondition(&info) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + + let fcg = mcg + .next_function() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionBegin(id as u32)), + &info, + ) + .map_err(|x| LoadError::Codegen(x))?; + + let sig = info + .signatures + .get( + *info + .func_assoc + .get(FuncIndex::new(id as usize + info.imported_functions.len())) + .unwrap(), + ) + .unwrap(); + for ret in sig.returns() { + fcg.feed_return(type_to_wp_type(*ret)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + for param in sig.params() { + fcg.feed_param(type_to_wp_type(*param)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + + let mut body_begun = false; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::FunctionBodyLocals { ref locals } => { + for &(count, ty) in locals.iter() { + fcg.feed_local(ty, count as usize) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + ParserState::CodeOperator(ref op) => { + if !body_begun { + body_begun = true; + fcg.begin_body(&info) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + middlewares + .run(Some(fcg), Event::Wasm(op), &info) + .map_err(|x| LoadError::Codegen(x))?; + } + ParserState::EndFunctionBody => break, + _ => unreachable!(), + } + } + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionEnd), + &info, + ) + .map_err(|x| LoadError::Codegen(x))?; + fcg.finalize() + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + ParserState::BeginActiveElementSectionEntry(table_index) => { + let table_index = TableIndex::new(table_index as usize); + let mut elements: Option> = None; + let mut base: Option = None; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + base = Some(eval_init_expr(op)?) + } + ParserState::ElementSectionEntryBody(ref _elements) => { + elements = Some( + _elements + .iter() + .cloned() + .map(|index| FuncIndex::new(index as usize)) + .collect(), + ); + } + ParserState::BeginInitExpressionBody + | ParserState::EndInitExpressionBody => {} + ParserState::EndElementSectionEntry => break, + _ => unreachable!(), + } + } + + let table_init = TableInitializer { + table_index, + base: base.unwrap(), + elements: elements.unwrap(), + }; + + info.elem_initializers.push(table_init); + } + ParserState::BeginActiveDataSectionEntry(memory_index) => { + let memory_index = MemoryIndex::new(memory_index as usize); + let mut base: Option = None; + let mut data: Vec = vec![]; + + loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + base = Some(eval_init_expr(op)?) + } + ParserState::DataSectionEntryBodyChunk(chunk) => { + data.extend_from_slice(chunk); + } + ParserState::BeginInitExpressionBody + | ParserState::EndInitExpressionBody => {} + ParserState::BeginDataSectionEntryBody(_) + | ParserState::EndDataSectionEntryBody => {} + ParserState::EndDataSectionEntry => break, + _ => unreachable!(), + } + } + + let data_init = DataInitializer { + memory_index, + base: base.unwrap(), + data, + }; + info.data_initializers.push(data_init); + } + ParserState::BeginGlobalSectionEntry(ty) => { + let init = loop { + let state = parser.read(); + match *state { + ParserState::Error(err) => return Err(LoadError::Parse(err)), + ParserState::InitExpressionOperator(ref op) => { + break eval_init_expr(op)?; + } + ParserState::BeginInitExpressionBody => {} + _ => unreachable!(), + } + }; + let desc = GlobalDescriptor { + mutable: ty.mutable, + ty: wp_type_to_type(ty.content_type)?, + }; + + let global_init = GlobalInit { desc, init }; + + info.globals.push(global_init); + } + + _ => {} + } + } +} + +pub fn wp_type_to_type(ty: WpType) -> Result { + Ok(match ty { + WpType::I32 => Type::I32, + WpType::I64 => Type::I64, + WpType::F32 => Type::F32, + WpType::F64 => Type::F64, + WpType::V128 => { + return Err(BinaryReaderError { + message: "the wasmer llvm backend does not yet support the simd extension", + offset: -1isize as usize, + }); + } + _ => panic!("broken invariant, invalid type"), + }) +} + +pub fn type_to_wp_type(ty: Type) -> WpType { + match ty { + Type::I32 => WpType::I32, + Type::I64 => WpType::I64, + Type::F32 => WpType::F32, + Type::F64 => WpType::F64, + } +} + +fn func_type_to_func_sig(func_ty: &FuncType) -> Result { + assert_eq!(func_ty.form, WpType::Func); + + Ok(FuncSig::new( + func_ty + .params + .iter() + .cloned() + .map(wp_type_to_type) + .collect::, _>>()?, + func_ty + .returns + .iter() + .cloned() + .map(wp_type_to_type) + .collect::, _>>()?, + )) +} + +fn eval_init_expr(op: &Operator) -> Result { + Ok(match *op { + Operator::GetGlobal { global_index } => { + Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) + } + Operator::I32Const { value } => Initializer::Const(Value::I32(value)), + Operator::I64Const { value } => Initializer::Const(Value::I64(value)), + Operator::F32Const { value } => { + Initializer::Const(Value::F32(f32::from_bits(value.bits()))) + } + Operator::F64Const { value } => { + Initializer::Const(Value::F64(f64::from_bits(value.bits()))) + } + _ => { + return Err(BinaryReaderError { + message: "init expr evaluation failed: unsupported opcode", + offset: -1isize as usize, + }); + } + }) +} diff --git a/lib/runtime-core/src/sig_registry.rs b/lib/runtime-core/src/sig_registry.rs index 1f6d87b4f..653294f32 100644 --- a/lib/runtime-core/src/sig_registry.rs +++ b/lib/runtime-core/src/sig_registry.rs @@ -23,10 +23,18 @@ struct GlobalSigRegistry { sig_assoc: Map>, } +/// The `SigRegistry` represents a process-global map of function signatures +/// to signature indexes and vice versa (the map goes both ways). +/// +/// This exists for two reasons: +/// 1. The `call_indirect` wasm instruction can compare two signature indices +/// to do signature validation very quickly. +/// 2. To intern function signatures, which may be expensive to create. #[derive(Debug)] pub struct SigRegistry; impl SigRegistry { + /// Map a `FuncSig` to a global `SigIndex`. pub fn lookup_sig_index(&self, func_sig: Sig) -> SigIndex where Sig: Into>, @@ -45,11 +53,15 @@ impl SigRegistry { sig_index } + /// Map a global `SigIndex` to an interned `FuncSig`. pub fn lookup_signature(&self, sig_index: SigIndex) -> Arc { let global = (*GLOBAL_SIG_REGISTRY).read(); Arc::clone(&global.sig_assoc[sig_index]) } + /// Register a function signature with the global signature registry. + /// + /// This will return an interned `FuncSig`. pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc { let mut global = (*GLOBAL_SIG_REGISTRY).write(); let global = &mut *global; diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index d47388170..888aeff4d 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -33,7 +33,13 @@ impl Memory { let protect = protection.to_protect_const(); - let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) }; + let flags = if protection == Protect::None { + MEM_RESERVE + } else { + MEM_RESERVE | MEM_COMMIT + }; + + let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, flags, protect) }; if ptr.is_null() { Err("unable to allocate memory".to_string()) @@ -229,3 +235,25 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize { fn round_down_to_page_size(size: usize, page_size: usize) -> usize { size & !(page_size - 1) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn clone() { + // these should work + let _ = Memory::with_size_protect(200_000, Protect::Read) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadWrite) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadExec) + .unwrap() + .clone(); + + // this would cause segmentation fault as uncommited memory with no access + //let _ = Memory::with_size_protect(200_000, Protect::None).unwrap().clone(); + } +} diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4191f347e..0a9bc8c2f 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,29 +1,111 @@ use crate::{ - backend::UserTrapper, error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, - types::{FuncSig, Type, WasmExternType}, - vm::Ctx, + types::{FuncSig, NativeWasmType, Type, WasmExternType}, + vm::{self, Ctx}, +}; +use std::{ + any::Any, + convert::Infallible, + ffi::c_void, + fmt, + marker::PhantomData, + mem, panic, + ptr::{self, NonNull}, + sync::Arc, }; -use std::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, panic, ptr, sync::Arc}; -thread_local! { - pub static EARLY_TRAPPER: UnsafeCell>> = UnsafeCell::new(None); +#[repr(C)] +pub enum WasmTrapInfo { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, } -pub trait Safeness {} -pub struct Safe; -pub struct Unsafe; -impl Safeness for Safe {} -impl Safeness for Unsafe {} +impl fmt::Display for WasmTrapInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + WasmTrapInfo::Unreachable => "unreachable", + WasmTrapInfo::IncorrectCallIndirectSignature => { + "incorrect `call_indirect` signature" + } + WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access", + WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds", + WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation", + WasmTrapInfo::Unknown => "unknown", + } + ) + } +} + +/// This is just an empty trait to constrict that types that +/// can be put into the third/fourth (depending if you include lifetimes) +/// of the `Func` struct. +pub trait Kind {} + +pub type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); +pub type Invoke = unsafe extern "C" fn( + Trampoline, + *mut Ctx, + NonNull, + *const u64, + *mut u64, + *mut WasmTrapInfo, + *mut Option>, + Option>, +) -> bool; + +/// TODO(lachlan): Naming TBD. +/// This contains the trampoline and invoke functions for a specific signature, +/// as well as the environment that the invoke function may or may not require. +#[derive(Copy, Clone)] +pub struct Wasm { + pub(crate) trampoline: Trampoline, + pub(crate) invoke: Invoke, + pub(crate) invoke_env: Option>, +} + +impl Wasm { + pub unsafe fn from_raw_parts( + trampoline: Trampoline, + invoke: Invoke, + invoke_env: Option>, + ) -> Self { + Self { + trampoline, + invoke, + invoke_env, + } + } +} + +/// This type, as part of the `Func` type signature, represents a function that is created +/// by the host. +pub struct Host(()); +impl Kind for Wasm {} +impl Kind for Host {} pub trait WasmTypeList { type CStruct; + type RetArray: AsMut<[u64]>; + fn from_ret_array(array: Self::RetArray) -> Self; + fn empty_ret_array() -> Self::RetArray; fn from_c_struct(c_struct: Self::CStruct) -> Self; fn into_c_struct(self) -> Self::CStruct; fn types() -> &'static [Type]; - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets + unsafe fn call( + self, + f: NonNull, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result where Rets: WasmTypeList; } @@ -33,21 +115,23 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(&self) -> *const (); + fn to_raw(&self) -> NonNull; } pub trait TrapEarly where Rets: WasmTypeList, { - fn report(self) -> Result>; + type Error: 'static; + fn report(self) -> Result; } impl TrapEarly for Rets where Rets: WasmTypeList, { - fn report(self) -> Result> { + type Error = Infallible; + fn report(self) -> Result { Ok(self) } } @@ -55,10 +139,11 @@ where impl TrapEarly for Result where Rets: WasmTypeList, - E: Any, + E: 'static, { - fn report(self) -> Result> { - self.map_err(|err| Box::new(err) as Box) + type Error = E; + fn report(self) -> Result { + self } } @@ -71,19 +156,25 @@ where // Func::new(f) // } -pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> { - f: *const (), +pub struct Func<'a, Args = (), Rets = (), Inner: Kind = Wasm> { + inner: Inner, + f: NonNull, ctx: *mut Ctx, - _phantom: PhantomData<(&'a (), Safety, Args, Rets)>, + _phantom: PhantomData<(&'a (), Args, Rets)>, } -impl<'a, Args, Rets> Func<'a, Args, Rets, Safe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Wasm> where Args: WasmTypeList, Rets: WasmTypeList, { - pub(crate) unsafe fn new_from_ptr(f: *const (), ctx: *mut Ctx) -> Func<'a, Args, Rets, Safe> { + pub(crate) unsafe fn from_raw_parts( + inner: Wasm, + f: NonNull, + ctx: *mut Ctx, + ) -> Func<'a, Args, Rets, Wasm> { Func { + inner, f, ctx, _phantom: PhantomData, @@ -91,16 +182,17 @@ where } } -impl<'a, Args, Rets> Func<'a, Args, Rets, Unsafe> +impl<'a, Args, Rets> Func<'a, Args, Rets, Host> where Args: WasmTypeList, Rets: WasmTypeList, { - pub fn new(f: F) -> Func<'a, Args, Rets, Unsafe> + pub fn new(f: F) -> Func<'a, Args, Rets, Host> where F: ExternalFunction, { Func { + inner: Host(()), f: f.to_raw(), ctx: ptr::null_mut(), _phantom: PhantomData, @@ -108,11 +200,11 @@ where } } -impl<'a, Args, Rets, Safety> Func<'a, Args, Rets, Safety> +impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { pub fn params(&self) -> &'static [Type] { Args::types() @@ -122,110 +214,205 @@ where } } +impl WasmTypeList for Infallible { + type CStruct = Infallible; + type RetArray = [u64; 0]; + fn from_ret_array(_: Self::RetArray) -> Self { + unreachable!() + } + fn empty_ret_array() -> Self::RetArray { + unreachable!() + } + fn from_c_struct(_: Self::CStruct) -> Self { + unreachable!() + } + fn into_c_struct(self) -> Self::CStruct { + unreachable!() + } + fn types() -> &'static [Type] { + &[] + } + #[allow(non_snake_case)] + unsafe fn call( + self, + _: NonNull, + _: Wasm, + _: *mut Ctx, + ) -> Result { + unreachable!() + } +} + impl WasmTypeList for (A,) { type CStruct = S1; + type RetArray = [u64; 1]; + fn from_ret_array(array: Self::RetArray) -> Self { + (WasmExternType::from_native(NativeWasmType::from_binary( + array[0], + )),) + } + fn empty_ret_array() -> Self::RetArray { + [0u64] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { let S1(a) = c_struct; - (a,) + (WasmExternType::from_native(a),) } fn into_c_struct(self) -> Self::CStruct { #[allow(unused_parens, non_snake_case)] let (a,) = self; - S1(a) + S1(WasmExternType::to_native(a)) } fn types() -> &'static [Type] { - &[A::TYPE] + &[A::Native::TYPE] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f); + unsafe fn call( + self, + f: NonNull, + wasm: Wasm, + ctx: *mut Ctx, + ) -> Result { let (a,) = self; - f(ctx, a) + 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, Safe> +impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Wasm> where Rets: WasmTypeList, { pub fn call(&self, a: A) -> Result { - Ok(unsafe { ::call(a, self.f, self.ctx) }) + unsafe { ::call(a, self.f, self.inner, self.ctx) } } } macro_rules! impl_traits { ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { #[repr($repr)] - pub struct $struct_name <$( $x ),*> ( $( $x ),* ); + pub struct $struct_name <$( $x: WasmExternType ),*> ( $( <$x as WasmExternType>::Native ),* ); impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) { type CStruct = $struct_name<$( $x ),*>; + type RetArray = [u64; count_idents!( $( $x ),* )]; + fn from_ret_array(array: Self::RetArray) -> Self { + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* ) + } + fn empty_ret_array() -> Self::RetArray { + [0; count_idents!( $( $x ),* )] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { #[allow(non_snake_case)] let $struct_name ( $( $x ),* ) = c_struct; - ( $( $x ),* ) + ( $( WasmExternType::from_native($x) ),* ) } fn into_c_struct(self) -> Self::CStruct { #[allow(unused_parens, non_snake_case)] let ( $( $x ),* ) = self; - $struct_name ( $( $x ),* ) + $struct_name ( $( WasmExternType::to_native($x) ),* ) } fn types() -> &'static [Type] { - &[$( $x::TYPE, )*] + &[$( $x::Native::TYPE, )*] } #[allow(non_snake_case)] - unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); + unsafe fn call(self, f: NonNull, wasm: Wasm, ctx: *mut Ctx) -> Result { #[allow(unused_parens)] let ( $( $x ),* ) = self; - let c_struct = f(ctx $( ,$x )*); - Rets::from_c_struct(c_struct) + let args = [ $( $x.to_native().to_binary()),* ]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + let mut user_error = None; + + if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, &mut user_error, wasm.invoke_env) { + 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< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { #[allow(non_snake_case)] - fn to_raw(&self) -> *const () { + fn to_raw(&self) -> NonNull { assert_eq!(mem::size_of::(), 0, "you cannot use a closure that captures state for `Func`."); - extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct { + /// This is required for the llvm backend to be able to unwind through this function. + #[cfg_attr(nightly, unwind(allowed))] + extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct { let f: FN = unsafe { mem::transmute_copy(&()) }; let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| { - f( ctx $( ,$x )* ).report() + f( ctx $( ,WasmExternType::from_native($x) )* ).report() })) { Ok(Ok(returns)) => return returns.into_c_struct(), - Ok(Err(err)) => err, + Ok(Err(err)) => { + let b: Box<_> = err.into(); + b as Box + }, Err(err) => err, }; unsafe { - if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) { - early_trapper.do_early_trap(err) - } else { - eprintln!("panic handling not setup"); - std::process::exit(1) - } + (&*ctx.module).runnable_module.do_early_trap(err) } } - wrap::<$( $x, )* Rets, Trap, Self> as *const () + NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap() } } - impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Safe> + impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Wasm> where Rets: WasmTypeList, { #[allow(non_snake_case)] pub fn call(&self, $( $x: $x, )* ) -> Result { #[allow(unused_parens)] - Ok(unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.ctx) }) + unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.inner, self.ctx) } } } }; } +macro_rules! count_idents { + ( $($idents:ident),* ) => {{ + #[allow(dead_code, non_camel_case_types)] + enum Idents { $($idents,)* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + }}; +} + impl_traits!([C] S0,); impl_traits!([transparent] S1, A); impl_traits!([C] S2, A, B); @@ -240,14 +427,14 @@ impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J); impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K); impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L); -impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety> +impl<'a, Args, Rets, Inner> IsExport for Func<'a, Args, Rets, Inner> where Args: WasmTypeList, Rets: WasmTypeList, - Safety: Safeness, + Inner: Kind, { fn to_export(&self) -> Export { - let func = unsafe { FuncPointer::new(self.f as _) }; + let func = unsafe { FuncPointer::new(self.f.as_ptr()) }; let ctx = Context::Internal; let signature = Arc::new(FuncSig::new(Args::types(), Rets::types())); diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 9ee5270dd..975b5cfba 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,5 +1,5 @@ use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages}; -use std::{borrow::Cow, mem}; +use std::borrow::Cow; /// Represents a WebAssembly type. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -71,29 +71,150 @@ impl From for Value { } } -pub unsafe trait WasmExternType: Copy + Clone +pub unsafe trait NativeWasmType: Copy + Into where Self: Sized, { const TYPE: Type; + fn from_binary(bits: u64) -> Self; + fn to_binary(self) -> u64; +} + +unsafe impl NativeWasmType for i32 { + const TYPE: Type = Type::I32; + fn from_binary(bits: u64) -> Self { + bits as _ + } + fn to_binary(self) -> u64 { + self as _ + } +} +unsafe impl NativeWasmType for i64 { + const TYPE: Type = Type::I64; + fn from_binary(bits: u64) -> Self { + bits as _ + } + fn to_binary(self) -> u64 { + self as _ + } +} +unsafe impl NativeWasmType for f32 { + const TYPE: Type = Type::F32; + fn from_binary(bits: u64) -> Self { + f32::from_bits(bits as u32) + } + fn to_binary(self) -> u64 { + self.to_bits() as _ + } +} +unsafe impl NativeWasmType for f64 { + const TYPE: Type = Type::F64; + fn from_binary(bits: u64) -> Self { + f64::from_bits(bits) + } + fn to_binary(self) -> u64 { + self.to_bits() + } +} + +pub unsafe trait WasmExternType: Copy +where + Self: Sized, +{ + type Native: NativeWasmType; + fn from_native(native: Self::Native) -> Self; + fn to_native(self) -> Self::Native; +} + +unsafe impl WasmExternType for i8 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for u8 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for i16 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } +} +unsafe impl WasmExternType for u16 { + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for i32 { - const TYPE: Type = Type::I32; + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for u32 { - const TYPE: Type = Type::I32; + type Native = i32; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for i64 { - const TYPE: Type = Type::I64; + type Native = i64; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for u64 { - const TYPE: Type = Type::I64; + type Native = i64; + fn from_native(native: Self::Native) -> Self { + native as _ + } + fn to_native(self) -> Self::Native { + self as _ + } } unsafe impl WasmExternType for f32 { - const TYPE: Type = Type::F32; + type Native = f32; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } unsafe impl WasmExternType for f64 { - const TYPE: Type = Type::F64; + type Native = f64; + fn from_native(native: Self::Native) -> Self { + native + } + fn to_native(self) -> Self::Native { + self + } } // pub trait IntegerAtomic @@ -113,34 +234,15 @@ unsafe impl WasmExternType for f64 { // fn swap(&self, other: Self::Primitive) -> Self::Primitive; // } -pub enum ValueError { - BufferTooSmall, -} - -pub trait ValueType: Copy +pub unsafe trait ValueType: Copy where Self: Sized, { - fn into_le(self, buffer: &mut [u8]); - fn from_le(buffer: &[u8]) -> Result; } macro_rules! convert_value_impl { ($t:ty) => { - impl ValueType for $t { - fn into_le(self, buffer: &mut [u8]) { - buffer[..mem::size_of::()].copy_from_slice(&self.to_le_bytes()); - } - fn from_le(buffer: &[u8]) -> Result { - if buffer.len() >= mem::size_of::() { - let mut array = [0u8; mem::size_of::()]; - array.copy_from_slice(&buffer[..mem::size_of::()]); - Ok(Self::from_le_bytes(array)) - } else { - Err(ValueError::BufferTooSmall) - } - } - } + unsafe impl ValueType for $t {} }; ( $($t:ty),* ) => { $( @@ -149,25 +251,7 @@ macro_rules! convert_value_impl { }; } -convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64); - -impl ValueType for f32 { - fn into_le(self, buffer: &mut [u8]) { - self.to_bits().into_le(buffer); - } - fn from_le(buffer: &[u8]) -> Result { - Ok(f32::from_bits(::from_le(buffer)?)) - } -} - -impl ValueType for f64 { - fn into_le(self, buffer: &mut [u8]) { - self.to_bits().into_le(buffer); - } - fn from_le(buffer: &[u8]) -> Result { - Ok(f64::from_bits(::from_le(buffer)?)) - } -} +convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementType { @@ -207,6 +291,7 @@ pub enum Initializer { GetGlobal(ImportedGlobalIndex), } +/// Describes the mutability and type of a Global #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct GlobalDescriptor { pub mutable: bool, @@ -316,7 +401,7 @@ pub trait LocalImport { macro_rules! define_map_index { ($ty:ident) => { #[derive(Serialize, Deserialize)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); impl TypedIndex for $ty { #[doc(hidden)] @@ -431,3 +516,58 @@ where } } } + +#[cfg(test)] +mod tests { + use crate::types::NativeWasmType; + use crate::types::WasmExternType; + + #[test] + fn test_native_types_round_trip() { + assert_eq!( + 42i32, + i32::from_native(i32::from_binary((42i32).to_native().to_binary())) + ); + + assert_eq!( + -42i32, + i32::from_native(i32::from_binary((-42i32).to_native().to_binary())) + ); + + use std::i64; + let xi64 = i64::MAX; + assert_eq!( + xi64, + i64::from_native(i64::from_binary((xi64).to_native().to_binary())) + ); + let yi64 = i64::MIN; + assert_eq!( + yi64, + i64::from_native(i64::from_binary((yi64).to_native().to_binary())) + ); + + assert_eq!( + 16.5f32, + f32::from_native(f32::from_binary((16.5f32).to_native().to_binary())) + ); + + assert_eq!( + -16.5f32, + f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary())) + ); + + use std::f64; + let xf64: f64 = f64::MAX; + assert_eq!( + xf64, + f64::from_native(f64::from_binary((xf64).to_native().to_binary())) + ); + + let yf64: f64 = f64::MIN; + assert_eq!( + yf64, + f64::from_native(f64::from_binary((yf64).to_native().to_binary())) + ); + } + +} diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index 56d501438..e8232efc8 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -4,8 +4,10 @@ use std::{ ops::{Add, Sub}, }; -const WASM_PAGE_SIZE: usize = 65_536; -const WASM_MAX_PAGES: usize = 65_536; +pub const WASM_PAGE_SIZE: usize = 65_536; +pub const WASM_MAX_PAGES: usize = 65_536; +// From emscripten resize_heap implementation +pub const WASM_MIN_PAGES: usize = 256; /// Units of WebAssembly pages (as specified to be 65,536 bytes). #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index d3c3f3e4d..6649b6a59 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -11,7 +11,16 @@ use hashbrown::HashMap; /// The context of the currently running WebAssembly instance. /// +/// This is implicitly passed to every WebAssembly function. +/// Since this is per-instance, each field has a statically +/// (as in after compiling the wasm) known size, so no +/// runtime checks are necessary. /// +/// While the runtime currently just passes this around +/// as the first, implicit parameter of every function, +/// it may someday be pinned to a register (especially +/// on arm, which has a ton of registers) to reduce +/// register shuffling. #[derive(Debug)] #[repr(C)] pub struct Ctx { @@ -20,12 +29,26 @@ pub struct Ctx { pub(crate) local_functions: *const *const Func, + /// These are pointers to things that are known to be owned + /// by the owning `Instance`. local_backing: *mut LocalBacking, import_backing: *mut ImportBacking, - module: *const ModuleInner, + pub module: *const ModuleInner, + //// This is intended to be user-supplied, per-instance + /// contextual data. There are currently some issue with it, + /// notably that it cannot be set before running the `start` + /// function in a WebAssembly module. + /// + /// [#219](https://github.com/wasmerio/wasmer/pull/219) fixes that + /// issue, as well as allowing the user to have *per-function* + /// context, instead of just per-instance. pub data: *mut c_void, - pub data_finalizer: Option, + + /// If there's a function set in this field, it gets called + /// when the context is destructed, e.g. when an `Instance` + /// is dropped. + pub data_finalizer: Option, } /// The internal context of the currently running WebAssembly instance. @@ -100,7 +123,7 @@ impl Ctx { import_backing: &mut ImportBacking, module: &ModuleInner, data: *mut c_void, - data_finalizer: extern "C" fn(*mut c_void), + data_finalizer: fn(*mut c_void), ) -> Self { Self { internal: InternalCtx { @@ -481,7 +504,7 @@ mod vm_ctx_tests { str: String, } - extern "C" fn test_data_finalizer(data: *mut c_void) { + fn test_data_finalizer(data: *mut c_void) { let test_data: &mut TestData = unsafe { &mut *(data as *mut TestData) }; assert_eq!(test_data.x, 10); assert_eq!(test_data.y, true); @@ -544,52 +567,38 @@ mod vm_ctx_tests { fn generate_module() -> ModuleInner { use super::Func; - use crate::backend::{ - sys::Memory, Backend, CacheGen, FuncResolver, ProtectedCaller, Token, UserTrapper, - }; + use crate::backend::{sys::Memory, Backend, CacheGen, RunnableModule}; use crate::cache::Error as CacheError; - use crate::error::RuntimeResult; - use crate::types::{FuncIndex, LocalFuncIndex, Value}; + use crate::typed_func::Wasm; + use crate::types::{LocalFuncIndex, SigIndex}; use hashbrown::HashMap; + use std::any::Any; use std::ptr::NonNull; struct Placeholder; - impl FuncResolver for Placeholder { - fn get( + impl RunnableModule for Placeholder { + fn get_func( &self, - _module: &ModuleInner, + _module: &ModuleInfo, _local_func_index: LocalFuncIndex, ) -> Option> { None } - } - impl ProtectedCaller for Placeholder { - fn call( - &self, - _module: &ModuleInner, - _func_index: FuncIndex, - _params: &[Value], - _import_backing: &ImportBacking, - _vmctx: *mut Ctx, - _: Token, - ) -> RuntimeResult> { - Ok(vec![]) + + fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option { + unimplemented!() } - fn get_early_trapper(&self) -> Box { + unsafe fn do_early_trap(&self, _: Box) -> ! { unimplemented!() } } impl CacheGen for Placeholder { - fn generate_cache( - &self, - module: &ModuleInner, - ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { unimplemented!() } } ModuleInner { - func_resolver: Box::new(Placeholder), - protected_caller: Box::new(Placeholder), + runnable_module: Box::new(Placeholder), cache_gen: Box::new(Placeholder), info: ModuleInfo { memories: Map::new(), diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index c137922f0..5e620bc66 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.2.1" +version = "0.4.1" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,17 +9,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-dynasm-backend = { path = "../dynasm-backend", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } lazy_static = "1.2.0" memmap = "0.7.0" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.2.1" +version = "0.4.1" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.2.0" +version = "0.4.1" optional = true [dev-dependencies] @@ -37,7 +37,7 @@ default-compiler = ["wasmer-clif-backend"] cache = ["default-compiler"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] llvm = ["wasmer-llvm-backend"] -dynasm = ["wasmer-dynasm-backend"] +singlepass = ["wasmer-singlepass-backend"] [[bench]] name = "nginx" diff --git a/lib/runtime/README.md b/lib/runtime/README.md index 5e5542c8d..cbb6388aa 100644 --- a/lib/runtime/README.md +++ b/lib/runtime/README.md @@ -1,12 +1,41 @@ -# Wasmer-Runtime +

+ + Wasmer logo + +

-Wasmer-runtime is a library that makes embedding WebAssembly -in your application easy, efficient, and safe. +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

-# How to use Wasmer-Runtime +# Wasmer Runtime -The easiest way is to use the [`instantiate`] function to create an [`Instance`]. -Then you can use [`call`] or [`func`] and then [`call`][func.call] to call an exported function safely. +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate represents the high-level runtime API, making embedding +WebAssembly in your application easy, efficient, and safe. + +## How to use Wasmer Runtime + +The easiest way is to use the [`instantiate`] function to create an +[`Instance`]. Then you can use [`call`] or [`func`] and then +[`call`][func.call] to call an exported function safely. [`instantiate`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.instantiate.html [`Instance`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Instance.html @@ -14,7 +43,7 @@ Then you can use [`call`] or [`func`] and then [`call`][func.call] to call an ex [`func`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Instance.html#method.func [func.call]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/struct.Function.html#method.call -## Here's an example: +## Example Given this WebAssembly: @@ -27,7 +56,7 @@ Given this WebAssembly: i32.add)) ``` -compiled into wasm bytecode, we can call the exported "add_one" function: +compiled into Wasm bytecode, we can call the exported `add_one` function: ```rust static WASM: &'static [u8] = &[ @@ -63,13 +92,14 @@ fn main() -> error::Result<()> { } ``` -# Additional Notes: +## Additional Notes -The `wasmer-runtime` is build to support compiler multiple backends. -Currently, we support the [Cranelift] compiler with the [`wasmer-clif-backend`] crate. +The `wasmer-runtime` crate is build to support multiple compiler +backends. Currently, we support the [Cranelift] compiler with the +[`wasmer-clif-backend`] crate by default. You can specify the compiler you wish to use with the [`compile_with`] function. [Cranelift]: https://github.com/CraneStation/cranelift [`wasmer-clif-backend`]: https://crates.io/crates/wasmer-clif-backend -[`compile_with`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.compile_with.html \ No newline at end of file +[`compile_with`]: https://docs.rs/wasmer-runtime/*/wasmer_runtime/fn.compile_with.html diff --git a/lib/runtime/examples/call.rs b/lib/runtime/examples/call.rs index dbd29c7e8..cbc632d2e 100644 --- a/lib/runtime/examples/call.rs +++ b/lib/runtime/examples/call.rs @@ -1,11 +1,14 @@ -use wasmer_runtime::{compile, error, imports, Ctx, Func, Value}; +use wasmer_runtime::{compile, error, error::RuntimeError, imports, Ctx, Func, Value}; use wabt::wat2wasm; static WAT: &'static str = r#" (module (type (;0;) (func (result i32))) + (import "env" "do_panic" (func $do_panic (type 0))) (func $dbz (result i32) + call $do_panic + drop i32.const 42 i32.const 0 i32.div_u @@ -29,10 +32,19 @@ fn get_wasm() -> Vec { wat2wasm(WAT).unwrap() } -fn foobar(ctx: &mut Ctx) -> i32 { +fn foobar(_ctx: &mut Ctx) -> i32 { 42 } +#[derive(Debug)] +struct ExitCode { + code: i32, +} + +fn do_panic(_ctx: &mut Ctx) -> Result { + Err(ExitCode { code: 42 }) +} + fn main() -> Result<(), error::Error> { let wasm = get_wasm(); @@ -46,13 +58,23 @@ fn main() -> Result<(), error::Error> { // }; println!("instantiating"); - let instance = module.instantiate(&imports! {})?; + let instance = module.instantiate(&imports! { + "env" => { + "do_panic" => Func::new(do_panic), + }, + })?; - let foo = instance.dyn_func("dbz")?; + let foo: Func<(), i32> = instance.func("dbz")?; - let result = foo.call(&[]); + let result = foo.call(); println!("result: {:?}", result); + if let Err(RuntimeError::Error { data }) = result { + if let Ok(exit_code) = data.downcast::() { + println!("exit code: {:?}", exit_code); + } + } + Ok(()) } diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 66d9c6ace..bfdf6c9f7 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + //! Wasmer-runtime is a library that makes embedding WebAssembly //! in your application easy, efficient, and safe. //! @@ -74,6 +76,7 @@ //! [`wasmer-clif-backend`]: https://crates.io/crates/wasmer-clif-backend //! [`compile_with`]: fn.compile_with.html +pub use wasmer_runtime_core::export::Export; pub use wasmer_runtime_core::global::Global; pub use wasmer_runtime_core::import::ImportObject; pub use wasmer_runtime_core::instance::{DynFunc, Instance}; @@ -95,7 +98,9 @@ pub mod wasm { //! Various types exposed by the Wasmer Runtime. pub use wasmer_runtime_core::global::Global; pub use wasmer_runtime_core::table::Table; - pub use wasmer_runtime_core::types::{FuncSig, MemoryDescriptor, TableDescriptor, Type, Value}; + pub use wasmer_runtime_core::types::{ + FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value, + }; } pub mod error { @@ -138,6 +143,16 @@ pub fn compile_with_config( wasmer_runtime_core::compile_with_config(&wasm[..], default_compiler(), compiler_config) } +/// The same as `compile_with_config` but takes a `Compiler` for the purpose of +/// changing the backend. +pub fn compile_with_config_with( + wasm: &[u8], + compiler_config: CompilerConfig, + compiler: &dyn Compiler, +) -> error::CompileResult { + wasmer_runtime_core::compile_with_config(&wasm[..], compiler, compiler_config) +} + /// Compile and instantiate WebAssembly code without /// creating a [`Module`]. /// @@ -168,10 +183,10 @@ pub fn default_compiler() -> &'static dyn Compiler { #[cfg(feature = "llvm")] use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; - #[cfg(feature = "dynasm")] - use wasmer_dynasm_backend::SinglePassCompiler as DefaultCompiler; + #[cfg(feature = "singlepass")] + use wasmer_singlepass_backend::SinglePassCompiler as DefaultCompiler; - #[cfg(not(any(feature = "llvm", feature = "dynasm")))] + #[cfg(not(any(feature = "llvm", feature = "singlepass")))] use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; lazy_static! { diff --git a/lib/runtime/tests/error_propagation.rs b/lib/runtime/tests/error_propagation.rs new file mode 100644 index 000000000..1b9f9fccf --- /dev/null +++ b/lib/runtime/tests/error_propagation.rs @@ -0,0 +1,49 @@ +#[test] +fn error_propagation() { + use std::convert::Infallible; + use wabt::wat2wasm; + use wasmer_runtime::{compile, error::RuntimeError, imports, Ctx, Func}; + + static WAT: &'static str = r#" + (module + (type (;0;) (func)) + (import "env" "ret_err" (func $ret_err (type 0))) + (func $call_panic + call $ret_err + ) + (export "call_err" (func $call_panic)) + ) + "#; + + #[derive(Debug)] + struct ExitCode { + code: i32, + } + + fn ret_err(_ctx: &mut Ctx) -> Result { + Err(ExitCode { code: 42 }) + } + + let wasm = wat2wasm(WAT).unwrap(); + + let module = compile(&wasm).unwrap(); + + let instance = module + .instantiate(&imports! { + "env" => { + "ret_err" => Func::new(ret_err), + }, + }) + .unwrap(); + + let foo: Func<(), ()> = instance.func("call_err").unwrap(); + + let result = foo.call(); + + if let Err(RuntimeError::Error { data }) = result { + let exit_code = data.downcast::().unwrap(); + assert_eq!(exit_code.code, 42); + } else { + panic!("didn't return RuntimeError::Error") + } +} diff --git a/lib/dynasm-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml similarity index 53% rename from lib/dynasm-backend/Cargo.toml rename to lib/singlepass-backend/Cargo.toml index f7fe3a147..72ec5200a 100644 --- a/lib/dynasm-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,19 +1,20 @@ [package] -name = "wasmer-dynasm-backend" -version = "0.1.0" +name = "wasmer-singlepass-backend" +version = "0.4.1" repository = "https://github.com/wasmerio/wasmer" -description = "Wasmer runtime Dynasm compiler backend" +description = "Wasmer runtime single pass compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core" } -wasmparser = "0.28.0" +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +wasmparser = "0.29.2" dynasm = "0.3.1" dynasmrt = "0.3.1" lazy_static = "1.2.0" byteorder = "1" nix = "0.13.0" libc = "0.2.49" -hashbrown = "0.1" \ No newline at end of file +smallvec = "0.6.9" +hashbrown = "0.1" diff --git a/lib/singlepass-backend/README.md b/lib/singlepass-backend/README.md new file mode 100644 index 000000000..3d8c63615 --- /dev/null +++ b/lib/singlepass-backend/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer singlepass backend + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate represents the singlepass backend. diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs new file mode 100644 index 000000000..9aa6c9151 --- /dev/null +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -0,0 +1,4214 @@ +#![allow(clippy::forget_copy)] // Used by dynasm. + +use crate::emitter_x64::*; +use crate::machine::*; +use crate::protect_unix; +use dynasmrt::{ + x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer, +}; +use smallvec::SmallVec; +use std::ptr::NonNull; +use std::{any::Any, collections::HashMap, sync::Arc}; +use wasmer_runtime_core::{ + backend::{sys::Memory, Backend, CacheGen, RunnableModule, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, + memory::MemoryType, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, + typed_func::Wasm, + types::{ + FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, + vm::{self, LocalGlobal, LocalMemory, LocalTable}, + vmcalls, +}; +use wasmparser::{Operator, Type as WpType}; + +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 = { + let mut assembler = Assembler::new().unwrap(); + let offset = assembler.offset(); + dynasm!( + assembler + ; push r15 + ; push r14 + ; push r13 + ; push r12 + ; push r11 + ; push rbp + ; mov rbp, rsp + + ; mov r15, rdi + ; mov r14, rsi + ; mov r13, rdx + ; mov r12, rcx + + ; mov rdi, r13 // ctx + + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rsi, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rdx, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rcx, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov r8, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov r9, [r14] + ; sub r14, 8 + ; cmp r14, r15 + ; jb >stack_ready + + ; mov rax, r14 + ; sub rax, r15 + ; sub rsp, rax + ; sub rsp, 8 + ; mov rax, QWORD 0xfffffffffffffff0u64 as i64 + ; and rsp, rax + ; mov rax, rsp + ; loop_begin: + ; mov r11, [r14] + ; mov [rax], r11 + ; sub r14, 8 + ; add rax, 8 + ; cmp r14, r15 + ; jb >stack_ready + ; jmp , + signatures: Option>>, + function_signatures: Option>>, + function_labels: Option)>>, + assembler: Option, + func_import_count: usize, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum LocalOrTemp { + Local, + Temp, +} + +pub struct X64FunctionCode { + signatures: Arc>, + function_signatures: Arc>, + + assembler: Option, + function_labels: Option)>>, + br_table_data: Option>>, + breakpoints: Option>>, + returns: SmallVec<[WpType; 1]>, + locals: Vec, + num_params: usize, + num_locals: usize, + value_stack: Vec<(Location, LocalOrTemp)>, + control_stack: Vec, + machine: Machine, + unreachable_depth: usize, +} + +enum FuncPtrInner {} +#[repr(transparent)] +#[derive(Copy, Clone, Debug)] +struct FuncPtr(*const FuncPtrInner); +unsafe impl Send for FuncPtr {} +unsafe impl Sync for FuncPtr {} + +pub struct X64ExecutionContext { + #[allow(dead_code)] + code: ExecutableBuffer, + #[allow(dead_code)] + functions: Vec, + function_pointers: Vec, + signatures: Arc>, + _br_table_data: Vec>, + breakpoints: Arc>>, + func_import_count: usize, +} + +#[derive(Debug)] +pub struct ControlFrame { + pub label: DynamicLabel, + pub loop_like: bool, + pub if_else: IfElseState, + pub returns: SmallVec<[WpType; 1]>, + pub value_stack_depth: usize, +} + +#[derive(Debug, Copy, Clone)] +pub enum IfElseState { + None, + If(DynamicLabel), + Else, +} + +impl RunnableModule for X64ExecutionContext { + fn get_func( + &self, + _: &ModuleInfo, + local_func_index: LocalFuncIndex, + ) -> Option> { + self.function_pointers[self.func_import_count..] + .get(local_func_index.index()) + .and_then(|ptr| NonNull::new(ptr.0 as *mut vm::Func)) + } + + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { + use std::ffi::c_void; + use wasmer_runtime_core::typed_func::WasmTrapInfo; + + unsafe extern "C" fn invoke( + _trampoline: unsafe extern "C" fn( + *mut vm::Ctx, + NonNull, + *const u64, + *mut u64, + ), + ctx: *mut vm::Ctx, + func: NonNull, + args: *const u64, + rets: *mut u64, + trap_info: *mut WasmTrapInfo, + user_error: *mut Option>, + num_params_plus_one: Option>, + ) -> bool { + let rm: &Box = &(&*(*ctx).module).runnable_module; + let execution_context = + ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + + let args = ::std::slice::from_raw_parts( + args, + num_params_plus_one.unwrap().as_ptr() as usize - 1, + ); + let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect(); + protect_unix::BKPT_MAP + .with(|x| x.borrow_mut().push(execution_context.breakpoints.clone())); + 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(), + ) + }) { + Ok(x) => { + if !rets.is_null() { + *rets = x; + } + true + } + Err(err) => { + match err { + protect_unix::CallProtError::Trap(info) => *trap_info = info, + protect_unix::CallProtError::Error(data) => *user_error = Some(data), + } + false + } + }; + protect_unix::BKPT_MAP.with(|x| x.borrow_mut().pop().unwrap()); + ret + } + + unsafe extern "C" fn dummy_trampoline( + _: *mut vm::Ctx, + _: NonNull, + _: *const u64, + _: *mut u64, + ) { + unreachable!() + } + + Some(unsafe { + Wasm::from_raw_parts( + dummy_trampoline, + invoke, + NonNull::new((self.signatures.get(sig_index).unwrap().params().len() + 1) as _), // +1 to keep it non-zero + ) + }) + } + + unsafe fn do_early_trap(&self, data: Box) -> ! { + protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data))); + protect_unix::trigger_trap(); + } +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: &'static str, +} + +impl ModuleCodeGenerator + for X64ModuleCodeGenerator +{ + fn new() -> X64ModuleCodeGenerator { + X64ModuleCodeGenerator { + functions: vec![], + signatures: None, + function_signatures: None, + function_labels: Some(HashMap::new()), + assembler: Some(Assembler::new().unwrap()), + func_import_count: 0, + } + } + + fn backend_id() -> Backend { + Backend::Singlepass + } + + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> { + let (mut assembler, mut function_labels, br_table_data, breakpoints) = + match self.functions.last_mut() { + Some(x) => ( + x.assembler.take().unwrap(), + x.function_labels.take().unwrap(), + x.br_table_data.take().unwrap(), + x.breakpoints.take().unwrap(), + ), + None => ( + self.assembler.take().unwrap(), + self.function_labels.take().unwrap(), + vec![], + HashMap::new(), + ), + }; + let begin_offset = assembler.offset(); + let begin_label_info = function_labels + .entry(self.functions.len() + self.func_import_count) + .or_insert_with(|| (assembler.new_dynamic_label(), None)); + + begin_label_info.1 = Some(begin_offset); + let begin_label = begin_label_info.0; + + dynasm!( + assembler + ; => begin_label + //; int 3 + ); + let code = X64FunctionCode { + signatures: self.signatures.as_ref().unwrap().clone(), + function_signatures: self.function_signatures.as_ref().unwrap().clone(), + + assembler: Some(assembler), + function_labels: Some(function_labels), + br_table_data: Some(br_table_data), + breakpoints: Some(breakpoints), + returns: smallvec![], + locals: vec![], + num_params: 0, + num_locals: 0, + value_stack: vec![], + control_stack: vec![], + machine: Machine::new(), + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize( + mut self, + _: &ModuleInfo, + ) -> Result<(X64ExecutionContext, Box), CodegenError> { + let (assembler, mut br_table_data, breakpoints) = match self.functions.last_mut() { + Some(x) => ( + x.assembler.take().unwrap(), + x.br_table_data.take().unwrap(), + x.breakpoints.take().unwrap(), + ), + None => { + return Err(CodegenError { + message: "no function", + }); + } + }; + let output = assembler.finalize().unwrap(); + + for table in &mut br_table_data { + for entry in table { + *entry = output.ptr(AssemblyOffset(*entry)) as usize; + } + } + + let function_labels = if let Some(x) = self.functions.last() { + x.function_labels.as_ref().unwrap() + } else { + self.function_labels.as_ref().unwrap() + }; + let mut out_labels: Vec = vec![]; + + for i in 0..function_labels.len() { + let (_, offset) = match function_labels.get(&i) { + Some(x) => x, + None => { + return Err(CodegenError { + message: "label not found", + }); + } + }; + let offset = match offset { + Some(x) => x, + None => { + return Err(CodegenError { + message: "offset is none", + }); + } + }; + out_labels.push(FuncPtr(output.ptr(*offset) as _)); + } + + let breakpoints: Arc> = Arc::new( + breakpoints + .into_iter() + .map(|(offset, f)| (output.ptr(offset) as usize, f)) + .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(), + )) + } + } + + Ok(( + X64ExecutionContext { + code: output, + functions: self.functions, + signatures: self.signatures.as_ref().unwrap().clone(), + _br_table_data: br_table_data, + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + }, + Box::new(Placeholder), + )) + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = Some(Arc::new(signatures)); + Ok(()) + } + + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + let labels = self.function_labels.as_mut().unwrap(); + let id = labels.len(); + + let a = self.assembler.as_mut().unwrap(); + let offset = a.offset(); + let label = a.get_label(); + a.emit_label(label); + labels.insert(id, (label, Some(offset))); + + // Emits a tail call trampoline that loads the address of the target import function + // from Ctx and jumps to it. + + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDI, vm::Ctx::offset_imported_funcs() 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::GPR(GPR::RAX), + ); + a.emit_jmp_location(Location::GPR(GPR::RAX)); + + self.func_import_count += 1; + + Ok(()) + } + + unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result { + Err(CacheError::Unknown( + "the singlepass compiler API doesn't support caching yet".to_string(), + )) + } +} + +impl X64FunctionCode { + /// Moves `loc` to a valid location for `div`/`idiv`. + fn emit_relaxed_xdiv( + a: &mut Assembler, + _m: &mut Machine, + op: fn(&mut Assembler, Size, Location), + sz: Size, + loc: Location, + ) { + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + a.emit_mov(sz, loc, Location::GPR(GPR::RCX)); // must not be used during div (rax, rdx) + op(a, sz, Location::GPR(GPR::RCX)); + } + _ => { + op(a, sz, loc); + } + } + } + + /// Moves `src` and `dst` to valid locations for `movzx`/`movsx`. + fn emit_relaxed_zx_sx( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, Size, Location, Size, Location), + sz_src: Size, + 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 { + Location::Imm32(_) | Location::Imm64(_) => { + a.emit_mov(Size::S64, src, Location::GPR(tmp_src)); + src = Location::GPR(tmp_src); + } + Location::Memory(_, _) | Location::GPR(_) => {} + _ => unreachable!(), + } + + match dst { + Location::Imm32(_) | Location::Imm64(_) => unreachable!(), + Location::Memory(_, _) => { + op(a, sz_src, src, sz_dst, Location::GPR(tmp_dst)); + a.emit_mov(Size::S64, Location::GPR(tmp_dst), dst); + } + Location::GPR(_) => { + op(a, sz_src, src, sz_dst, dst); + } + _ => unreachable!(), + } + + m.release_temp_gpr(tmp_dst); + m.release_temp_gpr(tmp_src); + } + + /// Moves `src` and `dst` to valid locations for generic instructions. + fn emit_relaxed_binop( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, Size, Location, Location), + sz: Size, + src: Location, + dst: Location, + ) { + enum RelaxMode { + Direct, + SrcToGPR, + DstToGPR, + BothToGPR, + } + let mode = match (src, dst) { + (Location::Memory(_, _), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::Imm64(_)) | (Location::Imm64(_), Location::Imm32(_)) => { + RelaxMode::BothToGPR + } + (_, Location::Imm32(_)) | (_, Location::Imm64(_)) => RelaxMode::DstToGPR, + (Location::Imm64(_), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::GPR(_)) + if (op as *const u8 != Assembler::emit_mov as *const u8) => + { + RelaxMode::SrcToGPR + } + (_, Location::XMM(_)) => RelaxMode::SrcToGPR, + _ if (op as *const u8 == Assembler::emit_imul as *const u8) => RelaxMode::BothToGPR, // TODO: optimize this + _ => RelaxMode::Direct, + }; + + match mode { + RelaxMode::SrcToGPR => { + let temp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, src, Location::GPR(temp)); + op(a, sz, Location::GPR(temp), dst); + m.release_temp_gpr(temp); + } + RelaxMode::DstToGPR => { + let temp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, dst, Location::GPR(temp)); + op(a, sz, src, Location::GPR(temp)); + m.release_temp_gpr(temp); + } + RelaxMode::BothToGPR => { + let temp_src = m.acquire_temp_gpr().unwrap(); + let temp_dst = m.acquire_temp_gpr().unwrap(); + a.emit_mov(sz, src, Location::GPR(temp_src)); + a.emit_mov(sz, dst, Location::GPR(temp_dst)); + op(a, sz, Location::GPR(temp_src), Location::GPR(temp_dst)); + match dst { + Location::Memory(_, _) | Location::GPR(_) => { + a.emit_mov(sz, Location::GPR(temp_dst), dst); + } + _ => {} + } + m.release_temp_gpr(temp_dst); + m.release_temp_gpr(temp_src); + } + RelaxMode::Direct => { + op(a, sz, src, dst); + } + } + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx( + a: &mut Assembler, + m: &mut Machine, + op: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + src1: Location, + src2: Location, + dst: Location, + ) { + Self::emit_relaxed_avx_base( + a, + m, + |a, _, src1, src2, dst| op(a, src1, src2, dst), + src1, + src2, + dst, + ) + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx_base( + a: &mut Assembler, + m: &mut Machine, + op: F, + src1: Location, + src2: Location, + dst: Location, + ) { + let tmp1 = m.acquire_temp_xmm().unwrap(); + let tmp2 = m.acquire_temp_xmm().unwrap(); + let tmp3 = m.acquire_temp_xmm().unwrap(); + let tmpg = m.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(tmpg)); + a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + Location::Imm64(_) => { + a.emit_mov(Size::S64, src1, Location::GPR(tmpg)); + a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + _ => unreachable!(), + }; + + let src2 = match src2 { + Location::XMM(x) => XMMOrMemory::XMM(x), + Location::Memory(base, disp) => XMMOrMemory::Memory(base, disp), + Location::GPR(_) => { + a.emit_mov(Size::S64, src2, Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm32(_) => { + a.emit_mov(Size::S32, src2, Location::GPR(tmpg)); + a.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm64(_) => { + a.emit_mov(Size::S64, src2, Location::GPR(tmpg)); + a.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + _ => unreachable!(), + }; + + match dst { + Location::XMM(x) => { + op(a, m, src1, src2, x); + } + Location::Memory(_, _) | Location::GPR(_) => { + op(a, m, src1, src2, tmp3); + a.emit_mov(Size::S64, Location::XMM(tmp3), dst); + } + _ => unreachable!(), + } + + m.release_temp_gpr(tmpg); + m.release_temp_xmm(tmp3); + m.release_temp_xmm(tmp2); + m.release_temp_xmm(tmp1); + } + + /// I32 binary operation with both operands popped from the virtual stack. + fn emit_binop_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + // Using Red Zone here. + 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(a, &[WpType::I32], false)[0]; + + if loc_a != ret { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + loc_a, + Location::GPR(tmp), + ); + Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, Location::GPR(tmp)); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp), + ret, + ); + m.release_temp_gpr(tmp); + } else { + Self::emit_relaxed_binop(a, m, f, Size::S32, loc_b, ret); + } + + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 binary operation with both operands popped from the virtual stack. + fn emit_binop_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + // Using Red Zone here. + 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(a, &[WpType::I64], false)[0]; + + if loc_a != ret { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + loc_a, + Location::GPR(tmp), + ); + Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, Location::GPR(tmp)); + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp), + ret, + ); + m.release_temp_gpr(tmp); + } else { + Self::emit_relaxed_binop(a, m, f, Size::S64, loc_b, ret); + } + + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 comparison with `loc_b` from input. + fn emit_cmpop_i32_dynamic_b( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + loc_b: Location, + ) { + // Using Red Zone here. + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + match ret { + Location::GPR(x) => { + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); + a.emit_set(c, x); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S32, loc_b, loc_a); + a.emit_set(c, tmp); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + m.release_temp_gpr(tmp); + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + ) { + 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); + } + + /// I64 comparison with `loc_b` from input. + fn emit_cmpop_i64_dynamic_b( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + loc_b: Location, + ) { + // Using Red Zone here. + let loc_a = get_location_released(a, m, value_stack.pop().unwrap()); + + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + match ret { + Location::GPR(x) => { + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); + a.emit_set(c, x); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = m.acquire_temp_gpr().unwrap(); + Self::emit_relaxed_binop(a, m, Assembler::emit_cmp, Size::S64, loc_b, loc_a); + a.emit_set(c, tmp); + a.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + m.release_temp_gpr(tmp); + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + c: Condition, + ) { + 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); + } + + /// I32 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I32], false)[0]; + + match loc { + Location::Imm32(_) => { + let tmp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S32, Location::GPR(tmp), Location::GPR(out_tmp)); + a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S32, Location::GPR(tmp), ret); + } + m.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S32, loc, Location::GPR(out_tmp)); + a.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S32, loc, ret); + } + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::I64], false)[0]; + + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + let tmp = m.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S64, Location::GPR(tmp), Location::GPR(out_tmp)); + a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S64, Location::GPR(tmp), ret); + } + m.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = m.acquire_temp_gpr().unwrap(); + f(a, Size::S64, loc, Location::GPR(out_tmp)); + a.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + m.release_temp_gpr(out_tmp); + } else { + f(a, Size::S64, loc, ret); + } + } + _ => unreachable!(), + } + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I32 shift with both operands popped from the virtual stack. + fn emit_shift_i32( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + 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(a, &[WpType::I32], false)[0]; + + a.emit_mov(Size::S32, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S32, loc_a, ret); + } + + f(a, Size::S32, Location::GPR(GPR::RCX), ret); + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// I64 shift with both operands popped from the virtual stack. + fn emit_shift_i64( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, Size, Location, Location), + ) { + 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(a, &[WpType::I64], false)[0]; + + a.emit_mov(Size::S64, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + Self::emit_relaxed_binop(a, m, Assembler::emit_mov, Size::S64, loc_a, ret); + } + + f(a, Size::S64, Location::GPR(GPR::RCX), ret); + value_stack.push((ret, LocalOrTemp::Temp)); + } + + /// Floating point (AVX) binary operation with both operands popped from the virtual stack. + fn emit_fp_binop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + 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(a, &[WpType::F64], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); + } + + /// Floating point (AVX) comparison with both operands popped from the virtual stack. + fn emit_fp_cmpop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + 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(a, &[WpType::I32], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc_a, loc_b, ret); + a.emit_and(Size::S32, Location::Imm32(1), ret); // FIXME: Why? + } + + /// Floating point (AVX) binary operation with both operands popped from the virtual stack. + fn emit_fp_unop_avx( + a: &mut Assembler, + m: &mut Machine, + value_stack: &mut Vec<(Location, LocalOrTemp)>, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) { + let loc = get_location_released(a, m, value_stack.pop().unwrap()); + let ret = m.acquire_locations(a, &[WpType::F64], false)[0]; + value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_relaxed_avx(a, m, f, loc, loc, ret); + } + + /// Emits a System V call sequence. + /// + /// This function must not use RAX before `cb` is called. + fn emit_call_sysv, F: FnOnce(&mut Assembler)>( + a: &mut Assembler, + m: &mut Machine, + cb: F, + params: I, + ) { + let params: Vec<_> = params.collect(); + + // Save used GPRs. + let used_gprs = m.get_used_gprs(); + for r in used_gprs.iter() { + a.emit_push(Size::S64, Location::GPR(*r)); + } + + // Save used XMM registers. + let used_xmms = m.get_used_xmms(); + if used_xmms.len() > 0 { + a.emit_sub( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + + // FIXME: Possible dynasm bug. This is a workaround. + // Using RSP as the source/destination operand of a `mov` instruction produces invalid code. + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); + for (i, r) in used_xmms.iter().enumerate() { + a.emit_mov( + Size::S64, + Location::XMM(*r), + Location::Memory(GPR::RCX, (i * 8) as i32), + ); + } + } + + let mut stack_offset: usize = 0; + + // Calculate stack offset. + for (i, _param) in params.iter().enumerate() { + let loc = Machine::get_param_location(1 + i); + match loc { + Location::Memory(_, _) => { + stack_offset += 8; + } + _ => {} + } + } + + // Align stack to 16 bytes. + if (m.get_stack_offset() + used_gprs.len() * 8 + stack_offset) % 16 != 0 { + a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); + stack_offset += 8; + } + + let mut call_movs: Vec<(Location, GPR)> = vec![]; + + // Prepare register & stack parameters. + for (i, param) in params.iter().enumerate().rev() { + let loc = Machine::get_param_location(1 + i); + match loc { + Location::GPR(x) => { + call_movs.push((*param, x)); + } + Location::Memory(_, _) => { + match *param { + // Dynasm bug: RSP in memory operand does not work + Location::Imm64(_) | Location::XMM(_) => { + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::XMM(XMM::XMM0), + ); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RCX), + Location::XMM(XMM::XMM1), + ); + a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX)); + a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX)); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::Memory(GPR::RCX, 0), + ); + a.emit_mov( + Size::S64, + Location::XMM(XMM::XMM0), + Location::GPR(GPR::RAX), + ); + a.emit_mov( + Size::S64, + Location::XMM(XMM::XMM1), + Location::GPR(GPR::RCX), + ); + } + _ => a.emit_push(Size::S64, *param), + } + } + _ => unreachable!(), + } + } + + // Sort register moves so that register are not overwritten before read. + sort_call_movs(&mut call_movs); + + // Emit register moves. + for (loc, gpr) in call_movs { + if loc != Location::GPR(gpr) { + a.emit_mov(Size::S64, loc, Location::GPR(gpr)); + } + } + + // Put vmctx as the first parameter. + a.emit_mov( + Size::S64, + Location::GPR(Machine::get_vmctx_reg()), + Machine::get_param_location(0), + ); // vmctx + + cb(a); + + // Restore stack. + if stack_offset > 0 { + a.emit_add( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + + // Restore XMMs. + if used_xmms.len() > 0 { + // FIXME: Possible dynasm bug. This is a workaround. + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX)); + for (i, r) in used_xmms.iter().enumerate() { + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDX, (i * 8) as i32), + Location::XMM(*r), + ); + } + a.emit_add( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + } + + // Restore GPRs. + for r in used_gprs.iter().rev() { + a.emit_pop(Size::S64, Location::GPR(*r)); + } + } + + /// Emits a System V call sequence, specialized for labels as the call target. + fn emit_call_sysv_label>( + a: &mut Assembler, + m: &mut Machine, + label: DynamicLabel, + params: I, + ) { + Self::emit_call_sysv(a, m, |a| a.emit_call_label(label), params) + } + + /// Emits a memory operation. + fn emit_memory_op( + module_info: &ModuleInfo, + a: &mut Assembler, + m: &mut Machine, + addr: Location, + offset: usize, + value_size: usize, + cb: F, + ) { + let tmp_addr = m.acquire_temp_gpr().unwrap(); + let tmp_base = m.acquire_temp_gpr().unwrap(); + let tmp_bound = m.acquire_temp_gpr().unwrap(); + + // Loads both base and bound into temporary registers. + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + match MemoryIndex::new(0).local_or_import(module_info) { + LocalOrImport::Local(_) => vm::Ctx::offset_memories(), + LocalOrImport::Import(_) => vm::Ctx::offset_imported_memories(), + } as i32, + ), + Location::GPR(tmp_base), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp_base, 0), + Location::GPR(tmp_base), + ); + a.emit_mov( + Size::S32, + Location::Memory(tmp_base, LocalMemory::offset_bound() as i32), + Location::GPR(tmp_bound), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp_base, LocalMemory::offset_base() as i32), + Location::GPR(tmp_base), + ); + + // Adds base to bound so `tmp_bound` now holds the end of linear memory. + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound)); + + // 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], + LocalOrImport::Import(import_mem_index) => { + &module_info.imported_memories[import_mem_index].1 + } + }; + let need_check = match mem_desc.memory_type() { + MemoryType::Dynamic => true, + MemoryType::Static | MemoryType::SharedStatic => false, + }; + + if need_check { + a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); + + // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. + match (offset as u32).checked_add(value_size as u32) { + Some(x) => { + a.emit_add(Size::S64, Location::Imm32(x), Location::GPR(tmp_addr)); + } + None => { + a.emit_add( + Size::S64, + Location::Imm32(offset as u32), + Location::GPR(tmp_addr), + ); + a.emit_add( + Size::S64, + Location::Imm32(value_size as u32), + Location::GPR(tmp_addr), + ); + } + } + + // Trap if the end address of the requested area is above that of the linear memory. + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); + a.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr)); + a.emit_conditional_trap(Condition::Above); + } + + m.release_temp_gpr(tmp_bound); + + // Calculates the real address, and loads from it. + a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); + a.emit_add( + Size::S64, + Location::Imm32(offset as u32), + Location::GPR(tmp_addr), + ); + a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); + m.release_temp_gpr(tmp_base); + + cb(a, m, tmp_addr); + + m.release_temp_gpr(tmp_addr); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F32. + fn emit_f32_int_conv_check( + a: &mut Assembler, + m: &mut Machine, + reg: XMM, + lower_bound: f32, + upper_bound: f32, + ) { + let lower_bound = f32::to_bits(lower_bound); + let upper_bound = f32::to_bits(upper_bound); + + let trap = a.get_label(); + let end = a.get_label(); + + let tmp = m.acquire_temp_gpr().unwrap(); + let tmp_x = m.acquire_temp_xmm().unwrap(); + + // Underflow. + a.emit_mov(Size::S32, Location::Imm32(lower_bound), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpless(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // Overflow. + a.emit_mov(Size::S32, Location::Imm32(upper_bound), Location::GPR(tmp)); + a.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpgess(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // NaN. + a.emit_vcmpeqss(reg, XMMOrMemory::XMM(reg), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::Equal, trap); + + a.emit_jmp(Condition::None, end); + a.emit_label(trap); + a.emit_ud2(); + a.emit_label(end); + + m.release_temp_xmm(tmp_x); + m.release_temp_gpr(tmp); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F64. + fn emit_f64_int_conv_check( + a: &mut Assembler, + m: &mut Machine, + reg: XMM, + lower_bound: f64, + upper_bound: f64, + ) { + let lower_bound = f64::to_bits(lower_bound); + let upper_bound = f64::to_bits(upper_bound); + + let trap = a.get_label(); + let end = a.get_label(); + + let tmp = m.acquire_temp_gpr().unwrap(); + let tmp_x = m.acquire_temp_xmm().unwrap(); + + // Underflow. + a.emit_mov(Size::S64, Location::Imm64(lower_bound), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmplesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // Overflow. + a.emit_mov(Size::S64, Location::Imm64(upper_bound), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + a.emit_vcmpgesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::NotEqual, trap); + + // NaN. + a.emit_vcmpeqsd(reg, XMMOrMemory::XMM(reg), tmp_x); + a.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + a.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + a.emit_jmp(Condition::Equal, trap); + + a.emit_jmp(Condition::None, end); + a.emit_label(trap); + a.emit_ud2(); + a.emit_label(end); + + m.release_temp_xmm(tmp_x); + m.release_temp_gpr(tmp); + } +} + +impl FunctionCodeGenerator for X64FunctionCode { + fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> { + self.returns.push(ty); + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + self.num_params += 1; + self.num_locals += 1; + Ok(()) + } + + fn feed_local(&mut self, _ty: WpType, n: usize) -> Result<(), CodegenError> { + self.num_locals += n; + Ok(()) + } + + fn begin_body(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + let a = self.assembler.as_mut().unwrap(); + a.emit_push(Size::S64, Location::GPR(GPR::RBP)); + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); + + self.locals = self + .machine + .init_locals(a, self.num_locals, self.num_params); + + a.emit_sub(Size::S64, Location::Imm32(32), Location::GPR(GPR::RSP)); // simulate "red zone" if not supported by the platform + + self.control_stack.push(ControlFrame { + label: a.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: self.returns.clone(), + value_stack_depth: 0, + }); + Ok(()) + } + + fn finalize(&mut self) -> Result<(), CodegenError> { + let a = self.assembler.as_mut().unwrap(); + a.emit_ud2(); + Ok(()) + } + + fn feed_event(&mut self, ev: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + //println!("{:?} {}", op, self.value_stack.len()); + let was_unreachable; + + if self.unreachable_depth > 0 { + was_unreachable = true; + + if let Event::Wasm(op) = ev { + match *op { + Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => { + self.unreachable_depth += 1; + } + Operator::End => { + self.unreachable_depth -= 1; + } + Operator::Else => { + // We are in a reachable true branch + if self.unreachable_depth == 1 { + if let Some(IfElseState::If(_)) = + self.control_stack.last().map(|x| x.if_else) + { + self.unreachable_depth -= 1; + } + } + } + _ => {} + } + } + if self.unreachable_depth > 0 { + return Ok(()); + } + } else { + was_unreachable = false; + } + + let a = self.assembler.as_mut().unwrap(); + + let op = match ev { + Event::Wasm(x) => x, + Event::Internal(x) => { + match x { + InternalEvent::Breakpoint(callback) => { + a.emit_bkpt(); + self.breakpoints + .as_mut() + .unwrap() + .insert(a.get_offset(), callback); + } + InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} + _ => unimplemented!(), + } + return Ok(()); + } + }; + match *op { + Operator::GetGlobal { global_index } => { + let global_index = global_index as usize; + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let loc = match GlobalIndex::new(global_index).local_or_import(module_info) { + LocalOrImport::Local(local_index) => { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_globals() as i32, + ), + Location::GPR(tmp), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp, (local_index.index() as i32) * 8), + Location::GPR(tmp), + ); + self.machine.acquire_locations( + a, + &[type_to_wp_type(module_info.globals[local_index].desc.ty)], + false, + )[0] + } + LocalOrImport::Import(import_index) => { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_imported_globals() as i32, + ), + Location::GPR(tmp), + ); + a.emit_mov( + Size::S64, + Location::Memory(tmp, (import_index.index() as i32) * 8), + Location::GPR(tmp), + ); + self.machine.acquire_locations( + a, + &[type_to_wp_type( + module_info.imported_globals[import_index].1.ty, + )], + false, + )[0] + } + }; + self.value_stack.push((loc, LocalOrTemp::Temp)); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::Memory(tmp, LocalGlobal::offset_data() as i32), + loc, + ); + + self.machine.release_temp_gpr(tmp); + } + Operator::SetGlobal { global_index } => { + let mut global_index = global_index as usize; + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + if global_index < module_info.imported_globals.len() { + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_imported_globals() as i32, + ), + Location::GPR(tmp), + ); + } else { + global_index -= module_info.imported_globals.len(); + assert!(global_index < module_info.globals.len()); + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_globals() as i32, + ), + Location::GPR(tmp), + ); + } + a.emit_mov( + Size::S64, + Location::Memory(tmp, (global_index as i32) * 8), + Location::GPR(tmp), + ); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::Memory(tmp, LocalGlobal::offset_data() as i32), + ); + + self.machine.release_temp_gpr(tmp); + } + Operator::GetLocal { local_index } => { + let local_index = local_index as usize; + self.value_stack + .push((self.locals[local_index], LocalOrTemp::Local)); + } + Operator::SetLocal { local_index } => { + let local_index = local_index as usize; + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + self.locals[local_index], + ); + } + Operator::TeeLocal { local_index } => { + let local_index = local_index as usize; + let (loc, _) = *self.value_stack.last().unwrap(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + self.locals[local_index], + ); + } + Operator::I32Const { value } => self + .value_stack + .push((Location::Imm32(value as u32), LocalOrTemp::Temp)), + Operator::I32Add => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_add, + ), + Operator::I32Sub => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sub, + ), + Operator::I32Mul => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_imul, + ), + Operator::I32DivU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32DivS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_cdq(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32RemU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S32, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I32RemS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + + let normal_path = a.get_label(); + let end = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0x80000000), + loc_a, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0xffffffff), + loc_b, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + a.emit_mov(Size::S32, Location::Imm32(0), ret); + a.emit_jmp(Condition::None, end); + + a.emit_label(normal_path); + a.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + a.emit_cdq(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S32, + loc_b, + ); + a.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + + a.emit_label(end); + } + Operator::I32And => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_and, + ), + Operator::I32Or => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_or, + ), + Operator::I32Xor => Self::emit_binop_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_xor, + ), + Operator::I32Eq => Self::emit_cmpop_i32( + a, + &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 => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_lzcnt, + ), + Operator::I32Ctz => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_tzcnt, + ), + Operator::I32Popcnt => Self::emit_xcnt_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_popcnt, + ), + Operator::I32Shl => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shl, + ), + Operator::I32ShrU => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shr, + ), + Operator::I32ShrS => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sar, + ), + Operator::I32Rotl => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_rol, + ), + Operator::I32Rotr => Self::emit_shift_i32( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_ror, + ), + Operator::I32LtU => Self::emit_cmpop_i32( + a, + &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) + } + 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), LocalOrTemp::Temp)); + } + Operator::I64Add => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_add, + ), + Operator::I64Sub => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sub, + ), + Operator::I64Mul => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_imul, + ), + Operator::I64DivU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64DivS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_cqo(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64RemU => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_xor(Size::S64, Location::GPR(GPR::RDX), Location::GPR(GPR::RDX)); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_div, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + } + Operator::I64RemS => { + // We assume that RAX and RDX are temporary registers here. + let loc_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + + let normal_path = a.get_label(); + let end = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0x8000000000000000u64), + loc_a, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0xffffffffffffffffu64), + loc_b, + ); + a.emit_jmp(Condition::NotEqual, normal_path); + a.emit_mov(Size::S64, Location::Imm64(0), ret); + a.emit_jmp(Condition::None, end); + + a.emit_label(normal_path); + + a.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + a.emit_cqo(); + Self::emit_relaxed_xdiv( + a, + &mut self.machine, + Assembler::emit_idiv, + Size::S64, + loc_b, + ); + a.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_label(end); + } + Operator::I64And => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_and, + ), + Operator::I64Or => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_or, + ), + Operator::I64Xor => Self::emit_binop_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_xor, + ), + Operator::I64Eq => Self::emit_cmpop_i64( + a, + &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 => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_lzcnt, + ), + Operator::I64Ctz => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_tzcnt, + ), + Operator::I64Popcnt => Self::emit_xcnt_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_popcnt, + ), + Operator::I64Shl => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shl, + ), + Operator::I64ShrU => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_shr, + ), + Operator::I64ShrS => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_sar, + ), + Operator::I64Rotl => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_rol, + ), + Operator::I64Rotr => Self::emit_shift_i64( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_ror, + ), + Operator::I64LtU => Self::emit_cmpop_i64( + a, + &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) + } + 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()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + Operator::I64ExtendSI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_zx_sx( + a, + &mut self.machine, + Assembler::emit_movsx, + Size::S32, + loc, + Size::S64, + ret, + ); + } + Operator::I32WrapI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + + Operator::F32Const { value } => self + .value_stack + .push((Location::Imm32(value.bits()), LocalOrTemp::Temp)), + Operator::F32Add => Self::emit_fp_binop_avx( + a, + &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 => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxss, + ), + Operator::F32Min => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminss, + ), + Operator::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 = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc_a, Location::GPR(tmp1)); + a.emit_mov(Size::S32, loc_b, Location::GPR(tmp2)); + a.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp1), + ); + a.emit_and( + Size::S32, + Location::Imm32(0x80000000u32), + Location::GPR(tmp2), + ); + a.emit_or(Size::S32, Location::GPR(tmp2), Location::GPR(tmp1)); + a.emit_mov(Size::S32, Location::GPR(tmp1), ret); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F32Abs => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + a.emit_mov(Size::S32, loc, Location::GPR(tmp)); + a.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp), + ); + a.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + + Operator::F32Neg => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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 } => self + .value_stack + .push((Location::Imm64(value.bits()), LocalOrTemp::Temp)), + Operator::F64Add => Self::emit_fp_binop_avx( + a, + &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 => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vmaxsd, + ), + Operator::F64Min => Self::emit_fp_binop_avx( + a, + &mut self.machine, + &mut self.value_stack, + Assembler::emit_vminsd, + ), + Operator::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 = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + let c = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc_a, Location::GPR(tmp1)); + a.emit_mov(Size::S64, loc_b, Location::GPR(tmp2)); + + a.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp1)); + + a.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp2)); + + a.emit_or(Size::S64, Location::GPR(tmp2), Location::GPR(tmp1)); + a.emit_mov(Size::S64, Location::GPR(tmp1), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F64Abs => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let c = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov(Size::S64, loc, Location::GPR(tmp)); + a.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + a.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp)); + a.emit_mov(Size::S64, Location::GPR(tmp), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp); + } + + Operator::F64Neg => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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( + a, + &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 = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + } + Operator::F32ReinterpretI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S32, + loc, + ret, + ); + } + } + + Operator::I64ReinterpretF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + ret, + ); + } + } + Operator::F64ReinterpretI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + if loc != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + ret, + ); + } + } + + Operator::I32TruncUF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.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); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncSF32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.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 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.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(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + a.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 + + 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 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.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); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncSF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncSF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + a.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); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncUF64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + a.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, + ); + + 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 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertUI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertSI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + Operator::F32ConvertUI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + } + Operator::F64ConvertUI32 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + } + Operator::F64ConvertSI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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); + } + Operator::F64ConvertUI64 => { + let loc = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + 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 } => { + let function_index = function_index as usize; + let label = self + .function_labels + .as_mut() + .unwrap() + .entry(function_index) + .or_insert_with(|| (a.get_label(), None)) + .0; + let sig_index = *self + .function_signatures + .get(FuncIndex::new(function_index)) + .unwrap(); + let sig = self.signatures.get(sig_index).unwrap(); + let param_types: SmallVec<[WpType; 8]> = + sig.params().iter().cloned().map(type_to_wp_type).collect(); + let return_types: SmallVec<[WpType; 1]> = + sig.returns().iter().cloned().map(type_to_wp_type).collect(); + + let params: SmallVec<[_; 8]> = self + .value_stack + .drain(self.value_stack.len() - param_types.len()..) + .collect(); + let released: SmallVec<[Location; 8]> = params + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_only_regs(&released); + + Self::emit_call_sysv_label( + a, + &mut self.machine, + label, + params.iter().map(|&(x, _)| x), + ); + + self.machine.release_locations_only_stack(a, &released); + + if return_types.len() > 0 { + let ret = self.machine.acquire_locations(a, &[return_types[0]], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + Operator::CallIndirect { index, table_index } => { + assert_eq!(table_index, 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(); + let return_types: SmallVec<[WpType; 1]> = + sig.returns().iter().cloned().map(type_to_wp_type).collect(); + + let func_index = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + let params: SmallVec<[_; 8]> = self + .value_stack + .drain(self.value_stack.len() - param_types.len()..) + .collect(); + let released: SmallVec<[Location; 8]> = params + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_only_regs(&released); + + let table_base = self.machine.acquire_temp_gpr().unwrap(); + let table_count = self.machine.acquire_temp_gpr().unwrap(); + let sigidx = self.machine.acquire_temp_gpr().unwrap(); + + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + match TableIndex::new(0).local_or_import(module_info) { + LocalOrImport::Local(_) => vm::Ctx::offset_tables(), + LocalOrImport::Import(_) => vm::Ctx::offset_imported_tables(), + } as i32, + ), + Location::GPR(table_base), + ); + a.emit_mov( + Size::S64, + Location::Memory(table_base, 0), + Location::GPR(table_base), + ); + a.emit_mov( + Size::S32, + Location::Memory(table_base, LocalTable::offset_count() as i32), + Location::GPR(table_count), + ); + a.emit_mov( + Size::S64, + Location::Memory(table_base, LocalTable::offset_base() as i32), + Location::GPR(table_base), + ); + a.emit_cmp(Size::S32, func_index, Location::GPR(table_count)); + a.emit_conditional_trap(Condition::BelowEqual); + a.emit_mov(Size::S64, func_index, Location::GPR(table_count)); + a.emit_imul_imm32_gpr64(vm::Anyfunc::size() as u32, table_count); + a.emit_add( + Size::S64, + Location::GPR(table_base), + Location::GPR(table_count), + ); + a.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + vm::Ctx::offset_signatures() as i32, + ), + Location::GPR(sigidx), + ); + a.emit_mov( + Size::S32, + Location::Memory(sigidx, (index * 4) as i32), + Location::GPR(sigidx), + ); + a.emit_cmp( + Size::S32, + Location::GPR(sigidx), + Location::Memory(table_count, (vm::Anyfunc::offset_sig_id() as usize) as i32), + ); + a.emit_conditional_trap(Condition::NotEqual); + + self.machine.release_temp_gpr(sigidx); + self.machine.release_temp_gpr(table_count); + self.machine.release_temp_gpr(table_base); + + if table_count != GPR::RAX { + a.emit_mov( + Size::S64, + Location::GPR(table_count), + Location::GPR(GPR::RAX), + ); + } + + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_call_location(Location::Memory( + GPR::RAX, + (vm::Anyfunc::offset_func() as usize) as i32, + )); + }, + params.iter().map(|&(x, _)| x), + ); + + self.machine.release_locations_only_stack(a, &released); + + if return_types.len() > 0 { + let ret = self.machine.acquire_locations(a, &[return_types[0]], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + Operator::If { ty } => { + let label_end = a.get_label(); + let label_else = a.get_label(); + + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + self.control_stack.push(ControlFrame { + label: label_end, + loop_like: false, + if_else: IfElseState::If(label_else), + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, label_else); + } + Operator::Else => { + let mut frame = self.control_stack.last_mut().unwrap(); + + if !was_unreachable && frame.returns.len() > 0 { + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + + let released: Vec = self + .value_stack + .drain(frame.value_stack_depth..) + .filter(|&(_, lot)| lot == LocalOrTemp::Temp) + .map(|(x, _)| x) + .collect(); + self.machine.release_locations(a, &released); + + match frame.if_else { + IfElseState::If(label) => { + a.emit_jmp(Condition::None, frame.label); + a.emit_label(label); + frame.if_else = IfElseState::Else; + } + _ => unreachable!(), + } + } + Operator::Select => { + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let v_b = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let v_a = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + let end_label = a.get_label(); + let zero_label = a.get_label(); + + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, zero_label); + if v_a != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + v_a, + ret, + ); + } + a.emit_jmp(Condition::None, end_label); + a.emit_label(zero_label); + if v_b != ret { + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + v_b, + ret, + ); + } + a.emit_label(end_label); + } + Operator::Block { ty } => { + self.control_stack.push(ControlFrame { + label: a.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + } + Operator::Loop { ty } => { + let label = a.get_label(); + self.control_stack.push(ControlFrame { + label: label, + loop_like: true, + if_else: IfElseState::None, + returns: match ty { + WpType::EmptyBlockType => smallvec![], + _ => smallvec![ty], + }, + value_stack_depth: self.value_stack.len(), + }); + a.emit_label(label); + } + Operator::Nop => {} + Operator::MemorySize { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let target: usize = match memory_index.local_or_import(module_info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &module_info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::local_dynamic_memory_size as usize, + MemoryType::Static => vmcalls::local_static_memory_size as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &module_info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::imported_dynamic_memory_size as usize, + MemoryType::Static => vmcalls::imported_static_memory_size as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + }; + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_mov( + Size::S64, + Location::Imm64(target as u64), + Location::GPR(GPR::RAX), + ); + a.emit_call_location(Location::GPR(GPR::RAX)); + }, + ::std::iter::once(Location::Imm32(memory_index.index() as u32)), + ); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::MemoryGrow { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let target: usize = match memory_index.local_or_import(module_info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &module_info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::local_dynamic_memory_grow as usize, + MemoryType::Static => vmcalls::local_static_memory_grow as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &module_info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => vmcalls::imported_dynamic_memory_grow as usize, + MemoryType::Static => vmcalls::imported_static_memory_grow as usize, + MemoryType::SharedStatic => unimplemented!(), + } + } + }; + + let (param_pages, param_pages_lot) = self.value_stack.pop().unwrap(); + + if param_pages_lot == LocalOrTemp::Temp { + self.machine.release_locations_only_regs(&[param_pages]); + } + + Self::emit_call_sysv( + a, + &mut self.machine, + |a| { + a.emit_mov( + Size::S64, + Location::Imm64(target as u64), + Location::GPR(GPR::RAX), + ); + a.emit_call_location(Location::GPR(GPR::RAX)); + }, + ::std::iter::once(Location::Imm32(memory_index.index() as u32)) + .chain(::std::iter::once(param_pages)), + ); + + if param_pages_lot == LocalOrTemp::Temp { + self.machine.release_locations_only_stack(a, &[param_pages]); + } + + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::I32Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::F32Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I32Load8U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load8S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load16U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Load16S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I32], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + ); + }, + ); + } + Operator::I32Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::F32Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I32Store8 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I32Store16 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::F64Load { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::F64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I64Load8U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load8S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load16U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load16S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Load32U { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + match ret { + Location::GPR(_) => {} + _ => { + a.emit_mov(Size::S64, Location::Imm64(0), ret); + } + } + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + }, + ); + } + Operator::I64Load32S { ref memarg } => { + let target = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let ret = self.machine.acquire_locations(a, &[WpType::I64], false)[0]; + self.value_stack.push((ret, LocalOrTemp::Temp)); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_zx_sx( + a, + m, + Assembler::emit_movsx, + Size::S32, + Location::Memory(addr, 0), + Size::S64, + ret, + ); + }, + ); + } + Operator::I64Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::F64Store { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 8, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store8 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 1, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store16 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 2, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::I64Store32 { ref memarg } => { + let target_value = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let target_addr = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + + Self::emit_memory_op( + module_info, + a, + &mut self.machine, + target_addr, + memarg.offset as usize, + 4, + |a, m, addr| { + Self::emit_relaxed_binop( + a, + m, + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + }, + ); + } + Operator::Unreachable => { + a.emit_ud2(); + self.unreachable_depth = 1; + } + Operator::Return => { + let frame = &self.control_stack[0]; + if frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + self.unreachable_depth = 1; + } + Operator::Br { relative_depth } => { + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + if !frame.loop_like && frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + self.unreachable_depth = 1; + } + Operator::BrIf { relative_depth } => { + let after = a.get_label(); + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0), + cond, + ); + a.emit_jmp(Condition::Equal, after); + + 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); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + + a.emit_label(after); + } + Operator::BrTable { ref table } => { + let (targets, default_target) = table.read_table().unwrap(); + let cond = + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let mut table = vec![0usize; targets.len()]; + let default_br = a.get_label(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_cmp, + Size::S32, + Location::Imm32(targets.len() as u32), + cond, + ); + a.emit_jmp(Condition::AboveEqual, default_br); + + a.emit_mov( + Size::S64, + Location::Imm64(table.as_ptr() as usize as u64), + Location::GPR(GPR::RCX), + ); + a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); + a.emit_shl(Size::S32, Location::Imm8(3), Location::GPR(GPR::RDX)); + a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); + a.emit_jmp_location(Location::Memory(GPR::RDX, 0)); + + for (i, target) in targets.iter().enumerate() { + let AssemblyOffset(offset) = a.offset(); + table[i] = offset; + 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); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + } + a.emit_label(default_br); + + { + 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); + let (loc, _) = *self.value_stack.last().unwrap(); + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + let released: Vec = self.value_stack[frame.value_stack_depth..] + .iter() + .filter(|&&(_, lot)| lot == LocalOrTemp::Temp) + .map(|&(x, _)| x) + .collect(); + self.machine.release_locations_keep_state(a, &released); + a.emit_jmp(Condition::None, frame.label); + } + + self.br_table_data.as_mut().unwrap().push(table); + self.unreachable_depth = 1; + } + Operator::Drop => { + get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + } + Operator::End => { + let frame = self.control_stack.pop().unwrap(); + + if !was_unreachable && frame.returns.len() > 0 { + let (loc, _) = *self.value_stack.last().unwrap(); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + + if self.control_stack.len() == 0 { + a.emit_label(frame.label); + self.machine.finalize_locals(a, &self.locals); + a.emit_mov(Size::S64, Location::GPR(GPR::RBP), Location::GPR(GPR::RSP)); + a.emit_pop(Size::S64, Location::GPR(GPR::RBP)); + a.emit_ret(); + } else { + let released: Vec = self + .value_stack + .drain(frame.value_stack_depth..) + .filter(|&(_, lot)| lot == LocalOrTemp::Temp) + .map(|(x, _)| x) + .collect(); + self.machine.release_locations(a, &released); + + if !frame.loop_like { + a.emit_label(frame.label); + } + + if let IfElseState::If(label) = frame.if_else { + a.emit_label(label); + } + + if frame.returns.len() > 0 { + assert_eq!(frame.returns.len(), 1); + let loc = self.machine.acquire_locations(a, &frame.returns, false)[0]; + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), loc); + self.value_stack.push((loc, LocalOrTemp::Temp)); + } + } + } + _ => { + panic!("not yet implemented: {:?}", op); + } + } + + Ok(()) + } +} + +fn type_to_wp_type(ty: Type) -> WpType { + match ty { + Type::I32 => WpType::I32, + Type::I64 => WpType::I64, + Type::F32 => WpType::F32, + Type::F64 => WpType::F64, + } +} + +fn get_location_released( + a: &mut Assembler, + m: &mut Machine, + (loc, lot): (Location, LocalOrTemp), +) -> Location { + if lot == LocalOrTemp::Temp { + m.release_locations(a, &[loc]); + } + loc +} + +fn sort_call_movs(movs: &mut [(Location, GPR)]) { + for i in 0..movs.len() { + for j in (i + 1)..movs.len() { + if let Location::GPR(src_gpr) = movs[j].0 { + if src_gpr == movs[i].1 { + movs.swap(i, j); + } + } + } + } +} diff --git a/lib/singlepass-backend/src/emitter_x64.rs b/lib/singlepass-backend/src/emitter_x64.rs new file mode 100644 index 000000000..889b1099d --- /dev/null +++ b/lib/singlepass-backend/src/emitter_x64.rs @@ -0,0 +1,956 @@ +use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum GPR { + RAX, + RCX, + RDX, + RBX, + RSP, + RBP, + RSI, + RDI, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum XMM { + XMM0, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Location { + Imm8(u8), + Imm32(u32), + Imm64(u64), + GPR(GPR), + XMM(XMM), + Memory(GPR, i32), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Condition { + None, + Above, + AboveEqual, + Below, + BelowEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Equal, + NotEqual, + Signed, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Size { + S8, + S16, + S32, + S64, +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum XMMOrMemory { + XMM(XMM), + Memory(GPR, i32), +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum GPROrMemory { + GPR(GPR), + Memory(GPR, i32), +} + +pub trait Emitter { + type Label; + type Offset; + + fn get_label(&mut self) -> Self::Label; + fn get_offset(&mut self) -> Self::Offset; + + fn emit_label(&mut self, label: Self::Label); + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea_label(&mut self, label: Self::Label, dst: Location); + fn emit_cdq(&mut self); + fn emit_cqo(&mut self); + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location); + fn emit_jmp(&mut self, condition: Condition, label: Self::Label); + fn emit_jmp_location(&mut self, loc: Location); + fn emit_conditional_trap(&mut self, condition: Condition); + fn emit_set(&mut self, condition: Condition, dst: GPR); + fn emit_push(&mut self, sz: Size, src: Location); + fn emit_pop(&mut self, sz: Size, dst: Location); + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location); + fn emit_add(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location); + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location); + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR); + fn emit_div(&mut self, sz: Size, divisor: Location); + fn emit_idiv(&mut self, sz: Size, divisor: Location); + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location); + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location); + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location); + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location); + fn emit_and(&mut self, sz: Size, src: Location, dst: Location); + fn emit_or(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR); + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR); + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR); + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR); + + fn emit_vaddss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vaddsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpeqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpeqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpneqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpneqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpltss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpltsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpless(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmplesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgess(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vsqrtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsqrtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vroundss_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcvtss2sd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcvtsd2ss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM); + fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM); + + fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR); + + fn emit_vcvtsi2ss_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2ss_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + + fn emit_test_gpr_64(&mut self, reg: GPR); + + fn emit_ud2(&mut self); + fn emit_ret(&mut self); + fn emit_call_label(&mut self, label: Self::Label); + fn emit_call_location(&mut self, loc: Location); + + fn emit_bkpt(&mut self); +} + +macro_rules! unop_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rd(loc as u8)); + }, + (Size::S64, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rq(loc as u8)); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(loc as u8) + disp] ); + }, + (Size::S64, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(loc as u8) + disp] ); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_gpr_or_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + unop_gpr!($ins, $assembler, $sz, $loc, { + unop_mem!($ins, $assembler, $sz, $loc, $otherwise) + }) + }; +} + +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 ; $ins Rd(dst as u8), src as i32); // IMM32_2GPR + }, + (Size::S64, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), src as i32); // IMM32_2GPR + }, + _ => $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)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], src as i32); + }, + (Size::S64, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], src as i32); + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_imm64_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S64, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), QWORD src as i64); // IMM32_2GPR + }, + _ => $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 Rd(dst as u8), Rd(src as u8)); // GPR2GPR + }, + (Size::S64, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), Rq(src as u8)); // GPR2GPR + }, + _ => $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(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rd(src as u8)); // GPR2MEM + }, + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rq(src as u8)); // GPR2MEM + }, + _ => $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(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + _ => $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::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), cl); + }, + (Size::S32, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S32, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), imm as i8); + }, + (Size::S32, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], imm as i8); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), cl); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S64, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), imm as i8); + }, + (Size::S64, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], imm as i8); + }, + _ => $otherwise + } + } +} + +macro_rules! jmp_op { + ($ins:ident, $assembler:tt, $label:ident) => { + dynasm!($assembler ; $ins =>$label); + } +} + +macro_rules! trap_op { + ($ins:ident, $assembler:tt) => { + dynasm!($assembler + ; $ins >trap + ; jmp >after + ; trap: + ; ud2 + ; after: + ); + } +} + +macro_rules! avx_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + // Dynasm bug: AVX instructions are not encoded correctly. + match src2 { + XMMOrMemory::XMM(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rx((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rx((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rx((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rx((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rx((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rx((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rx((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rx((x as u8))), + }, + XMMOrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_64_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rq((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rq((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rq((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rq((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rq((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rq((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rq((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rq((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, QWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, QWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, QWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, QWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, QWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, QWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, QWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, QWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_32_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rd((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rd((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rd((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rd((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rd((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rd((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rd((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rd((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, DWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, DWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, DWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, DWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, DWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, DWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, DWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, DWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_round_fn { + ($ins:ident, $name:ident, $mode:expr) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + match src2 { + XMMOrMemory::XMM(x) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), Rx((x as u8)), $mode), + XMMOrMemory::Memory(base, disp) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), [Rq((base as u8)) + disp], $mode), + } + } + } +} + +impl Emitter for Assembler { + type Label = DynamicLabel; + type Offset = AssemblyOffset; + + fn get_label(&mut self) -> DynamicLabel { + self.new_dynamic_label() + } + + fn get_offset(&mut self) -> AssemblyOffset { + self.offset() + } + + fn emit_label(&mut self, label: Self::Label) { + dynasm!(self ; => label); + } + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(mov, self, sz, src, dst, { + binop_imm64_gpr!(mov, self, sz, src, dst, { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S8, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S8, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S16, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S16, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S16, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S32, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rd(dst as u8), src as i32); + } + (Size::S32, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov DWORD [Rq(dst as u8) + disp], src as i32); + } + (Size::S32, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), Rd(src as u8)); + } + (Size::S32, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movd Rd(dst as u8), Rx(src as u8)); + } + (Size::S32, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S32, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movd [Rq(dst as u8) + disp], Rx(src as u8)); + } + + (Size::S64, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rq(src as u8)); + } + (Size::S64, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movq Rq(dst as u8), Rx(src as u8)); + } + (Size::S64, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movq [Rq(dst as u8) + disp], Rx(src as u8)); + } + (_, Location::XMM(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rx(src as u8)); + } + + _ => panic!("MOV {:?} {:?} {:?}", 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)) => { + dynasm!(self ; lea Rd(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rq(dst as u8), [Rq(src as u8) + disp]); + } + _ => unreachable!(), + } + } + fn emit_lea_label(&mut self, label: Self::Label, dst: Location) { + match dst { + Location::GPR(x) => { + dynasm!(self ; lea Rq(x as u8), [=>label]); + } + _ => unreachable!(), + } + } + fn emit_cdq(&mut self) { + dynasm!(self ; cdq); + } + fn emit_cqo(&mut self) { + dynasm!(self ; cqo); + } + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(xor, self, sz, src, dst, { unreachable!() }); + } + fn emit_jmp(&mut self, condition: Condition, label: Self::Label) { + match condition { + Condition::None => jmp_op!(jmp, self, label), + Condition::Above => jmp_op!(ja, self, label), + Condition::AboveEqual => jmp_op!(jae, self, label), + Condition::Below => jmp_op!(jb, self, label), + Condition::BelowEqual => jmp_op!(jbe, self, label), + Condition::Greater => jmp_op!(jg, self, label), + Condition::GreaterEqual => jmp_op!(jge, self, label), + Condition::Less => jmp_op!(jl, self, label), + Condition::LessEqual => jmp_op!(jle, self, label), + Condition::Equal => jmp_op!(je, self, label), + Condition::NotEqual => jmp_op!(jne, self, label), + Condition::Signed => jmp_op!(js, self, label), + } + } + fn emit_jmp_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; jmp Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; jmp QWORD [Rq(base as u8) + disp]), + _ => unreachable!(), + } + } + fn emit_conditional_trap(&mut self, condition: Condition) { + match condition { + Condition::None => trap_op!(jmp, self), + Condition::Above => trap_op!(ja, self), + Condition::AboveEqual => trap_op!(jae, self), + Condition::Below => trap_op!(jb, self), + Condition::BelowEqual => trap_op!(jbe, self), + Condition::Greater => trap_op!(jg, self), + Condition::GreaterEqual => trap_op!(jge, self), + Condition::Less => trap_op!(jl, self), + Condition::LessEqual => trap_op!(jle, self), + Condition::Equal => trap_op!(je, self), + Condition::NotEqual => trap_op!(jne, self), + Condition::Signed => trap_op!(js, self), + } + } + fn emit_set(&mut self, condition: Condition, dst: GPR) { + match condition { + Condition::Above => dynasm!(self ; seta Rb(dst as u8)), + Condition::AboveEqual => dynasm!(self ; setae Rb(dst as u8)), + Condition::Below => dynasm!(self ; setb Rb(dst as u8)), + Condition::BelowEqual => dynasm!(self ; setbe Rb(dst as u8)), + Condition::Greater => dynasm!(self ; setg Rb(dst as u8)), + Condition::GreaterEqual => dynasm!(self ; setge Rb(dst as u8)), + Condition::Less => dynasm!(self ; setl Rb(dst as u8)), + Condition::LessEqual => dynasm!(self ; setle Rb(dst as u8)), + Condition::Equal => dynasm!(self ; sete Rb(dst as u8)), + Condition::NotEqual => dynasm!(self ; setne Rb(dst as u8)), + Condition::Signed => dynasm!(self ; sets Rb(dst as u8)), + _ => unreachable!(), + } + } + fn emit_push(&mut self, sz: Size, src: Location) { + 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]) + } + _ => panic!("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]) + } + _ => panic!("pop {:?} {:?}", sz, dst), + } + } + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location) { + binop_all_nofp!(cmp, self, sz, left, right, { + panic!("{:?} {:?} {:?}", sz, left, right); + }); + } + fn emit_add(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(add, self, sz, src, dst, { unreachable!() }); + } + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(sub, self, sz, src, dst, { unreachable!() }); + } + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(imul, self, sz, src, dst, { + binop_mem_gpr!(imul, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR) { + dynasm!(self ; imul Rq(dst as u8), Rq(dst as u8), src as i32); + } + fn emit_div(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(div, self, sz, divisor, { unreachable!() }); + } + fn emit_idiv(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(idiv, self, sz, divisor, { unreachable!() }); + } + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shl, self, sz, src, dst, { unreachable!() }); + } + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shr, self, sz, src, dst, { unreachable!() }); + } + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(sar, self, sz, src, dst, { unreachable!() }); + } + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(rol, self, sz, src, dst, { unreachable!() }); + } + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(ror, self, sz, src, dst, { unreachable!() }); + } + fn emit_and(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(and, self, sz, src, dst, { unreachable!() }); + } + fn emit_or(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(or, self, sz, src, dst, { unreachable!() }); + } + fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(lzcnt, self, sz, src, dst, { + binop_mem_gpr!(lzcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(tzcnt, self, sz, src, dst, { + binop_mem_gpr!(tzcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(popcnt, self, sz, src, dst, { + binop_mem_gpr!(popcnt, self, sz, src, dst, { unreachable!() }) + }); + } + fn emit_movzx(&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 ; movzx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + _ => 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 ; movsx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rd(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S32, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), DWORD [Rq(src as u8) + disp]); + } + _ => unreachable!(), + } + } + + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rd(dst as u8), BYTE src as i8); + } + + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rq(dst as u8), BYTE src as i8); + } + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rd(dst as u8), Rd(src as u8)); + } + + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rq(dst as u8), Rq(src as u8)); + } + + avx_fn!(vaddss, emit_vaddss); + avx_fn!(vaddsd, emit_vaddsd); + + avx_fn!(vsubss, emit_vsubss); + avx_fn!(vsubsd, emit_vsubsd); + + avx_fn!(vmulss, emit_vmulss); + avx_fn!(vmulsd, emit_vmulsd); + + avx_fn!(vdivss, emit_vdivss); + avx_fn!(vdivsd, emit_vdivsd); + + avx_fn!(vmaxss, emit_vmaxss); + avx_fn!(vmaxsd, emit_vmaxsd); + + avx_fn!(vminss, emit_vminss); + avx_fn!(vminsd, emit_vminsd); + + avx_fn!(vcmpeqss, emit_vcmpeqss); + avx_fn!(vcmpeqsd, emit_vcmpeqsd); + + avx_fn!(vcmpneqss, emit_vcmpneqss); + avx_fn!(vcmpneqsd, emit_vcmpneqsd); + + avx_fn!(vcmpltss, emit_vcmpltss); + avx_fn!(vcmpltsd, emit_vcmpltsd); + + avx_fn!(vcmpless, emit_vcmpless); + avx_fn!(vcmplesd, emit_vcmplesd); + + avx_fn!(vcmpgtss, emit_vcmpgtss); + avx_fn!(vcmpgtsd, emit_vcmpgtsd); + + avx_fn!(vcmpgess, emit_vcmpgess); + avx_fn!(vcmpgesd, emit_vcmpgesd); + + avx_fn!(vsqrtss, emit_vsqrtss); + avx_fn!(vsqrtsd, emit_vsqrtsd); + + avx_fn!(vcvtss2sd, emit_vcvtss2sd); + avx_fn!(vcvtsd2ss, emit_vcvtsd2ss); + + avx_round_fn!(vroundss, emit_vroundss_nearest, 0); + avx_round_fn!(vroundss, emit_vroundss_floor, 1); + avx_round_fn!(vroundss, emit_vroundss_ceil, 2); + avx_round_fn!(vroundss, emit_vroundss_trunc, 3); + avx_round_fn!(vroundsd, emit_vroundsd_nearest, 0); + avx_round_fn!(vroundsd, emit_vroundsd_floor, 1); + avx_round_fn!(vroundsd, emit_vroundsd_ceil, 2); + avx_round_fn!(vroundsd, emit_vroundsd_trunc, 3); + + avx_i2f_32_fn!(vcvtsi2ss, emit_vcvtsi2ss_32); + avx_i2f_32_fn!(vcvtsi2sd, emit_vcvtsi2sd_32); + avx_i2f_64_fn!(vcvtsi2ss, emit_vcvtsi2ss_64); + avx_i2f_64_fn!(vcvtsi2sd, emit_vcvtsi2sd_64); + + 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]) + } + } + } + + 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]) + } + } + } + + 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]) + } + } + } + + 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]) + } + } + } + + 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]) + } + } + } + + 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]) + } + } + } + + fn emit_test_gpr_64(&mut self, reg: GPR) { + dynasm!(self ; test Rq(reg as u8), Rq(reg as u8)); + } + + fn emit_ud2(&mut self) { + dynasm!(self ; ud2); + } + fn emit_ret(&mut self) { + dynasm!(self ; ret); + } + + fn emit_call_label(&mut self, label: Self::Label) { + dynasm!(self ; call =>label); + } + fn emit_call_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; call Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; call QWORD [Rq(base as u8) + disp]), + _ => unreachable!(), + } + } + + fn emit_bkpt(&mut self) { + dynasm!(self ; int 0x3); + } +} diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs new file mode 100644 index 000000000..f1011591d --- /dev/null +++ b/lib/singlepass-backend/src/lib.rs @@ -0,0 +1,36 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] +#![feature(proc_macro_hygiene)] + +#[cfg(not(any( + all(target_os = "macos", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "x86_64"), +)))] +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; + +#[macro_use] +extern crate dynasm; + +#[macro_use] +extern crate lazy_static; + +extern crate byteorder; +#[macro_use] +extern crate smallvec; + +mod codegen_x64; +mod emitter_x64; +mod machine; +mod protect_unix; + +pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator; +pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator; + +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; +pub type SinglePassCompiler = SimpleStreamingCompilerGen< + codegen_x64::X64ModuleCodeGenerator, + codegen_x64::X64FunctionCode, + codegen_x64::X64ExecutionContext, + codegen_x64::CodegenError, +>; diff --git a/lib/singlepass-backend/src/machine.rs b/lib/singlepass-backend/src/machine.rs new file mode 100644 index 000000000..bd2e43fc0 --- /dev/null +++ b/lib/singlepass-backend/src/machine.rs @@ -0,0 +1,419 @@ +use crate::emitter_x64::*; +use smallvec::SmallVec; +use std::collections::HashSet; +use wasmparser::Type as WpType; + +struct MachineStackOffset(usize); + +pub struct Machine { + used_gprs: HashSet, + used_xmms: HashSet, + stack_offset: MachineStackOffset, + save_area_offset: Option, +} + +impl Machine { + pub fn new() -> Self { + Machine { + used_gprs: HashSet::new(), + used_xmms: HashSet::new(), + stack_offset: MachineStackOffset(0), + save_area_offset: None, + } + } + + pub fn get_stack_offset(&self) -> usize { + self.stack_offset.0 + } + + pub fn get_used_gprs(&self) -> Vec { + self.used_gprs.iter().cloned().collect() + } + + pub fn get_used_xmms(&self) -> Vec { + self.used_xmms.iter().cloned().collect() + } + + pub fn get_vmctx_reg() -> GPR { + GPR::R15 + } + + /// Picks an unused general purpose register for local/stack/argument use. + /// + /// This method does not mark the register as used. + pub fn pick_gpr(&self) -> Option { + use GPR::*; + static REGS: &'static [GPR] = &[RSI, RDI, R8, R9, R10, R11]; + for r in REGS { + if !self.used_gprs.contains(r) { + return Some(*r); + } + } + None + } + + /// Picks an unused general purpose register for internal temporary use. + /// + /// This method does not mark the register as used. + pub fn pick_temp_gpr(&self) -> Option { + use GPR::*; + static REGS: &'static [GPR] = &[RAX, RCX, RDX]; + for r in REGS { + if !self.used_gprs.contains(r) { + return Some(*r); + } + } + None + } + + /// Acquires a temporary GPR. + pub fn acquire_temp_gpr(&mut self) -> Option { + let gpr = self.pick_temp_gpr(); + if let Some(x) = gpr { + self.used_gprs.insert(x); + } + gpr + } + + /// Releases a temporary GPR. + pub fn release_temp_gpr(&mut self, gpr: GPR) { + assert_eq!(self.used_gprs.remove(&gpr), true); + } + + /// Picks an unused XMM register. + /// + /// This method does not mark the register as used. + pub fn pick_xmm(&self) -> Option { + use XMM::*; + static REGS: &'static [XMM] = &[XMM3, XMM4, XMM5, XMM6, XMM7]; + for r in REGS { + if !self.used_xmms.contains(r) { + return Some(*r); + } + } + None + } + + /// Picks an unused XMM register for internal temporary use. + /// + /// This method does not mark the register as used. + pub fn pick_temp_xmm(&self) -> Option { + use XMM::*; + static REGS: &'static [XMM] = &[XMM0, XMM1, XMM2]; + for r in REGS { + if !self.used_xmms.contains(r) { + return Some(*r); + } + } + None + } + + /// Acquires a temporary XMM register. + pub fn acquire_temp_xmm(&mut self) -> Option { + let xmm = self.pick_temp_xmm(); + if let Some(x) = xmm { + self.used_xmms.insert(x); + } + xmm + } + + /// Releases a temporary XMM register. + pub fn release_temp_xmm(&mut self, xmm: XMM) { + assert_eq!(self.used_xmms.remove(&xmm), true); + } + + /// Acquires locations from the machine state. + /// + /// If the returned locations are used for stack value, `release_location` needs to be called on them; + /// Otherwise, if the returned locations are used for locals, `release_location` does not need to be called on them. + pub fn acquire_locations( + &mut self, + assembler: &mut E, + tys: &[WpType], + zeroed: bool, + ) -> SmallVec<[Location; 1]> { + let mut ret = smallvec![]; + let mut delta_stack_offset: usize = 0; + + for ty in tys { + let loc = match *ty { + WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM), + WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR), + _ => unreachable!(), + }; + + let loc = if let Some(x) = loc { + x + } else { + self.stack_offset.0 += 8; + delta_stack_offset += 8; + Location::Memory(GPR::RBP, -(self.stack_offset.0 as i32)) + }; + if let Location::GPR(x) = loc { + self.used_gprs.insert(x); + } else if let Location::XMM(x) = loc { + self.used_xmms.insert(x); + } + ret.push(loc); + } + + if delta_stack_offset != 0 { + assembler.emit_sub( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + if zeroed { + for i in 0..tys.len() { + assembler.emit_mov(Size::S64, Location::Imm32(0), ret[i]); + } + } + ret + } + + /// Releases locations used for stack value. + pub fn release_locations(&mut self, assembler: &mut E, locs: &[Location]) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::GPR(ref x) => { + assert_eq!(self.used_gprs.remove(x), true); + } + Location::XMM(ref x) => { + assert_eq!(self.used_xmms.remove(x), true); + } + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + self.stack_offset.0 -= 8; + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn release_locations_only_regs(&mut self, locs: &[Location]) { + for loc in locs.iter().rev() { + match *loc { + Location::GPR(ref x) => { + assert_eq!(self.used_gprs.remove(x), true); + } + Location::XMM(ref x) => { + assert_eq!(self.used_xmms.remove(x), true); + } + _ => {} + } + } + } + + pub fn release_locations_only_stack( + &mut self, + assembler: &mut E, + locs: &[Location], + ) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + self.stack_offset.0 -= 8; + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn release_locations_keep_state(&self, assembler: &mut E, locs: &[Location]) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 { + unreachable!(); + } + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + pub fn init_locals( + &mut self, + a: &mut E, + n: usize, + n_params: usize, + ) -> Vec { + // Use callee-saved registers for locals. + fn get_local_location(idx: usize) -> Location { + match idx { + 0 => Location::GPR(GPR::R12), + 1 => Location::GPR(GPR::R13), + 2 => Location::GPR(GPR::R14), + 3 => Location::GPR(GPR::RBX), + _ => Location::Memory(GPR::RBP, -(((idx - 3) * 8) as i32)), + } + } + + let mut locations: Vec = vec![]; + let mut allocated: usize = 0; + + // Determine locations for parameters. + for i in 0..n_params { + let loc = Self::get_param_location(i + 1); + locations.push(match loc { + Location::GPR(_) => { + let old_idx = allocated; + allocated += 1; + get_local_location(old_idx) + } + Location::Memory(_, _) => loc, + _ => unreachable!(), + }); + } + + // Determine locations for normal locals. + for _ in n_params..n { + locations.push(get_local_location(allocated)); + allocated += 1; + } + + // How many machine stack slots did all the locals use? + let num_mem_slots = locations + .iter() + .filter(|&&loc| match loc { + Location::Memory(_, _) => true, + _ => false, + }) + .count(); + + // Move RSP down to reserve space for machine stack slots. + if num_mem_slots > 0 { + a.emit_sub( + Size::S64, + Location::Imm32((num_mem_slots * 8) as u32), + Location::GPR(GPR::RSP), + ); + self.stack_offset.0 += num_mem_slots * 8; + } + + // Save callee-saved registers. + for loc in locations.iter() { + if let Location::GPR(_) = *loc { + a.emit_push(Size::S64, *loc); + self.stack_offset.0 += 8; + } + } + + // Save R15 for vmctx use. + a.emit_push(Size::S64, Location::GPR(GPR::R15)); + self.stack_offset.0 += 8; + + // Save the offset of static area. + self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0)); + + // Load in-register parameters into the allocated locations. + for i in 0..n_params { + let loc = Self::get_param_location(i + 1); + match loc { + Location::GPR(_) => { + a.emit_mov(Size::S64, loc, locations[i]); + } + _ => break, + } + } + + // Load vmctx. + a.emit_mov( + Size::S64, + Self::get_param_location(0), + Location::GPR(GPR::R15), + ); + + // Initialize all normal locals to zero. + for i in n_params..n { + a.emit_mov(Size::S64, Location::Imm32(0), locations[i]); + } + + locations + } + + pub fn finalize_locals(&mut self, a: &mut E, locations: &[Location]) { + // Unwind stack to the "save area". + a.emit_lea( + Size::S64, + Location::Memory( + GPR::RBP, + -(self.save_area_offset.as_ref().unwrap().0 as i32), + ), + Location::GPR(GPR::RSP), + ); + + // Restore R15 used by vmctx. + a.emit_pop(Size::S64, Location::GPR(GPR::R15)); + + // Restore callee-saved registers. + for loc in locations.iter().rev() { + if let Location::GPR(_) = *loc { + a.emit_pop(Size::S64, *loc); + } + } + } + + pub fn get_param_location(idx: usize) -> Location { + match idx { + 0 => Location::GPR(GPR::RDI), + 1 => Location::GPR(GPR::RSI), + 2 => Location::GPR(GPR::RDX), + 3 => Location::GPR(GPR::RCX), + 4 => Location::GPR(GPR::R8), + 5 => Location::GPR(GPR::R9), + _ => Location::Memory(GPR::RBP, (16 + (idx - 6) * 8) as i32), + } + } +} diff --git a/lib/dynasm-backend/src/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs similarity index 69% rename from lib/dynasm-backend/src/protect_unix.rs rename to lib/singlepass-backend/src/protect_unix.rs index 4daf633aa..6204134a7 100644 --- a/lib/dynasm-backend/src/protect_unix.rs +++ b/lib/singlepass-backend/src/protect_unix.rs @@ -1,5 +1,5 @@ //! Installing signal handlers allows us to handle traps and out-of-bounds memory -//! accesses that occur when runniing webassembly. +//! accesses that occur when runniing WebAssembly. //! //! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 //! @@ -12,11 +12,16 @@ use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, + SIGTRAP, }; -use std::cell::{Cell, UnsafeCell}; +use std::any::Any; +use std::cell::{Cell, RefCell, UnsafeCell}; +use std::collections::HashMap; use std::ptr; +use std::sync::Arc; use std::sync::Once; -use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; +use wasmer_runtime_core::codegen::BkptInfo; +use wasmer_runtime_core::typed_func::WasmTrapInfo; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -24,6 +29,20 @@ extern "C" fn signal_trap_handler( ucontext: *mut c_void, ) { unsafe { + match Signal::from_c_int(signum) { + Ok(SIGTRAP) => { + let (_, ip) = get_faulting_addr_and_ip(siginfo as _, ucontext); + let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone())); + if let Some(bkpt_map) = bkpt_map { + if let Some(ref x) = bkpt_map.get(&(ip as usize)) { + (x)(BkptInfo {}); + return; + } + } + } + _ => {} + } + do_unwind(signum, siginfo as _, ucontext); } } @@ -43,6 +62,7 @@ pub unsafe fn install_sighandler() { sigaction(SIGILL, &sa).unwrap(); sigaction(SIGSEGV, &sa).unwrap(); sigaction(SIGBUS, &sa).unwrap(); + sigaction(SIGTRAP, &sa).unwrap(); } const SETJMP_BUFFER_LEN: usize = 27; @@ -52,6 +72,8 @@ thread_local! { pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]); pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null())); pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static BKPT_MAP: RefCell>>>> = RefCell::new(Vec::new()); } pub unsafe fn trigger_trap() -> ! { @@ -60,7 +82,12 @@ pub unsafe fn trigger_trap() -> ! { longjmp(jmp_buf as *mut c_void, 0) } -pub fn call_protected(f: impl FnOnce() -> T) -> RuntimeResult { +pub enum CallProtError { + Trap(WasmTrapInfo), + Error(Box), +} + +pub fn call_protected(f: impl FnOnce() -> T) -> Result { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let prev_jmp_buf = *jmp_buf; @@ -73,21 +100,26 @@ pub fn call_protected(f: impl FnOnce() -> T) -> RuntimeResult { if signum != 0 { *jmp_buf = prev_jmp_buf; - let (faulting_addr, _inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); + if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(CallProtError::Error(data)) + } else { + // let (faulting_addr, _inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); - let signal = match Signal::from_c_int(signum) { - Ok(SIGFPE) => "floating-point exception", - Ok(SIGILL) => "illegal instruction", - Ok(SIGSEGV) => "segmentation violation", - Ok(SIGBUS) => "bus error", - Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", - }; - // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Trap { - msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), + // let signal = match Signal::from_c_int(signum) { + // Ok(SIGFPE) => "floating-point exception", + // Ok(SIGILL) => "illegal instruction", + // Ok(SIGSEGV) => "segmentation violation", + // Ok(SIGBUS) => "bus error", + // Err(_) => "error while getting the Signal", + // _ => "unknown trapped signal", + // }; + // // When the trap-handler is fully implemented, this will return more information. + // Err(RuntimeError::Trap { + // msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), + // } + // .into()) + Err(CallProtError::Trap(WasmTrapInfo::Unknown)) } - .into()) } else { let ret = f(); // TODO: Switch stack? *jmp_buf = prev_jmp_buf; diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index 8dd8f83f4..88136daa1 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.2.0" +version = "0.4.1" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0", optional = true } -wasmer-dynasm-backend = { path = "../dynasm-backend", version = "0.1.0", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } [build-dependencies] wabt = "0.7.2" @@ -25,4 +25,4 @@ default = ["fast-tests"] fast-tests = [] clif = [] llvm = ["wasmer-llvm-backend"] -dynasm = ["wasmer-dynasm-backend"] \ No newline at end of file +singlepass = ["wasmer-singlepass-backend"] diff --git a/lib/spectests/README.md b/lib/spectests/README.md new file mode 100644 index 000000000..8f9cee65b --- /dev/null +++ b/lib/spectests/README.md @@ -0,0 +1,32 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer Spectests + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate allows to test the Wasmer runtime against the official +specification test suite. diff --git a/lib/spectests/build/spectests.rs b/lib/spectests/build/spectests.rs index a13faefd1..10fb45678 100644 --- a/lib/spectests/build/spectests.rs +++ b/lib/spectests/build/spectests.rs @@ -107,13 +107,13 @@ fn get_compiler() -> impl Compiler { LLVMCompiler::new() } -#[cfg(feature = "dynasm")] +#[cfg(feature = "singlepass")] fn get_compiler() -> impl Compiler { - use wasmer_dynasm_backend::SinglePassCompiler; + use wasmer_singlepass_backend::SinglePassCompiler; SinglePassCompiler::new() } -#[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))] +#[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; diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index c61cdb18f..357adb5f7 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -22,13 +22,13 @@ fn get_compiler() -> impl Compiler { LLVMCompiler::new() } -#[cfg(feature = "dynasm")] +#[cfg(feature = "singlepass")] fn get_compiler() -> impl Compiler { - use wasmer_dynasm_backend::SinglePassCompiler; + use wasmer_singlepass_backend::SinglePassCompiler; SinglePassCompiler::new() } -#[cfg(not(any(feature = "llvm", feature = "clif", feature = "dynasm")))] +#[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; diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml new file mode 100644 index 000000000..c4c936a02 --- /dev/null +++ b/lib/wasi/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "wasmer-wasi" +version = "0.4.1" +description = "Wasmer runtime WASI implementation library" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +libc = "0.2.50" +rand = "0.6.5" +# wasmer-runtime-abi = { path = "../runtime-abi" } +hashbrown = "0.1.8" +generational-arena = "0.2.2" +log = "0.4.6" +byteorder = "1.3.1" diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs new file mode 100644 index 000000000..86f9ceaac --- /dev/null +++ b/lib/wasi/src/lib.rs @@ -0,0 +1,102 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + +#[macro_use] +extern crate log; + +#[macro_use] +mod macros; +mod ptr; +mod state; +mod syscalls; +mod utils; + +use self::state::{WasiFs, WasiState}; +use self::syscalls::*; + +use std::ffi::c_void; + +pub use self::utils::is_wasi_module; + +use wasmer_runtime_core::{func, import::ImportObject, imports}; + +/// This is returned in the Box RuntimeError::Error variant. +/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`. +pub struct ExitCode { + pub code: syscalls::types::__wasi_exitcode_t, +} + +pub fn generate_import_object( + args: Vec>, + envs: Vec>, + preopened_files: Vec, +) -> ImportObject { + let state_gen = move || { + fn state_destructor(data: *mut c_void) { + unsafe { + drop(Box::from_raw(data as *mut WasiState)); + } + } + + let state = Box::new(WasiState { + fs: WasiFs::new(&preopened_files).unwrap(), + args: &args[..], + envs: &envs[..], + }); + + ( + Box::leak(state) as *mut WasiState 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!(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!(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!(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!(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/macros.rs b/lib/wasi/src/macros.rs new file mode 100644 index 000000000..1420b8f2d --- /dev/null +++ b/lib/wasi/src/macros.rs @@ -0,0 +1,19 @@ +macro_rules! wasi_try { + ($expr:expr) => {{ + let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr; + match res { + Ok(val) => { + debug!("wasi::wasi_try::val: {:?}", val); + val + } + Err(err) => { + debug!("wasi::wasi_try::err: {:?}", err); + return err; + } + } + }}; + ($expr:expr; $e:expr) => {{ + let opt: Option<_> = $expr; + wasi_try!(opt.ok_or($e)) + }}; +} diff --git a/lib/wasi/src/ptr.rs b/lib/wasi/src/ptr.rs new file mode 100644 index 000000000..55a16d40c --- /dev/null +++ b/lib/wasi/src/ptr.rs @@ -0,0 +1,114 @@ +use crate::syscalls::types::{__wasi_errno_t, __WASI_EFAULT}; +use std::{cell::Cell, fmt, marker::PhantomData, mem}; +use wasmer_runtime_core::{ + memory::Memory, + types::{ValueType, WasmExternType}, +}; + +pub struct Array; +pub struct Item; + +#[repr(transparent)] +pub struct WasmPtr { + offset: u32, + _phantom: PhantomData<(T, Ty)>, +} + +impl WasmPtr { + #[inline] + pub fn new(offset: u32) -> Self { + Self { + offset, + _phantom: PhantomData, + } + } + + #[inline] + pub fn offset(self) -> u32 { + self.offset + } +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>(self, memory: &'a Memory) -> Result<&'a Cell, __wasi_errno_t> { + if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { + return Err(__WASI_EFAULT); + } + unsafe { + // clears bits below aligment amount (assumes power of 2) to align pointer + let aligner = |ptr: usize, align: usize| ptr & !(align - 1); + let cell_ptr = aligner( + memory.view::().as_ptr().add(self.offset as usize) as usize, + mem::align_of::(), + ) as *const Cell; + Ok(&*cell_ptr) + } + } +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>( + self, + memory: &'a Memory, + index: u32, + length: u32, + ) -> Result<&'a [Cell], __wasi_errno_t> { + if (self.offset as usize) + (mem::size_of::() * ((index + length) as usize)) + >= memory.size().bytes().0 + { + return Err(__WASI_EFAULT); + } + + unsafe { + let cell_ptrs = memory.view::().get_unchecked( + ((self.offset as usize) / mem::size_of::()) + (index as usize) + ..((self.offset() as usize) / mem::size_of::()) + + ((index + length) as usize), + ) as *const _; + Ok(&*cell_ptrs) + } + } +} + +unsafe impl WasmExternType for WasmPtr { + type Native = i32; + + fn to_native(self) -> Self::Native { + self.offset as i32 + } + fn from_native(n: Self::Native) -> Self { + Self { + offset: n as u32, + _phantom: PhantomData, + } + } +} + +unsafe impl ValueType for WasmPtr {} + +impl Clone for WasmPtr { + fn clone(&self) -> Self { + Self { + offset: self.offset, + _phantom: PhantomData, + } + } +} + +impl Copy for WasmPtr {} + +impl PartialEq for WasmPtr { + fn eq(&self, other: &Self) -> bool { + self.offset == other.offset + } +} + +impl Eq for WasmPtr {} + +impl fmt::Debug for WasmPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WasmPtr({:#x})", self.offset) + } +} diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs new file mode 100644 index 000000000..f1608e768 --- /dev/null +++ b/lib/wasi/src/state.rs @@ -0,0 +1,424 @@ +// use wasmer_runtime_abi::vfs::{ +// vfs::Vfs, +// file_like::{FileLike, Metadata}; +// }; +use crate::syscalls::types::*; +use generational_arena::{Arena, Index as Inode}; +use hashbrown::hash_map::{Entry, HashMap}; +use std::{ + cell::Cell, + fs, + io::{self, Read, Seek, Write}, + path::PathBuf, + time::SystemTime, +}; +use wasmer_runtime_core::debug; + +pub const MAX_SYMLINKS: usize = 100; + +#[derive(Debug)] +pub enum WasiFile { + HostFile(fs::File), +} + +impl Write for WasiFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.flush(), + } + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.write_all(buf), + } + } + + fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.write_fmt(fmt), + } + } +} + +impl Read for WasiFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read(buf), + } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read_to_end(buf), + } + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.read_to_string(buf), + } + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + match self { + WasiFile::HostFile(hf) => hf.read_exact(buf), + } + } +} + +impl Seek for WasiFile { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match self { + WasiFile::HostFile(hf) => hf.seek(pos), + } + } +} + +#[derive(Debug)] +pub struct InodeVal { + pub stat: __wasi_filestat_t, + pub is_preopened: bool, + pub name: String, + pub kind: Kind, +} + +impl InodeVal { + // TODO: clean this up + pub fn from_file_metadata( + metadata: &std::fs::Metadata, + name: String, + is_preopened: bool, + kind: Kind, + ) -> Self { + InodeVal { + stat: __wasi_filestat_t { + st_filetype: if metadata.is_dir() { + __WASI_FILETYPE_DIRECTORY + } else { + __WASI_FILETYPE_REGULAR_FILE + }, + st_size: metadata.len(), + st_atim: metadata + .accessed() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + st_ctim: metadata + .created() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + st_mtim: metadata + .modified() + .ok() + .and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0), + ..__wasi_filestat_t::default() + }, + is_preopened, + name, + kind, + } + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub enum Kind { + File { + handle: WasiFile, + }, + Dir { + // TODO: wrap it like WasiFile + /// The path on the host system where the directory is located + path: PathBuf, + /// The entries of a directory are lazily filled. + entries: HashMap, + }, + Symlink { + forwarded: Inode, + }, + Buffer { + buffer: Vec, + }, +} + +#[derive(Clone, Debug)] +pub struct Fd { + pub rights: __wasi_rights_t, + pub rights_inheriting: __wasi_rights_t, + pub flags: __wasi_fdflags_t, + pub offset: u64, + pub inode: Inode, +} + +#[derive(Debug)] +pub struct WasiFs { + //pub repo: Repo, + pub name_map: HashMap, + pub inodes: Arena, + pub fd_map: HashMap, + pub next_fd: Cell, + pub inode_counter: Cell, +} + +impl WasiFs { + pub fn new(preopened_dirs: &[String]) -> Result { + /*let repo = RepoOpener::new() + .create(true) + .open("mem://wasmer-test-fs", "") + .map_err(|e| e.to_string())?;*/ + debug!("wasi::fs::inodes"); + let inodes = Arena::new(); + let mut wasi_fs = Self { + //repo: repo, + name_map: HashMap::new(), + inodes: inodes, + fd_map: HashMap::new(), + next_fd: Cell::new(3), + inode_counter: Cell::new(1000), + }; + for dir in preopened_dirs { + debug!("Attempting to preopen {}", &dir); + // TODO: think about this + let default_rights = 0x1FFFFFFF; // all rights + let cur_dir = PathBuf::from(dir); + let cur_dir_metadata = cur_dir.metadata().expect("Could not find directory"); + let kind = if cur_dir_metadata.is_dir() { + Kind::Dir { + path: cur_dir.clone(), + entries: Default::default(), + } + } else { + return Err(format!( + "WASI only supports pre-opened directories right now; found \"{}\"", + &dir + )); + }; + // TODO: handle nested pats in `file` + let inode_val = + InodeVal::from_file_metadata(&cur_dir_metadata, dir.clone(), true, kind); + + let inode = wasi_fs.inodes.insert(inode_val); + wasi_fs.inodes[inode].stat.st_ino = wasi_fs.inode_counter.get(); + wasi_fs + .create_fd(default_rights, default_rights, 0, inode) + .expect("Could not open fd"); + } + debug!("wasi::fs::end"); + Ok(wasi_fs) + } + + #[allow(dead_code)] + fn get_inode(&mut self, path: &str) -> Option { + Some(match self.name_map.entry(path.to_string()) { + Entry::Occupied(o) => *o.get(), + Entry::Vacant(_v) => { + return None; + // let file = if let Ok(file) = OpenOptions::new() + // .read(true) + // .write(true) + // .create(false) + // .open(&mut self.repo, path) + // { + // file + // } else { + // return None; + // }; + + // let metadata = file.metadata().unwrap(); + // let inode_index = { + // let index = self.inode_counter.get(); + // self.inode_counter.replace(index + 1) + // }; + + // let systime_to_nanos = |systime: SystemTime| { + // let duration = systime + // .duration_since(SystemTime::UNIX_EPOCH) + // .expect("should always be after unix epoch"); + // duration.as_nanos() as u64 + // }; + + // let inode = self.inodes.insert(InodeVal { + // stat: __wasi_filestat_t { + // st_dev: 0, + // st_ino: inode_index, + // st_filetype: match metadata.file_type() { + // FileType::File => __WASI_FILETYPE_REGULAR_FILE, + // FileType::Dir => __WASI_FILETYPE_DIRECTORY, + // }, + // st_nlink: 0, + // st_size: metadata.content_len() as u64, + // st_atim: systime_to_nanos(SystemTime::now()), + // st_mtim: systime_to_nanos(metadata.modified_at()), + // st_ctim: systime_to_nanos(metadata.created_at()), + // }, + // is_preopened: false, + // name: path.to_string(), + // kind: match metadata.file_type() { + // FileType::File => Kind::File { handle: file }, + // FileType::Dir => Kind::Dir { + // handle: file, + // entries: HashMap::new(), + // }, + // }, + // }); + // v.insert(inode); + // inode + } + }) + } + + #[allow(dead_code)] + fn filestat_inode( + &self, + inode: Inode, + flags: __wasi_lookupflags_t, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let inode_val = &self.inodes[inode]; + if let (true, Kind::Symlink { mut forwarded }) = + (flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind) + { + // Time to follow the symlink. + let mut counter = 0; + + while counter <= MAX_SYMLINKS { + let inode_val = &self.inodes[forwarded]; + if let &Kind::Symlink { + forwarded: new_forwarded, + } = &inode_val.kind + { + counter += 1; + forwarded = new_forwarded; + } else { + return Ok(inode_val.stat); + } + } + + Err(__WASI_EMLINK) + } else { + Ok(inode_val.stat) + } + } + + #[allow(dead_code)] + pub fn filestat_path( + &mut self, + preopened_fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: &str, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + warn!("Should use preopned_fd: {}", preopened_fd); + let inode = self.get_inode(path).ok_or(__WASI_EINVAL)?; + + self.filestat_inode(inode, flags) + } + + pub fn filestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + Ok(self.inodes[fd.inode].stat) + } + + pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + debug!("fdstat: {:?}", fd); + + Ok(__wasi_fdstat_t { + fs_filetype: match self.inodes[fd.inode].kind { + Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE, + Kind::Dir { .. } => __WASI_FILETYPE_DIRECTORY, + Kind::Symlink { .. } => __WASI_FILETYPE_SYMBOLIC_LINK, + _ => __WASI_FILETYPE_UNKNOWN, + }, + fs_flags: fd.flags, + fs_rights_base: fd.rights, + fs_rights_inheriting: fd.rights_inheriting, // TODO(lachlan): Is this right? + }) + } + + pub fn prestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_prestat_t, __wasi_errno_t> { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + + debug!("in prestat_fd {:?}", fd); + let inode_val = &self.inodes[fd.inode]; + + if inode_val.is_preopened { + Ok(__wasi_prestat_t { + pr_type: __WASI_PREOPENTYPE_DIR, + u: PrestatEnum::Dir { + // REVIEW: + pr_name_len: inode_val.name.len() as u32 + 1, + } + .untagged(), + }) + } else { + Err(__WASI_EBADF) + } + } + + pub fn flush(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> { + match fd { + 0 => (), + 1 => io::stdout().flush().map_err(|_| __WASI_EIO)?, + 2 => io::stderr().flush().map_err(|_| __WASI_EIO)?, + _ => { + let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + if fd.rights & __WASI_RIGHT_FD_DATASYNC == 0 { + return Err(__WASI_EACCES); + } + + let inode = &mut self.inodes[fd.inode]; + + match &mut inode.kind { + Kind::File { handle } => handle.flush().map_err(|_| __WASI_EIO)?, + // TODO: verify this behavior + Kind::Dir { .. } => return Err(__WASI_EISDIR), + Kind::Symlink { .. } => unimplemented!(), + Kind::Buffer { .. } => (), + } + } + } + Ok(()) + } + + pub fn create_fd( + &mut self, + rights: __wasi_rights_t, + rights_inheriting: __wasi_rights_t, + flags: __wasi_fdflags_t, + inode: Inode, + ) -> Result { + let idx = self.next_fd.get(); + self.next_fd.set(idx + 1); + self.fd_map.insert( + idx, + Fd { + rights, + rights_inheriting, + flags, + offset: 0, + inode, + }, + ); + Ok(idx) + } +} + +#[derive(Debug)] +pub struct WasiState<'a> { + pub fs: WasiFs, + pub args: &'a [Vec], + pub envs: &'a [Vec], +} diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs new file mode 100644 index 000000000..0389273fc --- /dev/null +++ b/lib/wasi/src/syscalls/mod.rs @@ -0,0 +1,1646 @@ +#![allow(unused)] +pub mod types; +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod unix; +#[cfg(any(target_os = "windows"))] +pub mod windows; + +use self::types::*; +use crate::{ + ptr::{Array, WasmPtr}, + state::{Fd, InodeVal, Kind, WasiFile, WasiState, MAX_SYMLINKS}, + ExitCode, +}; +use rand::{thread_rng, Rng}; +use std::cell::Cell; +use std::convert::Infallible; +use std::io::{self, Read, Seek, Write}; +use wasmer_runtime_core::{debug, memory::Memory, vm::Ctx}; + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub use unix::*; + +#[cfg(any(target_os = "windows"))] +pub use windows::*; + +#[allow(clippy::mut_from_ref)] +fn get_wasi_state(ctx: &Ctx) -> &mut WasiState { + unsafe { &mut *(ctx.data as *mut WasiState) } +} + +fn write_bytes( + mut write_loc: T, + memory: &Memory, + iovs_arr_cell: &[Cell<__wasi_ciovec_t>], +) -> Result { + let mut bytes_written = 0; + for iov in iovs_arr_cell { + let iov_inner = iov.get(); + let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?; + write_loc + .write(&bytes.iter().map(|b_cell| b_cell.get()).collect::>()) + .map_err(|_| { + write_loc.flush(); + __WASI_EIO + })?; + + // TODO: handle failure more accurately + bytes_written += iov_inner.buf_len; + } + write_loc.flush(); + Ok(bytes_written) +} + +/// checks that `rights_check_set` is a subset of `rights_set` +fn has_rights(rights_set: __wasi_rights_t, rights_check_set: __wasi_rights_t) -> bool { + rights_set | rights_check_set == rights_set +} + +#[must_use] +fn write_buffer_array( + memory: &Memory, + from: &[Vec], + ptr_buffer: WasmPtr, Array>, + buffer: WasmPtr, +) -> __wasi_errno_t { + let ptrs = wasi_try!(ptr_buffer.deref(memory, 0, from.len() as u32)); + + let mut current_buffer_offset = 0; + for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) { + ptr.set(WasmPtr::new(buffer.offset() + current_buffer_offset)); + + let cells = + wasi_try!(buffer.deref(memory, current_buffer_offset, sub_buffer.len() as u32 + 1)); + + for (cell, &byte) in cells.iter().zip(sub_buffer.iter().chain([0].iter())) { + cell.set(byte); + } + current_buffer_offset += sub_buffer.len() as u32 + 1; + } + + __WASI_ESUCCESS +} + +/// ### `args_get()` +/// Read command-line argument data. +/// The sizes of the buffers should match that returned by [`args_sizes_get()`](#args_sizes_get). +/// Inputs: +/// - `char **argv` +/// A pointer to a buffer to write the argument pointers. +/// - `char *argv_buf` +/// A pointer to a buffer to write the argument string data. +/// +pub fn args_get( + ctx: &mut Ctx, + argv: WasmPtr, Array>, + argv_buf: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::args_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let result = write_buffer_array(memory, &*state.args, argv, argv_buf); + + debug!( + "=> args:\n{}", + state + .args + .iter() + .enumerate() + .map(|(i, v)| format!( + "{:>20}: {}", + i, + ::std::str::from_utf8(v).unwrap().to_string() + )) + .collect::>() + .join("\n") + ); + + result +} + +/// ### `args_sizes_get()` +/// Return command-line argument data sizes. +/// Outputs: +/// - `size_t *argc` +/// The number of arguments. +/// - `size_t *argv_buf_size` +/// The size of the argument string data. +pub fn args_sizes_get( + ctx: &mut Ctx, + argc: WasmPtr, + argv_buf_size: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::args_sizes_get"); + let memory = ctx.memory(0); + + let argc = wasi_try!(argc.deref(memory)); + let argv_buf_size = wasi_try!(argv_buf_size.deref(memory)); + + let state = get_wasi_state(ctx); + + let argc_val = state.args.len() as u32; + let argv_buf_size_val = state.args.iter().map(|v| v.len() as u32 + 1).sum(); + argc.set(argc_val); + argv_buf_size.set(argv_buf_size_val); + + debug!("=> argc={}, argv_buf_size={}", argc_val, argv_buf_size_val); + + __WASI_ESUCCESS +} + +/// ### `clock_res_get()` +/// Get the resolution of the specified clock +/// Input: +/// - `__wasi_clockid_t clock_id` +/// The ID of the clock to get the resolution of +/// Output: +/// - `__wasi_timestamp_t *resolution` +/// The resolution of the clock in nanoseconds +pub fn clock_res_get( + ctx: &mut Ctx, + clock_id: __wasi_clockid_t, + resolution: WasmPtr<__wasi_timestamp_t>, +) -> __wasi_errno_t { + debug!("wasi::clock_res_get"); + let memory = ctx.memory(0); + + let out_addr = wasi_try!(resolution.deref(memory)); + platform_clock_res_get(clock_id, out_addr) +} + +/// ### `clock_time_get()` +/// Get the time of the specified clock +/// Inputs: +/// - `__wasi_clockid_t clock_id` +/// The ID of the clock to query +/// - `__wasi_timestamp_t precision` +/// The maximum amount of error the reading may have +/// Output: +/// - `__wasi_timestamp_t *time` +/// The value of the clock in nanoseconds +pub fn clock_time_get( + ctx: &mut Ctx, + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: WasmPtr<__wasi_timestamp_t>, +) -> __wasi_errno_t { + debug!("wasi::clock_time_get"); + let memory = ctx.memory(0); + + let out_addr = wasi_try!(time.deref(memory)); + platform_clock_time_get(clock_id, precision, out_addr) +} + +/// ### `environ_get()` +/// Read environment variable data. +/// The sizes of the buffers should match that returned by [`environ_sizes_get()`](#environ_sizes_get). +/// Inputs: +/// - `char **environ` +/// A pointer to a buffer to write the environment variable pointers. +/// - `char *environ_buf` +/// A pointer to a buffer to write the environment variable string data. +pub fn environ_get( + ctx: &mut Ctx, + environ: WasmPtr, Array>, + environ_buf: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::environ_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + write_buffer_array(memory, &*state.args, environ, environ_buf) +} + +/// ### `environ_sizes_get()` +/// Return command-line argument data sizes. +/// Outputs: +/// - `size_t *environ_count` +/// The number of environment variables. +/// - `size_t *environ_buf_size` +/// The size of the environment variable string data. +pub fn environ_sizes_get( + ctx: &mut Ctx, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::environ_sizes_get"); + let memory = ctx.memory(0); + + let environ_count = wasi_try!(environ_count.deref(memory)); + let environ_buf_size = wasi_try!(environ_buf_size.deref(memory)); + + let state = get_wasi_state(ctx); + + environ_count.set(state.envs.len() as u32); + environ_buf_size.set(state.envs.iter().map(|v| v.len() as u32).sum()); + + __WASI_ESUCCESS +} + +/// ### `fd_advise()` +/// Advise the system about how a file will be used +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor the advice applies to +/// - `__wasi_filesize_t offset` +/// The offset from which the advice applies +/// - `__wasi_filesize_t len` +/// The length from the offset to which the advice applies +/// - `__wasi_advice_t advice` +/// The advice to give +pub fn fd_advise( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, + advice: __wasi_advice_t, +) -> __wasi_errno_t { + debug!("wasi::fd_advise: fd={}", fd); + + // this is used for our own benefit, so just returning success is a valid + // implementation for now + __WASI_ESUCCESS +} + +/// ### `fd_allocate` +/// Allocate extra space for a file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to allocate for +/// - `__wasi_filesize_t offset` +/// The offset from the start marking the beginning of the allocation +/// - `__wasi_filesize_t len` +/// The length from the offset marking the end of the allocation +pub fn fd_allocate( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, +) -> __wasi_errno_t { + debug!("wasi::fd_allocate"); + unimplemented!("wasi::fd_allocate") +} + +/// ### `fd_close()` +/// Close an open file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// A file descriptor mapping to an open file to close +/// Errors: +/// - `__WASI_EISDIR` +/// If `fd` is a directory +/// - `__WASI_EBADF` +/// If `fd` is invalid or not open (TODO: consider __WASI_EINVAL) +pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_close"); + // FD is too large + return __WASI_EMFILE; + // FD is a directory (due to user input) + return __WASI_EISDIR; + // FD is invalid + return __WASI_EBADF; + __WASI_ESUCCESS +} + +/// ### `fd_datasync()` +/// Synchronize the file data to disk +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to sync +pub fn fd_datasync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_datasync"); + let state = get_wasi_state(ctx); + + if let Err(e) = state.fs.flush(fd) { + e + } else { + __WASI_ESUCCESS + } +} + +/// ### `fd_fdstat_get()` +/// Get metadata of a file descriptor +/// Input: +/// - `__wasi_fd_t fd` +/// The file descriptor whose metadata will be accessed +/// Output: +/// - `__wasi_fdstat_t *buf` +/// The location where the metadata will be written +pub fn fd_fdstat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf_ptr: WasmPtr<__wasi_fdstat_t>, +) -> __wasi_errno_t { + debug!( + "wasi::fd_fdstat_get: fd={}, buf_ptr={}", + fd, + buf_ptr.offset() + ); + let mut state = get_wasi_state(ctx); + let memory = ctx.memory(0); + let stat = wasi_try!(state.fs.fdstat(fd)); + let buf = wasi_try!(buf_ptr.deref(memory)); + + buf.set(stat); + + __WASI_ESUCCESS +} + +/// ### `fd_fdstat_set_flags()` +/// Set file descriptor flags for a file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to apply the new flags to +/// - `__wasi_fdflags_t flags` +/// The flags to apply to `fd` +pub fn fd_fdstat_set_flags( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_fdflags_t, +) -> __wasi_errno_t { + debug!("wasi::fd_fdstat_set_flags"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS) { + return __WASI_EACCES; + } + + fd_entry.flags = flags; + __WASI_ESUCCESS +} + +/// ### `fd_fdstat_set_rights()` +/// Set the rights of a file descriptor. This can only be used to remove rights +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to apply the new rights to +/// - `__wasi_rights_t fs_rights_base` +/// The rights to apply to `fd` +/// - `__wasi_rights_t fs_rights_inheriting` +/// The inheriting rights to apply to `fd` +pub fn fd_fdstat_set_rights( + ctx: &mut Ctx, + fd: __wasi_fd_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, +) -> __wasi_errno_t { + debug!("wasi::fd_fdstat_set_rights"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + // ensure new rights are a subset of current rights + if fd_entry.rights | fs_rights_base != fd_entry.rights + || fd_entry.rights_inheriting | fs_rights_inheriting != fd_entry.rights_inheriting + { + return __WASI_ENOTCAPABLE; + } + + fd_entry.rights = fs_rights_base; + fd_entry.rights_inheriting = fs_rights_inheriting; + + __WASI_ESUCCESS +} + +/// ### `fd_filestat_get()` +/// Get the metadata of an open file +/// Input: +/// - `__wasi_fd_t fd` +/// The open file descriptor whose metadata will be read +/// Output: +/// - `__wasi_filestat_t *buf` +/// Where the metadata from `fd` will be written +pub fn fd_filestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_filestat_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_get"); + let mut state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let stat = wasi_try!(state.fs.filestat_fd(fd)); + + let buf = wasi_try!(buf.deref(memory)); + buf.set(stat); + + __WASI_ESUCCESS +} + +pub fn fd_filestat_set_size( + ctx: &mut Ctx, + fd: __wasi_fd_t, + st_size: __wasi_filesize_t, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_set_size"); + unimplemented!("wasi::fd_filestat_set_size") +} + +/// ### `fd_filestat_set_times()` +/// Set timestamp metadata on a file +/// Inputs: +/// - `__wasi_timestamp_t st_atim` +/// Last accessed time +/// - `__wasi_timestamp_t st_mtim` +/// Last modified time +/// - `__wasi_fstflags_t fst_flags` +/// Bit-vector for controlling which times get set +pub fn fd_filestat_set_times( + ctx: &mut Ctx, + fd: __wasi_fd_t, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + debug!("wasi::fd_filestat_set_times"); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FILESTAT_SET_TIMES) { + return __WASI_EACCES; + } + + if (fst_flags & __WASI_FILESTAT_SET_ATIM != 0 && fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0) + || (fst_flags & __WASI_FILESTAT_SET_MTIM != 0 + && fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0) + { + return __WASI_EINVAL; + } + + let inode = &mut state.fs.inodes[fd_entry.inode]; + + if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 { + inode.stat.st_atim = st_atim; + } else if fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 { + // set to current real time + unimplemented!("Set filestat time to the current real time"); + } + + if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 { + inode.stat.st_mtim = st_mtim; + } else if fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 { + // set to current real time + unimplemented!("Set filestat time to the current real time"); + } + + __WASI_ESUCCESS +} + +pub fn fd_pread( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, Array>, + iovs_len: u32, + offset: __wasi_filesize_t, + nread: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_pread"); + let memory = ctx.memory(0); + + let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nread_cell = wasi_try!(nread.deref(memory)); + + unimplemented!("wasi::fd_pread"); + + __WASI_ESUCCESS +} + +/// ### `fd_prestat_get()` +/// Get metadata about a preopened file descriptor +/// Input: +/// - `__wasi_fd_t fd` +/// The preopened file descriptor to query +/// Output: +/// - `__wasi_prestat *buf` +/// Where the metadata will be written +pub fn fd_prestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_prestat_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_prestat_get: fd={}", fd); + let memory = ctx.memory(0); + + let prestat_ptr = wasi_try!(buf.deref(memory)); + + let state = get_wasi_state(ctx); + prestat_ptr.set(wasi_try!(state.fs.prestat_fd(fd))); + + __WASI_ESUCCESS +} + +pub fn fd_prestat_dir_name( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!( + "wasi::fd_prestat_dir_name: fd={}, path_len={}", + fd, path_len + ); + let memory = ctx.memory(0); + let path_chars = wasi_try!(path.deref(memory, 0, path_len)); + + let state = get_wasi_state(ctx); + let real_fd = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + let inode_val = &state.fs.inodes[real_fd.inode]; + + // check inode-val.is_preopened? + + if let Kind::Dir { .. } = inode_val.kind { + // TODO: verify this: null termination, etc + if inode_val.name.len() <= path_len as usize { + let mut i = 0; + for c in inode_val.name.bytes() { + path_chars[i].set(c); + i += 1 + } + path_chars[i].set(0); + + debug!( + "=> result: \"{}\"", + ::std::str::from_utf8(unsafe { &*(&path_chars[..] as *const [_] as *const [u8]) }) + .unwrap() + ); + + __WASI_ESUCCESS + } else { + __WASI_EOVERFLOW + } + } else { + __WASI_ENOTDIR + } +} + +/// ### `fd_pwrite()` +/// Write to a file without adjusting its offset +/// Inputs: +/// - `__wasi_fd_t` +/// File descriptor (opened with writing) to write to +/// - `const __wasi_ciovec_t *iovs` +/// List of vectors to read data from +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// - `__wasi_filesize_t offset` +/// The offset to write at +/// Output: +/// - `u32 *nwritten` +/// Number of bytes written +pub fn fd_pwrite( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, Array>, + iovs_len: u32, + offset: __wasi_filesize_t, + nwritten: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_pwrite"); + // TODO: refactor, this is just copied from `fd_write`... + let memory = ctx.memory(0); + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nwritten_cell = wasi_try!(nwritten.deref(memory)); + + let bytes_written = match fd { + 0 => return __WASI_EINVAL, + 1 => { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + + 2 => { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_written = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"), + Kind::Buffer { buffer } => wasi_try!(write_bytes( + &mut buffer[(offset as usize)..], + memory, + iovs_arr_cell + )), + }; + + bytes_written + } + }; + + nwritten_cell.set(bytes_written); + + __WASI_ESUCCESS +} + +/// ### `fd_read()` +/// Read data from file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor from which data will be read +/// - `const __wasi_iovec_t *iovs` +/// Vectors where data will be stored +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// Output: +/// - `u32 *nread` +/// Number of bytes read +pub fn fd_read( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, Array>, + iovs_len: u32, + nread: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_read: fd={}", fd); + let memory = ctx.memory(0); + + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nread_cell = wasi_try!(nread.deref(memory)); + + fn read_bytes( + mut reader: T, + memory: &Memory, + iovs_arr_cell: &[Cell<__wasi_iovec_t>], + ) -> Result { + let mut bytes_read = 0; + + for iov in iovs_arr_cell { + let iov_inner = iov.get(); + let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?; + let mut raw_bytes: &mut [u8] = + unsafe { &mut *(bytes as *const [_] as *mut [_] as *mut [u8]) }; + bytes_read += reader.read(raw_bytes).map_err(|_| __WASI_EIO)? as u32; + } + Ok(bytes_read) + } + + let bytes_read = match fd { + 0 => { + let stdin = io::stdin(); + let mut handle = stdin.lock(); + + wasi_try!(read_bytes(handle, memory, iovs_arr_cell)) + } + 1 | 2 => return __WASI_EINVAL, + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let offset = fd_entry.offset as usize; + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_read = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + wasi_try!(read_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), + Kind::Buffer { buffer } => { + wasi_try!(read_bytes(&buffer[offset..], memory, iovs_arr_cell)) + } + }; + + fd_entry.offset += bytes_read as u64; + + bytes_read + } + }; + + nread_cell.set(bytes_read); + + __WASI_ESUCCESS +} + +/// ### `fd_readdir()` +/// Read data from directory specified by file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor from which directory data will be read +/// - `void *buf` +/// Buffer where directory entries are stored +/// - `u32 buf_len` +/// Length of data in `buf` +/// - `__wasi_dircookie_t cookie` +/// Where the directory reading should start from +/// Output: +/// - `u32 *bufused` +/// The Number of bytes stored in `buf`; if less than `buf_len` then entire +/// directory has been read +pub fn fd_readdir( + ctx: &mut Ctx, + fd: __wasi_fd_t, + buf: WasmPtr, + buf_len: u32, + cookie: __wasi_dircookie_t, + bufused: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_readdir"); + let memory = ctx.memory(0); + + if let (Ok(buf_arr_cell), Ok(bufused_cell)) = + (buf.deref(memory, 0, buf_len), bufused.deref(memory)) + { + unimplemented!("wasi::fd_readdir") + } else { + __WASI_EFAULT + } +} + +/// ### `fd_renumber()` +/// Atomically copy file descriptor +/// Inputs: +/// - `__wasi_fd_t from` +/// File descriptor to copy +/// - `__wasi_fd_t to` +/// Location to copy file descriptor to +pub fn fd_renumber(ctx: &mut Ctx, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_renumber: from={}, to={}", from, to); + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get(&from).ok_or(__WASI_EBADF)); + + state.fs.fd_map.insert( + to, + Fd { + // TODO: verify this is correct + rights: fd_entry.rights_inheriting, + ..*fd_entry + }, + ); + __WASI_ESUCCESS +} + +/// ### `fd_seek()` +/// Update file descriptor offset +/// Inputs: +/// - `__wasi_fd_t fd` +/// File descriptor to mutate +/// - `__wasi_filedelta_t offset` +/// Number of bytes to adjust offset by +/// - `__wasi_whence_t whence` +/// What the offset is relative to +/// Output: +/// - `__wasi_filesize_t *fd` +/// The new offset relative to the start of the file +pub fn fd_seek( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: __wasi_filedelta_t, + whence: __wasi_whence_t, + newoffset: WasmPtr<__wasi_filesize_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_seek: fd={}, offset={}", fd, offset); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + let new_offset_cell = wasi_try!(newoffset.deref(memory)); + + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK) { + return __WASI_EACCES; + } + + // TODO: handle case if fd is a dir? + match whence { + __WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64, + __WASI_WHENCE_END => unimplemented!("__WASI__WHENCE_END in wasi::fd_seek"), + __WASI_WHENCE_SET => fd_entry.offset = offset as u64, + _ => return __WASI_EINVAL, + } + + new_offset_cell.set(fd_entry.offset); + + __WASI_ESUCCESS +} + +/// ### `fd_sync()` +/// Synchronize file and metadata to disk (TODO: expand upon what this means in our system) +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to sync +/// Errors: +/// TODO: figure out which errors this should return +/// - `__WASI_EPERM` +/// - `__WASI_ENOTCAPABLE` +pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { + debug!("wasi::fd_sync"); + // TODO: check __WASI_RIGHT_FD_SYNC + unimplemented!("wasi::fd_sync") +} + +/// ### `fd_tell()` +/// Get the offset of the file descriptor +/// Inputs: +/// - `__wasi_fd_t fd` +/// The file descriptor to access +/// Output: +/// - `__wasi_filesize_t *offset` +/// The offset of `fd` relative to the start of the file +pub fn fd_tell( + ctx: &mut Ctx, + fd: __wasi_fd_t, + offset: WasmPtr<__wasi_filesize_t>, +) -> __wasi_errno_t { + debug!("wasi::fd_tell"); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + let offset_cell = wasi_try!(offset.deref(memory)); + + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_TELL) { + return __WASI_EACCES; + } + + offset_cell.set(fd_entry.offset); + + __WASI_ESUCCESS +} + +/// ### `fd_write()` +/// Write data to the file descriptor +/// Inputs: +/// - `__wasi_fd_t` +/// File descriptor (opened with writing) to write to +/// - `const __wasi_ciovec_t *iovs` +/// List of vectors to read data from +/// - `u32 iovs_len` +/// Length of data in `iovs` +/// Output: +/// - `u32 *nwritten` +/// Number of bytes written +/// Errors: +/// +pub fn fd_write( + ctx: &mut Ctx, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, Array>, + iovs_len: u32, + nwritten: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::fd_write: fd={}", fd); + let memory = ctx.memory(0); + let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); + let nwritten_cell = wasi_try!(nwritten.deref(memory)); + + let bytes_written = match fd { + 0 => return __WASI_EINVAL, + 1 => { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + + 2 => { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + _ => { + let state = get_wasi_state(ctx); + let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { + // TODO: figure out the error to return when lacking rights + return __WASI_EACCES; + } + + let offset = fd_entry.offset as usize; + let inode = &mut state.fs.inodes[fd_entry.inode]; + + let bytes_written = match &mut inode.kind { + Kind::File { handle } => { + handle.seek(::std::io::SeekFrom::Start(offset as u64)); + + wasi_try!(write_bytes(handle, memory, iovs_arr_cell)) + } + Kind::Dir { .. } => { + // TODO: verify + return __WASI_EISDIR; + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"), + Kind::Buffer { buffer } => { + wasi_try!(write_bytes(&mut buffer[offset..], memory, iovs_arr_cell)) + } + }; + + fd_entry.offset += bytes_written as u64; + + bytes_written + } + }; + + nwritten_cell.set(bytes_written); + + __WASI_ESUCCESS +} + +/// ### `path_create_directory()` +/// Create directory at a path +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory that the path is relative to +/// - `const char *path` +/// String containing path data +/// - `u32 path_len` +/// The length of `path` +/// Errors: +/// Required Rights: +/// - __WASI_RIGHT_PATH_CREATE_DIRECTORY +/// This right must be set on the directory that the file is created in (TODO: verify that this is true) +pub fn path_create_directory( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_create_directory"); + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + + let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_CREATE_DIRECTORY) { + return __WASI_EACCES; + } + let path_cells = wasi_try!(path.deref(memory, 0, path_len)); + let path_string = + wasi_try!( + std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) + .map_err(|_| __WASI_EINVAL) + ); + debug!("=> path: {}", &path_string); + + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); + if path_vec.is_empty() { + return __WASI_EINVAL; + } + + assert!( + path_vec.len() == 1, + "path_create_directory for paths greater than depth 1 has not been implemented because our WASI FS abstractions are a work in progress. We apologize for the inconvenience" + ); + debug!("Path vec: {:#?}", path_vec); + + wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO)); + + let kind = Kind::Dir { + //parent: Some(working_dir.inode), + path: path.clone(), + entries: Default::default(), + }; + let new_inode = state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, + name: path_vec[0].clone(), + kind, + }); + + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind { + entries.insert(path_vec[0].clone(), new_inode); + } else { + return __WASI_ENOTDIR; + } + + __WASI_ESUCCESS +} + +/// ### `path_filestat_get()` +/// Access metadata about a file or directory +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory that `path` is relative to +/// - `__wasi_lookupflags_t flags` +/// Flags to control how `path` is understood +/// - `const char *path` +/// String containing the file path +/// - `u32 path_len` +/// The length of the `path` string +/// Output: +/// - `__wasi_file_stat_t *buf` +/// The location where the metadata will be stored +pub fn path_filestat_get( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + buf: WasmPtr<__wasi_filestat_t>, +) -> __wasi_errno_t { + debug!("wasi::path_filestat_get"); + let state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let root_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + + if !has_rights(root_dir.rights, __WASI_RIGHT_PATH_FILESTAT_GET) { + return __WASI_EACCES; + } + + let path_string = wasi_try!(::std::str::from_utf8(unsafe { + &*(wasi_try!(path.deref(memory, 0, path_len)) as *const [_] as *const [u8]) + }) + .map_err(|_| __WASI_EINVAL)); + debug!("=> path: {}", &path_string); + let path = std::path::PathBuf::from(path_string); + let path_vec = path + .components() + .map(|comp| comp.as_os_str().to_string_lossy().to_string()) + .collect::>(); + let buf_cell = wasi_try!(buf.deref(memory)); + + if path_vec.is_empty() { + return __WASI_EINVAL; + } + let mut cumulative_path = std::path::PathBuf::new(); + + debug!("=> Path vec: {:?}:", &path_vec); + // find the inode by traversing the path + let mut inode = root_dir.inode; + 'outer: for segment in &path_vec[..(path_vec.len() - 1)] { + // loop to traverse symlinks + // TODO: proper cycle detection + let mut sym_count = 0; + loop { + match &state.fs.inodes[inode].kind { + Kind::Dir { entries, .. } => { + cumulative_path.push(&segment); + if let Some(entry) = entries.get(segment) { + debug!("Entry {:?} found", &segment); + inode = entry.clone(); + } else { + // lazily load + debug!("Lazily loading entry {:?}", &segment); + let path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_ENOENT)); + if !path_metadata.is_dir() { + // TODO: should this just return invalid arg? + return __WASI_ENOTDIR; + } + let kind = Kind::Dir { + path: std::path::PathBuf::from(&segment), + entries: Default::default(), + }; + let inode_val = InodeVal::from_file_metadata( + &path_metadata, + segment.clone(), + false, + kind, + ); + let new_inode = state.fs.inodes.insert(inode_val); + let inode_idx = state.fs.inode_counter.get(); + state.fs.inode_counter.replace(inode_idx + 1); + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + // check that we're not displacing any entries + assert!(entries.insert(segment.clone(), new_inode).is_none()); + state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get(); + inode = new_inode; + } + debug!("Directory {:#?} lazily loaded", &cumulative_path); + } + continue 'outer; + } + Kind::Symlink { forwarded } => { + // TODO: updated cumulative path + sym_count += 1; + inode = forwarded.clone(); + if sym_count > MAX_SYMLINKS { + return __WASI_ELOOP; + } + } + _ => { + return __WASI_ENOTDIR; + } + } + } + } + + let final_inode = match &state.fs.inodes[inode].kind { + Kind::Dir { path, entries, .. } => { + // TODO: fail earlier if size 0 + let last_segment = path_vec.last().unwrap(); + cumulative_path.push(last_segment); + + if entries.contains_key(last_segment) { + entries[last_segment] + } else { + // lazily load it if we can + if !cumulative_path.exists() { + return __WASI_ENOENT; + } + let final_path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO)); + let new_inode = if final_path_metadata.is_dir() { + debug!("Opening host directory {:#?}", &cumulative_path); + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::Dir { + path: std::path::PathBuf::from(&last_segment), + entries: Default::default(), + }, + }) + } else { + debug!("Opening host file {:#?}", &cumulative_path); + let real_open_file = wasi_try!(std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&cumulative_path) + .map_err(|_| __WASI_ENOENT)); + + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::File { + handle: WasiFile::HostFile(real_open_file), + }, + }) + }; + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + entries.insert(last_segment.clone(), new_inode); + } + new_inode + } + } + _ => { + return __WASI_ENOTDIR; + } + }; + + let stat = state.fs.inodes[final_inode].stat; + + buf_cell.set(stat); + + __WASI_ESUCCESS +} + +/// ### `path_filestat_set_times()` +/// Update time metadata on a file or directory +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory relative to which the path is resolved +/// - `__wasi_lookupflags_t flags` +/// Flags to control how the path is understood +/// - `const char *path` +/// String containing the file path +/// - `u32 path_len` +/// The length of the `path` string +/// - `__wasi_timestamp_t st_atim` +/// The timestamp that the last accessed time attribute is set to +/// - `__wasi_timestamp_t st_mtim` +/// The timestamp that the last modified time attribute is set to +/// - `__wasi_fstflags_t fst_flags` +/// A bitmask controlling which attributes are set +pub fn path_filestat_set_times( + ctx: &mut Ctx, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + debug!("wasi::path_filestat_set_times"); + unimplemented!("wasi::path_filestat_set_times") +} + +/// ### `path_link()` +/// Create a hard link +/// Inputs: +/// - `__wasi_fd_t old_fd` +/// The directory relative to which the `old_path` is +/// - `__wasi_lookupflags_t old_flags` +/// Flags to control how `old_path` is understood +/// - `const char *old_path` +/// String containing the old file path +/// - `u32 old_path_len` +/// Length of the `old_path` string +/// - `__wasi_fd_t new_fd` +/// The directory relative to which the `new_path` is +/// - `const char *new_path` +/// String containing the new file path +/// - `u32 old_path_len` +/// Length of the `new_path` string +pub fn path_link( + ctx: &mut Ctx, + old_fd: __wasi_fd_t, + old_flags: __wasi_lookupflags_t, + old_path: WasmPtr, + old_path_len: u32, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_link"); + unimplemented!("wasi::path_link") +} + +/// ### `path_open()` +/// Open file located at the given path +/// Inputs: +/// - `__wasi_fd_t dirfd` +/// The fd corresponding to the directory that the file is in +/// - `__wasi_lookupflags_t dirflags` +/// Flags specifying how the path will be resolved +/// - `char *path` +/// The path of the file or directory to open +/// - `u32 path_len` +/// The length of the `path` string +/// - `__wasi_oflags_t o_flags` +/// How the file will be opened +/// - `__wasi_rights_t fs_rights_base` +/// The rights of the created file descriptor +/// - `__wasi_rights_t fs_rightsinheriting` +/// The rights of file descriptors derived from the created file descriptor +/// - `__wasi_fdflags_t fs_flags` +/// The flags of the file descriptor +/// Output: +/// - `__wasi_fd_t* fd` +/// The new file descriptor +/// Possible Errors: +/// - `__WASI_EACCES`, `__WASI_EBADF`, `__WASI_EFAULT`, `__WASI_EFBIG?`, `__WASI_EINVAL`, `__WASI_EIO`, `__WASI_ELOOP`, `__WASI_EMFILE`, `__WASI_ENAMETOOLONG?`, `__WASI_ENFILE`, `__WASI_ENOENT`, `__WASI_ENOTDIR`, `__WASI_EROFS`, and `__WASI_ENOTCAPABLE` +pub fn path_open( + ctx: &mut Ctx, + dirfd: __wasi_fd_t, + dirflags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: u32, + o_flags: __wasi_oflags_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, + fs_flags: __wasi_fdflags_t, + fd: WasmPtr<__wasi_fd_t>, +) -> __wasi_errno_t { + debug!("wasi::path_open"); + let memory = ctx.memory(0); + /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */ + if path_len > 1024 * 1024 { + return __WASI_ENAMETOOLONG; + } + + let fd_cell = wasi_try!(fd.deref(memory)); + let path_cells = wasi_try!(path.deref(memory, 0, path_len)); + let state = get_wasi_state(ctx); + + // o_flags: + // - __WASI_O_FLAG_CREAT (create if it does not exist) + // - __WASI_O_DIRECTORY (fail if not dir) + // - __WASI_O_EXCL (fail if file exists) + // - __WASI_O_TRUNC (truncate size to 0) + + let working_dir = wasi_try!(state.fs.fd_map.get(&dirfd).ok_or(__WASI_EBADF)); + + // ASSUMPTION: open rights apply recursively + if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_OPEN) { + return __WASI_EACCES; + } + + let path_string = + wasi_try!( + std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) + .map_err(|_| __WASI_EINVAL) + ); + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); + debug!("Path vec: {:#?}", path_vec); + + if path_vec.is_empty() { + return __WASI_EINVAL; + } + + let mut cur_dir_inode = working_dir.inode; + let mut cumulative_path = std::path::PathBuf::from("."); + + // traverse path + if path_vec.len() > 1 { + for path_segment in &path_vec[..(path_vec.len() - 1)] { + match &state.fs.inodes[cur_dir_inode].kind { + Kind::Dir { entries, .. } => { + if let Some(child) = entries.get(path_segment) { + cumulative_path.push(path_segment); + let inode_val = *child; + cur_dir_inode = inode_val; + } else { + // attempt to lazily load or create + if path_segment == ".." { + unimplemented!( + "\"..\" in paths in `path_open` has not been implemented yet" + ); + } + // lazily load + cumulative_path.push(path_segment); + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true); + // ASSUMPTION: __WASI_O_CREAT applies recursively + let open_options = if o_flags & __WASI_O_CREAT != 0 { + open_options.create(true) + } else { + open_options + }; + // TODO: handle __WASI_O_TRUNC on directories + + // TODO: refactor and reuse + let cur_file_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL)); + let kind = if cur_file_metadata.is_dir() { + Kind::Dir { + path: cumulative_path.clone(), + entries: Default::default(), + } + } else { + return __WASI_ENOTDIR; + }; + let inode_val = InodeVal::from_file_metadata( + &cur_file_metadata, + path_segment.clone(), + false, + kind, + ); + + let new_inode = state.fs.inodes.insert(inode_val); + let inode_idx = state.fs.inode_counter.get(); + state.fs.inode_counter.replace(inode_idx + 1); + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind + { + assert!(entries.insert(path_segment.clone(), new_inode).is_none()); + state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get(); + cur_dir_inode = new_inode; + } + } + } + Kind::Symlink { .. } => unimplemented!("Symlinks not yet supported in `path_open`"), + _ => return __WASI_ENOTDIR, + } + } + } + + let file_name = path_vec.last().unwrap(); + + debug!( + "Looking for file {} in directory {:#?}", + file_name, cumulative_path + ); + cumulative_path.push(file_name); + let file_path = cumulative_path; + + let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind { + if let Some(child) = entries.get(file_name).cloned() { + let child_inode_val = &state.fs.inodes[child]; + // early return based on flags + if o_flags & __WASI_O_EXCL != 0 { + return __WASI_EEXIST; + } + if o_flags & __WASI_O_DIRECTORY != 0 { + match &child_inode_val.kind { + Kind::Dir { .. } => (), + Kind::Symlink { .. } => { + unimplemented!("Symlinks not yet supported in path_open") + } + _ => return __WASI_ENOTDIR, + } + } + // do logic on child + wasi_try!(state + .fs + .create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child)) + } else { + // if entry does not exist in parent directory, try to lazily + // load it; possibly creating or truncating it if flags set + let real_opened_file = { + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true); + let open_options = if o_flags & __WASI_O_CREAT != 0 { + debug!( + "File {} may be created when opened if it does not exist", + &path_string + ); + open_options.create(true) + } else { + open_options + }; + let open_options = if o_flags & __WASI_O_TRUNC != 0 { + debug!("File {} will be truncated when opened", &path_string); + open_options.truncate(true) + } else { + open_options + }; + let real_open_file = + wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO)); + debug!("Opening host file {}", &path_string); + + real_open_file + }; + // record lazily loaded or newly created fd + let new_inode = state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, + name: file_name.clone(), + kind: Kind::File { + handle: WasiFile::HostFile(real_opened_file), + }, + }); + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind { + entries.insert(file_name.clone(), new_inode); + } + let new_fd = wasi_try!(state.fs.create_fd( + fs_rights_base, + fs_rights_inheriting, + fs_flags, + new_inode, + )); + + new_fd + } + } else { + // working_dir did not match on Kind::Dir + return __WASI_ENOTDIR; + }; + + fd_cell.set(out_fd); + + __WASI_ESUCCESS +} + +pub fn path_readlink( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, + buf: WasmPtr, + buf_len: u32, + bufused: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::path_readlink"); + unimplemented!("wasi::path_readlink") +} +pub fn path_remove_directory( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_remove_directory"); + unimplemented!("wasi::path_remove_directory") +} +pub fn path_rename( + ctx: &mut Ctx, + old_fd: __wasi_fd_t, + old_path: WasmPtr, + old_path_len: u32, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_rename"); + unimplemented!("wasi::path_rename") +} +pub fn path_symlink( + ctx: &mut Ctx, + old_path: WasmPtr, + old_path_len: u32, + fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_symlink"); + unimplemented!("wasi::path_symlink") +} +pub fn path_unlink_file( + ctx: &mut Ctx, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: u32, +) -> __wasi_errno_t { + debug!("wasi::path_unlink_file"); + unimplemented!("wasi::path_unlink_file") +} +pub fn poll_oneoff( + ctx: &mut Ctx, + in_: WasmPtr<__wasi_subscription_t, Array>, + out_: WasmPtr<__wasi_event_t, Array>, + nsubscriptions: u32, + nevents: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::poll_oneoff"); + unimplemented!("wasi::poll_oneoff") +} +pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result { + debug!("wasi::proc_exit, {}", code); + Err(ExitCode { code }) +} +pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t { + debug!("wasi::proc_raise"); + unimplemented!("wasi::proc_raise") +} + +/// ### `random_get()` +/// Fill buffer with high-quality random data. This function may be slow and block +/// Inputs: +/// - `void *buf` +/// A pointer to a buffer where the random bytes will be written +/// - `size_t buf_len` +/// The number of bytes that will be written +pub fn random_get(ctx: &mut Ctx, buf: WasmPtr, buf_len: u32) -> __wasi_errno_t { + debug!("wasi::random_get"); + let mut rng = thread_rng(); + let memory = ctx.memory(0); + + let buf = wasi_try!(buf.deref(memory, 0, buf_len)); + + unsafe { + let u8_buffer = &mut *(buf as *const [_] as *mut [_] as *mut [u8]); + thread_rng().fill(u8_buffer); + } + + __WASI_ESUCCESS +} + +/// ### `sched_yield()` +/// Yields execution of the thread +pub fn sched_yield(ctx: &mut Ctx) -> __wasi_errno_t { + debug!("wasi::sched_yield"); + ::std::thread::yield_now(); + __WASI_ESUCCESS +} + +pub fn sock_recv( + ctx: &mut Ctx, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, Array>, + ri_data_len: u32, + ri_flags: __wasi_riflags_t, + ro_datalen: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t>, +) -> __wasi_errno_t { + debug!("wasi::sock_recv"); + unimplemented!("wasi::sock_recv") +} +pub fn sock_send( + ctx: &mut Ctx, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, Array>, + si_data_len: u32, + si_flags: __wasi_siflags_t, + so_datalen: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::sock_send"); + unimplemented!("wasi::sock_send") +} +pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { + debug!("wasi::sock_shutdown"); + unimplemented!("wasi::sock_shutdown") +} diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs new file mode 100644 index 000000000..636e1858e --- /dev/null +++ b/lib/wasi/src/syscalls/types.rs @@ -0,0 +1,459 @@ +#![allow(non_camel_case_types)] + +use crate::ptr::{Array, WasmPtr}; +use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use std::fmt; +use std::mem; +use wasmer_runtime_core::types::ValueType; + +pub type __wasi_advice_t = u8; +pub const __WASI_ADVICE_DONTNEED: u8 = 0; +pub const __WASI_ADVICE_NOREUSE: u8 = 1; +pub const __WASI_ADVICE_NORMAL: u8 = 2; +pub const __WASI_ADVICE_RANDOM: u8 = 3; +pub const __WASI_ADVICE_SEQUENTIAL: u8 = 4; +pub const __WASI_ADVICE_WILLNEED: u8 = 5; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_ciovec_t { + pub buf: WasmPtr, + pub buf_len: u32, +} + +unsafe impl ValueType for __wasi_ciovec_t {} + +pub type __wasi_clockid_t = u32; +pub const __WASI_CLOCK_MONOTONIC: u32 = 0; +pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: u32 = 1; +pub const __WASI_CLOCK_REALTIME: u32 = 2; +pub const __WASI_CLOCK_THREAD_CPUTIME_ID: u32 = 3; + +pub type __wasi_device_t = u64; + +pub type __wasi_dircookie_t = u64; +pub const __WASI_DIRCOOKIE_START: u64 = 0; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_dirent_t { + pub d_next: __wasi_dircookie_t, + pub d_ino: __wasi_inode_t, + pub d_namlen: u32, + pub d_type: __wasi_filetype_t, +} + +pub type __wasi_errno_t = u16; +pub const __WASI_ESUCCESS: u16 = 0; +pub const __WASI_E2BIG: u16 = 1; +pub const __WASI_EACCES: u16 = 2; +pub const __WASI_EADDRINUSE: u16 = 3; +pub const __WASI_EADDRNOTAVAIL: u16 = 4; +pub const __WASI_EAFNOSUPPORT: u16 = 5; +pub const __WASI_EAGAIN: u16 = 6; +pub const __WASI_EALREADY: u16 = 7; +pub const __WASI_EBADF: u16 = 8; +pub const __WASI_EBADMSG: u16 = 9; +pub const __WASI_EBUSY: u16 = 10; +pub const __WASI_ECANCELED: u16 = 11; +pub const __WASI_ECHILD: u16 = 12; +pub const __WASI_ECONNABORTED: u16 = 13; +pub const __WASI_ECONNREFUSED: u16 = 14; +pub const __WASI_ECONNRESET: u16 = 15; +pub const __WASI_EDEADLK: u16 = 16; +pub const __WASI_EDESTADDRREQ: u16 = 17; +pub const __WASI_EDOM: u16 = 18; +pub const __WASI_EDQUOT: u16 = 19; +pub const __WASI_EEXIST: u16 = 20; +pub const __WASI_EFAULT: u16 = 21; +pub const __WASI_EFBIG: u16 = 22; +pub const __WASI_EHOSTUNREACH: u16 = 23; +pub const __WASI_EIDRM: u16 = 24; +pub const __WASI_EILSEQ: u16 = 25; +pub const __WASI_EINPROGRESS: u16 = 26; +pub const __WASI_EINTR: u16 = 27; +pub const __WASI_EINVAL: u16 = 28; +pub const __WASI_EIO: u16 = 29; +pub const __WASI_EISCONN: u16 = 30; +pub const __WASI_EISDIR: u16 = 31; +pub const __WASI_ELOOP: u16 = 32; +pub const __WASI_EMFILE: u16 = 33; +pub const __WASI_EMLINK: u16 = 34; +pub const __WASI_EMSGSIZE: u16 = 35; +pub const __WASI_EMULTIHOP: u16 = 36; +pub const __WASI_ENAMETOOLONG: u16 = 37; +pub const __WASI_ENETDOWN: u16 = 38; +pub const __WASI_ENETRESET: u16 = 39; +pub const __WASI_ENETUNREACH: u16 = 40; +pub const __WASI_ENFILE: u16 = 41; +pub const __WASI_ENOBUFS: u16 = 42; +pub const __WASI_ENODEV: u16 = 43; +pub const __WASI_ENOENT: u16 = 44; +pub const __WASI_ENOEXEC: u16 = 45; +pub const __WASI_ENOLCK: u16 = 46; +pub const __WASI_ENOLINK: u16 = 47; +pub const __WASI_ENOMEM: u16 = 48; +pub const __WASI_ENOMSG: u16 = 49; +pub const __WASI_ENOPROTOOPT: u16 = 50; +pub const __WASI_ENOSPC: u16 = 51; +pub const __WASI_ENOSYS: u16 = 52; +pub const __WASI_ENOTCONN: u16 = 53; +pub const __WASI_ENOTDIR: u16 = 54; +pub const __WASI_ENOTEMPTY: u16 = 55; +pub const __WASI_ENOTRECOVERABLE: u16 = 56; +pub const __WASI_ENOTSOCK: u16 = 57; +pub const __WASI_ENOTSUP: u16 = 58; +pub const __WASI_ENOTTY: u16 = 59; +pub const __WASI_ENXIO: u16 = 60; +pub const __WASI_EOVERFLOW: u16 = 61; +pub const __WASI_EOWNERDEAD: u16 = 62; +pub const __WASI_EPERM: u16 = 63; +pub const __WASI_EPIPE: u16 = 64; +pub const __WASI_EPROTO: u16 = 65; +pub const __WASI_EPROTONOSUPPORT: u16 = 66; +pub const __WASI_EPROTOTYPE: u16 = 67; +pub const __WASI_ERANGE: u16 = 68; +pub const __WASI_EROFS: u16 = 69; +pub const __WASI_ESPIPE: u16 = 70; +pub const __WASI_ESRCH: u16 = 71; +pub const __WASI_ESTALE: u16 = 72; +pub const __WASI_ETIMEDOUT: u16 = 73; +pub const __WASI_ETXTBSY: u16 = 74; +pub const __WASI_EXDEV: u16 = 75; +pub const __WASI_ENOTCAPABLE: u16 = 76; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_event_fd_readwrite_t { + pub nbytes: __wasi_filesize_t, + pub flags: __wasi_eventrwflags_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_event_u { + fd_readwrite: __wasi_event_fd_readwrite_t, +} + +#[derive(Copy, Clone)] +pub enum EventEnum { + FdReadWrite { + nbytes: __wasi_filesize_t, + flags: __wasi_eventrwflags_t, + }, +} + +impl EventEnum { + pub fn untagged(self) -> __wasi_event_u { + match self { + EventEnum::FdReadWrite { nbytes, flags } => __wasi_event_u { + fd_readwrite: __wasi_event_fd_readwrite_t { nbytes, flags }, + }, + } + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct __wasi_event_t { + pub userdata: __wasi_userdata_t, + pub error: __wasi_errno_t, + pub type_: __wasi_eventtype_t, + pub u: __wasi_event_u, +} + +impl __wasi_event_t { + pub fn tagged(&self) -> Option { + match self.type_ { + __WASI_EVENTTYPE_FD_READ | __WASI_EVENTTYPE_FD_WRITE => Some(EventEnum::FdReadWrite { + nbytes: unsafe { self.u.fd_readwrite.nbytes }, + flags: unsafe { self.u.fd_readwrite.flags }, + }), + _ => None, + } + } +} + +pub type __wasi_eventrwflags_t = u16; +pub const __WASI_EVENT_FD_READWRITE_HANGUP: u16 = 1 << 0; + +pub type __wasi_eventtype_t = u8; +pub const __WASI_EVENTTYPE_CLOCK: u8 = 0; +pub const __WASI_EVENTTYPE_FD_READ: u8 = 1; +pub const __WASI_EVENTTYPE_FD_WRITE: u8 = 2; + +pub type __wasi_exitcode_t = u32; + +pub type __wasi_fd_t = u32; +pub const __WASI_STDIN_FILENO: u32 = 0; +pub const __WASI_STDOUT_FILENO: u32 = 1; +pub const __WASI_STDERR_FILENO: u32 = 2; + +pub type __wasi_fdflags_t = u16; +pub const __WASI_FDFLAG_APPEND: u16 = 1 << 0; +pub const __WASI_FDFLAG_DSYNC: u16 = 1 << 1; +pub const __WASI_FDFLAG_NONBLOCK: u16 = 1 << 2; +pub const __WASI_FDFLAG_RSYNC: u16 = 1 << 3; +pub const __WASI_FDFLAG_SYNC: u16 = 1 << 4; + +pub type __wasi_preopentype_t = u8; +pub const __WASI_PREOPENTYPE_DIR: u8 = 0; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_prestat_u_dir_t { + pub pr_name_len: u32, +} + +unsafe impl ValueType for __wasi_prestat_u_dir_t {} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_prestat_u { + dir: __wasi_prestat_u_dir_t, +} + +impl fmt::Debug for __wasi_prestat_u { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "__wasi_prestat_u") + } +} + +unsafe impl ValueType for __wasi_prestat_u {} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct __wasi_prestat_t { + pub pr_type: __wasi_preopentype_t, + pub u: __wasi_prestat_u, +} + +#[derive(Copy, Clone)] +pub enum PrestatEnum { + Dir { pr_name_len: u32 }, +} + +impl PrestatEnum { + pub fn untagged(self) -> __wasi_prestat_u { + match self { + PrestatEnum::Dir { pr_name_len } => __wasi_prestat_u { + dir: __wasi_prestat_u_dir_t { pr_name_len }, + }, + } + } +} + +impl __wasi_prestat_t { + pub fn tagged(&self) -> Option { + match self.pr_type { + __WASI_PREOPENTYPE_DIR => Some(PrestatEnum::Dir { + pr_name_len: unsafe { self.u.dir.pr_name_len }, + }), + _ => None, + } + } +} + +unsafe impl ValueType for __wasi_prestat_t {} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_fdstat_t { + pub fs_filetype: __wasi_filetype_t, + pub fs_flags: __wasi_fdflags_t, + pub fs_rights_base: __wasi_rights_t, + pub fs_rights_inheriting: __wasi_rights_t, +} + +unsafe impl ValueType for __wasi_fdstat_t {} + +pub type __wasi_filedelta_t = i64; + +pub type __wasi_filesize_t = u64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[repr(C)] +pub struct __wasi_filestat_t { + pub st_dev: __wasi_device_t, + pub st_ino: __wasi_inode_t, + pub st_filetype: __wasi_filetype_t, + pub st_nlink: __wasi_linkcount_t, + pub st_size: __wasi_filesize_t, + pub st_atim: __wasi_timestamp_t, + pub st_mtim: __wasi_timestamp_t, + pub st_ctim: __wasi_timestamp_t, +} + +unsafe impl ValueType for __wasi_filestat_t {} + +pub type __wasi_filetype_t = u8; +pub const __WASI_FILETYPE_UNKNOWN: u8 = 0; +pub const __WASI_FILETYPE_BLOCK_DEVICE: u8 = 1; +pub const __WASI_FILETYPE_CHARACTER_DEVICE: u8 = 2; +pub const __WASI_FILETYPE_DIRECTORY: u8 = 3; +pub const __WASI_FILETYPE_REGULAR_FILE: u8 = 4; +pub const __WASI_FILETYPE_SOCKET_DGRAM: u8 = 5; +pub const __WASI_FILETYPE_SOCKET_STREAM: u8 = 6; +pub const __WASI_FILETYPE_SYMBOLIC_LINK: u8 = 7; + +pub type __wasi_fstflags_t = u16; +pub const __WASI_FILESTAT_SET_ATIM: u16 = 1 << 0; +pub const __WASI_FILESTAT_SET_ATIM_NOW: u16 = 1 << 1; +pub const __WASI_FILESTAT_SET_MTIM: u16 = 1 << 2; +pub const __WASI_FILESTAT_SET_MTIM_NOW: u16 = 1 << 3; + +pub type __wasi_inode_t = u64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_iovec_t { + pub buf: WasmPtr, + pub buf_len: u32, +} + +unsafe impl ValueType for __wasi_iovec_t {} + +pub type __wasi_linkcount_t = u32; + +pub type __wasi_lookupflags_t = u32; +pub const __WASI_LOOKUP_SYMLINK_FOLLOW: u32 = 1 << 0; + +pub type __wasi_oflags_t = u16; +pub const __WASI_O_CREAT: u16 = 1 << 0; +pub const __WASI_O_DIRECTORY: u16 = 1 << 1; +pub const __WASI_O_EXCL: u16 = 1 << 2; +pub const __WASI_O_TRUNC: u16 = 1 << 3; + +pub type __wasi_riflags_t = u16; +pub const __WASI_SOCK_RECV_PEEK: u16 = 1 << 0; +pub const __WASI_SOCK_RECV_WAITALL: u16 = 1 << 1; + +pub type __wasi_rights_t = u64; +pub const __WASI_RIGHT_FD_DATASYNC: u64 = 1 << 0; +pub const __WASI_RIGHT_FD_READ: u64 = 1 << 1; +pub const __WASI_RIGHT_FD_SEEK: u64 = 1 << 2; +pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: u64 = 1 << 3; +pub const __WASI_RIGHT_FD_SYNC: u64 = 1 << 4; +pub const __WASI_RIGHT_FD_TELL: u64 = 1 << 5; +pub const __WASI_RIGHT_FD_WRITE: u64 = 1 << 6; +pub const __WASI_RIGHT_FD_ADVISE: u64 = 1 << 7; +pub const __WASI_RIGHT_FD_ALLOCATE: u64 = 1 << 8; +pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: u64 = 1 << 9; +pub const __WASI_RIGHT_PATH_CREATE_FILE: u64 = 1 << 10; +pub const __WASI_RIGHT_PATH_LINK_SOURCE: u64 = 1 << 11; +pub const __WASI_RIGHT_PATH_LINK_TARGET: u64 = 1 << 12; +pub const __WASI_RIGHT_PATH_OPEN: u64 = 1 << 13; +pub const __WASI_RIGHT_FD_READDIR: u64 = 1 << 14; +pub const __WASI_RIGHT_PATH_READLINK: u64 = 1 << 15; +pub const __WASI_RIGHT_PATH_RENAME_SOURCE: u64 = 1 << 16; +pub const __WASI_RIGHT_PATH_RENAME_TARGET: u64 = 1 << 17; +pub const __WASI_RIGHT_PATH_FILESTAT_GET: u64 = 1 << 18; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: u64 = 1 << 19; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: u64 = 1 << 20; +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_POLL_FD_READWRITE: u64 = 1 << 27; +pub const __WASI_RIGHT_SOCK_SHUTDOWN: u64 = 1 << 28; + +pub type __wasi_roflags_t = u16; +pub const __WASI_SOCK_RECV_DATA_TRUNCATED: u16 = 1 << 0; + +pub type __wasi_sdflags_t = u8; +pub const __WASI_SHUT_RD: u8 = 1 << 0; +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_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_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 type __wasi_subclockflags_t = u16; +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, + pub flags: __wasi_subclockflags_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct __wasi_subscription_fs_readwrite_t { + pub fd: __wasi_fd_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_subscription_u { + clock: __wasi_subscription_clock_t, + fd_readwrite: __wasi_subscription_fs_readwrite_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct __wasi_subscription_t { + pub userdata: __wasi_userdata_t, + pub type_: __wasi_eventtype_t, + pub u: __wasi_subscription_u, +} + +pub enum SubscriptionEnum { + Clock(__wasi_subscription_clock_t), + FdReadWrite(__wasi_subscription_fs_readwrite_t), +} + +impl __wasi_subscription_t { + pub fn tagged(&self) -> Option { + match self.type_ { + __WASI_EVENTTYPE_CLOCK => Some(SubscriptionEnum::Clock(unsafe { self.u.clock })), + __WASI_EVENTTYPE_FD_READ | __WASI_EVENTTYPE_FD_WRITE => { + Some(SubscriptionEnum::FdReadWrite(unsafe { + self.u.fd_readwrite + })) + } + _ => None, + } + } +} + +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; diff --git a/lib/wasi/src/syscalls/unix/mod.rs b/lib/wasi/src/syscalls/unix/mod.rs new file mode 100644 index 000000000..4c9cfada3 --- /dev/null +++ b/lib/wasi/src/syscalls/unix/mod.rs @@ -0,0 +1,59 @@ +use crate::syscalls::types::*; +use libc::{ + clock_getres, clock_gettime, timespec, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, + CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID, +}; +use std::cell::Cell; +use std::mem; + +pub fn platform_clock_res_get( + clock_id: __wasi_clockid_t, + resolution: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + let unix_clock_id = match clock_id { + __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, + __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, + __WASI_CLOCK_REALTIME => CLOCK_REALTIME, + __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, + _ => return __WASI_EINVAL, + }; + + let (output, timespec_out) = unsafe { + let mut timespec_out: timespec = mem::uninitialized(); + (clock_getres(unix_clock_id, &mut timespec_out), timespec_out) + }; + + resolution.set(timespec_out.tv_nsec as __wasi_timestamp_t); + + // TODO: map output of clock_getres to __wasi_errno_t + __WASI_ESUCCESS +} + +pub fn platform_clock_time_get( + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + let unix_clock_id = match clock_id { + __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, + __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, + __WASI_CLOCK_REALTIME => CLOCK_REALTIME, + __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, + _ => return __WASI_EINVAL, + }; + + let (output, timespec_out) = unsafe { + let mut timespec_out: timespec = mem::uninitialized(); + ( + clock_gettime(unix_clock_id, &mut timespec_out), + timespec_out, + ) + }; + + // TODO: adjust output by precision... + + time.set(timespec_out.tv_nsec as __wasi_timestamp_t); + + // TODO: map output of clock_gettime to __wasi_errno_t + __WASI_ESUCCESS +} diff --git a/lib/wasi/src/syscalls/windows.rs b/lib/wasi/src/syscalls/windows.rs new file mode 100644 index 000000000..6273695d3 --- /dev/null +++ b/lib/wasi/src/syscalls/windows.rs @@ -0,0 +1,17 @@ +use crate::syscalls::types::*; +use std::cell::Cell; + +pub fn platform_clock_res_get( + clock_id: __wasi_clockid_t, + resolution: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + __WASI_EINVAL +} + +pub fn platform_clock_time_get( + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: &Cell<__wasi_timestamp_t>, +) -> __wasi_errno_t { + unimplemented!() +} diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs new file mode 100644 index 000000000..4c3680be2 --- /dev/null +++ b/lib/wasi/src/utils.rs @@ -0,0 +1,15 @@ +use wasmer_runtime_core::module::Module; + +/// Check if a provided module is compiled with WASI support +pub fn is_wasi_module(module: &Module) -> bool { + for (_, import_name) in &module.info().imported_functions { + let namespace = module + .info() + .namespace_table + .get(import_name.namespace_index); + if namespace == "wasi_unstable" { + return true; + } + } + false +} diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index 18855212b..fe9342feb 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.2.0" +version = "0.4.1" 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.2.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.49" diff --git a/lib/win-exception-handler/README.md b/lib/win-exception-handler/README.md new file mode 100644 index 000000000..9a32e1753 --- /dev/null +++ b/lib/win-exception-handler/README.md @@ -0,0 +1,31 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer Windows Exception Handler + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate provides an API for exception handling on Windows. diff --git a/lib/win-exception-handler/build.rs b/lib/win-exception-handler/build.rs index 7376c6329..1ed351206 100644 --- a/lib/win-exception-handler/build.rs +++ b/lib/win-exception-handler/build.rs @@ -1,8 +1,7 @@ -use cmake::Config; - fn main() { #[cfg(target_os = "windows")] { + use cmake::Config; let project_name = "exception_handling"; let dst = Config::new(project_name).build(); println!("cargo:rustc-link-search=native={}", dst.display()); diff --git a/lib/win-exception-handler/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c index 3c7990ec2..c3ecbab1d 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.c +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -5,6 +5,7 @@ #define CALL_FIRST 1 __declspec(thread) jmp_buf jmpBuf; +__declspec(thread) DWORD caughtExceptionCode; __declspec(thread) PVOID caughtExceptionAddress; __declspec(thread) DWORD64 caughtInstructionPointer; __declspec(thread) PVOID savedStackPointer; @@ -25,6 +26,7 @@ static LONG WINAPI exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { EXCEPTION_RECORD* pExceptionRecord = ExceptionInfo->ExceptionRecord; PCONTEXT pCONTEXT = ExceptionInfo->ContextRecord; + caughtExceptionCode = pExceptionRecord->ExceptionCode; caughtExceptionAddress = pExceptionRecord->ExceptionAddress; caughtInstructionPointer = pCONTEXT->Rip; if (alreadyHandlingException == TRUE) { @@ -61,8 +63,9 @@ uint8_t callProtected(trampoline_t trampoline, } // jmp jmp jmp! - int signum = setjmp(jmpBuf); - if (signum == 0) { + int status = setjmp(jmpBuf); + if (status == 0) // 0 means the original call + { // save the stack pointer savedStackPointer = get_callee_frame_address(); trampoline(ctx, func, param_vec, return_vec); @@ -74,7 +77,7 @@ uint8_t callProtected(trampoline_t trampoline, return TRUE; } - out_result->code = (uint64_t)signum; + out_result->code = (uint64_t)caughtExceptionCode; out_result->exception_address = (uint64_t)caughtExceptionAddress; out_result->instruction_pointer = caughtInstructionPointer; @@ -83,4 +86,4 @@ uint8_t callProtected(trampoline_t trampoline, removeExceptionHandler(); return FALSE; -} +} \ No newline at end of file diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index 966432a70..c448392b0 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -1,7 +1,7 @@ -use std::ffi::c_void; +use std::ptr::NonNull; use wasmer_runtime_core::vm::{Ctx, Func}; -type Trampoline = unsafe extern "C" fn(*mut Ctx, *const Func, *const u64, *mut u64) -> c_void; +type Trampoline = unsafe extern "C" fn(*mut Ctx, NonNull, *const u64, *mut u64); type CallProtectedResult = Result<(), CallProtectedData>; #[repr(C)] @@ -16,7 +16,7 @@ extern "C" { pub fn __call_protected( trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, out_result: *mut CallProtectedData, @@ -26,7 +26,7 @@ extern "C" { pub fn _call_protected( trampoline: Trampoline, ctx: *mut Ctx, - func: *const Func, + func: NonNull, param_vec: *const u64, return_vec: *mut u64, ) -> CallProtectedResult { diff --git a/lib/win-exception-handler/src/lib.rs b/lib/win-exception-handler/src/lib.rs index bc4a142b5..5329e1cc0 100644 --- a/lib/win-exception-handler/src/lib.rs +++ b/lib/win-exception-handler/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[cfg(windows)] mod exception_handling; diff --git a/media/wizard_logo.ico b/media/wizard_logo.ico new file mode 100644 index 000000000..664b0d7a1 Binary files /dev/null and b/media/wizard_logo.ico differ diff --git a/media/wizard_logo_2.bmp b/media/wizard_logo_2.bmp new file mode 100644 index 000000000..959d76829 Binary files /dev/null and b/media/wizard_logo_2.bmp differ diff --git a/media/wizard_logo_small.bmp b/media/wizard_logo_small.bmp new file mode 100644 index 000000000..9ca204fca Binary files /dev/null and b/media/wizard_logo_small.bmp differ diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 61bd50dac..14386d1b1 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + extern crate structopt; use std::env; @@ -6,15 +8,42 @@ use std::io; use std::io::Read; use std::path::PathBuf; use std::process::exit; +use std::str::FromStr; use hashbrown::HashMap; use structopt::StructOpt; -use wasmer::webassembly::InstanceABI; use wasmer::*; -use wasmer_emscripten; -use wasmer_runtime::cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH}; -use wasmer_runtime_core::backend::CompilerConfig; +use wasmer_clif_backend::CraneliftCompiler; +#[cfg(feature = "backend:llvm")] +use wasmer_llvm_backend::LLVMCompiler; +use wasmer_runtime::{ + cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH}, + error::RuntimeError, + Func, Value, +}; +use wasmer_runtime_core::{ + self, + backend::{Compiler, CompilerConfig}, +}; +#[cfg(feature = "backend:singlepass")] +use wasmer_singlepass_backend::SinglePassCompiler; +#[cfg(feature = "wasi")] +use wasmer_wasi; + +// stub module to make conditional compilation happy +#[cfg(not(feature = "wasi"))] +mod wasmer_wasi { + use wasmer_runtime_core::{import::ImportObject, module::Module}; + + pub fn is_wasi_module(_module: &Module) -> bool { + false + } + + pub fn generate_import_object(_args: Vec>, _envs: Vec>) -> ImportObject { + unimplemented!() + } +} #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.")] @@ -28,6 +57,10 @@ enum CLIOptions { #[structopt(name = "cache")] Cache(Cache), + /// Validate a Web Assembly binary + #[structopt(name = "validate")] + Validate(Validate), + /// Update wasmer to the latest version #[structopt(name = "self-update")] SelfUpdate, @@ -43,13 +76,68 @@ struct Run { #[structopt(parse(from_os_str))] path: PathBuf, + // Disable the cache + #[structopt( + long = "backend", + default_value = "cranelift", + raw(possible_values = "Backend::variants()", case_insensitive = "true") + )] + backend: Backend, + + /// Emscripten symbol map + #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] + em_symbol_map: Option, + + /// Begin execution at the specified symbol + #[structopt(long = "em-entrypoint", group = "emscripten")] + em_entrypoint: Option, + + /// WASI pre-opened directory + #[structopt(long = "dir", multiple = true, group = "wasi")] + pre_opened_directories: Vec, + + #[structopt(long = "command-name", hidden = true)] + command_name: Option, + /// Application arguments #[structopt(name = "--", raw(multiple = "true"))] args: Vec, +} - /// Emscripten symbol map - #[structopt(long = "em-symbol-map", parse(from_os_str))] - em_symbol_map: Option, +#[allow(dead_code)] +#[derive(Debug)] +enum Backend { + Cranelift, + Singlepass, + LLVM, +} + +impl Backend { + pub fn variants() -> &'static [&'static str] { + &[ + "cranelift", + #[cfg(feature = "backend:singlepass")] + "singlepass", + #[cfg(feature = "backend:llvm")] + "llvm", + ] + } +} + +impl FromStr for Backend { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "singlepass" => Ok(Backend::Singlepass), + "cranelift" => Ok(Backend::Cranelift), + "llvm" => Ok(Backend::LLVM), + // "llvm" => Err( + // "The LLVM backend option is not enabled by default due to binary size constraints" + // .to_string(), + // ), + _ => Err(format!("The backend {} doesn't exist", s)), + } + } } #[derive(Debug, StructOpt)] @@ -63,6 +151,13 @@ enum Cache { Dir, } +#[derive(Debug, StructOpt)] +struct Validate { + /// Input file + #[structopt(parse(from_os_str))] + path: PathBuf, +} + /// Read the contents of a file fn read_file_contents(path: &PathBuf) -> Result, io::Error> { let mut buffer: Vec = Vec::new(); @@ -151,6 +246,18 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; } + let compiler: Box = match options.backend { + #[cfg(feature = "backend:singlepass")] + Backend::Singlepass => Box::new(SinglePassCompiler::new()), + #[cfg(not(feature = "backend:singlepass"))] + Backend::Singlepass => return Err("The singlepass backend is not enabled".to_string()), + Backend::Cranelift => Box::new(CraneliftCompiler::new()), + #[cfg(feature = "backend:llvm")] + Backend::LLVM => Box::new(LLVMCompiler::new()), + #[cfg(not(feature = "backend:llvm"))] + Backend::LLVM => return Err("the llvm backend is not enabled".to_string()), + }; + let module = if !disable_cache { // If we have cache enabled @@ -176,11 +283,12 @@ fn execute_wasm(options: &Run) -> Result<(), String> { module } Err(_) => { - let module = webassembly::compile_with_config( + let module = webassembly::compile_with_config_with( &wasm_binary[..], CompilerConfig { symbol_map: em_symbol_map, }, + &*compiler, ) .map_err(|e| format!("Can't compile module: {:?}", e))?; // We try to save the module into a cache file @@ -191,41 +299,93 @@ fn execute_wasm(options: &Run) -> Result<(), String> { }; module } else { - webassembly::compile_with_config( + webassembly::compile_with_config_with( &wasm_binary[..], CompilerConfig { symbol_map: em_symbol_map, }, + &*compiler, ) .map_err(|e| format!("Can't compile module: {:?}", e))? }; - let (_abi, import_object, _em_globals) = if wasmer_emscripten::is_emscripten_module(&module) { + // TODO: refactor this + if wasmer_emscripten::is_emscripten_module(&module) { let mut emscripten_globals = wasmer_emscripten::EmscriptenGlobals::new(&module); - ( - InstanceABI::Emscripten, - wasmer_emscripten::generate_emscripten_env(&mut emscripten_globals), - Some(emscripten_globals), // TODO Em Globals is here to extend, lifetime, find better solution + let import_object = wasmer_emscripten::generate_emscripten_env(&mut emscripten_globals); + let mut instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate module: {:?}", e))?; + + wasmer_emscripten::run_emscripten_instance( + &module, + &mut instance, + if let Some(cn) = &options.command_name { + cn + } else { + options.path.to_str().unwrap() + }, + options.args.iter().map(|arg| arg.as_str()).collect(), + options.em_entrypoint.clone(), ) + .map_err(|e| format!("{:?}", e))?; } else { - ( - InstanceABI::None, - wasmer_runtime_core::import::ImportObject::new(), - None, - ) - }; + 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() + .map(|(k, v)| format!("{}={}", k, v).into_bytes()) + .collect(), + options.pre_opened_directories.clone(), + ); - let mut instance = module - .instantiate(&import_object) - .map_err(|e| format!("Can't instantiate module: {:?}", e))?; + let instance = module + .instantiate(&import_object) + .map_err(|e| format!("Can't instantiate module: {:?}", e))?; - webassembly::run_instance( - &module, - &mut instance, - options.path.to_str().unwrap(), - options.args.iter().map(|arg| arg.as_str()).collect(), - ) - .map_err(|e| format!("{:?}", e))?; + let start: Func<(), ()> = instance.func("_start").map_err(|e| format!("{:?}", e))?; + + let result = start.call(); + + if let Err(ref err) = result { + match err { + RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg), + RuntimeError::Error { data } => { + if let Some(error_code) = data.downcast_ref::() { + std::process::exit(error_code.code as i32) + } + } + } + panic!("error: {:?}", err) + } + } 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 args: Vec = options + .args + .iter() + .map(|arg| arg.as_str()) + .map(|x| Value::I32(x.parse().unwrap())) + .collect(); + instance + .dyn_func("main") + .map_err(|e| format!("{:?}", e))? + .call(&args) + .map_err(|e| format!("{:?}", e))?; + } + } Ok(()) } @@ -240,6 +400,42 @@ fn run(options: Run) { } } +fn validate_wasm(validate: Validate) -> Result<(), String> { + let wasm_path = validate.path; + let wasm_path_as_str = wasm_path.to_str().unwrap(); + + let wasm_binary: Vec = read_file_contents(&wasm_path).map_err(|err| { + format!( + "Can't read the file {}: {}", + wasm_path.as_os_str().to_string_lossy(), + err + ) + })?; + + if !utils::is_wasm_binary(&wasm_binary) { + return Err(format!( + "Cannot recognize \"{}\" as a WASM binary", + wasm_path_as_str, + )); + } + + wasmer_runtime_core::validate_and_report_errors(&wasm_binary) + .map_err(|err| format!("Validation failed: {}", err))?; + + Ok(()) +} + +/// Runs logic for the `validate` subcommand +fn validate(validate: Validate) { + match validate_wasm(validate) { + Err(message) => { + eprintln!("Error: {}", message); + exit(-1); + } + _ => (), + } +} + fn main() { let options = CLIOptions::from_args(); match options { @@ -264,6 +460,9 @@ fn main() { println!("{}", get_cache_dir().to_string_lossy()); } }, + CLIOptions::Validate(validate_options) => { + validate(validate_options); + } #[cfg(target_os = "windows")] CLIOptions::Cache(_) => { println!("Caching is disabled for Windows."); diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index f844eb4da..16a396593 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -1,6 +1,6 @@ [Setup] AppName=Wasmer -AppVersion=1.5 +AppVersion=0.4.0 DefaultDirName={pf}\Wasmer DefaultGroupName=Wasmer Compression=lzma2 @@ -9,9 +9,17 @@ OutputDir=.\ DisableProgramGroupPage=yes ChangesEnvironment=yes OutputBaseFilename=WasmerInstaller +WizardImageFile=..\..\media\wizard_logo_2.bmp +WizardSmallImageFile=..\..\media\wizard_logo_small.bmp +SetupIconFile=..\..\media\wizard_logo.ico +DisableWelcomePage=no [Files] Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" +Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin" + +[Dirs] +Name: "{%USERPROFILE}\.wasmer" [Code] const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; @@ -61,11 +69,17 @@ end; procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep = ssPostInstall - then EnvAddPath(ExpandConstant('{app}') +'\bin'); + then begin + EnvAddPath(ExpandConstant('{app}') +'\bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); + end end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usPostUninstall - then EnvRemovePath(ExpandConstant('{app}') +'\bin'); + then begin + EnvRemovePath(ExpandConstant('{app}') +'\bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); + end end; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 25af8304f..78d6dae28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; // extern crate wasmer_emscripten; diff --git a/src/webassembly.rs b/src/webassembly.rs index d34966060..0df8b59e8 100644 --- a/src/webassembly.rs +++ b/src/webassembly.rs @@ -1,13 +1,6 @@ use std::panic; -use wasmer_runtime::{ - self as runtime, - error::{CallResult, Result}, - ImportObject, Instance, Module, -}; -use wasmer_runtime_core::backend::CompilerConfig; -use wasmer_runtime_core::types::Value; - -use wasmer_emscripten::{is_emscripten_module, run_emscripten_instance}; +pub use wasmer_runtime::compile_with_config_with; +use wasmer_runtime::{self as runtime, error::Result, ImportObject, Instance, Module}; pub struct ResultObject { /// A webassembly::Module object representing the compiled WebAssembly module. @@ -21,6 +14,7 @@ pub struct ResultObject { #[derive(PartialEq)] pub enum InstanceABI { Emscripten, + WASI, None, } @@ -76,34 +70,3 @@ pub fn compile(buffer_source: &[u8]) -> Result { let module = runtime::compile(buffer_source)?; Ok(module) } - -/// The same as `compile` but takes a `CompilerConfig` for the purpose of -/// changing the compiler's behavior -pub fn compile_with_config( - buffer_source: &[u8], - compiler_config: CompilerConfig, -) -> Result { - let module = runtime::compile_with_config(buffer_source, compiler_config)?; - Ok(module) -} - -/// Performs common instance operations needed when an instance is first run -/// including data setup, handling arguments and calling a main function -pub fn run_instance( - module: &Module, - instance: &mut Instance, - path: &str, - args: Vec<&str>, -) -> CallResult<()> { - if is_emscripten_module(module) { - run_emscripten_instance(module, instance, path, args)?; - } else { - let args: Vec = args - .into_iter() - .map(|x| Value::I32(x.parse().unwrap())) - .collect(); - instance.call("main", &args)?; - }; - - Ok(()) -} diff --git a/update_version_numbers.sh b/update_version_numbers.sh new file mode 100755 index 000000000..db448940e --- /dev/null +++ b/update_version_numbers.sh @@ -0,0 +1,17 @@ +PREVIOUS_VERSION='0.4.0' +NEXT_VERSION='0.4.1' + +# quick hack +fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" +echo "manually check changes to Cargo.toml" + +# Order to upload packages in +## runtime-core +## win-exception-handler +## clif-backend +## llvm-backend +## singlepass-backend +## emscripten +## wasi +## runtime +## runtime-c-api diff --git a/wapm-cli b/wapm-cli new file mode 160000 index 000000000..8286d0a4b --- /dev/null +++ b/wapm-cli @@ -0,0 +1 @@ +Subproject commit 8286d0a4bcd771c2f5e622b40543143e25e096df

+ + Wasmer logo + +