diff --git a/.appveyor.yml b/.appveyor.yml index 488e400fd..56df0ef21 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,8 +2,10 @@ version: "{build} ~ {branch}" os: Visual Studio 2017 -# Do not build feature branch with open Pull Requests -skip_branch_with_pr: true +branches: + only: + - staging + - trying environment: matrix: @@ -14,30 +16,51 @@ environment: cache: - 'C:\Users\appveyor\.cargo' + - target install: - - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - rustup-init.exe -yv --default-host %target% - - set PATH=%PATH%;%USERPROFILE%\.cargo\bin + # # Install LLVM + # - mkdir C:\projects\deps + # - cd C:\projects\deps + # - appveyor DownloadFile http://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe -FileName llvm.exe + # - 7z x llvm.exe -oC:\projects\deps\llvm + # # - set "PATH=%PATH%;C:\projects\deps\llvm\bin" + # - set "LLD_LINK=C:\projects\deps\llvm\bin\lld-link.exe" + # - set "LLVM_SYS_70_PREFIX=C:\projects\deps\llvm" + # - cd "%APPVEYOR_BUILD_FOLDER%" + + # Install Rust + # uncomment these lines if the cache is cleared, or if we must re-install rust for some reason + # - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + # - rustup-init.exe -yv --default-host %target% + - set PATH=%PATH%;C:\\Libraries\\llvm-5.0.0\\bin;%USERPROFILE%\.cargo\bin + - rustup default stable-%target% + - rustup update - rustc -vV - cargo -vV + # Install InnoSetup + - appveyor-retry appveyor DownloadFile https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-08-22-is.exe + - 2017-08-22-is.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- + - set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH% +# uncomment to RDP to appveyor +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + build_script: - - cargo build --verbose + - cargo build --release --verbose test_script: - - set RUST_BACKTRACE=1 - - cd ./lib/spectests && cargo test -- --test-threads 1 && cd ../.. + - cargo test --manifest-path lib/spectests/Cargo.toml --features clif before_deploy: - cd ./src/installer - iscc wasmer.iss - copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe - - appveyor PushArtifact WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe + - appveyor PushArtifact ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe + - cd ..\..\ -artifacts: - - path: WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe - name: WasmerInstaller.exe +matrix: + fast_finish: true deploy: description: 'WasmerInstaller' diff --git a/.circleci/config.yml b/.circleci/config.yml index 52160f216..9f186b434 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,55 +1,85 @@ +run_with_build_env_vars: &run_with_build_env_vars + environment: + LLVM_SYS_70_PREFIX: /home/circleci/project/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/ + +run_install_dependencies: &run_install_dependencies + 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 + 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: - image: circleci/rust:latest + <<: *run_with_build_env_vars steps: - checkout - restore_cache: keys: - - v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Install dependencies - command: | - sudo apt-get install -y cmake + - v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + - v8-lint-{{ arch }} + - <<: *run_install_dependencies - run: name: Install lint deps command: | + git config --global --unset url."ssh://git@github.com".insteadOf || true + rustup toolchain install nightly-2019-02-27 rustup component add rustfmt - rustup component add clippy + rustup component add clippy --toolchain=nightly-2019-02-27 || cargo +nightly-2019-02-27 install --git https://github.com/rust-lang/rust-clippy/ --force clippy - run: name: Execute lints - command: make lint + command: | + make lint - save_cache: paths: - /usr/local/cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: v6-lint-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} test: docker: - image: circleci/rust:latest + <<: *run_with_build_env_vars steps: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Install dependencies + - 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: | - sudo apt-get install -y cmake - - run: make test - - run: make integration-tests + make test-emscripten-clif + make test-emscripten-llvm + - run: + name: Integration Tests + command: make integration-tests - 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" }} + key: v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} test-macos: macos: @@ -58,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: | @@ -66,6 +97,9 @@ jobs: curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz tar xf cmake-3.4.1-Darwin-x86_64.tar.gz export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + # Installing LLVM outside of brew + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz + tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz - run: name: Install Rust command: | @@ -73,19 +107,32 @@ jobs: export PATH="$HOME/.cargo/bin:$PATH" cargo --version - run: - name: Execute tests + name: Tests command: | export PATH="$HOME/.cargo/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" # 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 - run: - name: Execute integration tests + name: Emscripten Tests command: | export PATH="$HOME/.cargo/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" + # 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-clif + make test-emscripten-llvm + - run: + name: Integration Tests + command: | + export PATH="$HOME/.cargo/bin:$PATH" + export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" make integration-tests - save_cache: paths: @@ -96,7 +143,7 @@ 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: @@ -105,24 +152,43 @@ jobs: - checkout - restore_cache: keys: - - v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: + - 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: Execute tests - command: make test - - run: - name: Make release build + name: Tests command: | - make release + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test + - run: + 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-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 production-release 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: | + cargo check --features "debug" - persist_to_workspace: root: . paths: @@ -136,7 +202,7 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} test-and-build-macos: macos: @@ -145,7 +211,8 @@ jobs: - checkout - 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: | @@ -153,31 +220,54 @@ jobs: curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz tar xf cmake-3.4.1-Darwin-x86_64.tar.gz export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + # Installing LLVM outside of brew + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz + tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz - run: name: Install Rust command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly-2019-04-11 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: Execute tests + name: Tests 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/" # 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 - run: - name: Make release build + name: Emscripten Tests command: | export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH" - make release + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" + # 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-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 production-release 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: @@ -191,7 +281,7 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: v6-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }} + key: v8-cargo-cache-darwin-nightly-{ arch }}-{{ checksum "Cargo.lock" }} test-rust-nightly: docker: @@ -200,20 +290,28 @@ jobs: - checkout - restore_cache: keys: - - v6-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly - - run: + - 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 - - run: rustup default nightly - - run: make test + 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-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-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: @@ -240,32 +338,57 @@ jobs: # echo "Versions don't match. Wasmer output version (wasmer --version) is ${VERSION} while Git tag is ${VERSION_TAG}" # exit 1 #fi + trigger-benchmark-build: + docker: + - image: circleci/rust:latest + steps: + - run: + name: "Trigger Benchmark Build" + command: | + if [[ -z "${CIRCLE_API_USER_TOKEN}" ]]; then + echo "CIRCLE_API_USER_TOKEN environment variable not set" + exit 1 + else + echo "Triggering benchmark build" + curl -u ${CIRCLE_API_USER_TOKEN} \ + -d build_parameters[CIRCLE_JOB]=bench \ + https://circleci.com/api/v1.1/project/github/wasmerio/wasmer-bench/tree/master + fi workflows: version: 2 main: jobs: + - changelog - lint - test: filters: branches: - ignore: master + only: + - trying + - staging - test-macos: filters: branches: - ignore: master + only: + - trying + - staging - test-and-build: filters: branches: - only: master + only: + - master - test-and-build-macos: filters: branches: - only: master + only: + - master - test-rust-nightly: filters: branches: - only: master + only: + - trying + - staging - publish-github-release: requires: - lint @@ -274,3 +397,10 @@ workflows: filters: branches: only: master + - trigger-benchmark-build: + requires: + - test-and-build + - lint + filters: + branches: + only: master 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/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 000000000..7a7e83e12 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,43 @@ +--- +name: "\U0001F41E Bug report" +about: Create a report to help us improve +title: '' +labels: "\U0001F41E bug" +assignees: '' + +--- + +Thanks for the bug report! + +### Describe the bug + +A clear and concise description of what the bug is. + +Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment. + +```bash +echo "`wasmer -V` | `rustc -V` | `uname -m`" +``` + +### Steps to reproduce + +1. Go to '…' +2. Compile with '…' +3. Run '…' +4. See error + +If applicable, add a link to a test case (as a zip file or link to a repository we can clone). + +### Expected behavior + +A clear and concise description of what you expected to happen. + +### Actual behavior + +A clear and concise description of what actually happened. + +If applicable, add screenshots to help explain your problem. + +### Additional context + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md new file mode 100644 index 000000000..be5746331 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -0,0 +1,26 @@ +--- +name: "\U0001F389 Feature request" +about: Suggest an idea for this project +title: '' +labels: "\U0001F389 enhancement" +assignees: '' + +--- + +Thanks for proposing a new feature! + +### Motivation + +A clear and concise description of what the motivation for the new feature is, and what problem it is solving. + +### Proposed solution + +A clear and concise description of the feature you would like to add, and how it solves the motivating problem. + +### Alternatives + +A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better. + +### Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/--question.md b/.github/ISSUE_TEMPLATE/--question.md new file mode 100644 index 000000000..ad79ab2f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/--question.md @@ -0,0 +1,16 @@ +--- +name: "❓ Question" +about: Ask a question about this project +title: '' +labels: "❓ question" +assignees: '' + +--- + +### Summary + +A clear and concise summary of your question. + +### Additional details + +Provide any additional details here. diff --git a/.gitignore b/.gitignore index dd8330f3e..d1122a798 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /artifacts .DS_Store .idea +**/.vscode +install/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..9284916aa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All PRs to the Wasmer repository must add to this file. + +Blocks of changes will separated by version increments. + +## **[Unreleased]** +- [#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). diff --git a/Cargo.lock b/Cargo.lock index 5eddfcff2..bf77b0390 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,54 @@ # 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.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" -version = "0.6.9" +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)", ] +[[package]] +name = "android_log-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "android_logger" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_log-sys 0.1.2 (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)", +] + [[package]] 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]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayvec" version = "0.4.10" @@ -29,9 +62,9 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (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]] @@ -41,15 +74,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.13" +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.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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.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]] @@ -57,8 +90,16 @@ name = "backtrace-sys" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -67,18 +108,18 @@ version = "0.46.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)", - "cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.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.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)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.26 (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.0 (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)", ] @@ -87,11 +128,79 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2b_simd" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "blob" +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.90 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "capstone-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "capstone-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cast" version = "0.2.2" @@ -99,35 +208,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cbindgen" -version = "0.7.1" +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)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (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.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.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.28" +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)", +] [[package]] name = "cexpr" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nom 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cfg-if" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -136,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.48 (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)", ] @@ -164,80 +278,118 @@ 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.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-foundation" +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.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -249,17 +401,17 @@ 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.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)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (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)", ] @@ -283,35 +435,76 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-epoch" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.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)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +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)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.5" +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)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[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.85 (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]] @@ -331,19 +524,65 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.5.1" +name = "dtoa" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dynasm" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.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.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dynasmrt" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding_rs" +version = "0.8.17" +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 = "enum-methods" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "env_logger" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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.0 (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)", ] @@ -353,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.48 (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]] @@ -363,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.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -371,7 +610,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.13 (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)", ] @@ -380,9 +619,9 @@ name = "failure_derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (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.26 (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)", ] @@ -391,6 +630,49 @@ name = "field-offset" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "filetime" +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.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 = "flate2" +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.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)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -405,11 +687,33 @@ name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futures" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] 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" @@ -423,6 +727,33 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "goblin" +version = "0.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "h2" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.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)", + "string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hashbrown" version = "0.1.8" @@ -430,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.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -446,6 +777,21 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "http" +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)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.2.0" @@ -454,17 +800,96 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper" +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.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.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)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.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)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#a14e62977504ef574dc2e933edc559cc79781ca7" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)", + "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)", +] + +[[package]] +name = "inkwell_internal_macros" +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.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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)", +] + [[package]] 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]] @@ -483,26 +908,57 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.2.0" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.48" -source = "git+https://github.com/rust-lang/libc#42cd3ba27254c423e03f6f4324de57075047f6a0" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "libc" -version = "0.2.48" +name = "libflate" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "libloading" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (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]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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]] +name = "llvm-sys" +version = "70.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.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)", ] [[package]] @@ -519,15 +975,47 @@ name = "log" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lz4" +version = "1.23.1" +source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" +dependencies = [ + "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)", +] + +[[package]] +name = "lz4-sys" +version = "1.8.3" +source = "git+https://github.com/zboxfs/lz4-rs.git#4704144553d827a96d4fedeb683dbde1360e06e3" +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 = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memmap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -535,8 +1023,8 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (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]] @@ -545,11 +1033,97 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "meowhash" -version = "0.1.2" +name = "mime" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime_guess" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.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.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)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.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.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)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (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]] @@ -558,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.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -570,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.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -583,13 +1157,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nom" -version = "4.2.0" +version = "4.2.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)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.6" @@ -600,7 +1182,45 @@ name = "num_cpus" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.10.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "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.51 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.43" +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)", + "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)", +] + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -617,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.48 (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)", ] @@ -635,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.48 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -648,16 +1268,63 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "proc-macro2" -version = "0.3.8" +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -670,18 +1337,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.5.2" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "quote" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +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.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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -690,16 +1366,16 @@ 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.48 (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)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -742,34 +1418,34 @@ dependencies = [ [[package]] name = "rand_jitter" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (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]] name = "rand_os" -version = "0.1.1" +version = "0.1.3" 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-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (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.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]] name = "rand_pcg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -795,7 +1471,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.28 (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)", ] @@ -805,7 +1481,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)", ] @@ -815,8 +1491,8 @@ version = "1.4.1" 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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -830,7 +1506,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]] @@ -838,24 +1514,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.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.9 (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)", @@ -866,7 +1542,55 @@ 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.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.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.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)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.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.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp" +version = "0.8.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)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp-serde" +version = "0.13.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.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -895,11 +1619,60 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schannel" +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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scroll" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.5" +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.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.2.2" +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.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)", +] + +[[package]] +name = "security-framework-sys" +version = "0.2.3" +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.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -915,10 +1688,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.85" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -927,50 +1700,81 @@ 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.85 (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_bytes" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (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.58" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.13.11 (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.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.37" +version = "1.0.39" 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.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "smallvec" -version = "0.6.8" +name = "serde_test" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unreachable 1.0.0 (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_urlencoded" +version = "0.5.4" +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.90 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "string" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.7.0" @@ -978,63 +1782,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.26 (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.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.13.11" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.15.26" +version = "0.15.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (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)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (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.26 (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)", ] +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tar" +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.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)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1042,12 +1879,12 @@ name = "tempfile" version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (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.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]] @@ -1063,8 +1900,8 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (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)", ] @@ -1081,7 +1918,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.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)", ] [[package]] @@ -1089,9 +1926,9 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (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]] @@ -1099,8 +1936,129 @@ name = "tinytemplate" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (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)", +] + +[[package]] +name = "tokio" +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.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)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", + "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +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.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +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.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)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-sync" +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.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.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)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (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)", + "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)", +] + +[[package]] +name = "tokio-timer" +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.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)", +] + +[[package]] +name = "tokio-trace-core" +version = "0.1.0" +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)", ] [[package]] @@ -1108,9 +2066,14 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.10.0" @@ -1121,6 +2084,38 @@ name = "ucd-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-segmentation" version = "1.2.1" @@ -1131,17 +2126,24 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "unreachable" -version = "1.0.0" +name = "url" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1149,6 +2151,19 @@ name = "utf8-ranges" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "uuid" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcpkg" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.1" @@ -1169,9 +2184,9 @@ name = "wabt" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (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)", ] @@ -1180,8 +2195,8 @@ name = "wabt-sys" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (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)", ] @@ -1191,153 +2206,234 @@ 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)", ] [[package]] -name = "wasmer" -version = "0.2.0" +name = "want" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "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.3.0" +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)", - "structopt 0.2.14 (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.1.2", - "wasmer-emscripten 0.1.0", - "wasmer-golang 0.1.0", - "wasmer-runtime 0.1.4", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.3.0", + "wasmer-emscripten 0.3.0", + "wasmer-llvm-backend 0.3.0", + "wasmer-runtime 0.3.0", + "wasmer-runtime-abi 0.3.0", + "wasmer-runtime-core 0.3.0", + "wasmer-singlepass-backend 0.3.0", + "wasmer-wasi 0.3.0", ] [[package]] name = "wasmer-clif-backend" -version = "0.1.2" +version = "0.3.0" 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.48 (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)", - "serde 1.0.85 (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.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.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.58 (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.1.2", - "wasmer-win-exception-handler 0.0.1", + "serde_bytes 0.10.5 (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.3.0", + "wasmer-win-exception-handler 0.3.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)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.1.0" +version = "0.3.0" 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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (git+https://github.com/rust-lang/libc)", + "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)", "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.1.2", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.3.0", + "wasmer-llvm-backend 0.3.0", + "wasmer-runtime-core 0.3.0", + "wasmer-singlepass-backend 0.3.0", ] [[package]] -name = "wasmer-golang" -version = "0.1.0" +name = "wasmer-llvm-backend" +version = "0.3.0" 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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (git+https://github.com/rust-lang/libc)", - "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)", + "capstone 0.5.0 (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.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.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-clif-backend 0.1.2", - "wasmer-runtime-core 0.1.2", + "wasmer-runtime-core 0.3.0", + "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-runtime" -version = "0.1.4" +version = "0.3.0" dependencies = [ "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.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)", "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)", - "wasmer-clif-backend 0.1.2", - "wasmer-runtime-core 0.1.2", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.3.0", + "wasmer-llvm-backend 0.3.0", + "wasmer-runtime-core 0.3.0", + "wasmer-singlepass-backend 0.3.0", +] + +[[package]] +name = "wasmer-runtime-abi" +version = "0.3.0" +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.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.3.0", + "wasmparser 0.23.0 (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.1.4" +version = "0.3.0" dependencies = [ - "cbindgen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.1.4", - "wasmer-runtime-core 0.1.2", + "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.3.0", + "wasmer-runtime-core 0.3.0", ] [[package]] name = "wasmer-runtime-core" -version = "0.1.2" +version = "0.3.0" 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)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "meowhash 0.1.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.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.85 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", - "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_bytes 0.10.5 (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.3.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.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.3.0", + "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-spectests" -version = "0.1.2" +version = "0.3.0" dependencies = [ "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.3.0", + "wasmer-llvm-backend 0.3.0", + "wasmer-runtime-core 0.3.0", + "wasmer-singlepass-backend 0.3.0", +] + +[[package]] +name = "wasmer-wasi" +version = "0.3.0" +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.3.0", + "zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)", ] [[package]] name = "wasmer-win-exception-handler" -version = "0.0.1" +version = "0.3.0" 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.48 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.1.2", - "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.3.0", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmparser" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmparser" -version = "0.23.0" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasmparser" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1346,7 +2442,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.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1356,7 +2452,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)", @@ -1378,7 +2474,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]] @@ -1391,132 +2487,303 @@ 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)", ] +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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#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)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "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.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.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]] +name = "zstd" +version = "0.4.22+zstd.1.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zstd-safe 1.4.7+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +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.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)", +] + +[[package]] +name = "zstd-sys" +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.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.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] -"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" +"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.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" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "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.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"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" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e" +"checksum blob 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19803aa44ff8b43123bbe369efaddcb638ea7dc332e543972dd95ac7cb148b92" +"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.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32e01024aaf5390d6a8145047371a4f5b0063a14c1e411bc731353bd2278ca44" -"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" -"checksum cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "644d693ecfa91955ed32dcc7eda4914e1be97a641fb6f0645a37348e20b230da" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"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 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 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.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" "checksum criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4107e4a5abb94267e0149922b8ff49dc70a87cc202820fdbfc0d3e1edbdc4b16" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"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 csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"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 either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" -"checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e" +"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.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" "checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" "checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68" +"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" +"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.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" +"checksum h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "910a5e7be6283a9c91b3982fa5188368c8719cce2a3cf3b86048673bf9d9c36b" "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.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" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)" = "" +"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)" = "" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum libc 0.2.48 (git+https://github.com/rust-lang/libc)" = "" -"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"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.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" +"checksum llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60a9ee82fe0fa72ae6ef6d018b407296085863836451c7a97384f84ed7e26b9f" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum lz4 1.23.1 (git+https://github.com/zboxfs/lz4-rs.git)" = "" +"checksum lz4-sys 1.8.3 (git+https://github.com/zboxfs/lz4-rs.git)" = "" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" +"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum meowhash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b00a85ae2fa3525c3f0e6e7b87bec96ab5587100d7ba7b3e30e1885960a8230b" +"checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" +"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" +"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" +"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f" "checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nom 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b30adc557058ce00c9d0d7cb3c6e0b5bc6f36e2e2eabe74b0ba726d194abd588" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" +"checksum openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0d6b781aac4ac1bd6cafe2a2f0ad8c16ae8e1dd5184822a16c50139f8838d9" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)" = "33c86834957dd5b915623e94f2f4ab2c70dd8f6b70679824155d5ae21dbd495d" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" -"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"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.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f47842851e13bd803b506bdd1345328e0a1394733ee58e627b5e39332b9afafe" -"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" -"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" "checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" "checksum rayon 1.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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" -"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.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" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" +"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" +"checksum security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfab8dda0e7a327c696d893df9ffa19cadc4bd195797997f5223cf5831beaf05" +"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.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752" +"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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3" -"checksum serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ac38f51a52a556cd17545798e29536885fb1a3fa63d6399f5ef650f4a7d35901" -"checksum serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "4b90a9fbe1211e57d3e1c15670f1cb00802988fb23a1a4aad7a2b63544f1920e" -"checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" +"checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" +"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.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" +"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3" -"checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" -"checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" -"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"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.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 target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" +"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.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" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" @@ -1524,27 +2791,54 @@ dependencies = [ "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7655088894274afb52b807bd3c87072daa1fedd155068b8705cabfd628956115" +"checksum tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "65641e515a437b308ab131a82ce3042ff9795bef5d6c5a9be4eb24195c417fd9" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" +"checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ec5759cf26cf9659555f36c431b515e3d05f66831741c85b4b5d5dfb9cf1323c" +"checksum tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2910970404ba6fa78c5539126a9ae2045d62e3713041e447f695f41405a120c6" +"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41d17211f887da8e4a70a45b9536f26fc5de166b81e2d5d80de4a17fd22553bd" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"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.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" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "74e463a508e390cc7447e70f640fbf44ad52e1bd095314ace1fdf99516d32add" "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 wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e666ecb4a406483a59a49f9d0c17f327e70da53a128eccddae2eadb95865c" +"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" "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" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"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 a0b24bfbd..2fbe15ccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.2.0" +version = "0.3.0" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -19,24 +19,33 @@ include = [ ] [dependencies] +errno = "0.2.4" structopt = "0.2.11" wabt = "0.7.2" +hashbrown = "0.1.8" wasmer-clif-backend = { path = "lib/clif-backend" } +wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } 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-golang = { path = "lib/golang" } +wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } +wasmer-wasi = { path = "lib/wasi", optional = true } [workspace] -members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/golang"] +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"] [build-dependencies] wabt = "0.7.2" glob = "0.2.11" +rustc_version = "0.2.3" [features] -default = ["fast-tests"] - -debug = [] +default = ["fast-tests", "wasi"] +debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] +"backend:llvm" = ["wasmer-llvm-backend"] +"backend:singlepass" = ["wasmer-singlepass-backend"] +wasi = ["wasmer-wasi"] +vfs = ["wasmer-runtime-abi"] 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 2506f196c..18b5e080f 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,11 @@ spectests: emtests: WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten -capi: - WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS=1 cargo build --manifest-path lib/runtime-c-api/Cargo.toml --features generate-c-api-headers - # clean: # rm -rf artifacts build: - cargo build + cargo build --features debug install: cargo install --path . @@ -31,22 +28,57 @@ integration-tests: release lint: cargo fmt --all -- --check - cargo clippy --all + cargo +nightly-2019-02-27 clippy --all precommit: lint test +build-install: + mkdir -p ./install/bin + cp ./target/release/wasmer ./install/bin/ + tar -C ./install -zcvf wasmer.tar.gz 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 -- --test-threads=1 $(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-singlepass: + cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass + +test-emscripten-llvm: + cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --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" diff --git a/README.md b/README.md index 74e9ef575..551371f2b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Introduction -[Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go. +[Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with [WASI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/) and [Emscripten](https://emscripten.org/). Install Wasmer with: @@ -26,19 +26,29 @@ 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) ### 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 ``` @@ -70,6 +80,7 @@ Please select your operating system: - [macOS](#macos) - [Debian-based Linuxes](#debian-based-linuxes) +- [FreeBSD](#freebsd) - [Microsoft Windows](#windows-msvc) #### macOS @@ -92,18 +103,30 @@ sudo port install cmake sudo apt install cmake ``` +#### FreeBSD + +```sh +pkg install cmake +``` + #### Windows (MSVC) Windows support is _highly experimental_. Only simple Wasm programs may be run, and no syscalls are allowed. This means nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmerio/wasmer/issues/176) regarding Emscripten syscall polyfills for Windows. -1. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine. +1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) + +2. Install [Rust for Windows](https://win.rustup.rs) + +3. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine. Make sure to enable "Add python.exe to Path" during installation. -2. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default +4. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default settings for the installer are fine). -3. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. +5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. + +6. Install [LLVM 7.0](https://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe) ## Building @@ -158,6 +181,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)_ - [ ] 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 new file mode 100644 index 000000000..3ccbdc008 --- /dev/null +++ b/bors.toml @@ -0,0 +1,10 @@ +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 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/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/nginx/LICENSE b/examples/nginx/LICENSE new file mode 100644 index 000000000..c63e0ba4e --- /dev/null +++ b/examples/nginx/LICENSE @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2002-2019 Igor Sysoev + * Copyright (C) 2011-2019 Nginx, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ 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/single_pass_tests/br_table.wat b/examples/single_pass_tests/br_table.wat new file mode 100644 index 000000000..72e8f92fe --- /dev/null +++ b/examples/single_pass_tests/br_table.wat @@ -0,0 +1,37 @@ +(module + (func $main (export "main") + (i32.eq (call $test (i32.const 0)) (i32.const 2)) + (i32.eq (call $test (i32.const 1)) (i32.const 0)) + (i32.eq (call $test (i32.const 2)) (i32.const 1)) + (i32.eq (call $test (i32.const 3)) (i32.const 3)) + (i32.eq (call $test (i32.const 4)) (i32.const 3)) + (i32.and) + (i32.and) + (i32.and) + (i32.and) + (i32.const 1) + (i32.eq) + (br_if 0) + (unreachable) + ) + + (func $test (param $p i32) (result i32) + (block + (block + (block + (block + (block + (get_local $p) + (br_table 2 0 1 3) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) + ) + (return (i32.const 3)) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/call.wat b/examples/single_pass_tests/call.wat new file mode 100644 index 000000000..2986ac11a --- /dev/null +++ b/examples/single_pass_tests/call.wat @@ -0,0 +1,23 @@ +(module + (func $main (export "main") + (local $a i32) + (block + (set_local $a (i32.const 33)) + (i32.const 11) + (call $foo (get_local $a)) + (i32.add) + (i32.const 86) + (i32.eq) + (br_if 0) + (unreachable) + ) + ) + + (func $foo (param $input i32) (result i32) + (local $a i32) + (set_local $a (i32.const 42)) + (get_local $a) + (get_local $input) + (i32.add) + ) +) diff --git a/examples/single_pass_tests/call_indirect.wat b/examples/single_pass_tests/call_indirect.wat new file mode 100644 index 000000000..019f045e8 --- /dev/null +++ b/examples/single_pass_tests/call_indirect.wat @@ -0,0 +1,25 @@ +(module + (type $binop (func (param i32 i32) (result i32))) + (table 1 100 anyfunc) + (elem (i32.const 5) $sub) + (elem (i32.const 10) $add) + + (func $main (export "main") + (if (i32.eq (call_indirect (type $binop) (i32.const 42) (i32.const 1) (i32.const 10)) (i32.const 43)) + (then) + (else unreachable) + ) + (if (i32.eq (call_indirect (type $binop) (i32.const 42) (i32.const 1) (i32.const 5)) (i32.const 41)) + (then) + (else unreachable) + ) + ) + + (func $add (param i32) (param i32) (result i32) + (i32.add (get_local 0) (get_local 1)) + ) + + (func $sub (param i32) (param i32) (result i32) + (i32.sub (get_local 0) (get_local 1)) + ) +) diff --git a/examples/single_pass_tests/div.wat b/examples/single_pass_tests/div.wat new file mode 100644 index 000000000..3ef5d6e53 --- /dev/null +++ b/examples/single_pass_tests/div.wat @@ -0,0 +1,36 @@ +(module + (func $main (export "main") + (i32.const 1) + (if (i32.ne (i32.div_s (i32.const 2) (i32.const -1)) (i32.const -2)) + (then unreachable) + ) + (i32.const 2) + (if (i32.ne (i32.div_u (i32.const 2) (i32.const -1)) (i32.const 0)) + (then unreachable) + ) + (i32.const 3) + (if (i32.ne (i32.div_u (i32.const 10) (i32.const 5)) (i32.const 2)) + (then unreachable) + ) + (i32.const 4) + (if (i64.ne (i64.div_s (i64.const 300000000000) (i64.const -1)) (i64.const -300000000000)) + (then unreachable) + ) + (i32.const 5) + (if (i64.ne (i64.div_u (i64.const 300000000000) (i64.const -1)) (i64.const 0)) + (then unreachable) + ) + (i32.const 6) + (if (i64.ne (i64.div_u (i64.const 300000000000) (i64.const 2)) (i64.const 150000000000)) + (then unreachable) + ) + (i32.add) + (i32.add) + (i32.add) + (i32.add) + (i32.add) + (if (i32.ne (i32.const 21)) + (then unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/global.wat b/examples/single_pass_tests/global.wat new file mode 100644 index 000000000..f06e15302 --- /dev/null +++ b/examples/single_pass_tests/global.wat @@ -0,0 +1,26 @@ +(module + (global $g1 (mut i32) (i32.const 0)) + (global $g2 (mut i32) (i32.const 99)) + (func $main (export "main") + (if (i32.eq (get_global $g1) (i32.const 0)) + (then) + (else unreachable) + ) + (if (i32.eq (get_global $g2) (i32.const 99)) + (then) + (else unreachable) + ) + + (set_global $g1 (i32.add (get_global $g1) (i32.const 1))) + (set_global $g2 (i32.sub (get_global $g2) (i32.const 1))) + + (if (i32.eq (get_global $g1) (i32.const 1)) + (then) + (else unreachable) + ) + (if (i32.eq (get_global $g2) (i32.const 98)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/i32.wat b/examples/single_pass_tests/i32.wat new file mode 100644 index 000000000..66daadcc9 --- /dev/null +++ b/examples/single_pass_tests/i32.wat @@ -0,0 +1,44 @@ +(module + (func $main (export "main") (result i32) + (local $v1 i32) + (block + (i32.const 10) + (set_local $v1) + + (i32.const 42) + (get_local $v1) + (i32.add) + (i32.const 53) + (i32.eq) + (br_if 0) + + (i32.const 1) + (i32.const -100) + (i32.const 41) + (i32.lt_s) + (i32.sub) + (br_if 0) + + (i32.const -100) + (i32.const 41) + (i32.lt_u) + (br_if 0) + + (i32.const 1) + (i32.const 100) + (i32.const -41) + (i32.gt_s) + (i32.sub) + (br_if 0) + + (i32.const 100) + (i32.const -41) + (i32.gt_u) + (br_if 0) + + (i32.const 0) + (return) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/i64.wat b/examples/single_pass_tests/i64.wat new file mode 100644 index 000000000..ebe10eb99 --- /dev/null +++ b/examples/single_pass_tests/i64.wat @@ -0,0 +1,48 @@ +(module + (func $main (export "main") (result i64) + (local $v1 i64) + (block + (i64.const 10) + (set_local $v1) + + (i64.const 42) + (get_local $v1) + (i64.add) + (i64.const 53) + (i64.eq) + (br_if 0) + + (i64.const 1) + (i64.const -100) + (i64.const 41) + (i64.lt_s) + (i64.extend_u/i32) + (i64.sub) + (i32.wrap/i64) + (br_if 0) + + (i64.const -100) + (i64.const 41) + (i64.lt_u) + (br_if 0) + + (i64.const 1) + (i64.const 100) + (i64.const -41) + (i64.gt_s) + (i64.extend_u/i32) + (i64.sub) + (i32.wrap/i64) + (br_if 0) + + (i64.const 100) + (i64.const -41) + (i64.gt_u) + (br_if 0) + + (i64.const 0) + (return) + ) + (unreachable) + ) +) diff --git a/examples/single_pass_tests/if_else.wat b/examples/single_pass_tests/if_else.wat new file mode 100644 index 000000000..533b8f13d --- /dev/null +++ b/examples/single_pass_tests/if_else.wat @@ -0,0 +1,33 @@ +(module + (func $main (export "main") + (local $a i32) + (set_local $a (i32.const 33)) + + (block + (call $foo (if (result i32) (i32.eq (get_local $a) (i32.const 33)) + (then (i32.const 1)) + (else (i32.const 2)) + )) + (i32.eq (i32.const 43)) + (br_if 0) + (unreachable) + ) + (block + (call $foo (if (result i32) (i32.eq (get_local $a) (i32.const 30)) + (then (i32.const 1)) + (else (i32.const 2)) + )) + (i32.eq (i32.const 44)) + (br_if 0) + (unreachable) + ) + ) + + (func $foo (param $input i32) (result i32) + (local $a i32) + (set_local $a (i32.const 42)) + (get_local $a) + (get_local $input) + (i32.add) + ) +) diff --git a/examples/single_pass_tests/loop.wat b/examples/single_pass_tests/loop.wat new file mode 100644 index 000000000..4d7c7eac8 --- /dev/null +++ b/examples/single_pass_tests/loop.wat @@ -0,0 +1,17 @@ +(module + (func $main (export "main") + (local $count i32) + (local $sum i32) + (loop (result i32) + (set_local $count (i32.add (get_local $count) (i32.const 1))) + (set_local $sum (i32.add (get_local $sum) (get_local $count))) + (i32.sub (i32.const 1) (i32.eq + (get_local $count) + (i32.const 50000) + )) + (br_if 0) + (get_local $sum) + ) + (if (i32.ne (i32.const 1250025000)) (unreachable)) + ) +) diff --git a/examples/single_pass_tests/memory.wat b/examples/single_pass_tests/memory.wat new file mode 100644 index 000000000..9c15eb1ea --- /dev/null +++ b/examples/single_pass_tests/memory.wat @@ -0,0 +1,90 @@ +(module + (memory 1) + (func $main (export "main") + (call $test_stack_layout) + ) + + (func $test_stack_layout + (local $addr i32) + (set_local $addr (i32.const 16)) + + (i32.store (get_local $addr) (i32.const 10)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 655360)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 11)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 720896)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 12)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 786432)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 13)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 851968)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 14)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 917504)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 15)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 983040)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 16)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1048576)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 17)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1114112)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 18)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1179648)) + (then) + (else (unreachable)) + ) + + (i32.const 1) + (i32.store (get_local $addr) (i32.const 19)) + (if (i32.eq (i32.load (i32.const 14)) (i32.const 1245184)) + (then) + (else (unreachable)) + ) + + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + (drop) + ) +) diff --git a/examples/single_pass_tests/select.wat b/examples/single_pass_tests/select.wat new file mode 100644 index 000000000..cfdd36848 --- /dev/null +++ b/examples/single_pass_tests/select.wat @@ -0,0 +1,20 @@ +(module + (func $main (export "main") + (if (i32.eq (select + (i32.const 10) + (i32.const 20) + (i32.const 1) + ) (i32.const 10)) + (then) + (else (unreachable)) + ) + (if (i32.eq (select + (i32.const 10) + (i32.const 20) + (i32.const 0) + ) (i32.const 20)) + (then) + (else (unreachable)) + ) + ) +) diff --git a/examples/single_pass_tests/tee_local.wat b/examples/single_pass_tests/tee_local.wat new file mode 100644 index 000000000..70b9e4737 --- /dev/null +++ b/examples/single_pass_tests/tee_local.wat @@ -0,0 +1,11 @@ +(module + (func $main (export "main") + (local $x i32) + (tee_local $x (i32.const 3)) + (i32.add (i32.const 4)) + (if (i32.eq (i32.const 7)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/single_pass_tests/unwinding.wat b/examples/single_pass_tests/unwinding.wat new file mode 100644 index 000000000..165179808 --- /dev/null +++ b/examples/single_pass_tests/unwinding.wat @@ -0,0 +1,38 @@ +(module + (func $main (export "main") + (i32.const 5) + (block (result i32) + (i32.const 10) + (block + (i32.const 20) + (block + (i32.const 50) + (br 1) + ) + (unreachable) + ) + ) + (i32.add) + (if (i32.eq (i32.const 15)) + (then) + (else unreachable) + ) + + (block (result i32) + (i32.const 10) + (block (result i32) + (i32.const 20) + (block + (i32.const 50) + (br 1) + ) + (unreachable) + ) + (i32.add) + ) + (if (i32.eq (i32.const 60)) + (then) + (else unreachable) + ) + ) +) diff --git a/examples/sqlite.wasm b/examples/sqlite.wasm new file mode 100644 index 000000000..9dccfb125 Binary files /dev/null and b/examples/sqlite.wasm differ diff --git a/install.sh b/install.sh index 04b5f364a..e9e43d445 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() { @@ -133,7 +134,7 @@ wasmer_link() { SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" # We create the wasmer.sh file - echo "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" + printf "$SOURCE_STR" > "$HOME/.wasmer/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" @@ -369,12 +370,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 +418,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 +426,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/lua/test.sh b/integration_tests/lua/test.sh index b45752f0b..7c3cc9b6c 100755 --- a/integration_tests/lua/test.sh +++ b/integration_tests/lua/test.sh @@ -1,7 +1,6 @@ #! /bin/bash -nohup ./target/release/wasmer run examples/lua.wasm & -sleep 3s +nohup ./target/release/wasmer run examples/lua.wasm --disable-cache -- -v if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out then diff --git a/integration_tests/nginx/test.sh b/integration_tests/nginx/test.sh index aeb790e28..a2cfb7159 100755 --- a/integration_tests/nginx/test.sh +++ b/integration_tests/nginx/test.sh @@ -1,22 +1,14 @@ #! /bin/bash -nohup ./target/release/wasmer run examples/nginx/nginx.wasm -- -p integration_tests/nginx/ -c nginx.conf & -sleep 3s +nohup ./target/release/wasmer run examples/nginx/nginx.wasm --disable-cache -- -v -curl localhost:8080 > ./nginx.out - - -if grep "wasmer" ./nginx.out +if grep "nginx version: nginx/1.15.3" ./nohup.out then echo "nginx integration test succeeded" rm ./nohup.out - rm ./nginx.out - rm -rf ./integration_tests/nginx/*_temp exit 0 else echo "nginx integration test failed" rm ./nohup.out - rm ./nginx.out - rm -rf ./integration_tests/nginx/*_temp exit -1 fi diff --git a/lib/README.md b/lib/README.md index 79ae446d3..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)_ @@ -31,6 +32,8 @@ Wasmer intends to support different integrations: The Wasmer [runtime](./runtime) is designed to support multiple compiler backends, allowing the user to tune the codegen properties (compile speed, performance, etc) to best fit their use case. -Currently, we support a Cranelift compiler backend: +Currently, we support multiple backends for compiling WebAssembly to machine code: -- [clif-backend](./clif-backend/): The integration of Wasmer with Cranelift +- [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 701f821fc..9259a1903 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.1.2" +version = "0.3.0" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,17 +8,18 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } -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.3.0" } +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" +target-lexicon = "0.3.0" wasmparser = "0.23.0" byteorder = "1" nix = "0.13.0" -libc = "0.2.48" +libc = "0.2.49" +rayon = "1.0" # Dependencies for caching. [dependencies.serde] @@ -32,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.0.1" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.3.0" } [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..8b6cb4684 100644 --- a/lib/clif-backend/src/cache.rs +++ b/lib/clif-backend/src/cache.rs @@ -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/func_env.rs b/lib/clif-backend/src/func_env.rs index b08f56dd2..8a65444b7 100644 --- a/lib/clif-backend/src/func_env.rs +++ b/lib/clif-backend/src/func_env.rs @@ -445,6 +445,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { pos.ins().symbol_value(ir::types::I64, sig_index_global) + // let dynamic_sigindices_array_ptr = pos.ins().load( + // ptr_type, + // mflags, + + // ) + // let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64); // self.env.deduplicated[clif_sig_index] @@ -477,9 +483,10 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { call_args: &[ir::Value], ) -> cranelift_wasm::WasmResult { let callee_index: FuncIndex = Converter(clif_callee_index).into(); + let ptr_type = self.pointer_type(); match callee_index.local_or_import(&self.env.module.info) { - LocalOrImport::Local(_) => { + LocalOrImport::Local(local_function_index) => { // this is an internal function let vmctx = pos .func @@ -490,10 +497,28 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { args.push(vmctx); args.extend(call_args.iter().cloned()); - Ok(pos.ins().call(callee, &args)) + let sig_ref = pos.func.dfg.ext_funcs[callee].signature; + let function_ptr = { + let mflags = ir::MemFlags::trusted(); + + let function_array_ptr = pos.ins().load( + ptr_type, + mflags, + vmctx, + vm::Ctx::offset_local_functions() as i32, + ); + + pos.ins().load( + ptr_type, + mflags, + function_array_ptr, + (local_function_index.index() as i32) * 8, + ) + }; + + Ok(pos.ins().call_indirect(sig_ref, function_ptr, &args)) } LocalOrImport::Import(imported_func_index) => { - let ptr_type = self.pointer_type(); // this is an imported function let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext); diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index df2e4e208..d09da6812 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -16,7 +16,7 @@ use target_lexicon::Triple; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::{Compiler, Token}, + backend::{Compiler, CompilerConfig, Token}, error::{CompileError, CompileResult}, module::ModuleInner, }; @@ -24,6 +24,7 @@ use wasmer_runtime_core::{ #[macro_use] extern crate serde_derive; +extern crate rayon; extern crate serde; use wasmparser::{self, WasmDecoder}; @@ -38,12 +39,17 @@ impl CraneliftCompiler { impl Compiler for CraneliftCompiler { /// Compiles wasm binary to a wasmer module. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult { + fn compile( + &self, + wasm: &[u8], + compiler_config: CompilerConfig, + _: Token, + ) -> CompileResult { validate(wasm)?; let isa = get_isa(); - let mut module = module::Module::new(wasm); + let mut module = module::Module::new(&compiler_config); let module_env = module_env::ModuleEnv::new(&mut module, &*isa); let func_bodies = module_env.translate(wasm)?; diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 07efe9bd3..ad0576e59 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ - backend::Backend, + backend::{Backend, CompilerConfig}, error::CompileResult, module::{ModuleInfo, ModuleInner, StringTable}, structures::{Map, TypedIndex}, @@ -25,7 +25,7 @@ pub struct Module { } impl Module { - pub fn new(wasm: &[u8]) -> Self { + pub fn new(compiler_config: &CompilerConfig) -> Self { Self { info: ModuleInfo { memories: Map::new(), @@ -50,6 +50,9 @@ impl Module { namespace_table: StringTable::new(), name_table: StringTable::new(), + em_symbol_map: compiler_config.symbol_map.clone(), + + custom_sections: HashMap::new(), }, } } @@ -70,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, @@ -100,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, @@ -148,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 b80e8826d..7cf1dfaff 100644 --- a/lib/clif-backend/src/module_env.rs +++ b/lib/clif-backend/src/module_env.rs @@ -4,7 +4,6 @@ use crate::{ }; use cranelift_codegen::{ir, isa}; use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment}; -use std::sync::Arc; use wasmer_runtime_core::{ error::{CompileError, CompileResult}, module::{ @@ -51,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> { @@ -60,17 +73,9 @@ 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(Arc::new(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()] + self.module.info.signatures.push(Converter(sig).into()); } /// Declares a function import to the environment. @@ -96,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 @@ -110,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 { @@ -184,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; @@ -242,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())); @@ -380,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 = { @@ -394,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")] { @@ -534,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 604778f01..a55bf56d7 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -7,6 +7,7 @@ use crate::{ }, signal::HandlerData, }; +use rayon::prelude::*; use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ir, isa, Context}; @@ -20,7 +21,6 @@ use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::{ self, backend::{ - self, sys::{Memory, Protect}, SigRegistry, }, @@ -92,25 +92,44 @@ impl FuncResolverBuilder { function_bodies: Map, info: &ModuleInfo, ) -> CompileResult<(Self, HandlerData)> { - let mut compiled_functions: Vec> = Vec::with_capacity(function_bodies.len()); - let mut local_relocs = Map::with_capacity(function_bodies.len()); - let mut external_relocs = Map::new(); + let num_func_bodies = function_bodies.len(); + let mut local_relocs = Map::with_capacity(num_func_bodies); + let mut external_relocs = Map::with_capacity(num_func_bodies); let mut trap_sink = TrapSink::new(); - let mut local_trap_sink = LocalTrapSink::new(); - let mut ctx = Context::new(); + let compiled_functions: Result, (RelocSink, LocalTrapSink))>, CompileError> = + function_bodies + .into_vec() + .par_iter() + .map_init( + || Context::new(), + |ctx, func| { + let mut code_buf = Vec::new(); + ctx.func = func.to_owned(); + let mut reloc_sink = RelocSink::new(); + let mut local_trap_sink = LocalTrapSink::new(); + + ctx.compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut local_trap_sink, + ) + .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; + ctx.clear(); + Ok((code_buf, (reloc_sink, local_trap_sink))) + }, + ) + .collect(); + + let compiled_functions = compiled_functions?; let mut total_size = 0; - - for (_, func) in function_bodies { - ctx.func = func; - let mut code_buf = Vec::new(); - let mut reloc_sink = RelocSink::new(); - - ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut local_trap_sink) - .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; - ctx.clear(); - + // We separate into two iterators, one iterable and one into iterable + let (code_bufs, sinks): (Vec>, Vec<(RelocSink, LocalTrapSink)>) = + compiled_functions.into_iter().unzip(); + for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) + { // Clear the local trap sink and consolidate all trap info // into a single location. trap_sink.drain_local(total_size, &mut local_trap_sink); @@ -118,7 +137,6 @@ impl FuncResolverBuilder { // Round up each function's size to pointer alignment. total_size += round_up(code_buf.len(), mem::size_of::()); - compiled_functions.push(code_buf); local_relocs.push(reloc_sink.local_relocs.into_boxed_slice()); external_relocs.push(reloc_sink.external_relocs.into_boxed_slice()); } @@ -145,10 +163,10 @@ impl FuncResolverBuilder { *i = 0xCC; } - let mut map = Map::with_capacity(compiled_functions.len()); + let mut map = Map::with_capacity(num_func_bodies); let mut previous_end = 0; - for compiled in compiled_functions.iter() { + for compiled in code_bufs.iter() { let new_end = previous_end + round_up(compiled.len(), mem::size_of::()); unsafe { memory.as_slice_mut()[previous_end..previous_end + compiled.len()] @@ -202,7 +220,7 @@ impl FuncResolverBuilder { pub fn finalize( mut self, - signatures: &SliceMap>, + signatures: &SliceMap, trampolines: Arc, handler_data: HandlerData, ) -> CompileResult<(FuncResolver, BackendCache)> { @@ -269,8 +287,8 @@ impl FuncResolverBuilder { }, }, RelocationType::Signature(sig_index) => { - let sig_index = - SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index])); + let signature = SigRegistry.lookup_signature_ref(&signatures[sig_index]); + let sig_index = SigRegistry.lookup_sig_index(signature); sig_index.index() as _ } }; @@ -338,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) } } @@ -355,20 +368,26 @@ fn round_up(n: usize, multiple: usize) -> usize { } extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) { - print!(" i32: {},", n); + eprint!(" i32: {},", n); } extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) { - print!(" i64: {},", n); + eprint!(" i64: {},", n); } extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) { - print!(" f32: {},", n); + eprint!(" f32: {},", n); } extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) { - print!(" f64: {},", n); + eprint!(" f64: {},", n); } -extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) { - print!("func ({}), args: [", func_index); +extern "C" fn start_debug(ctx: &mut vm::Ctx, func_index: u32) { + if let Some(symbol_map) = unsafe { ctx.borrow_symbol_map() } { + if let Some(fn_name) = symbol_map.get(&func_index) { + eprint!("func ({} ({})), args: [", fn_name, func_index); + return; + } + } + eprint!("func ({}), args: [", func_index); } extern "C" fn end_debug(_ctx: &mut vm::Ctx) { - println!(" ]"); + eprintln!(" ]"); } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 9ccbf1822..8081f7cf8 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::{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)] @@ -25,167 +24,78 @@ pub use self::unix::*; pub use self::windows::*; thread_local! { - pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); -} - -pub struct Trapper; - -impl UserTrapper for Trapper { - unsafe fn do_early_trap(&self, msg: String) -> ! { - TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg))); - trigger_trap() - } + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } 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, + invoke_env: Option>, + ) -> bool { + let handler_data = &*invoke_env.unwrap().cast().as_ptr(); - assert!(self.func_export_set.contains(&func_index)); - - 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(), + #[cfg(not(target_os = "windows"))] + let res = call_protected(handler_data, || { + // Leap of faith. + trampoline(ctx, func, args, rets); }) - .collect(); + .is_ok(); - let mut return_vec = vec![0; signature.returns().len()]; + // the trampoline is called from C on windows + #[cfg(target_os = "windows")] + let res = call_protected(handler_data, trampoline, ctx, func, args, rets).is_ok(); + + res + } 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( - module: &ModuleInner, - import_backing: &ImportBacking, - func_index: FuncIndex, -) -> (*const vm::Func, Context, Arc, 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 = Arc::clone(&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 9d2f80eca..feaf4e2e2 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -18,11 +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}, - structures::TypedIndex, - types::{MemoryIndex, TableIndex}, -}; +use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -79,8 +75,8 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R if signum != 0 { *jmp_buf = prev_jmp_buf; - if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(RuntimeError::User { msg }) + if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(RuntimeError::Panic { data }) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); @@ -91,28 +87,28 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R { Err(match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { - TrapCode::BadSignature => RuntimeError::IndirectCallSignature { - table: TableIndex::new(0), + TrapCode::BadSignature => RuntimeError::Trap { + msg: "incorrect call_indirect signature".into(), }, - TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { - table: TableIndex::new(0), + TrapCode::IndirectCallToNull => RuntimeError::Trap { + msg: "indirect call to null".into(), }, - TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + TrapCode::HeapOutOfBounds => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), }, - TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { - table: TableIndex::new(0), + TrapCode::TableOutOfBounds => RuntimeError::Trap { + msg: "table out-of-bounds access".into(), }, - _ => RuntimeError::Unknown { - msg: "unknown trap".to_string(), + _ => RuntimeError::Trap { + msg: "unknown trap".into(), }, }, - Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), + }, + Ok(SIGFPE) => RuntimeError::Trap { + msg: "illegal arithmetic operation".into(), }, - Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation, _ => unimplemented!(), } .into()) @@ -126,8 +122,8 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R _ => "unkown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Unknown { - msg: format!("trap at {:p} - {}", faulting_addr, signal), + Err(RuntimeError::Trap { + msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), } .into()) } diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index 191085d91..358b73236 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -3,14 +3,10 @@ use crate::signal::HandlerData; use crate::trampoline::Trampoline; use std::cell::Cell; use std::ffi::c_void; -use std::ptr; +use std::ptr::{self, NonNull}; +use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Func; -use wasmer_runtime_core::{ - error::{RuntimeError, RuntimeResult}, - structures::TypedIndex, - types::{MemoryIndex, TableIndex}, -}; use wasmer_win_exception_handler::CallProtectedData; pub use wasmer_win_exception_handler::_call_protected; use winapi::shared::minwindef::DWORD; @@ -29,7 +25,7 @@ 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<()> { @@ -47,8 +43,8 @@ pub fn call_protected( let CallProtectedData { code: signum, - exceptionAddress: exception_address, - instructionPointer: instruction_pointer, + exception_address, + instruction_pointer, } = result.unwrap_err(); if let Some(TrapData { @@ -57,35 +53,34 @@ pub fn call_protected( }) = handler_data.lookup(instruction_pointer as _) { Err(match signum as DWORD { - EXCEPTION_ACCESS_VIOLATION => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), }, EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode { - TrapCode::BadSignature => RuntimeError::IndirectCallSignature { - table: TableIndex::new(0), + TrapCode::BadSignature => RuntimeError::Trap { + msg: "incorrect call_indirect signature".into(), }, - TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { - table: TableIndex::new(0), + TrapCode::IndirectCallToNull => RuntimeError::Trap { + msg: "indirect call to null".into(), }, - TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + TrapCode::HeapOutOfBounds => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), }, - TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { - table: TableIndex::new(0), + TrapCode::TableOutOfBounds => RuntimeError::Trap { + msg: "table out-of-bounds access".into(), }, - _ => RuntimeError::Unknown { - msg: "unknown trap".to_string(), + _ => RuntimeError::Trap { + msg: "unknown trap".into(), }, }, - EXCEPTION_STACK_OVERFLOW => RuntimeError::Unknown { - msg: "unknown trap".to_string(), + EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap { + msg: "stack overflow trap".into(), }, - EXCEPTION_INT_DIVIDE_BY_ZERO => RuntimeError::IllegalArithmeticOperation, - EXCEPTION_INT_OVERFLOW => RuntimeError::IllegalArithmeticOperation, - _ => RuntimeError::Unknown { - msg: "unknown trap".to_string(), + EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap { + msg: "illegal arithmetic operation".into(), + }, + _ => RuntimeError::Trap { + msg: "unknown trap".into(), }, } .into()) @@ -103,8 +98,8 @@ pub fn call_protected( _ => "unkown trapped signal", }; - Err(RuntimeError::Unknown { - msg: format!("trap at {} - {}", exception_address, signal), + Err(RuntimeError::Trap { + msg: format!("unknown trap at {} - {}", exception_address, signal).into(), } .into()) } 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/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index ec5e035d6..6f37da948 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.1.0" +version = "0.3.0" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,18 +9,26 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } lazy_static = "1.2.0" -libc = { git = "https://github.com/rust-lang/libc" } +libc = "0.2.49" byteorder = "1" time = "0.1.41" +wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.0" } wabt = "0.7.2" [build-dependencies] glob = "0.2.11" + +[features] +clif = [] +llvm = ["wasmer-llvm-backend"] +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/emtests/test_execvp.c b/lib/emscripten/emtests/test_execvp.c new file mode 100644 index 000000000..402654721 --- /dev/null +++ b/lib/emscripten/emtests/test_execvp.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + char command[] = "touch"; + char arg1[] = "foo.txt"; + char* argv[3]; + argv[0] = command; + argv[1] = arg1; + argv[2] = 0; + + printf("_execvp\n"); + int result = execvp(command, argv); + // should not return, and not print this message + printf("error"); + return 0; +} diff --git a/lib/emscripten/emtests/test_execvp.out b/lib/emscripten/emtests/test_execvp.out new file mode 100644 index 000000000..5048f1e2c --- /dev/null +++ b/lib/emscripten/emtests/test_execvp.out @@ -0,0 +1 @@ +_execvp diff --git a/lib/emscripten/emtests/test_execvp.wasm b/lib/emscripten/emtests/test_execvp.wasm new file mode 100644 index 000000000..a0959cc56 Binary files /dev/null and b/lib/emscripten/emtests/test_execvp.wasm differ diff --git a/lib/emscripten/emtests/test_execvp_windows.c b/lib/emscripten/emtests/test_execvp_windows.c new file mode 100644 index 000000000..c3920a4fa --- /dev/null +++ b/lib/emscripten/emtests/test_execvp_windows.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char command[] = "C:\\Windows\\System32\\cmd.exe"; + char arg1[] = "echo"; + char arg2[] = "foo"; + char* argv[4]; + argv[0] = command; + argv[1] = arg1; + argv[2] = arg2; + argv[3] = 0; + printf("_execvp\n"); + int result = execvp(command, argv); + // should not return, and not print this message + printf("error"); + return 0; +} diff --git a/lib/emscripten/emtests/test_execvp_windows.wasm b/lib/emscripten/emtests/test_execvp_windows.wasm new file mode 100644 index 000000000..ec3a210c5 Binary files /dev/null and b/lib/emscripten/emtests/test_execvp_windows.wasm differ diff --git a/lib/emscripten/emtests/test_getcwd.c b/lib/emscripten/emtests/test_getcwd.c new file mode 100644 index 000000000..a3955d470 --- /dev/null +++ b/lib/emscripten/emtests/test_getcwd.c @@ -0,0 +1,10 @@ +#include +#include + +int main() { + const unsigned int size = 256; + char cwd[size] = {}; + char* buf = getcwd(cwd, size); + printf("getcwd\n"); + return 0; +} diff --git a/lib/emscripten/emtests/test_getcwd.out b/lib/emscripten/emtests/test_getcwd.out new file mode 100644 index 000000000..263db4c74 --- /dev/null +++ b/lib/emscripten/emtests/test_getcwd.out @@ -0,0 +1 @@ +getcwd diff --git a/lib/emscripten/emtests/test_getcwd.wasm b/lib/emscripten/emtests/test_getcwd.wasm new file mode 100644 index 000000000..0cf6f73a2 Binary files /dev/null and b/lib/emscripten/emtests/test_getcwd.wasm differ diff --git a/lib/emscripten/emtests/test_pipe.c b/lib/emscripten/emtests/test_pipe.c new file mode 100644 index 000000000..c8f650310 --- /dev/null +++ b/lib/emscripten/emtests/test_pipe.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +int main() { + int mypipe[2]; + printf("pipe\n"); + if (pipe(mypipe)) { + printf("error\n"); + } + return 0; +} diff --git a/lib/emscripten/emtests/test_pipe.out b/lib/emscripten/emtests/test_pipe.out new file mode 100644 index 000000000..6b14dd0e0 --- /dev/null +++ b/lib/emscripten/emtests/test_pipe.out @@ -0,0 +1 @@ +pipe diff --git a/lib/emscripten/emtests/test_pipe.wasm b/lib/emscripten/emtests/test_pipe.wasm new file mode 100644 index 000000000..1aa68a5c4 Binary files /dev/null and b/lib/emscripten/emtests/test_pipe.wasm differ diff --git a/lib/emscripten/emtests/test_vfs.c b/lib/emscripten/emtests/test_vfs.c new file mode 100644 index 000000000..e7fc5c129 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int main() { + char data[256] = {0}; + ssize_t fd = open("data.txt", 0); + ssize_t result = read((int)fd, &data, 255); + printf("content: %s", data); + printf("fd: %zd\n", fd); + return 0; +} diff --git a/lib/emscripten/emtests/test_vfs.md b/lib/emscripten/emtests/test_vfs.md new file mode 100644 index 000000000..e399c8138 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.md @@ -0,0 +1,6 @@ +The wasm file `test_vfs.wasm` is generated by compiling the `test_vfs.c` and writing a tar.zst blob with a single file +named `data.txt`. + +The program expects to find a file named `data.txt` and reads the contents and the file descriptor. + +The runtime should mount the virtual filesystem and expose the file. \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.out b/lib/emscripten/emtests/test_vfs.out new file mode 100644 index 000000000..5d8528aff --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.out @@ -0,0 +1 @@ +content: wasmer is awesomer \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.wasm b/lib/emscripten/emtests/test_vfs.wasm new file mode 100644 index 000000000..d1f8e6f82 Binary files /dev/null and b/lib/emscripten/emtests/test_vfs.wasm differ diff --git a/lib/emscripten/emtests/test_vfs_bundle.wasm b/lib/emscripten/emtests/test_vfs_bundle.wasm new file mode 100644 index 000000000..92f3bbbf9 Binary files /dev/null and b/lib/emscripten/emtests/test_vfs_bundle.wasm differ diff --git a/lib/emscripten/emtests/test_vfs_data.txt b/lib/emscripten/emtests/test_vfs_data.txt new file mode 100644 index 000000000..00f2295a8 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs_data.txt @@ -0,0 +1 @@ +wasmer is awesomer \ No newline at end of file diff --git a/lib/emscripten/src/bitwise.rs b/lib/emscripten/src/bitwise.rs new file mode 100644 index 000000000..6868c402a --- /dev/null +++ b/lib/emscripten/src/bitwise.rs @@ -0,0 +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"); + 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 e850757f6..5b14a1099 100644 --- a/lib/emscripten/src/emscripten_target.rs +++ b/lib/emscripten/src/emscripten_target.rs @@ -1,265 +1,267 @@ +#![allow(non_snake_case)] + use crate::env::get_emscripten_data; +#[cfg(target_os = "linux")] +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 { debug!("emscripten::getTempRet0"); - 0 + get_emscripten_data(ctx).temp_ret_0 } -pub fn nullFunc_ji(ctx: &mut Ctx, a: i32) { - debug!("emscripten::nullFunc_ji"); -} -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 { + +pub fn __Unwind_Backtrace(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::__Unwind_Backtrace"); 0 } -pub fn __Unwind_FindEnclosingFunction(ctx: &mut Ctx, a: i32) -> i32 { +pub fn __Unwind_FindEnclosingFunction(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::__Unwind_FindEnclosingFunction"); 0 } -pub fn __Unwind_GetIPInfo(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn __Unwind_GetIPInfo(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::__Unwind_GetIPInfo"); 0 } -pub fn ___cxa_find_matching_catch_2(ctx: &mut Ctx) -> i32 { +pub fn ___cxa_find_matching_catch_2(_ctx: &mut Ctx) -> i32 { debug!("emscripten::___cxa_find_matching_catch_2"); 0 } -pub fn ___cxa_find_matching_catch_3(ctx: &mut Ctx, a: i32) -> i32 { +pub fn ___cxa_find_matching_catch_3(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::___cxa_find_matching_catch_3"); 0 } -pub fn ___cxa_free_exception(ctx: &mut Ctx, a: i32) { +pub fn ___cxa_free_exception(_ctx: &mut Ctx, _a: i32) { debug!("emscripten::___cxa_free_exception"); } -pub fn ___resumeException(ctx: &mut Ctx, a: i32) { +pub fn ___resumeException(_ctx: &mut Ctx, _a: i32) { debug!("emscripten::___resumeException"); } -pub fn _dladdr(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_dladdr"); 0 } -pub fn _pthread_cond_destroy(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_cond_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_cond_destroy"); 0 } -pub fn _pthread_cond_init(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 { + debug!("emscripten::_pthread_getspecific"); + 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_join(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_join"); + 0 +} +pub fn _pthread_cond_init(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_cond_init"); 0 } -pub fn _pthread_cond_signal(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_cond_signal(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_cond_signal"); 0 } -pub fn _pthread_cond_wait(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _pthread_cond_wait(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_cond_wait"); 0 } -pub fn _pthread_condattr_destroy(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_condattr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_condattr_destroy"); 0 } -pub fn _pthread_condattr_init(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_condattr_init(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_condattr_init"); 0 } -pub fn _pthread_condattr_setclock(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _pthread_condattr_setclock(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_condattr_setclock"); 0 } -pub fn _pthread_mutex_destroy(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_mutex_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_mutex_destroy"); 0 } -pub fn _pthread_mutex_init(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _pthread_mutex_init(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_mutex_init"); 0 } -pub fn _pthread_mutexattr_destroy(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_mutexattr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_mutexattr_destroy"); 0 } -pub fn _pthread_mutexattr_init(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_mutexattr_init(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_mutexattr_init"); 0 } -pub fn _pthread_mutexattr_settype(ctx: &mut Ctx, a: i32, b: i32) -> i32 { +pub fn _pthread_mutexattr_settype(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { debug!("emscripten::_pthread_mutexattr_settype"); 0 } -pub fn _pthread_rwlock_rdlock(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_rwlock_rdlock(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_rwlock_rdlock"); 0 } -pub fn _pthread_rwlock_unlock(ctx: &mut Ctx, a: i32) -> i32 { +pub fn _pthread_rwlock_unlock(_ctx: &mut Ctx, _a: i32) -> i32 { debug!("emscripten::_pthread_rwlock_unlock"); 0 } -pub fn ___gxx_personality_v0(ctx: &mut Ctx, a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) -> i32 { +pub fn _pthread_setcancelstate(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { + debug!("emscripten::_pthread_setcancelstate"); + 0 +} +pub fn ___gxx_personality_v0( + _ctx: &mut Ctx, + _a: i32, + _b: i32, + _c: i32, + _d: i32, + _e: i32, + _f: i32, +) -> i32 { debug!("emscripten::___gxx_personality_v0"); 0 } -// round 2 -pub fn nullFunc_dii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_dii"); +#[cfg(target_os = "linux")] +pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::getdtablesize"); + unsafe { getdtablesize() } } -pub fn nullFunc_diiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_diiii"); +#[cfg(not(target_os = "linux"))] +pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 { + debug!("emscripten::getdtablesize"); + -1 } -pub fn nullFunc_iiji(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_iiji"); +pub fn _gethostbyaddr(_ctx: &mut Ctx, _addr: i32, _addrlen: i32, _atype: i32) -> i32 { + debug!("emscripten::gethostbyaddr"); + 0 } -pub fn nullFunc_j(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_j"); +pub fn _gethostbyname_r( + _ctx: &mut Ctx, + _name: i32, + _ret: i32, + _buf: i32, + _buflen: i32, + _out: i32, + _err: i32, +) -> i32 { + debug!("emscripten::gethostbyname_r"); + 0 } -pub fn nullFunc_jij(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_jij"); +// NOTE: php.js has proper impl; libc has proper impl for linux +pub fn _getloadavg(_ctx: &mut Ctx, _loadavg: i32, _nelem: i32) -> i32 { + debug!("emscripten::getloadavg"); + 0 } -pub fn nullFunc_jjj(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_jjj"); + +// 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 _ + } + } + }}; } -pub fn nullFunc_vd(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_vd"); +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"); + } + } + }}; } -pub fn nullFunc_viiiiiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiiiiii"); + +// Invoke functions +pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 { + debug!("emscripten::invoke_i"); + invoke!(ctx, dyn_call_i, index) } -pub fn nullFunc_viiiiiiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiiiiiii"); +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 nullFunc_viiiiiiiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiiiiiiii"); +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 nullFunc_viiij(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiij"); +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 nullFunc_viiijiiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiijiiii"); +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 nullFunc_viiijiiiiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiijiiiiii"); +pub fn invoke_v(ctx: &mut Ctx, index: i32) { + debug!("emscripten::invoke_v"); + invoke_no_return!(ctx, dyn_call_v, index); } -pub fn nullFunc_viij(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viij"); +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 nullFunc_viiji(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viiji"); +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 nullFunc_viijiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viijiii"); + +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 nullFunc_viijj(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viijj"); -} -pub fn nullFunc_vij(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_vij"); -} -pub fn nullFunc_viji(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_viji"); -} -pub fn nullFunc_vijiii(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_vijiii"); -} -pub fn nullFunc_vijj(ctx: &mut Ctx, index: i32) { - debug!("emscripten::nullFunc_vijj"); +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, @@ -271,27 +273,129 @@ 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, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiii"); + invoke!(ctx, dyn_call_iiiiiii, index, a1, a2, a3, a4, a5, a6) +} +pub fn invoke_iiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiii"); + 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, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiiiiiii"); + 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, @@ -304,13 +408,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, @@ -324,13 +422,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, @@ -345,13 +437,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, @@ -367,21 +465,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"); @@ -399,6 +551,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 { @@ -522,6 +683,14 @@ 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_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 { @@ -563,3 +732,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"); + 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 89f55fd09..2f799a679 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -47,6 +47,13 @@ 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); @@ -70,8 +77,8 @@ pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) { // }; } -pub fn ___assert_fail(_ctx: &mut Ctx, a: c_int, b: c_int, c: c_int, d: c_int) { - debug!("emscripten::___assert_fail {} {} {} {}", a, b, c, d); +pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) { + debug!("emscripten::___assert_fail {} {} {} {}", _a, _b, _c, _d); // TODO: Implement like emscripten expects regarding memory/page size // TODO raise an error } diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index cc7e01b03..7c846150f 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -66,6 +66,8 @@ pub fn _unsetenv(ctx: &mut Ctx, name: c_int) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); + #[cfg(feature = "debug")] + let _ = name_ptr; #[repr(C)] struct GuestPasswd { diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index e95a66ec2..f738eccbb 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -7,7 +7,6 @@ use std::os::raw::c_char; use crate::env::call_malloc; use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm}; -use std::ffi::CStr; use wasmer_runtime_core::vm::Ctx; extern "C" { @@ -29,10 +28,8 @@ pub fn _getenv(ctx: &mut Ctx, name: u32) -> u32 { } /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); -pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, overwrite: u32) -> c_int { +pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, _overwrite: u32) -> c_int { debug!("emscripten::_setenv"); - let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); - let value_addr = emscripten_memory_pointer!(ctx.memory(0), value); // setenv does not exist on windows, so we hack it with _putenv let name = read_string_from_wasm(ctx.memory(0), name); let value = read_string_from_wasm(ctx.memory(0), value); @@ -47,17 +44,16 @@ pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, overwrite: u32) -> c_int { /// emscripten: _putenv // (name: *const char); pub fn _putenv(ctx: &mut Ctx, name: c_int) -> c_int { debug!("emscripten::_putenv"); - let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; - - debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + debug!("=> name({:?})", unsafe { + std::ffi::CStr::from_ptr(name_addr) + }); unsafe { putenv(name_addr) } } /// emscripten: _unsetenv // (name: *const char); pub fn _unsetenv(ctx: &mut Ctx, name: u32) -> c_int { debug!("emscripten::_unsetenv"); - let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); let name = read_string_from_wasm(ctx.memory(0), name); // no unsetenv on windows, so use putenv with an empty value let unsetenv_string = format!("{}=", name); @@ -70,6 +66,8 @@ pub fn _unsetenv(ctx: &mut Ctx, name: u32) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); + #[cfg(not(feature = "debug"))] + let _ = name_ptr; #[repr(C)] struct GuestPasswd { @@ -102,6 +100,8 @@ pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { #[allow(clippy::cast_ptr_alignment)] pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); + #[cfg(not(feature = "debug"))] + let _ = name_ptr; #[repr(C)] struct GuestGroup { @@ -126,6 +126,8 @@ pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { pub fn _sysconf(_ctx: &mut Ctx, name: c_int) -> c_long { debug!("emscripten::_sysconf {}", name); + #[cfg(not(feature = "debug"))] + let _ = name; // stub because sysconf is not valid on windows 0 } diff --git a/lib/emscripten/src/errno.rs b/lib/emscripten/src/errno.rs index 06267e210..ff1d09b7f 100644 --- a/lib/emscripten/src/errno.rs +++ b/lib/emscripten/src/errno.rs @@ -1,8 +1,8 @@ // use std::collections::HashMap; use wasmer_runtime_core::vm::Ctx; -pub fn ___seterrno(_ctx: &mut Ctx, value: i32) { - debug!("emscripten::___seterrno {}", value); +pub fn ___seterrno(_ctx: &mut Ctx, _value: i32) { + debug!("emscripten::___seterrno {}", _value); // TODO: Incomplete impl eprintln!("failed to set errno!"); // value diff --git a/lib/emscripten/src/exception.rs b/lib/emscripten/src/exception.rs index b0aab78dd..1f2b098d8 100644 --- a/lib/emscripten/src/exception.rs +++ b/lib/emscripten/src/exception.rs @@ -14,3 +14,17 @@ 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 +} diff --git a/lib/emscripten/src/exec.rs b/lib/emscripten/src/exec.rs new file mode 100644 index 000000000..5751e3199 --- /dev/null +++ b/lib/emscripten/src/exec.rs @@ -0,0 +1,53 @@ +use crate::varargs::VarArgs; +use libc::execvp as libc_execvp; +use std::cell::Cell; +use std::ffi::CString; +use wasmer_runtime_core::vm::Ctx; + +pub fn execvp(ctx: &mut Ctx, command_name_offset: u32, argv_offset: u32) -> i32 { + // a single reference to re-use + let emscripten_memory = ctx.memory(0); + + // read command name as string + let command_name_string_vec: Vec = emscripten_memory.view() + [(command_name_offset as usize)..] + .iter() + .map(|cell| cell.get()) + .take_while(|&byte| byte != 0) + .collect(); + let command_name_string = CString::new(command_name_string_vec).unwrap(); + + // get the array of args + let mut argv: Vec<*const i8> = emscripten_memory.view()[((argv_offset / 4) as usize)..] + .iter() + .map(|cell: &Cell| cell.get()) + .take_while(|&byte| byte != 0) + .map(|offset| { + let p: *const i8 = (emscripten_memory.view::()[(offset as usize)..]) + .iter() + .map(|cell| cell.as_ptr() as *const i8) + .collect::>()[0]; + p + }) + .collect(); + + // push a nullptr on to the end of the args array + argv.push(std::ptr::null()); + + // construct raw pointers and hand them to `execvp` + let command_pointer = command_name_string.as_ptr() as *const i8; + let args_pointer = argv.as_ptr(); + unsafe { libc_execvp(command_pointer, args_pointer) } +} + +/// execl +pub fn execl(_ctx: &mut Ctx, _path_ptr: i32, _arg0_ptr: i32, _varargs: VarArgs) -> i32 { + debug!("emscripten::execl"); + -1 +} + +/// execle +pub fn execle(_ctx: &mut Ctx, _path_ptr: i32, _arg0_ptr: i32, _varargs: VarArgs) -> i32 { + debug!("emscripten::execle"); + -1 +} diff --git a/lib/emscripten/src/exit.rs b/lib/emscripten/src/exit.rs new file mode 100644 index 000000000..d3f57a7a2 --- /dev/null +++ b/lib/emscripten/src/exit.rs @@ -0,0 +1,7 @@ +use wasmer_runtime_core::vm::Ctx; + +// __exit +pub fn exit(_ctx: &mut Ctx, value: i32) { + debug!("emscripten::exit {}", value); + ::std::process::exit(value); +} diff --git a/lib/emscripten/src/io/mod.rs b/lib/emscripten/src/io/mod.rs index aea5e6fc2..6666cd5af 100644 --- a/lib/emscripten/src/io/mod.rs +++ b/lib/emscripten/src/io/mod.rs @@ -9,3 +9,55 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; + +use wasmer_runtime_core::vm::Ctx; + +/// getprotobyname +pub fn getprotobyname(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { + debug!("emscripten::getprotobyname"); + unimplemented!() +} + +/// getprotobynumber +pub fn getprotobynumber(_ctx: &mut Ctx, _one: i32) -> i32 { + debug!("emscripten::getprotobynumber"); + unimplemented!() +} + +/// sigdelset +pub fn sigdelset(ctx: &mut Ctx, set: i32, signum: i32) -> i32 { + debug!("emscripten::sigdelset"); + 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, set: i32) -> i32 { + debug!("emscripten::sigfillset"); + 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 - stub"); + //unimplemented!() +} + +/// strptime +pub fn strptime(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { + debug!("emscripten::strptime"); + unimplemented!() +} diff --git a/lib/emscripten/src/io/unix.rs b/lib/emscripten/src/io/unix.rs index 38324ac1e..d120e754d 100644 --- a/lib/emscripten/src/io/unix.rs +++ b/lib/emscripten/src/io/unix.rs @@ -1,9 +1,9 @@ -use libc::printf as _printf; +use libc::{chroot as _chroot, printf as _printf}; use wasmer_runtime_core::vm::Ctx; /// putchar -pub fn putchar(ctx: &mut Ctx, chr: i32) { +pub fn putchar(_ctx: &mut Ctx, chr: i32) { unsafe { libc::putchar(chr) }; } @@ -15,3 +15,16 @@ pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { _printf(addr, extra) } } + +/// chroot +pub fn chroot(ctx: &mut Ctx, name_ptr: i32) -> i32 { + debug!("emscripten::chroot"); + let name = emscripten_memory_pointer!(ctx.memory(0), name_ptr) as *const i8; + unsafe { _chroot(name) } +} + +/// getpwuid +pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 { + debug!("emscripten::getpwuid"); + 0 +} diff --git a/lib/emscripten/src/io/windows.rs b/lib/emscripten/src/io/windows.rs index 903d05900..a3c6f70aa 100644 --- a/lib/emscripten/src/io/windows.rs +++ b/lib/emscripten/src/io/windows.rs @@ -1,4 +1,3 @@ -use libc::{c_char, c_int}; use wasmer_runtime_core::vm::Ctx; // This may be problematic for msvc which uses inline functions for the printf family @@ -15,16 +14,33 @@ use wasmer_runtime_core::vm::Ctx; //} /// putchar -pub fn putchar(ctx: &mut Ctx, chr: i32) { +pub fn putchar(_ctx: &mut Ctx, chr: i32) { unsafe { libc::putchar(chr) }; } /// printf -pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { +pub fn printf(_ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { debug!("emscripten::printf {}, {}", memory_offset, extra); + #[cfg(not(feature = "debug"))] + { + let _ = memory_offset; + let _ = extra; + } // unsafe { // let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _; // _printf(addr, extra) // } -1 } + +/// chroot +pub fn chroot(_ctx: &mut Ctx, _name_ptr: i32) -> i32 { + debug!("emscripten::chroot"); + unimplemented!() +} + +/// getpwuid +pub fn getpwuid(_ctx: &mut Ctx, _uid: i32) -> i32 { + debug!("emscripten::getpwuid"); + unimplemented!() +} diff --git a/lib/emscripten/src/jmp.rs b/lib/emscripten/src/jmp.rs index c5b54f02b..015496d29 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: _longjmp"); + 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 5a7f008f1..b3484770b 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -27,17 +27,19 @@ mod file_descriptor; pub mod stdio; // EMSCRIPTEN APIS +mod bitwise; mod emscripten_target; mod env; mod errno; mod exception; +mod exec; +mod exit; mod io; mod jmp; mod linking; mod lock; mod math; mod memory; -mod nullfunc; mod process; mod signal; mod storage; @@ -54,8 +56,6 @@ pub use self::utils::{ // TODO: Magic number - how is this calculated? const TOTAL_STACK: u32 = 5_242_880; -// TODO: Magic number - how is this calculated? -const DYNAMICTOP_PTR_DIFF: u32 = 1088; // TODO: make this variable const STATIC_BUMP: u32 = 215_536; @@ -71,22 +71,6 @@ lazy_static! { const GLOBAL_BASE: u32 = 1024; const STATIC_BASE: u32 = GLOBAL_BASE; -fn stacktop(static_bump: u32) -> u32 { - align_memory(dynamictop_ptr(static_bump) + 4) -} - -fn stack_max(static_bump: u32) -> u32 { - stacktop(static_bump) + TOTAL_STACK -} - -fn dynamic_base(static_bump: u32) -> u32 { - align_memory(stack_max(static_bump)) -} - -fn dynamictop_ptr(static_bump: u32) -> u32 { - static_bump + DYNAMICTOP_PTR_DIFF -} - pub struct EmscriptenData<'a> { pub malloc: Func<'a, u32, u32>, pub free: Func<'a, u32>, @@ -99,6 +83,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>, @@ -110,15 +95,27 @@ pub struct EmscriptenData<'a> { pub dyn_call_diiii: Option>, pub dyn_call_iiiii: Option>, 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>, @@ -129,10 +126,20 @@ 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_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> { @@ -151,6 +158,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(); @@ -162,15 +170,24 @@ impl<'a> EmscriptenData<'a> { let dyn_call_diiii = instance.func("dynCall_diiii").ok(); let dyn_call_iiiii = instance.func("dynCall_iiiii").ok(); 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(); @@ -180,10 +197,18 @@ 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_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, @@ -196,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, @@ -207,15 +233,24 @@ impl<'a> EmscriptenData<'a> { dyn_call_diiii, dyn_call_iiiii, 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, @@ -225,10 +260,19 @@ impl<'a> EmscriptenData<'a> { dyn_call_viiji, dyn_call_viijiii, dyn_call_viijj, + dyn_call_vj, 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, } } } @@ -243,6 +287,12 @@ pub fn run_emscripten_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", &[])?; } @@ -265,7 +315,7 @@ pub fn run_emscripten_instance( ), }; - // TODO atinit and atexit for emscripten + // TODO atexit for emscripten // println!("{:?}", data); Ok(()) } @@ -309,10 +359,6 @@ pub struct EmscriptenGlobalsData { table_base: u32, temp_double_ptr: u32, use_old_abort_on_cannot_grow_memory: bool, - - // Global namespace - infinity: f64, - nan: f64, } pub struct EmscriptenGlobals { @@ -323,6 +369,7 @@ pub struct EmscriptenGlobals { pub table: Table, pub memory_min: Pages, pub memory_max: Option, + pub null_func_names: Vec, } impl EmscriptenGlobals { @@ -341,7 +388,7 @@ impl EmscriptenGlobals { if name == "abortOnCannotGrowMemory" && namespace == "env" { let sig_index = module.info().func_assoc[index.convert_up(module.info())]; let expected_sig = &module.info().signatures[sig_index]; - if **expected_sig == *OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG { + if *expected_sig == *OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG { use_old_abort_on_cannot_grow_memory = true; } break; @@ -364,22 +411,22 @@ impl EmscriptenGlobals { minimum: table_min, maximum: table_max, }; - let mut table = Table::new(table_type).unwrap(); + let table = Table::new(table_type).unwrap(); let data = { let static_bump = STATIC_BUMP; - let mut STATIC_TOP = STATIC_BASE + static_bump; + let mut static_top = STATIC_BASE + static_bump; let memory_base = STATIC_BASE; let table_base = 0; - let temp_double_ptr = STATIC_TOP; - STATIC_TOP += 16; + let temp_double_ptr = static_top; + static_top += 16; - let dynamictop_ptr = static_alloc(&mut STATIC_TOP, 4); + let dynamictop_ptr = static_alloc(&mut static_top, 4); - let stacktop = align_memory(STATIC_TOP); + let stacktop = align_memory(static_top); let stack_max = stacktop + TOTAL_STACK; EmscriptenGlobalsData { @@ -391,20 +438,34 @@ impl EmscriptenGlobals { table_base, temp_double_ptr, use_old_abort_on_cannot_grow_memory, - - infinity: std::f64::INFINITY, - nan: std::f64::NAN, } }; emscripten_set_up_memory(&memory, &data); + let mut null_func_names = vec![]; + for ( + _, + ImportName { + namespace_index, + name_index, + }, + ) in &module.info().imported_functions + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + if namespace == "env" && name.starts_with("nullFunc_") { + null_func_names.push(name.to_string()) + } + } + Self { data, memory, table, memory_min, memory_max, + null_func_names, } } } @@ -416,291 +477,349 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject func!(crate::memory::abort_on_cannot_grow_memory).to_export() }; - imports! { - "env" => { - "memory" => Export::Memory(globals.memory.clone()), - "table" => Export::Table(globals.table.clone()), + let mut env_ns = namespace! { + "memory" => Export::Memory(globals.memory.clone()), + "table" => Export::Table(globals.table.clone()), - // Globals - "STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)), - "STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)), - "DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)), - "tableBase" => Global::new(Value::I32(globals.data.table_base as i32)), - "__table_base" => Global::new(Value::I32(globals.data.table_base as i32)), - "ABORT" => Global::new(Value::I32(globals.data.abort as i32)), - "memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)), - "__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)), - "tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)), + // Globals + "STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)), + "STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)), + "DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)), + "tableBase" => Global::new(Value::I32(globals.data.table_base as i32)), + "__table_base" => Global::new(Value::I32(globals.data.table_base as i32)), + "ABORT" => Global::new(Value::I32(globals.data.abort as i32)), + "memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)), + "__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)), + "tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)), - // IO - "printf" => func!(crate::io::printf), - "putchar" => func!(crate::io::putchar), - "___lock" => func!(crate::lock::___lock), - "___unlock" => func!(crate::lock::___unlock), - "___wait" => func!(crate::lock::___wait), + // IO + "printf" => func!(crate::io::printf), + "putchar" => func!(crate::io::putchar), + "___lock" => func!(crate::lock::___lock), + "___unlock" => func!(crate::lock::___unlock), + "___wait" => func!(crate::lock::___wait), + "_flock" => func!(crate::lock::_flock), + "_chroot" => func!(crate::io::chroot), + "_getprotobyname" => func!(crate::io::getprotobyname), + "_getprotobynumber" => func!(crate::io::getprotobynumber), + "_getpwuid" => func!(crate::io::getpwuid), + "_sigdelset" => func!(crate::io::sigdelset), + "_sigfillset" => func!(crate::io::sigfillset), + "_tzset" => func!(crate::io::tzset), + "_strptime" => func!(crate::io::strptime), - // Env - "___assert_fail" => func!(crate::env::___assert_fail), - "_getenv" => func!(crate::env::_getenv), - "_setenv" => func!(crate::env::_setenv), - "_putenv" => func!(crate::env::_putenv), - "_unsetenv" => func!(crate::env::_unsetenv), - "_getpwnam" => func!(crate::env::_getpwnam), - "_getgrnam" => func!(crate::env::_getgrnam), - "___buildEnvironment" => func!(crate::env::___build_environment), - "___setErrNo" => func!(crate::errno::___seterrno), - "_getpagesize" => func!(crate::env::_getpagesize), - "_sysconf" => func!(crate::env::_sysconf), - "_getaddrinfo" => func!(crate::env::_getaddrinfo), + // exec + "_execvp" => func!(crate::exec::execvp), + "_execl" => func!(crate::exec::execl), + "_execle" => func!(crate::exec::execle), - // Null func - "nullFunc_i" => func!(crate::nullfunc::nullfunc_i), - "nullFunc_ii" => func!(crate::nullfunc::nullfunc_ii), - "nullFunc_iii" => func!(crate::nullfunc::nullfunc_iii), - "nullFunc_iiii" => func!(crate::nullfunc::nullfunc_iiii), - "nullFunc_iiiii" => func!(crate::nullfunc::nullfunc_iiiii), - "nullFunc_iiiiii" => func!(crate::nullfunc::nullfunc_iiiiii), - "nullFunc_v" => func!(crate::nullfunc::nullfunc_v), - "nullFunc_vi" => func!(crate::nullfunc::nullfunc_vi), - "nullFunc_vii" => func!(crate::nullfunc::nullfunc_vii), - "nullFunc_viii" => func!(crate::nullfunc::nullfunc_viii), - "nullFunc_viiii" => func!(crate::nullfunc::nullfunc_viiii), - "nullFunc_viiiii" => func!(crate::nullfunc::nullfunc_viiiii), - "nullFunc_viiiiii" => func!(crate::nullfunc::nullfunc_viiiiii), + // exit + "__exit" => func!(crate::exit::exit), - // Syscalls - "___syscall1" => func!(crate::syscalls::___syscall1), - "___syscall3" => func!(crate::syscalls::___syscall3), - "___syscall4" => func!(crate::syscalls::___syscall4), - "___syscall5" => func!(crate::syscalls::___syscall5), - "___syscall6" => func!(crate::syscalls::___syscall6), - "___syscall10" => func!(crate::syscalls::___syscall10), - "___syscall12" => func!(crate::syscalls::___syscall12), - "___syscall15" => func!(crate::syscalls::___syscall15), - "___syscall20" => func!(crate::syscalls::___syscall20), - "___syscall39" => func!(crate::syscalls::___syscall39), - "___syscall38" => func!(crate::syscalls::___syscall38), - "___syscall40" => func!(crate::syscalls::___syscall40), - "___syscall54" => func!(crate::syscalls::___syscall54), - "___syscall57" => func!(crate::syscalls::___syscall57), - "___syscall60" => func!(crate::syscalls::___syscall60), - "___syscall63" => func!(crate::syscalls::___syscall63), - "___syscall64" => func!(crate::syscalls::___syscall64), - "___syscall66" => func!(crate::syscalls::___syscall66), - "___syscall75" => func!(crate::syscalls::___syscall75), - "___syscall85" => func!(crate::syscalls::___syscall85), - "___syscall91" => func!(crate::syscalls::___syscall191), - "___syscall97" => func!(crate::syscalls::___syscall97), - "___syscall102" => func!(crate::syscalls::___syscall102), - "___syscall110" => func!(crate::syscalls::___syscall110), - "___syscall114" => func!(crate::syscalls::___syscall114), - "___syscall122" => func!(crate::syscalls::___syscall122), - "___syscall140" => func!(crate::syscalls::___syscall140), - "___syscall142" => func!(crate::syscalls::___syscall142), - "___syscall145" => func!(crate::syscalls::___syscall145), - "___syscall146" => func!(crate::syscalls::___syscall146), - "___syscall168" => func!(crate::syscalls::___syscall168), - "___syscall180" => func!(crate::syscalls::___syscall180), - "___syscall181" => func!(crate::syscalls::___syscall181), - "___syscall191" => func!(crate::syscalls::___syscall191), - "___syscall192" => func!(crate::syscalls::___syscall192), - "___syscall194" => func!(crate::syscalls::___syscall194), - "___syscall195" => func!(crate::syscalls::___syscall195), - "___syscall196" => func!(crate::syscalls::___syscall196), - "___syscall197" => func!(crate::syscalls::___syscall197), - "___syscall199" => func!(crate::syscalls::___syscall199), - "___syscall201" => func!(crate::syscalls::___syscall201), - "___syscall202" => func!(crate::syscalls::___syscall202), - "___syscall212" => func!(crate::syscalls::___syscall212), - "___syscall220" => func!(crate::syscalls::___syscall220), - "___syscall221" => func!(crate::syscalls::___syscall221), - "___syscall268" => func!(crate::syscalls::___syscall268), - "___syscall272" => func!(crate::syscalls::___syscall272), - "___syscall295" => func!(crate::syscalls::___syscall295), - "___syscall300" => func!(crate::syscalls::___syscall300), - "___syscall330" => func!(crate::syscalls::___syscall330), - "___syscall334" => func!(crate::syscalls::___syscall334), - "___syscall340" => func!(crate::syscalls::___syscall340), + // Env + "___assert_fail" => func!(crate::env::___assert_fail), + "_getenv" => func!(crate::env::_getenv), + "_setenv" => func!(crate::env::_setenv), + "_putenv" => func!(crate::env::_putenv), + "_unsetenv" => func!(crate::env::_unsetenv), + "_getpwnam" => func!(crate::env::_getpwnam), + "_getgrnam" => func!(crate::env::_getgrnam), + "___buildEnvironment" => func!(crate::env::___build_environment), + "___setErrNo" => func!(crate::errno::___seterrno), + "_getpagesize" => func!(crate::env::_getpagesize), + "_sysconf" => func!(crate::env::_sysconf), + "_getaddrinfo" => func!(crate::env::_getaddrinfo), + "_times" => func!(crate::env::_times), - // Process - "abort" => func!(crate::process::em_abort), - "_abort" => func!(crate::process::_abort), - "abortStackOverflow" => func!(crate::process::abort_stack_overflow), - "_llvm_trap" => func!(crate::process::_llvm_trap), - "_fork" => func!(crate::process::_fork), - "_exit" => func!(crate::process::_exit), - "_system" => func!(crate::process::_system), - "_popen" => func!(crate::process::_popen), - "_endgrent" => func!(crate::process::_endgrent), - "_execve" => func!(crate::process::_execve), - "_kill" => func!(crate::process::_kill), - "_llvm_stackrestore" => func!(crate::process::_llvm_stackrestore), - "_llvm_stacksave" => func!(crate::process::_llvm_stacksave), - "_raise" => func!(crate::process::_raise), - "_sem_init" => func!(crate::process::_sem_init), - "_sem_post" => func!(crate::process::_sem_post), - "_sem_wait" => func!(crate::process::_sem_wait), - "_getgrent" => func!(crate::process::_getgrent), - "_sched_yield" => func!(crate::process::_sched_yield), - "_setgrent" => func!(crate::process::_setgrent), - "_setgroups" => func!(crate::process::_setgroups), - "_setitimer" => func!(crate::process::_setitimer), - "_usleep" => func!(crate::process::_usleep), - "_utimes" => func!(crate::process::_utimes), - "_waitpid" => func!(crate::process::_waitpid), + // Syscalls + "___syscall1" => func!(crate::syscalls::___syscall1), + "___syscall3" => func!(crate::syscalls::___syscall3), + "___syscall4" => func!(crate::syscalls::___syscall4), + "___syscall5" => func!(crate::syscalls::___syscall5), + "___syscall6" => func!(crate::syscalls::___syscall6), + "___syscall9" => func!(crate::syscalls::___syscall9), + "___syscall10" => func!(crate::syscalls::___syscall10), + "___syscall12" => func!(crate::syscalls::___syscall12), + "___syscall15" => func!(crate::syscalls::___syscall15), + "___syscall20" => func!(crate::syscalls::___syscall20), + "___syscall33" => func!(crate::syscalls::___syscall33), + "___syscall34" => func!(crate::syscalls::___syscall34), + "___syscall39" => func!(crate::syscalls::___syscall39), + "___syscall38" => func!(crate::syscalls::___syscall38), + "___syscall40" => func!(crate::syscalls::___syscall40), + "___syscall41" => func!(crate::syscalls::___syscall41), + "___syscall42" => func!(crate::syscalls::___syscall42), + "___syscall54" => func!(crate::syscalls::___syscall54), + "___syscall57" => func!(crate::syscalls::___syscall57), + "___syscall60" => func!(crate::syscalls::___syscall60), + "___syscall63" => func!(crate::syscalls::___syscall63), + "___syscall64" => func!(crate::syscalls::___syscall64), + "___syscall66" => func!(crate::syscalls::___syscall66), + "___syscall75" => func!(crate::syscalls::___syscall75), + "___syscall77" => func!(crate::syscalls::___syscall77), + "___syscall83" => func!(crate::syscalls::___syscall83), + "___syscall85" => func!(crate::syscalls::___syscall85), + "___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), + "___syscall114" => func!(crate::syscalls::___syscall114), + "___syscall118" => func!(crate::syscalls::___syscall118), + "___syscall122" => func!(crate::syscalls::___syscall122), + "___syscall140" => func!(crate::syscalls::___syscall140), + "___syscall142" => func!(crate::syscalls::___syscall142), + "___syscall145" => func!(crate::syscalls::___syscall145), + "___syscall146" => func!(crate::syscalls::___syscall146), + "___syscall148" => func!(crate::syscalls::___syscall148), + "___syscall168" => func!(crate::syscalls::___syscall168), + "___syscall180" => func!(crate::syscalls::___syscall180), + "___syscall181" => func!(crate::syscalls::___syscall181), + "___syscall183" => func!(crate::syscalls::___syscall183), + "___syscall191" => func!(crate::syscalls::___syscall191), + "___syscall192" => func!(crate::syscalls::___syscall192), + "___syscall194" => func!(crate::syscalls::___syscall194), + "___syscall195" => func!(crate::syscalls::___syscall195), + "___syscall196" => func!(crate::syscalls::___syscall196), + "___syscall197" => func!(crate::syscalls::___syscall197), + "___syscall198" => func!(crate::syscalls::___syscall198), + "___syscall199" => func!(crate::syscalls::___syscall199), + "___syscall200" => func!(crate::syscalls::___syscall200), + "___syscall201" => func!(crate::syscalls::___syscall201), + "___syscall202" => func!(crate::syscalls::___syscall202), + "___syscall205" => func!(crate::syscalls::___syscall205), + "___syscall207" => func!(crate::syscalls::___syscall207), + "___syscall212" => func!(crate::syscalls::___syscall212), + "___syscall219" => func!(crate::syscalls::___syscall219), + "___syscall220" => func!(crate::syscalls::___syscall220), + "___syscall221" => func!(crate::syscalls::___syscall221), + "___syscall268" => func!(crate::syscalls::___syscall268), + "___syscall272" => func!(crate::syscalls::___syscall272), + "___syscall295" => func!(crate::syscalls::___syscall295), + "___syscall300" => func!(crate::syscalls::___syscall300), + "___syscall324" => func!(crate::syscalls::___syscall324), + "___syscall330" => func!(crate::syscalls::___syscall330), + "___syscall334" => func!(crate::syscalls::___syscall334), + "___syscall340" => func!(crate::syscalls::___syscall340), + + // Process + "abort" => func!(crate::process::em_abort), + "_abort" => func!(crate::process::_abort), + "abortStackOverflow" => func!(crate::process::abort_stack_overflow), + "_llvm_trap" => func!(crate::process::_llvm_trap), + "_fork" => func!(crate::process::_fork), + "_exit" => func!(crate::process::_exit), + "_system" => func!(crate::process::_system), + "_popen" => func!(crate::process::_popen), + "_endgrent" => func!(crate::process::_endgrent), + "_execve" => func!(crate::process::_execve), + "_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_post" => func!(crate::process::_sem_post), + "_sem_wait" => func!(crate::process::_sem_wait), + "_getgrent" => func!(crate::process::_getgrent), + "_sched_yield" => func!(crate::process::_sched_yield), + "_setgrent" => func!(crate::process::_setgrent), + "_setgroups" => func!(crate::process::_setgroups), + "_setitimer" => func!(crate::process::_setitimer), + "_usleep" => func!(crate::process::_usleep), + "_nanosleep" => func!(crate::process::_nanosleep), + "_utimes" => func!(crate::process::_utimes), + "_waitpid" => func!(crate::process::_waitpid), - // Signal - "_sigemptyset" => func!(crate::signal::_sigemptyset), - "_sigaddset" => func!(crate::signal::_sigaddset), - "_sigprocmask" => func!(crate::signal::_sigprocmask), - "_sigaction" => func!(crate::signal::_sigaction), - "_signal" => func!(crate::signal::_signal), - "_sigsuspend" => func!(crate::signal::_sigsuspend), + // Signal + "_sigemptyset" => func!(crate::signal::_sigemptyset), + "_sigaddset" => func!(crate::signal::_sigaddset), + "_sigprocmask" => func!(crate::signal::_sigprocmask), + "_sigaction" => func!(crate::signal::_sigaction), + "_signal" => func!(crate::signal::_signal), + "_sigsuspend" => func!(crate::signal::_sigsuspend), - // Memory - "abortOnCannotGrowMemory" => abort_on_cannot_grow_memory_export, - "_emscripten_memcpy_big" => func!(crate::memory::_emscripten_memcpy_big), - "_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), - "getTotalMemory" => func!(crate::memory::get_total_memory), - "___map_file" => func!(crate::memory::___map_file), + // Memory + "abortOnCannotGrowMemory" => abort_on_cannot_grow_memory_export, + "_emscripten_memcpy_big" => func!(crate::memory::_emscripten_memcpy_big), + "_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), + "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), + // 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), - // Time - "_gettimeofday" => func!(crate::time::_gettimeofday), - "_clock_gettime" => func!(crate::time::_clock_gettime), - "___clock_gettime" => func!(crate::time::_clock_gettime), - "_clock" => func!(crate::time::_clock), - "_difftime" => func!(crate::time::_difftime), - "_asctime" => func!(crate::time::_asctime), - "_asctime_r" => func!(crate::time::_asctime_r), - "_localtime" => func!(crate::time::_localtime), - "_time" => func!(crate::time::_time), - "_strftime" => func!(crate::time::_strftime), - "_localtime_r" => func!(crate::time::_localtime_r), - "_gmtime_r" => func!(crate::time::_gmtime_r), - "_mktime" => func!(crate::time::_mktime), - "_gmtime" => func!(crate::time::_gmtime), + // Time + "_gettimeofday" => func!(crate::time::_gettimeofday), + "_clock_gettime" => func!(crate::time::_clock_gettime), + "___clock_gettime" => func!(crate::time::_clock_gettime), + "_clock" => func!(crate::time::_clock), + "_difftime" => func!(crate::time::_difftime), + "_asctime" => func!(crate::time::_asctime), + "_asctime_r" => func!(crate::time::_asctime_r), + "_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), + "_gmtime" => func!(crate::time::_gmtime), - // Math - "f64-rem" => func!(crate::math::f64_rem), - "_llvm_log10_f64" => func!(crate::math::_llvm_log10_f64), - "_llvm_log2_f64" => func!(crate::math::_llvm_log2_f64), - "_llvm_log10_f32" => func!(crate::math::_llvm_log10_f32), - "_llvm_log2_f32" => func!(crate::math::_llvm_log2_f64), - "_emscripten_random" => func!(crate::math::_emscripten_random), + // Math + "f64-rem" => func!(crate::math::f64_rem), + "_llvm_log10_f64" => func!(crate::math::_llvm_log10_f64), + "_llvm_log2_f64" => func!(crate::math::_llvm_log2_f64), + "_llvm_log10_f32" => func!(crate::math::_llvm_log10_f32), + "_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), + "_emscripten_random" => func!(crate::math::_emscripten_random), - // Jump - "__setjmp" => func!(crate::jmp::__setjmp), - "__longjmp" => func!(crate::jmp::__longjmp), + // Jump + "__setjmp" => func!(crate::jmp::__setjmp), + "__longjmp" => func!(crate::jmp::__longjmp), + "_longjmp" => func!(crate::jmp::_longjmp), + "_emscripten_longjmp" => func!(crate::jmp::_longjmp), - // Linking - "_dlclose" => func!(crate::linking::_dlclose), - "_dlerror" => func!(crate::linking::_dlerror), - "_dlopen" => func!(crate::linking::_dlopen), - "_dlsym" => func!(crate::linking::_dlsym), + // Bitwise + "_llvm_bswap_i64" => func!(crate::bitwise::_llvm_bswap_i64), - // wasm32-unknown-emscripten - "setTempRet0" => func!(crate::emscripten_target::setTempRet0), - "getTempRet0" => func!(crate::emscripten_target::getTempRet0), - "nullFunc_ji" => func!(crate::emscripten_target::nullFunc_ji), - "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_v" => func!(crate::emscripten_target::invoke_v), - "invoke_vi" => func!(crate::emscripten_target::invoke_vi), - "invoke_vii" => func!(crate::emscripten_target::invoke_vii), - "invoke_viii" => func!(crate::emscripten_target::invoke_viii), - "invoke_viiii" => func!(crate::emscripten_target::invoke_viiii), - "__Unwind_Backtrace" => func!(crate::emscripten_target::__Unwind_Backtrace), - "__Unwind_FindEnclosingFunction" => func!(crate::emscripten_target::__Unwind_FindEnclosingFunction), - "__Unwind_GetIPInfo" => func!(crate::emscripten_target::__Unwind_GetIPInfo), - "___cxa_find_matching_catch_2" => func!(crate::emscripten_target::___cxa_find_matching_catch_2), - "___cxa_find_matching_catch_3" => func!(crate::emscripten_target::___cxa_find_matching_catch_3), - "___cxa_free_exception" => func!(crate::emscripten_target::___cxa_free_exception), - "___resumeException" => func!(crate::emscripten_target::___resumeException), - "_dladdr" => func!(crate::emscripten_target::_dladdr), - "_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy), - "_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init), - "_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal), - "_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), - "_pthread_condattr_setclock" => func!(crate::emscripten_target::_pthread_condattr_setclock), - "_pthread_mutex_destroy" => func!(crate::emscripten_target::_pthread_mutex_destroy), - "_pthread_mutex_init" => func!(crate::emscripten_target::_pthread_mutex_init), - "_pthread_mutexattr_destroy" => func!(crate::emscripten_target::_pthread_mutexattr_destroy), - "_pthread_mutexattr_init" => func!(crate::emscripten_target::_pthread_mutexattr_init), - "_pthread_mutexattr_settype" => func!(crate::emscripten_target::_pthread_mutexattr_settype), - "_pthread_rwlock_rdlock" => func!(crate::emscripten_target::_pthread_rwlock_rdlock), - "_pthread_rwlock_unlock" => func!(crate::emscripten_target::_pthread_rwlock_unlock), - "___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0), - // round 2 - "nullFunc_dii" => func!(crate::emscripten_target::nullFunc_dii), - "nullFunc_diiii" => func!(crate::emscripten_target::nullFunc_diiii), - "nullFunc_iiji" => func!(crate::emscripten_target::nullFunc_iiji), - "nullFunc_j" => func!(crate::emscripten_target::nullFunc_j), - "nullFunc_jij" => func!(crate::emscripten_target::nullFunc_jij), - "nullFunc_jjj" => func!(crate::emscripten_target::nullFunc_jjj), - "nullFunc_vd" => func!(crate::emscripten_target::nullFunc_vd), - "nullFunc_viiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiii), - "nullFunc_viiiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiiii), - "nullFunc_viiiiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiiiii), - "nullFunc_viiij" => func!(crate::emscripten_target::nullFunc_viiij), - "nullFunc_viiijiiii" => func!(crate::emscripten_target::nullFunc_viiijiiii), - "nullFunc_viiijiiiiii" => func!(crate::emscripten_target::nullFunc_viiijiiiiii), - "nullFunc_viij" => func!(crate::emscripten_target::nullFunc_viij), - "nullFunc_viiji" => func!(crate::emscripten_target::nullFunc_viiji), - "nullFunc_viijiii" => func!(crate::emscripten_target::nullFunc_viijiii), - "nullFunc_viijj" => func!(crate::emscripten_target::nullFunc_viijj), - "nullFunc_vij" => func!(crate::emscripten_target::nullFunc_vij), - "nullFunc_viji" => func!(crate::emscripten_target::nullFunc_viji), - "nullFunc_vijiii" => func!(crate::emscripten_target::nullFunc_vijiii), - "nullFunc_vijj" => func!(crate::emscripten_target::nullFunc_vijj), - "invoke_dii" => func!(crate::emscripten_target::invoke_dii), - "invoke_diiii" => func!(crate::emscripten_target::invoke_diiii), - "invoke_iiiii" => func!(crate::emscripten_target::invoke_iiiii), - "invoke_iiiiii" => func!(crate::emscripten_target::invoke_iiiiii), - "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_iiji" => func!(crate::emscripten_target::invoke_iiji), - "invoke_j" => func!(crate::emscripten_target::invoke_j), - "invoke_ji" => func!(crate::emscripten_target::invoke_ji), - "invoke_jij" => func!(crate::emscripten_target::invoke_jij), - "invoke_jjj" => func!(crate::emscripten_target::invoke_jjj), - "invoke_viiij" => func!(crate::emscripten_target::invoke_viiij), - "invoke_viiijiiii" => func!(crate::emscripten_target::invoke_viiijiiii), - "invoke_viiijiiiiii" => func!(crate::emscripten_target::invoke_viiijiiiiii), - "invoke_viij" => func!(crate::emscripten_target::invoke_viij), - "invoke_viiji" => func!(crate::emscripten_target::invoke_viiji), - "invoke_viijiii" => func!(crate::emscripten_target::invoke_viijiii), - "invoke_viijj" => func!(crate::emscripten_target::invoke_viijj), - "invoke_vij" => func!(crate::emscripten_target::invoke_vij), - "invoke_viji" => func!(crate::emscripten_target::invoke_viji), - "invoke_vijiii" => func!(crate::emscripten_target::invoke_vijiii), - "invoke_vijj" => func!(crate::emscripten_target::invoke_vijj), - }, + // Linking + "_dlclose" => func!(crate::linking::_dlclose), + "_dlerror" => func!(crate::linking::_dlerror), + "_dlopen" => func!(crate::linking::_dlopen), + "_dlsym" => func!(crate::linking::_dlsym), + + // wasm32-unknown-emscripten + "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_vii" => func!(crate::emscripten_target::invoke_vii), + "invoke_viii" => func!(crate::emscripten_target::invoke_viii), + "invoke_viiii" => func!(crate::emscripten_target::invoke_viiii), + "__Unwind_Backtrace" => func!(crate::emscripten_target::__Unwind_Backtrace), + "__Unwind_FindEnclosingFunction" => func!(crate::emscripten_target::__Unwind_FindEnclosingFunction), + "__Unwind_GetIPInfo" => func!(crate::emscripten_target::__Unwind_GetIPInfo), + "___cxa_find_matching_catch_2" => func!(crate::emscripten_target::___cxa_find_matching_catch_2), + "___cxa_find_matching_catch_3" => func!(crate::emscripten_target::___cxa_find_matching_catch_3), + "___cxa_free_exception" => func!(crate::emscripten_target::___cxa_free_exception), + "___resumeException" => func!(crate::emscripten_target::___resumeException), + "_dladdr" => func!(crate::emscripten_target::_dladdr), + "_pthread_create" => func!(crate::emscripten_target::_pthread_create), + "_pthread_join" => func!(crate::emscripten_target::_pthread_join), + "_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy), + "_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init), + "_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal), + "_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), + "_pthread_condattr_setclock" => func!(crate::emscripten_target::_pthread_condattr_setclock), + "_pthread_mutex_destroy" => func!(crate::emscripten_target::_pthread_mutex_destroy), + "_pthread_mutex_init" => func!(crate::emscripten_target::_pthread_mutex_init), + "_pthread_mutexattr_destroy" => func!(crate::emscripten_target::_pthread_mutexattr_destroy), + "_pthread_mutexattr_init" => func!(crate::emscripten_target::_pthread_mutexattr_init), + "_pthread_mutexattr_settype" => func!(crate::emscripten_target::_pthread_mutexattr_settype), + "_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_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), + "_gethostbyname_r" => func!(crate::emscripten_target::_gethostbyname_r), + "_getloadavg" => func!(crate::emscripten_target::_getloadavg), + "invoke_dii" => func!(crate::emscripten_target::invoke_dii), + "invoke_diiii" => func!(crate::emscripten_target::invoke_diiii), + "invoke_iiiii" => func!(crate::emscripten_target::invoke_iiiii), + "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), + "invoke_viiijiiii" => func!(crate::emscripten_target::invoke_viiijiiii), + "invoke_viiijiiiiii" => func!(crate::emscripten_target::invoke_viiijiiiiii), + "invoke_viij" => func!(crate::emscripten_target::invoke_viij), + "invoke_viiji" => func!(crate::emscripten_target::invoke_viiji), + "invoke_viijiii" => func!(crate::emscripten_target::invoke_viijiii), + "invoke_viijj" => func!(crate::emscripten_target::invoke_viijj), + "invoke_vij" => func!(crate::emscripten_target::invoke_vij), + "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() { + env_ns.insert(null_func_name.as_str(), Func::new(nullfunc).to_export()); + } + + let import_object: ImportObject = imports! { + "env" => env_ns, "global" => { "NaN" => Global::new(Value::F64(f64::NAN)), "Infinity" => Global::new(Value::F64(f64::INFINITY)), }, "global.Math" => { "pow" => func!(crate::math::pow), + "exp" => func!(crate::math::exp), + "log" => func!(crate::math::log), }, "asm2wasm" => { "f64-rem" => func!(crate::math::f64_rem), + "f64-to-int" => func!(crate::math::f64_to_int), }, - } + }; + + import_object +} + +pub fn nullfunc(ctx: &mut Ctx, _x: u32) { + use crate::process::abort_with_message; + debug!("emscripten::nullfunc_i {}", _x); + abort_with_message(ctx, "Invalid function pointer. Perhaps this is an invalid value \ + (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an \ + incorrect type, which will fail? (it is worth building your source files with -Werror (\ + warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } /// The current version of this crate diff --git a/lib/emscripten/src/lock.rs b/lib/emscripten/src/lock.rs index 45f09581f..f6804e9d1 100644 --- a/lib/emscripten/src/lock.rs +++ b/lib/emscripten/src/lock.rs @@ -2,16 +2,21 @@ use libc::c_int; use wasmer_runtime_core::vm::Ctx; // NOTE: Not implemented by Emscripten -pub fn ___lock(_ctx: &mut Ctx, what: c_int) { - debug!("emscripten::___lock {}", what); +pub fn ___lock(_ctx: &mut Ctx, _what: c_int) { + debug!("emscripten::___lock {}", _what); } // NOTE: Not implemented by Emscripten -pub fn ___unlock(_ctx: &mut Ctx, what: c_int) { - debug!("emscripten::___unlock {}", what); +pub fn ___unlock(_ctx: &mut Ctx, _what: c_int) { + debug!("emscripten::___unlock {}", _what); } // NOTE: Not implemented by Emscripten pub fn ___wait(_ctx: &mut Ctx, _which: u32, _varargs: u32, _three: u32, _four: u32) { debug!("emscripten::___wait"); } + +pub fn _flock(_ctx: &mut Ctx, _fd: u32, _op: u32) -> u32 { + debug!("emscripten::_flock"); + 0 +} diff --git a/lib/emscripten/src/math.rs b/lib/emscripten/src/math.rs index a60966a1c..e0eb10c7d 100644 --- a/lib/emscripten/src/math.rs +++ b/lib/emscripten/src/math.rs @@ -12,6 +12,18 @@ pub fn _llvm_log2_f64(_ctx: &mut Ctx, value: f64) -> f64 { value.log2() } +/// emscripten: _llvm_sin_f64 +pub fn _llvm_sin_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_sin_f64"); + value.sin() +} + +/// emscripten: _llvm_cos_f64 +pub fn _llvm_cos_f64(_ctx: &mut Ctx, value: f64) -> f64 { + debug!("emscripten::_llvm_cos_f64"); + value.cos() +} + pub fn _llvm_log10_f32(_ctx: &mut Ctx, _value: f64) -> f64 { debug!("emscripten::_llvm_log10_f32"); -1.0 @@ -22,12 +34,22 @@ 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 _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 @@ -37,3 +59,19 @@ pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { pub fn pow(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { x.powf(y) } + +// emscripten: global.Math exp +pub fn exp(_ctx: &mut Ctx, value: f64) -> f64 { + value.exp() +} + +// emscripten: global.Math log +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 dcd98c317..9a0874015 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 { @@ -19,15 +22,44 @@ 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 + ctx.memory(0).size().bytes().0 as u32 +} + +// 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 +/// 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); - // TODO: Fix implementation - 0 + 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 +67,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 @@ -47,8 +79,11 @@ pub fn enlarge_memory(_ctx: &mut Ctx) -> u32 { } /// emscripten: abortOnCannotGrowMemory -pub fn abort_on_cannot_grow_memory(ctx: &mut Ctx, requested_size: u32) -> u32 { - debug!("emscripten::abort_on_cannot_grow_memory {}", requested_size); +pub fn abort_on_cannot_grow_memory(ctx: &mut Ctx, _requested_size: u32) -> u32 { + debug!( + "emscripten::abort_on_cannot_grow_memory {}", + _requested_size + ); abort_with_message(ctx, "Cannot enlarge memory arrays!"); 0 } diff --git a/lib/emscripten/src/nullfunc.rs b/lib/emscripten/src/nullfunc.rs deleted file mode 100644 index 7fb44ab11..000000000 --- a/lib/emscripten/src/nullfunc.rs +++ /dev/null @@ -1,67 +0,0 @@ -use super::process::abort_with_message; -use wasmer_runtime_core::vm::Ctx; - -pub fn nullfunc_i(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_i {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'i'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_ii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_ii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'ii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_iii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_iii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'iii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_iiii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_iiii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'iiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_iiiii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_iiiii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'iiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_iiiiii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_iiiiii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'iiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_v(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_v {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_vi(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_vi {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'vi'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_vii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_vii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'vii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_viii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_viii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'viii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_viiii(ctx: &mut Ctx, x: u32) { - debug!("emscripten::nullfunc_viiii {}", x); - abort_with_message(ctx, "Invalid function pointer called with signature 'viiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_viiiii(ctx: &mut Ctx, _x: u32) { - debug!("emscripten::nullfunc_viiiii"); - abort_with_message(ctx, "Invalid function pointer called with signature 'viiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} - -pub fn nullfunc_viiiiii(ctx: &mut Ctx, _x: u32) { - debug!("emscripten::nullfunc_viiiiii"); - abort_with_message(ctx, "Invalid function pointer called with signature 'viiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); -} diff --git a/lib/emscripten/src/process.rs b/lib/emscripten/src/process.rs index 8d1dddb48..4c6e33e70 100644 --- a/lib/emscripten/src/process.rs +++ b/lib/emscripten/src/process.rs @@ -1,10 +1,9 @@ use libc::{abort, c_char, c_int, exit, EAGAIN}; #[cfg(not(target_os = "windows"))] -use libc::pid_t; - +type PidT = libc::pid_t; #[cfg(target_os = "windows")] -type pid_t = c_int; +type PidT = c_int; use std::ffi::CStr; use wasmer_runtime_core::vm::Ctx; @@ -22,7 +21,7 @@ pub fn _abort(_ctx: &mut Ctx) { } } -pub fn _fork(_ctx: &mut Ctx) -> pid_t { +pub fn _fork(_ctx: &mut Ctx) -> PidT { debug!("emscripten::_fork"); // unsafe { // fork() @@ -98,7 +97,7 @@ pub fn _sem_wait(_ctx: &mut Ctx, _one: i32) -> i32 { } #[allow(clippy::cast_ptr_alignment)] -pub fn _getgrent(ctx: &mut Ctx) -> c_int { +pub fn _getgrent(_ctx: &mut Ctx) -> c_int { debug!("emscripten::_getgrent"); -1 } @@ -122,6 +121,11 @@ pub fn _usleep(_ctx: &mut Ctx, _one: i32) -> i32 { -1 } +pub fn _nanosleep(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::_nanosleep"); + -1 +} + pub fn _utimes(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_utimes"); -1 @@ -146,6 +150,11 @@ 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 diff --git a/lib/emscripten/src/signal.rs b/lib/emscripten/src/signal.rs index f7657630b..37c3ed8df 100644 --- a/lib/emscripten/src/signal.rs +++ b/lib/emscripten/src/signal.rs @@ -11,8 +11,8 @@ pub fn _sigemptyset(ctx: &mut Ctx, set: u32) -> i32 { 0 } -pub fn _sigaction(_ctx: &mut Ctx, signum: u32, act: u32, oldact: u32) -> i32 { - debug!("emscripten::_sigaction {}, {}, {}", signum, act, oldact); +pub fn _sigaction(_ctx: &mut Ctx, _signum: u32, _act: u32, _oldact: u32) -> i32 { + debug!("emscripten::_sigaction {}, {}, {}", _signum, _act, _oldact); 0 } @@ -36,7 +36,7 @@ pub fn _sigprocmask(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { 0 } -pub fn _signal(_ctx: &mut Ctx, sig: u32, _two: i32) -> i32 { - debug!("emscripten::_signal ({})", sig); +pub fn _signal(_ctx: &mut Ctx, _sig: u32, _two: i32) -> i32 { + debug!("emscripten::_signal ({})", _sig); 0 } diff --git a/lib/emscripten/src/stdio.rs b/lib/emscripten/src/stdio.rs index d7d8ab736..2b95bafc7 100644 --- a/lib/emscripten/src/stdio.rs +++ b/lib/emscripten/src/stdio.rs @@ -15,7 +15,7 @@ pub struct StdioCapturer { use libc::{STDERR_FILENO, STDOUT_FILENO}; #[cfg(target_os = "windows")] -const STDIN_FILENO: libc::c_int = 0; +const _STDIN_FILENO: libc::c_int = 0; #[cfg(target_os = "windows")] const STDOUT_FILENO: libc::c_int = 1; #[cfg(target_os = "windows")] diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index ef9ef40ee..37f6e2f51 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -28,6 +28,7 @@ use libc::{ getpid, // iovec, lseek, + off_t, // open, read, // readv, @@ -40,20 +41,15 @@ use libc::{ use wasmer_runtime_core::vm::Ctx; use super::env; +use std::cell::Cell; +#[allow(unused_imports)] +use std::io::Error; +use std::mem; use std::slice; -// use std::sys::fd::FileDesc; - -// Another conditional constant for name resolution: Macos et iOS use -// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. -// Other platforms do otherwise. -#[cfg(target_os = "darwin")] -use libc::SO_NOSIGPIPE; -#[cfg(not(target_os = "darwin"))] -const SO_NOSIGPIPE: c_int = 0; /// exit -pub fn ___syscall1(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) { - debug!("emscripten::___syscall1 (exit) {}", which); +pub fn ___syscall1(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) { + debug!("emscripten::___syscall1 (exit) {}", _which); let status: i32 = varargs.get(ctx); unsafe { exit(status); @@ -61,47 +57,47 @@ pub fn ___syscall1(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) { } /// read -pub fn ___syscall3(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { +pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall3 (read) {}", which); + debug!("emscripten::___syscall3 (read) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); - let count = varargs.get(ctx); + let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_void; - let ret = unsafe { read(fd, buf_addr, count) }; + let ret = unsafe { read(fd, buf_addr, count as _) }; debug!("=> ret: {}", ret); ret as _ } /// write -pub fn ___syscall4(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall4 (write) {}", which); +pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall4 (write) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); - let count = varargs.get(ctx); + let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *const c_void; - unsafe { write(fd, buf_addr, count) as i32 } + unsafe { write(fd, buf_addr, count as _) as i32 } } /// close -pub fn ___syscall6(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall6 (close) {}", which); +pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall6 (close) {}", _which); let fd: i32 = varargs.get(ctx); debug!("fd: {}", fd); unsafe { close(fd) } } // chdir -pub fn ___syscall12(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall12 (chdir) {}", which); +pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall12 (chdir) {}", _which); let path_addr: i32 = varargs.get(ctx); unsafe { let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const i8; - let path = std::ffi::CStr::from_ptr(path_ptr); + let _path = std::ffi::CStr::from_ptr(path_ptr); let ret = chdir(path_ptr); - debug!("=> path: {:?}, ret: {}", path, ret); + debug!("=> path: {:?}, ret: {}", _path, ret); ret } } @@ -135,14 +131,40 @@ pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int unsafe { rmdir(pathname_addr) } } +// pipe +pub fn ___syscall42(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall42 (pipe)"); + // offset to a file descriptor, which contains a read end and write end, 2 integers + let fd_offset: u32 = varargs.get(ctx); + + let emscripten_memory = ctx.memory(0); + + // convert the file descriptor into a vec with two slots + let mut fd_vec: Vec = emscripten_memory.view()[((fd_offset / 4) as usize)..] + .iter() + .map(|pipe_end: &Cell| pipe_end.get()) + .take(2) + .collect(); + + // get it as a mutable pointer + let fd_ptr = fd_vec.as_mut_ptr(); + + // call pipe and store the pointers in this array + #[cfg(target_os = "windows")] + let result: c_int = unsafe { libc::pipe(fd_ptr, 2048, 0) }; + #[cfg(not(target_os = "windows"))] + let result: c_int = unsafe { libc::pipe(fd_ptr) }; + result +} + pub fn ___syscall60(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall60"); -1 } // dup2 -pub fn ___syscall63(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall63 (dup2) {}", which); +pub fn ___syscall63(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall63 (dup2) {}", _which); let src: i32 = varargs.get(ctx); let dst: i32 = varargs.get(ctx); @@ -172,8 +194,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 { @@ -186,18 +208,39 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// getcwd +pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall183"); + use std::env; + let buf_offset: c_int = varargs.get(ctx); + let _size: c_int = varargs.get(ctx); + let path = env::current_dir(); + let path_string = path.unwrap().display().to_string(); + let len = path_string.len(); + unsafe { + let pointer_to_buffer = + emscripten_memory_pointer!(ctx.memory(0), buf_offset) as *mut libc::c_char; + let slice = slice::from_raw_parts_mut(pointer_to_buffer, len.clone()); + for (byte, loc) in path_string.bytes().zip(slice.iter_mut()) { + *loc = byte as _; + } + *pointer_to_buffer.add(len.clone()) = 0; + } + buf_offset +} + // mmap2 -pub fn ___syscall192(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall192 (mmap2) {}", which); - let addr: i32 = varargs.get(ctx); +pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall192 (mmap2) {}", _which); + let _addr: i32 = varargs.get(ctx); let len: u32 = varargs.get(ctx); - let prot: i32 = varargs.get(ctx); - let flags: i32 = varargs.get(ctx); + let _prot: i32 = varargs.get(ctx); + let _flags: i32 = varargs.get(ctx); let fd: i32 = varargs.get(ctx); - let off: i32 = varargs.get(ctx); + let _off: i32 = varargs.get(ctx); debug!( "=> addr: {}, len: {}, prot: {}, flags: {}, fd: {}, off: {}", - addr, len, prot, flags, fd, off + _addr, len, _prot, _flags, fd, _off ); if fd == -1 { @@ -213,27 +256,39 @@ pub fn ___syscall192(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int } /// lseek -pub fn ___syscall140(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { +pub fn ___syscall140(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> c_int - debug!("emscripten::___syscall140 (lseek) {}", which); + debug!("emscripten::___syscall140 (lseek) {}", _which); let fd: i32 = varargs.get(ctx); - let offset = varargs.get(ctx); + let _ = varargs.get::(ctx); // ignore high offset + let offset_low: i32 = varargs.get(ctx); + let result_ptr_value = varargs.get::(ctx); let whence: i32 = varargs.get(ctx); - debug!("=> fd: {}, offset: {}, whence = {}", fd, offset, whence); - unsafe { lseek(fd, offset, whence) as _ } + let offset = offset_low as off_t; + let ret = unsafe { lseek(fd, offset, whence) as i32 }; + #[allow(clippy::cast_ptr_alignment)] + let result_ptr = emscripten_memory_pointer!(ctx.memory(0), result_ptr_value) as *mut i32; + assert_eq!(8, mem::align_of_val(&result_ptr)); + unsafe { + *result_ptr = ret; + } + debug!( + "=> fd: {}, offset: {}, result_ptr: {}, whence: {} = {}\nlast os error: {}", + fd, + offset, + result_ptr_value, + whence, + 0, + Error::last_os_error(), + ); + 0 } /// readv #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall145(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> i32 { +pub fn ___syscall145(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall145 (readv) {}", which); - // let fd: i32 = varargs.get(ctx); - // let iov: u32 = varargs.get(ctx); - // let iovcnt: i32 = varargs.get(ctx); - // debug!("=> fd: {}, iov: {}, iovcnt = {}", fd, iov, iovcnt); - // let iov_addr = emscripten_memory_pointer!(ctx.memory(0), iov) as *mut iovec; - // unsafe { readv(fd, iov_addr, iovcnt) } + debug!("emscripten::___syscall145 (readv) {}", _which); let fd: i32 = varargs.get(ctx); let iov: i32 = varargs.get(ctx); @@ -268,9 +323,9 @@ pub fn ___syscall145(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> i32 { // writev #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall146(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { +pub fn ___syscall146(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall146 (writev) {}", which); + debug!("emscripten::___syscall146 (writev) {}", _which); let fd: i32 = varargs.get(ctx); let iov: i32 = varargs.get(ctx); let iovcnt: i32 = varargs.get(ctx); @@ -312,24 +367,14 @@ pub fn ___syscall191(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } -pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall194 - stub"); - -1 -} - -pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall194 - stub"); - -1 -} - pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall199 - stub"); -1 } // stat64 -pub fn ___syscall195(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall195 (stat64) {}", which); +pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall195 (stat64) {}", _which); let pathname: u32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); @@ -338,7 +383,14 @@ pub fn ___syscall195(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int unsafe { let mut _stat: stat = std::mem::zeroed(); let ret = stat(pathname_addr, &mut _stat); - debug!("ret: {}", ret); + debug!( + "=> pathname: {}, buf: {}, path: {} = {}\nlast os error: {}", + pathname, + buf, + std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap(), + ret, + Error::last_os_error() + ); if ret != 0 { return ret; } @@ -348,8 +400,8 @@ pub fn ___syscall195(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int } // fstat64 -pub fn ___syscall197(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall197 (fstat64) {}", which); +pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall197 (fstat64) {}", _which); let fd: c_int = varargs.get(ctx); let buf: u32 = varargs.get(ctx); @@ -372,13 +424,19 @@ pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } // fcntl64 -pub fn ___syscall221(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall221 (fcntl64) {}", which); +pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall221 (fcntl64) {}", _which); // fcntl64 let _fd: i32 = varargs.get(ctx); let cmd: u32 = varargs.get(ctx); + // (FAPPEND - 0x08 + // |FASYNC - 0x40 + // |FFSYNC - 0x80 + // |FNONBLOCK - 0x04 + debug!("=> fd: {}, cmd: {}", _fd, cmd); match cmd { 2 => 0, + 13 | 14 => 0, // pretend file locking worked _ => -1, } } @@ -409,8 +467,8 @@ pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } // prlimit64 -pub fn ___syscall340(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall340 (prlimit64), {}", which); +pub fn ___syscall340(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall340 (prlimit64), {}", _which); // NOTE: Doesn't really matter. Wasm modules cannot exceed WASM_PAGE_SIZE anyway. let _pid: i32 = varargs.get(ctx); let _resource: i32 = varargs.get(ctx); diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index a73456d09..32b63593f 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -3,27 +3,40 @@ use crate::varargs::VarArgs; /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html use libc::{ accept, + access, bind, - // ENOTTY, c_char, c_int, c_void, chown, // fcntl, setsockopt, getppid connect, + dup, dup2, + fchmod, + fchown, fcntl, + // ENOTTY, + fsync, getgid, + getgroups, getpeername, + getrusage, getsockname, getsockopt, + gid_t, in_addr_t, in_port_t, ioctl, + lchown, + link, // iovec, listen, mkdir, + mode_t, msghdr, + nice, + off_t, open, pid_t, pread, @@ -40,9 +53,13 @@ use libc::{ sendto, setpgid, setsockopt, + size_t, sockaddr, socket, socklen_t, + stat, + symlink, + uid_t, uname, utsname, EINVAL, @@ -57,6 +74,9 @@ use libc::{ }; use wasmer_runtime_core::vm::Ctx; +use crate::utils; +#[allow(unused_imports)] +use std::io::Error; use std::mem; // Linking to functions that are not provided by rust libc @@ -64,10 +84,13 @@ use std::mem; #[link(name = "c")] extern "C" { pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t; + pub fn madvise(addr: *mut c_void, len: size_t, advice: c_int) -> c_int; + pub fn fdatasync(fd: c_int) -> c_int; + pub fn lstat64(path: *const c_char, buf: *mut c_void) -> c_int; } #[cfg(not(target_os = "macos"))] -use libc::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. @@ -78,24 +101,125 @@ use libc::SO_NOSIGPIPE; const SO_NOSIGPIPE: c_int = 0; /// open -pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall5 (open) {}", which); +pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall5 (open) {}", _which); let pathname: u32 = varargs.get(ctx); let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + let _path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; let fd = unsafe { open(pathname_addr, flags, mode) }; debug!( - "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", - pathname, flags, mode, fd, path_str + "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}\nlast os error: {}", + pathname, + flags, + mode, + fd, + _path_str, + Error::last_os_error(), ); fd } +/// link +pub fn ___syscall9(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall9 (link) {}", _which); + + let oldname: c_int = varargs.get(ctx); + let newname: c_int = varargs.get(ctx); + let oldname_ptr = emscripten_memory_pointer!(ctx.memory(0), oldname) as *const i8; + let newname_ptr = emscripten_memory_pointer!(ctx.memory(0), newname) as *const i8; + let result = unsafe { link(oldname_ptr, newname_ptr) }; + debug!( + "=> oldname: {}, newname: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(oldname_ptr).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(newname_ptr).to_str().unwrap() }, + result, + ); + result +} + +/// getrusage +pub fn ___syscall77(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall77 (getrusage) {}", _which); + + let resource: c_int = varargs.get(ctx); + let rusage_ptr: c_int = varargs.get(ctx); + #[allow(clippy::cast_ptr_alignment)] + let rusage = emscripten_memory_pointer!(ctx.memory(0), rusage_ptr) as *mut rusage; + assert_eq!(8, mem::align_of_val(&rusage)); + unsafe { getrusage(resource, rusage) } +} + +/// symlink +pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall83 (symlink) {}", _which); + + let path1_ptr: c_int = varargs.get(ctx); + let path2_ptr: c_int = varargs.get(ctx); + let path1 = emscripten_memory_pointer!(ctx.memory(0), path1_ptr) as *mut i8; + let path2 = emscripten_memory_pointer!(ctx.memory(0), path2_ptr) as *mut i8; + let result = unsafe { symlink(path1, path2) }; + debug!( + "=> path1: {}, path2: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path1).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(path2).to_str().unwrap() }, + result, + ); + result +} + +/// ftruncate64 +pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall194 (ftruncate64) {}", _which); + let _fd: c_int = varargs.get(ctx); + let _length: i64 = varargs.get(ctx); + #[cfg(not(target_os = "macos"))] + unsafe { + ftruncate64(_fd, _length) + } + #[cfg(target_os = "macos")] + unimplemented!() +} + +/// lchown +pub fn ___syscall198(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall198 (lchown) {}", _which); + let path: c_int = varargs.get(ctx); + let uid: uid_t = varargs.get(ctx); + let gid: gid_t = varargs.get(ctx); + let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path) as *const i8; + let result = unsafe { lchown(path_ptr, uid, gid) }; + debug!( + "=> path: {}, uid: {}, gid: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path_ptr).to_str().unwrap() }, + uid, + gid, + result, + ); + result +} + +/// getgroups +pub fn ___syscall205(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall205 (getgroups) {}", _which); + let ngroups_max: c_int = varargs.get(ctx); + let groups: c_int = varargs.get(ctx); + + #[allow(clippy::cast_ptr_alignment)] + let gid_ptr = emscripten_memory_pointer!(ctx.memory(0), groups) as *mut gid_t; + assert_eq!(4, mem::align_of_val(&gid_ptr)); + let result = unsafe { getgroups(ngroups_max, gid_ptr) }; + debug!( + "=> ngroups_max: {}, gid_ptr: {:?}, result: {}", + ngroups_max, gid_ptr, result, + ); + result +} + // chown -pub fn ___syscall212(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall212 (chown) {}", which); +pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); let pathname: u32 = varargs.get(ctx); let owner: u32 = varargs.get(ctx); @@ -106,15 +230,63 @@ pub fn ___syscall212(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int unsafe { chown(pathname_addr, owner, group) } } +/// madvise +pub fn ___syscall219(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); + + let addr_ptr: c_int = varargs.get(ctx); + let len: usize = varargs.get(ctx); + let advice: c_int = varargs.get(ctx); + + let addr = emscripten_memory_pointer!(ctx.memory(0), addr_ptr) as *mut c_void; + + unsafe { madvise(addr, len, advice) } +} + +/// access +pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall33 (access) {}", _which); + let path_ptr: c_int = varargs.get(ctx); + let amode: c_int = varargs.get(ctx); + let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; + let result = unsafe { access(path, amode) }; + debug!( + "=> path: {}, result: {}", + unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + result + ); + result +} + +/// nice +pub fn ___syscall34(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall34 (nice) {}", _which); + let inc_r: c_int = varargs.get(ctx); + unsafe { nice(inc_r) } +} + // mkdir -pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall39 (mkdir) {}", which); +pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall39 (mkdir) {}", _which); let pathname: u32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; unsafe { mkdir(pathname_addr, mode as _) } } +/// dup +pub fn ___syscall41(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall41 (dup) {}", _which); + let fd: c_int = varargs.get(ctx); + unsafe { dup(fd) } +} + +/// getgid +pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall200 (getgid)"); + unsafe { getgid() as i32 } +} + // getgid pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); @@ -134,6 +306,15 @@ pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { } } +/// fchown +pub fn ___syscall207(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall207 (fchown) {}", _which); + let fd: c_int = varargs.get(ctx); + let owner: uid_t = varargs.get(ctx); + let group: gid_t = varargs.get(ctx); + unsafe { fchown(fd, owner, group) } +} + /// dup3 pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { // Implementation based on description at https://linux.die.net/man/2/dup3 @@ -169,8 +350,8 @@ pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ } /// ioctl -pub fn ___syscall54(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall54 (ioctl) {}", which); +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); @@ -212,8 +393,8 @@ pub fn ___syscall54(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int // socketcall #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall102 (socketcall) {}", which); +pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall102 (socketcall) {}", _which); let call: u32 = varargs.get(ctx); let mut socket_varargs: VarArgs = varargs.get(ctx); @@ -279,13 +460,11 @@ pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; // Debug received address - unsafe { - let proper_address = address as *const GuestSockaddrIn; - debug!( + let _proper_address = address as *const GuestSockaddrIn; + debug!( "=> address.sin_family: {:?}, address.sin_port: {:?}, address.sin_addr.s_addr: {:?}", - (*proper_address).sin_family, (*proper_address).sin_port, (*proper_address).sin_addr.s_addr + unsafe { (*_proper_address).sin_family }, unsafe { (*_proper_address).sin_port }, unsafe { (*_proper_address).sin_addr.s_addr } ); - } let status = unsafe { bind(socket, address, address_len) }; // debug!("=> status: {}", status); @@ -464,8 +643,8 @@ pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int } // pread -pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall180 (pread) {}", which); +pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall180 (pread) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); let count: u32 = varargs.get(ctx); @@ -481,8 +660,8 @@ pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int } // pwrite -pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall181 (pwrite) {}", which); +pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall181 (pwrite) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); let count: u32 = varargs.get(ctx); @@ -501,6 +680,14 @@ pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int status } +/// fchmod +pub fn ___syscall94(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fchmod) {}", _which); + let fd: c_int = varargs.get(ctx); + let mode: mode_t = varargs.get(ctx); + unsafe { fchmod(fd, mode) } +} + /// wait4 #[allow(clippy::cast_ptr_alignment)] pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { @@ -510,6 +697,7 @@ pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ let options: c_int = varargs.get(ctx); let rusage: u32 = varargs.get(ctx); let status_addr = emscripten_memory_pointer!(ctx.memory(0), status) as *mut c_int; + let rusage_addr = emscripten_memory_pointer!(ctx.memory(0), rusage) as *mut rusage; let res = unsafe { wait4(pid, status_addr, options, rusage_addr) }; debug!( @@ -519,10 +707,17 @@ pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_ res } +/// fsync +pub fn ___syscall118(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fsync) {}", _which); + let fd: c_int = varargs.get(ctx); + unsafe { fsync(fd) } +} + // select #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall142(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall142 (newselect) {}", which); +pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall142 (newselect) {}", _which); let nfds: i32 = varargs.get(ctx); let readfds: u32 = varargs.get(ctx); @@ -539,9 +734,18 @@ pub fn ___syscall142(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } } +/// fdatasync +pub fn ___syscall148(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall148 (fdatasync) {}", _which); + + let fd: i32 = varargs.get(ctx); + + unsafe { fdatasync(fd) } +} + // setpgid -pub fn ___syscall57(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall57 (setpgid) {}", which); +pub fn ___syscall57(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall57 (setpgid) {}", _which); let pid: i32 = varargs.get(ctx); let pgid: i32 = varargs.get(ctx); unsafe { setpgid(pid, pgid) } @@ -549,10 +753,55 @@ pub fn ___syscall57(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. -pub fn ___syscall122(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { - debug!("emscripten::___syscall122 (uname) {}", which); +pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall122 (uname) {}", _which); let buf: u32 = varargs.get(ctx); debug!("=> buf: {}", buf); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut utsname; unsafe { uname(buf_addr) } } + +/// lstat64 +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: 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 +pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall324 (fallocate) {}", _which); + let _fd: c_int = varargs.get(ctx); + let _mode: c_int = varargs.get(ctx); + let _offset: off_t = varargs.get(ctx); + let _len: off_t = varargs.get(ctx); + #[cfg(not(target_os = "macos"))] + unsafe { + fallocate(_fd, _mode, _offset, _len) + } + #[cfg(target_os = "macos")] + { + unimplemented!() + } +} diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index d2584bbd4..a7ac8c11c 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -1,22 +1,23 @@ use crate::utils::copy_cstr_into_wasm; -use crate::utils::read_string_from_wasm; use crate::varargs::VarArgs; use libc::mkdir; use libc::open; use rand::Rng; use std::env; -use std::ffi::CStr; use std::ffi::CString; use std::fs::File; use std::io::Write; use std::os::raw::c_int; use wasmer_runtime_core::vm::Ctx; +#[allow(non_camel_case_types)] type pid_t = c_int; /// open pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall5 (open) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; let pathname: u32 = varargs.get(ctx); let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); @@ -34,15 +35,15 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { let mut urandom_file = File::create(tmp_dir).unwrap(); // create some random bytes and put them into the file let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); - urandom_file.write_all(&random_bytes); + let _ = urandom_file.write_all(&random_bytes).unwrap(); // put the file path string into wasm memory let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) }; let raw_pointer_to_urandom_file = emscripten_memory_pointer!(ctx.memory(0), urandom_file_offset) as *const i8; let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) }; debug!( - "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", - pathname, flags, mode, fd, s + "=> pathname: {}, flags: {}, mode: {} = fd: {}", + pathname, flags, mode, fd ); fd } @@ -57,21 +58,78 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { } } +/// link +pub fn ___syscall9(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall9 (link) {}", _which); + unimplemented!() +} + +/// ftruncate64 +pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall194 - stub"); + unimplemented!() +} + // chown -pub fn ___syscall212(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall212(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// access +pub fn ___syscall33(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall33 (access) {}", _which); + unimplemented!() +} + +/// nice +pub fn ___syscall34(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall34 (nice) {}", _which); + unimplemented!() +} + // mkdir pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; let pathname: u32 = varargs.get(ctx); - let mode: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; unsafe { mkdir(pathname_addr) } } +/// dup +pub fn ___syscall41(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall41 (dup) {}", _which); + unimplemented!() +} + +/// getrusage +pub fn ___syscall77(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall77 (getrusage) {}", _which); + unimplemented!() +} + +/// symlink +pub fn ___syscall83(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall83 (symlink) {}", _which); + unimplemented!() +} + +/// lchown +pub fn ___syscall198(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall198 (lchown) {}", _which); + unimplemented!() +} + +/// getgid +pub fn ___syscall200(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall200 (getgid)"); + unimplemented!() +} + // getgid pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); @@ -85,60 +143,122 @@ pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +/// getgroups +pub fn ___syscall205(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall205 (getgroups) {}", _which); + unimplemented!() +} + +/// madvise +pub fn ___syscall219(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); + unimplemented!() +} + /// dup3 -pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { +pub fn ___syscall330(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> pid_t { debug!("emscripten::___syscall330 (dup3)"); -1 } /// ioctl -pub fn ___syscall54(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall54(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall54 (ioctl) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fchmod +pub fn ___syscall94(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fchmod) {}", _which); + unimplemented!() +} + // socketcall #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall102(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall102 (socketcall) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fsync +pub fn ___syscall118(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall118 (fsync) {}", _which); + unimplemented!() +} + // pread -pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall180(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall180 (pread) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } // pwrite -pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall181(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall181 (pwrite) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } /// wait4 #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { +pub fn ___syscall114(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> pid_t { debug!("emscripten::___syscall114 (wait4)"); -1 } // select #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall142(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall142(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall142 (newselect) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } +/// fdatasync +pub fn ___syscall148(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall148 (fdatasync) {}", _which); + unimplemented!(); +} + // setpgid -pub fn ___syscall57(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall57(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall57 (setpgid) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. -pub fn ___syscall122(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { +pub fn ___syscall122(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall122 (uname) {}", which); + #[cfg(not(feature = "debug"))] + let _ = which; -1 } + +/// lstat64 +pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall196 (lstat64) - stub"); + -1 +} + +/// fchown +pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall207 (fchown) {}", _which); + unimplemented!() +} + +/// fallocate +pub fn ___syscall324(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall324 (fallocate) {}", _which); + unimplemented!() +} diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 7efb22e15..312808a82 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -10,6 +10,7 @@ use libc::{clockid_t, time as libc_time}; use libc::time_t; #[cfg(target_os = "windows")] +#[allow(non_camel_case_types)] type clockid_t = c_int; #[cfg(target_os = "windows")] @@ -75,8 +76,10 @@ pub fn _clock_gettime(ctx: &mut Ctx, clk_id: clockid_t, tp: c_int) -> c_int { tv_nsec: i32, } + #[allow(unreachable_patterns)] let timespec = match clk_id { CLOCK_REALTIME => time::get_time(), + CLOCK_MONOTONIC | CLOCK_MONOTONIC_COARSE => { let precise_ns = time::precise_time_ns(); time::Timespec::new( @@ -295,7 +298,7 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 { /// emscripten: _strftime pub fn _strftime( - _ctx: &mut Ctx, + ctx: &mut Ctx, s_ptr: c_int, maxsize: u32, format_ptr: c_int, @@ -305,5 +308,67 @@ pub fn _strftime( "emscripten::_strftime {} {} {} {}", 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 { + return 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 678032374..e1cf13ef5 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -90,8 +90,9 @@ pub unsafe fn allocate_cstr_on_stack<'a>(ctx: &'a mut Ctx, s: &str) -> (u32, &'a (offset, slice) } +#[cfg(not(target_os = "windows"))] pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_char) -> u32 { - let total_num = { + let _total_num = { let mut ptr = cstrs; let mut counter = 0; while !(*ptr).is_null() { @@ -102,7 +103,7 @@ pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_ }; debug!( "emscripten::copy_terminated_array_of_cstrs::total_num: {}", - total_num + _total_num ); 0 } @@ -124,7 +125,7 @@ pub struct GuestStat { st_atime: u64, st_mtime: u64, st_ctime: u64, - st_ino: u64, + st_ino: u32, } #[allow(clippy::cast_ptr_alignment)] @@ -155,6 +156,7 @@ pub unsafe fn copy_stat_into_wasm(ctx: &mut Ctx, buf: u32, stat: &stat) { (*stat_ptr).st_ino = stat.st_ino as _; } +#[allow(dead_code)] // it's used in `env/windows/mod.rs`. pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String { let v: Vec = memory.view()[(offset as usize)..] .iter() @@ -169,15 +171,40 @@ mod tests { use super::is_emscripten_module; use std::sync::Arc; use wabt::wat2wasm; - use wasmer_clif_backend::CraneliftCompiler; + use wasmer_runtime_core::backend::Compiler; use wasmer_runtime_core::compile_with; + #[cfg(feature = "clif")] + fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + #[cfg(feature = "llvm")] + fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() + } + + #[cfg(feature = "singlepass")] + fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] + fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + #[test] fn should_detect_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_true.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = compile_with(&wasm_binary[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); + let module = + compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(is_emscripten_module(&module)); } @@ -186,8 +213,8 @@ mod tests { fn should_detect_non_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_false.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = compile_with(&wasm_binary[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); + let module = + compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(!is_emscripten_module(&module)); } diff --git a/lib/emscripten/src/varargs.rs b/lib/emscripten/src/varargs.rs index cd9073cb9..3775d102c 100644 --- a/lib/emscripten/src/varargs.rs +++ b/lib/emscripten/src/varargs.rs @@ -20,4 +20,11 @@ impl VarArgs { unsafe impl WasmExternType for VarArgs { const TYPE: Type = Type::I32; + + fn to_bits(self) -> u64 { + self.pointer as u64 + } + fn from_bits(n: u64) -> Self { + Self { pointer: n as u32 } + } } diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 8d3e00296..18664e4ea 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -1,16 +1,41 @@ macro_rules! assert_emscripten_output { ($file:expr, $name:expr, $args:expr, $expected:expr) => {{ - use wasmer_clif_backend::CraneliftCompiler; use wasmer_emscripten::{ EmscriptenGlobals, generate_emscripten_env, stdio::StdioCapturer }; + use wasmer_runtime_core::backend::Compiler; + + #[cfg(feature = "clif")] + fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + #[cfg(feature = "llvm")] + fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() + } + + #[cfg(feature = "singlepass")] + fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] + fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } let wasm_bytes = include_bytes!($file); - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) + let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler()) .expect("WASM can't be compiled"); // let module = compile(&wasm_bytes[..]) @@ -41,3 +66,37 @@ 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}; + +// 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 capturer = StdioCapturer::new(); + +// wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) +// .expect("run_emscripten_instance finishes"); + +// 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(); + +// let contains_output = output.contains(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 37c88a041..645351227 100644 --- a/lib/emscripten/tests/emtests/mod.rs +++ b/lib/emscripten/tests/emtests/mod.rs @@ -43,6 +43,7 @@ mod test_exceptions_2; mod test_exceptions_multi; mod test_exceptions_std; mod test_exceptions_white_list; +mod test_execvp; mod test_fast_math; mod test_flexarray_struct; mod test_float32_precise; @@ -54,6 +55,7 @@ mod test_funcptrfunc; mod test_funcs; mod test_functionpointer_libfunc_varargs; mod test_fwrite_0; +mod test_getcwd; mod test_getgep; mod test_getloadavg; mod test_getopt; @@ -117,6 +119,7 @@ mod test_nested_struct_varargs; mod test_nl_types; mod test_perrar; mod test_phiundef; +mod test_pipe; mod test_poll; mod test_posixtime; mod test_printf_2; @@ -173,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 new file mode 100644 index 000000000..085e41d9a --- /dev/null +++ b/lib/emscripten/tests/emtests/test_execvp.rs @@ -0,0 +1,10 @@ +#[test] +#[ignore] +fn test_test_execvp() { + assert_emscripten_output!( + "../../emtests/test_execvp.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 new file mode 100644 index 000000000..98723df87 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_getcwd.rs @@ -0,0 +1,9 @@ +#[test] +fn test_test_getcwd() { + assert_emscripten_output!( + "../../emtests/test_getcwd.wasm", + "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 new file mode 100644 index 000000000..e7caee28b --- /dev/null +++ b/lib/emscripten/tests/emtests/test_pipe.rs @@ -0,0 +1,10 @@ +#[test] +#[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 new file mode 100644 index 000000000..afd339042 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_vfs.rs @@ -0,0 +1,10 @@ +#[test] +#[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 new file mode 100644 index 000000000..944db6d41 --- /dev/null +++ b/lib/llvm-backend/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wasmer-llvm-backend" +version = "0.3.0" +authors = ["Lachlan Sneff "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } +wasmparser = "0.28.0" +hashbrown = "0.1.8" +smallvec = "0.6.8" +goblin = "0.0.20" +libc = "0.2.49" +nix = "0.13.0" +capstone = { version = "0.5.0", optional = true } + +[build-dependencies] +cc = "1.0" +lazy_static = "1.2.0" +regex = "1.1.0" +semver = "0.9" +rustc_version = "0.2.3" + +[dev-dependencies] +wabt = "0.7.4" + +[features] +debug = ["wasmer-runtime-core/debug"] +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 @@ +

+ + Wasmer logo + +

+ +

+ + 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 new file mode 100644 index 000000000..ed96cae9d --- /dev/null +++ b/lib/llvm-backend/build.rs @@ -0,0 +1,221 @@ +//! This file was mostly taken from the llvm-sys crate. +//! (https://bitbucket.org/tari/llvm-sys.rs/src/21ab524ec4df1450035df895209c3f8fbeb8775f/build.rs?at=default&fileviewer=file-view-default) + +use lazy_static::lazy_static; +use regex::Regex; +use semver::Version; +use std::env; +use std::ffi::OsStr; +use std::io::{self, ErrorKind}; +use std::path::PathBuf; +use std::process::Command; + +lazy_static! { + /// LLVM version used by this version of the crate. + static ref CRATE_VERSION: Version = { + let crate_version = Version::parse(env!("CARGO_PKG_VERSION")) + .expect("Crate version is somehow not valid semver"); + Version { + major: crate_version.major / 10, + minor: crate_version.major % 10, + .. crate_version + } + }; + + static ref LLVM_CONFIG_BINARY_NAMES: Vec = { + vec![ + "llvm-config".into(), + // format!("llvm-config-{}", CRATE_VERSION.major), + // format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor), + ] + }; + + /// Filesystem path to an llvm-config binary for the correct version. + static ref LLVM_CONFIG_PATH: PathBuf = { + // Try llvm-config via PATH first. + if let Some(name) = locate_system_llvm_config() { + return name.into(); + } else { + println!("Didn't find usable system-wide LLVM."); + } + + // Did the user give us a binary path to use? If yes, try + // to use that and fail if it doesn't work. + let binary_prefix_var = "LLVM_SYS_70_PREFIX"; + + let path = if let Some(path) = env::var_os(&binary_prefix_var) { + Some(path.to_str().unwrap().to_owned()) + } else if let Ok(mut file) = std::fs::File::open(".llvmenv") { + use std::io::Read; + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + s.truncate(s.len() - 4); + Some(s) + } else { + None + }; + + if let Some(path) = path { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + let mut pb: PathBuf = path.clone().into(); + pb.push("bin"); + pb.push(binary_name); + + let ver = llvm_version(&pb) + .expect(&format!("Failed to execute {:?}", &pb)); + if is_compatible_llvm(&ver) { + return pb; + } else { + println!("LLVM binaries specified by {} are the wrong version. + (Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION); + } + } + } + + println!("No suitable version of LLVM was found system-wide or pointed + to by {}. + + Consider using `llvmenv` to compile an appropriate copy of LLVM, and + refer to the llvm-sys documentation for more information. + + llvm-sys: https://crates.io/crates/llvm-sys + llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var); + panic!("Could not find a compatible version of LLVM"); + }; +} + +/// Try to find a system-wide version of llvm-config that is compatible with +/// this crate. +/// +/// Returns None on failure. +fn locate_system_llvm_config() -> Option<&'static str> { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + match llvm_version(binary_name) { + Ok(ref version) if is_compatible_llvm(version) => { + // Compatible version found. Nice. + return Some(binary_name); + } + Ok(version) => { + // Version mismatch. Will try further searches, but warn that + // we're not using the system one. + println!( + "Found LLVM version {} on PATH, but need {}.", + version, *CRATE_VERSION + ); + } + Err(ref e) if e.kind() == ErrorKind::NotFound => { + // Looks like we failed to execute any llvm-config. Keep + // searching. + } + // Some other error, probably a weird failure. Give up. + Err(e) => panic!("Failed to search PATH for llvm-config: {}", e), + } + } + + None +} + +/// Check whether the given LLVM version is compatible with this version of +/// the crate. +fn is_compatible_llvm(llvm_version: &Version) -> bool { + let strict = env::var_os(format!( + "LLVM_SYS_{}_STRICT_VERSIONING", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some() + || cfg!(feature = "strict-versioning"); + if strict { + llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor + } else { + llvm_version.major >= CRATE_VERSION.major + || (llvm_version.major == CRATE_VERSION.major + && llvm_version.minor >= CRATE_VERSION.minor) + } +} + +/// Get the output from running `llvm-config` with the given argument. +/// +/// Lazily searches for or compiles LLVM as configured by the environment +/// variables. +fn llvm_config(arg: &str) -> String { + llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config") +} + +/// Invoke the specified binary as llvm-config. +/// +/// Explicit version of the `llvm_config` function that bubbles errors +/// up. +fn llvm_config_ex>(binary: S, arg: &str) -> io::Result { + Command::new(binary) + .arg(arg) + .arg("--link-static") // Don't use dylib for >= 3.9 + .output() + .map(|output| { + String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8") + }) +} + +/// Get the LLVM version using llvm-config. +fn llvm_version>(binary: S) -> io::Result { + let version_str = llvm_config_ex(binary.as_ref(), "--version")?; + + // LLVM isn't really semver and uses version suffixes to build + // version strings like '3.8.0svn', so limit what we try to parse + // to only the numeric bits. + let re = Regex::new(r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))??").unwrap(); + let c = re + .captures(&version_str) + .expect("Could not determine LLVM version from llvm-config."); + + // some systems don't have a patch number but Version wants it so we just append .0 if it isn't + // there + let s = match c.name("patch") { + None => format!("{}.0", &c[0]), + Some(_) => c[0].to_string(), + }; + Ok(Version::parse(&s).unwrap()) +} + +fn get_llvm_cxxflags() -> String { + let output = llvm_config("--cxxflags"); + + // llvm-config includes cflags from its own compilation with --cflags that + // may not be relevant to us. In particularly annoying cases, these might + // include flags that aren't understood by the default compiler we're + // using. Unless requested otherwise, clean CFLAGS of options that are + // known to be possibly-harmful. + let no_clean = env::var_os(format!( + "LLVM_SYS_{}_NO_CLEAN_CFLAGS", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some(); + if no_clean || cfg!(target_env = "msvc") { + // MSVC doesn't accept -W... options, so don't try to strip them and + // possibly strip something that should be retained. Also do nothing if + // the user requests it. + return output; + } + + output + .split(&[' ', '\n'][..]) + .filter(|word| !word.starts_with("-W")) + .filter(|word| word != &"-fno-exceptions") + .collect::>() + .join(" ") +} + +fn main() { + std::env::set_var("CXXFLAGS", get_llvm_cxxflags()); + cc::Build::new() + .cpp(true) + .file("cpp/object_loader.cpp") + .compile("llvm-backend"); + + println!("cargo:rustc-link-lib=static=llvm-backend"); + println!("cargo:rerun-if-changed=build.rs"); + + // 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/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp new file mode 100644 index 000000000..28bea6354 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -0,0 +1,204 @@ +#include "object_loader.hh" +#include +#include + +extern "C" void __register_frame(uint8_t *); +extern "C" void __deregister_frame(uint8_t *); + +struct MemoryManager : llvm::RuntimeDyld::MemoryManager { +public: + MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {} + + virtual ~MemoryManager() override { + deregisterEHFrames(); + // Deallocate all of the allocated memory. + callbacks.dealloc_memory(code_section.base, code_section.size); + callbacks.dealloc_memory(read_section.base, read_section.size); + callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size); + } + + virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override { + return allocate_bump(code_section, code_bump_ptr, size, alignment); + } + + virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool read_only) override { + // Allocate from the read-only section or the read-write section, depending on if this allocation + // should be read-only or not. + if (read_only) { + return allocate_bump(read_section, read_bump_ptr, size, alignment); + } else { + return allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment); + } + } + + virtual void reserveAllocationSpace( + uintptr_t code_size, + uint32_t code_align, + uintptr_t read_data_size, + uint32_t read_data_align, + uintptr_t read_write_data_size, + uint32_t read_write_data_align + ) override { + auto aligner = [](uintptr_t ptr, size_t align) { + if (ptr == 0) { + return align; + } + return (ptr + align - 1) & ~(align - 1); + }; + + + uint8_t *code_ptr_out = nullptr; + size_t code_size_out = 0; + auto code_result = callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, &code_ptr_out, &code_size_out); + assert(code_result == RESULT_OK); + code_section = Section { code_ptr_out, code_size_out }; + code_bump_ptr = (uintptr_t)code_ptr_out; + + uint8_t *read_ptr_out = nullptr; + size_t read_size_out = 0; + auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE, &read_ptr_out, &read_size_out); + assert(read_result == RESULT_OK); + read_section = Section { read_ptr_out, read_size_out }; + read_bump_ptr = (uintptr_t)read_ptr_out; + + uint8_t *readwrite_ptr_out = nullptr; + size_t readwrite_size_out = 0; + auto readwrite_result = callbacks.alloc_memory(aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, &readwrite_ptr_out, &readwrite_size_out); + assert(readwrite_result == RESULT_OK); + readwrite_section = Section { readwrite_ptr_out, readwrite_size_out }; + readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out; + } + + /* Turn on the `reserveAllocationSpace` callback. */ + virtual bool needsToReserveAllocationSpace() override { + return true; + } + + virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override { + eh_frame_ptr = addr; + eh_frame_size = size; + eh_frames_registered = true; + callbacks.visit_fde(addr, size, __register_frame); + } + + virtual void deregisterEHFrames() override { + if (eh_frames_registered) { + callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame); + } + } + + virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { + auto code_result = callbacks.protect_memory(code_section.base, code_section.size, mem_protect_t::PROTECT_READ_EXECUTE); + if (code_result != RESULT_OK) { + return false; + } + + auto read_result = callbacks.protect_memory(read_section.base, read_section.size, mem_protect_t::PROTECT_READ); + if (read_result != RESULT_OK) { + return false; + } + + // The readwrite section is already mapped as read-write. + + return false; + } + + virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {} +private: + struct Section { + uint8_t* base; + size_t size; + }; + + uint8_t* allocate_bump(Section& section, uintptr_t& bump_ptr, size_t size, size_t align) { + auto aligner = [](uintptr_t& ptr, size_t align) { + ptr = (ptr + align - 1) & ~(align - 1); + }; + + // Align the bump pointer to the requires alignment. + aligner(bump_ptr, align); + + auto ret_ptr = bump_ptr; + bump_ptr += size; + + assert(bump_ptr <= (uintptr_t)section.base + section.size); + + return (uint8_t*)ret_ptr; + } + + Section code_section, read_section, readwrite_section; + uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr; + uint8_t* eh_frame_ptr; + size_t eh_frame_size; + bool eh_frames_registered = false; + + callbacks_t callbacks; +}; + +struct SymbolLookup : llvm::JITSymbolResolver { +public: + SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {} + + virtual llvm::Expected lookup(const LookupSet& symbols) override { + LookupResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol)); + } + + return result; + } + + virtual llvm::Expected lookupFlags(const LookupSet& symbols) override { + LookupFlagsResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol).getFlags()); + } + + return result; + } + +private: + llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) { + uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size()); + + return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None); + } + + callbacks_t callbacks; +}; + +WasmModule::WasmModule( + const uint8_t *object_start, + size_t object_size, + callbacks_t callbacks +) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) +{ + + + 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)); + + runtime_dyld->setProcessAllSections(true); + + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); + + if (runtime_dyld->hasError()) { + _init_failed = true; + return; + } + } else { + _init_failed = true; + } +} + +void* WasmModule::get_func(llvm::StringRef name) const { + auto symbol = runtime_dyld->getSymbol(name); + return (void*)symbol.getAddress(); +} \ No newline at end of file diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh new file mode 100644 index 000000000..134396b78 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include + +typedef enum +{ + PROTECT_NONE, + PROTECT_READ, + PROTECT_READ_WRITE, + PROTECT_READ_EXECUTE, +} mem_protect_t; + +typedef enum +{ + RESULT_OK, + RESULT_ALLOCATE_FAILURE, + RESULT_PROTECT_FAILURE, + RESULT_DEALLOC_FAILURE, + 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 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 struct +{ + /* Memory management. */ + alloc_memory_t alloc_memory; + protect_memory_t protect_memory; + dealloc_memory_t dealloc_memory; + + lookup_vm_symbol_t lookup_vm_symbol; + + visit_fde_t visit_fde; +} callbacks_t; + +struct WasmException +{ + public: + virtual std::string description() const noexcept = 0; +}; + +struct UncatchableException : WasmException +{ + public: + virtual std::string description() const noexcept override + { + return "Uncatchable exception"; + } +}; + +struct UserException : UncatchableException +{ + public: + UserException(size_t data, size_t vtable) : data(data), vtable(vtable) {} + + virtual std::string description() const noexcept override + { + return "user exception"; + } + + // The parts of a `Box`. + size_t data, vtable; +}; + +struct WasmTrap : UncatchableException +{ + public: + enum Type + { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, + }; + + WasmTrap(Type type) : type(type) {} + + 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; + } + return out; + } +}; + +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 + { + return "catchable exception"; + } + + uint32_t type_id, value_num; + uint64_t values[1]; +}; + +struct WasmModule +{ + public: + WasmModule( + const uint8_t *object_start, + size_t object_size, + callbacks_t callbacks); + + void *get_func(llvm::StringRef name) const; + + 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) + { + *module_out = new WasmModule(mem_ptr, mem_size, callbacks); + + if ((*module_out)->_init_failed) { + return RESULT_OBJECT_LOAD_FAILURE; + } + + return RESULT_OK; + } + + [[noreturn]] void throw_trap(WasmTrap::Type ty) { + throw WasmTrap(ty); + } + + 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, + void *invoke_env) throw() + { + try + { + trampoline(ctx, func, params, results); + return true; + } + catch (const WasmTrap &e) + { + *trap_out = e.type; + return false; + } + catch (const WasmException &e) + { + *trap_out = WasmTrap::Type::Unknown; + return false; + } + catch (...) + { + *trap_out = WasmTrap::Type::Unknown; + return false; + } + } + + 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 new file mode 100644 index 000000000..783a432ff --- /dev/null +++ b/lib/llvm-backend/src/backend.rs @@ -0,0 +1,429 @@ +use crate::intrinsics::Intrinsics; +use inkwell::{ + memory_buffer::MemoryBuffer, + module::Module, + targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, + OptimizationLevel, +}; +use libc::{ + c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ, + PROT_WRITE, +}; +use std::{ + any::Any, + ffi::{c_void, CString}, + mem, + ops::Deref, + ptr::{self, NonNull}, + slice, str, + sync::{Arc, Once}, +}; +use wasmer_runtime_core::{ + backend::{ + sys::{Memory, Protect}, + CacheGen, RunnableModule, + }, + cache::Error as CacheError, + module::ModuleInfo, + structures::TypedIndex, + typed_func::{Wasm, WasmTrapInfo}, + types::{LocalFuncIndex, SigIndex}, + vm, vmcalls, +}; + +#[repr(C)] +struct LLVMModule { + _private: [u8; 0], +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +enum MemProtect { + NONE, + READ, + READ_WRITE, + READ_EXECUTE, +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +enum LLVMResult { + OK, + ALLOCATE_FAILURE, + PROTECT_FAILURE, + DEALLOC_FAILURE, + OBJECT_LOAD_FAILURE, +} + +#[repr(C)] +struct Callbacks { + alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult, + protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult, + dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult, + + lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func, + visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)), +} + +extern "C" { + fn module_load( + mem_ptr: *const u8, + mem_size: usize, + callbacks: Callbacks, + module_out: &mut *mut LLVMModule, + ) -> LLVMResult; + fn module_delete(module: *mut LLVMModule); + fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func; + + 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, NonNull, *const u64, *mut u64), + vmctx_ptr: *mut vm::Ctx, + func_ptr: NonNull, + params: *const u64, + results: *mut u64, + trap_out: *mut WasmTrapInfo, + invoke_env: Option>, + ) -> bool; +} + +fn get_callbacks() -> Callbacks { + fn round_up_to_page_size(size: usize) -> usize { + (size + (4096 - 1)) & !(4096 - 1) + } + + extern "C" fn alloc_memory( + size: usize, + protect: MemProtect, + ptr_out: &mut *mut u8, + size_out: &mut usize, + ) -> LLVMResult { + let size = round_up_to_page_size(size); + let ptr = unsafe { + mmap( + ptr::null_mut(), + size, + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ) + }; + if ptr as isize == -1 { + return LLVMResult::ALLOCATE_FAILURE; + } + *ptr_out = ptr as _; + *size_out = size; + LLVMResult::OK + } + + extern "C" fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult { + let res = unsafe { + mprotect( + ptr as _, + round_up_to_page_size(size), + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + ) + }; + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::PROTECT_FAILURE + } + } + + extern "C" fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult { + let res = unsafe { munmap(ptr as _, round_up_to_page_size(size)) }; + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::DEALLOC_FAILURE + } + } + + extern "C" fn lookup_vm_symbol(name_ptr: *const c_char, length: usize) -> *const vm::Func { + #[cfg(target_os = "macos")] + macro_rules! fn_name { + ($s:literal) => { + concat!("_", $s) + }; + } + + #[cfg(not(target_os = "macos"))] + macro_rules! fn_name { + ($s:literal) => { + $s + }; + } + + let name_slice = unsafe { slice::from_raw_parts(name_ptr as *const u8, length) }; + let name = str::from_utf8(name_slice).unwrap(); + + match name { + fn_name!("vm.memory.grow.dynamic.local") => vmcalls::local_dynamic_memory_grow as _, + fn_name!("vm.memory.size.dynamic.local") => vmcalls::local_dynamic_memory_size as _, + fn_name!("vm.memory.grow.static.local") => vmcalls::local_static_memory_grow as _, + fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _, + + fn_name!("vm.exception.trap") => throw_trap as _, + + _ => ptr::null(), + } + } + + extern "C" fn visit_fde(fde: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + unsafe { + crate::platform::visit_fde(fde, size, visitor); + } + } + + Callbacks { + alloc_memory, + protect_memory, + dealloc_memory, + lookup_vm_symbol, + visit_fde, + } +} + +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)] + buffer: Arc, +} + +impl LLVMBackend { + pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) { + Target::initialize_x86(&InitializationConfig { + asm_parser: true, + asm_printer: true, + base: true, + disassembler: true, + info: true, + machine_code: true, + }); + let triple = TargetMachine::get_default_triple().to_string(); + let target = Target::from_triple(&triple).unwrap(); + let target_machine = target + .create_target_machine( + &triple, + &TargetMachine::get_host_cpu_name().to_string(), + &TargetMachine::get_host_cpu_features().to_string(), + OptimizationLevel::Aggressive, + RelocMode::PIC, + CodeModel::Default, + ) + .unwrap(); + + let memory_buffer = target_machine + .write_to_memory_buffer(&module, FileType::Object) + .unwrap(); + let mem_buf_slice = memory_buffer.as_slice(); + + let callbacks = get_callbacks(); + let mut module: *mut LLVMModule = ptr::null_mut(); + + let res = unsafe { + module_load( + mem_buf_slice.as_ptr(), + mem_buf_slice.len(), + callbacks, + &mut module, + ) + }; + + static SIGNAL_HANDLER_INSTALLED: Once = Once::new(); + + SIGNAL_HANDLER_INSTALLED.call_once(|| unsafe { + crate::platform::install_signal_handler(); + }); + + if res != LLVMResult::OK { + panic!("failed to load object") + } + + let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer)); + + ( + Self { + module, + buffer: Arc::clone(&buffer), + }, + LLVMCache { buffer }, + ) + } + + 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 = unsafe { 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(|| unsafe { + 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, + ) -> Option> { + let index = info.imported_functions.len() + local_func_index.index(); + let name = if cfg!(target_os = "macos") { + format!("_fn{}", index) + } else { + format!("fn{}", index) + }; + + let c_str = CString::new(name).ok()?; + let ptr = unsafe { get_func_symbol(self.module, c_str.as_ptr()) }; + + NonNull::new(ptr as _) + } + + 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 { + format!("trmp{}", sig_index.index()) + }; + + let c_str = CString::new(name).unwrap(); + let symbol = get_func_symbol(self.module, c_str.as_ptr()); + assert!(!symbol.is_null()); + + mem::transmute(symbol) + }; + + Some(unsafe { Wasm::from_raw_parts(trampoline, invoke_trampoline, None) }) + } + + unsafe fn do_early_trap(&self, data: Box) -> ! { + throw_any(Box::leak(data)) + } +} + +unsafe impl Send for LLVMCache {} +unsafe impl Sync for LLVMCache {} + +pub struct LLVMCache { + buffer: Arc, +} + +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 buffer = self.buffer.deref(); + + unsafe { + memory.as_slice_mut()[..buffer.len()].copy_from_slice(buffer); + } + + Ok(([].as_ref().into(), memory)) + } +} + +#[cfg(feature = "disasm")] +unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) { + use capstone::arch::BuildsCapstone; + let mut cs = capstone::Capstone::new() // Call builder-pattern + .x86() // X86 architecture + .mode(capstone::arch::x86::ArchMode::Mode64) // 64-bit mode + .detail(true) // Generate extra instruction details + .build() + .expect("Failed to create Capstone object"); + + // Get disassembled instructions + let insns = cs + .disasm_count( + std::slice::from_raw_parts(ptr, size), + ptr as u64, + inst_count, + ) + .expect("Failed to disassemble"); + + println!("count = {}", insns.len()); + for insn in insns.iter() { + println!( + "0x{:x}: {:6} {}", + insn.address(), + insn.mnemonic().unwrap_or(""), + insn.op_str().unwrap_or("") + ); + } +} diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs new file mode 100644 index 000000000..05cde0694 --- /dev/null +++ b/lib/llvm-backend/src/code.rs @@ -0,0 +1,2462 @@ +use inkwell::{ + builder::Builder, + context::Context, + module::{Linkage, Module}, + passes::PassManager, + types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, + values::{BasicValue, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue}, + AddressSpace, FloatPredicate, IntPredicate, +}; +use smallvec::SmallVec; +use wasmer_runtime_core::{ + memory::MemoryType, + module::ModuleInfo, + structures::{Map, SliceMap, TypedIndex}, + types::{ + FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, +}; +use wasmparser::{ + BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, +}; + +use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; +use crate::read_info::type_to_type; +use crate::state::{ControlFrame, IfElseState, State}; +use crate::trampolines::generate_trampolines; + +fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType { + let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty)); + + let param_types: Vec<_> = std::iter::once(intrinsics.ctx_ptr_ty.as_basic_type_enum()) + .chain(user_param_types) + .collect(); + + match sig.returns() { + &[] => intrinsics.void_ty.fn_type(¶m_types, false), + &[single_value] => type_to_llvm(intrinsics, single_value).fn_type(¶m_types, false), + returns @ _ => { + let basic_types: Vec<_> = returns + .iter() + .map(|&ty| type_to_llvm(intrinsics, ty)) + .collect(); + + context + .struct_type(&basic_types, false) + .fn_type(¶m_types, false) + } + } +} + +fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { + match ty { + Type::I32 => intrinsics.i32_ty.as_basic_type_enum(), + Type::I64 => intrinsics.i64_ty.as_basic_type_enum(), + Type::F32 => intrinsics.f32_ty.as_basic_type_enum(), + Type::F64 => intrinsics.f64_ty.as_basic_type_enum(), + } +} + +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, + &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, + 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 function = functions[func_index]; + 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::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + + 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 param_len = locals.len(); + + let mut local_idx = 0; + for local in locals_reader.into_iter() { + let (count, ty) = local?; + let wasmer_ty = type_to_type(ty)?; + let ty = type_to_llvm(intrinsics, wasmer_ty); + + let default_value = match wasmer_ty { + Type::I32 => intrinsics.i32_zero.as_basic_value_enum(), + Type::I64 => intrinsics.i64_zero.as_basic_value_enum(), + Type::F32 => intrinsics.f32_zero.as_basic_value_enum(), + Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), + }; + + 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); + local_idx += 1; + } + } + + 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); + + 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; + + for op in op_reader { + let op = op?; + if !state.reachable { + match op { + Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { + unreachable_depth += 1; + continue; + } + Operator::Else => { + if unreachable_depth != 0 { + continue; + } + } + Operator::End => { + if unreachable_depth != 0 { + unreachable_depth -= 1; + continue; + } + } + _ => { + continue; + } + } + } + + match op { + /*************************** + * Control Flow instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions + ***************************/ + Operator::Block { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let end_block = context.append_basic_block(&function, "end"); + builder.position_at_end(&end_block); + + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + state.push_block(end_block, phis); + builder.position_at_end(¤t_block); + } + Operator::Loop { ty } => { + let loop_body = context.append_basic_block(&function, "loop_body"); + let loop_next = context.append_basic_block(&function, "loop_outer"); + + builder.build_unconditional_branch(&loop_body); + + builder.position_at_end(&loop_next); + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(&loop_body); + state.push_loop(loop_body, loop_next, phis); + } + Operator::Br { relative_depth } => { + let frame = state.frame_at_depth(relative_depth)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let values = state.peekn(value_len)?; + + // For each result of the block we're branching to, + // pop a value off the value stack and load it into + // the corresponding phi. + for (phi, value) in frame.phis().iter().zip(values.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + builder.build_unconditional_branch(frame.br_dest()); + + state.popn(value_len)?; + state.reachable = false; + } + Operator::BrIf { relative_depth } => { + let cond = state.pop1()?; + let frame = state.frame_at_depth(relative_depth)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let param_stack = state.peekn(value_len)?; + + for (phi, value) in frame.phis().iter().zip(param_stack.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + let else_block = context.append_basic_block(&function, "else"); + + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block); + builder.position_at_end(&else_block); + } + Operator::BrTable { ref table } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let (label_depths, default_depth) = table.read_table()?; + + let index = state.pop1()?; + + let default_frame = state.frame_at_depth(default_depth)?; + + let args = if default_frame.is_loop() { + &[] + } else { + let res_len = default_frame.phis().len(); + state.peekn(res_len)? + }; + + for (phi, value) in default_frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + let cases: Vec<_> = label_depths + .iter() + .enumerate() + .map(|(case_index, &depth)| { + let frame = state.frame_at_depth(depth)?; + let case_index_literal = + context.i32_type().const_int(case_index as u64, false); + + for (phi, value) in frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + Ok((case_index_literal, frame.br_dest())) + }) + .collect::>()?; + + builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); + + state.popn(args.len())?; + state.reachable = false; + } + Operator::If { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + let if_then_block = context.append_basic_block(&function, "if_then"); + let if_else_block = context.append_basic_block(&function, "if_else"); + let end_block = context.append_basic_block(&function, "if_end"); + + let end_phis = { + builder.position_at_end(&end_block); + + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(¤t_block); + phis + }; + + let cond = state.pop1()?; + + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + + builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block); + builder.position_at_end(&if_then_block); + state.push_if(if_then_block, if_else_block, end_block, end_phis); + } + Operator::Else => { + if state.reachable { + let frame = state.frame_at_depth(0)?; + builder.build_unconditional_branch(frame.code_after()); + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + for phi in frame.phis().to_vec().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]) + } + } + + let (if_else_block, if_else_state) = if let ControlFrame::IfElse { + if_else, + if_else_state, + .. + } = state.frame_at_depth_mut(0)? + { + (if_else, if_else_state) + } else { + unreachable!() + }; + + *if_else_state = IfElseState::Else; + + builder.position_at_end(if_else_block); + state.reachable = true; + } + + Operator::End => { + let frame = state.pop_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + if state.reachable { + builder.build_unconditional_branch(frame.code_after()); + + for phi in frame.phis().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]); + } + } + + if let ControlFrame::IfElse { + if_else, + next, + if_else_state, + .. + } = &frame + { + if let IfElseState::If = if_else_state { + builder.position_at_end(if_else); + builder.build_unconditional_branch(next); + } + } + + builder.position_at_end(frame.code_after()); + state.reset_stack(&frame); + + state.reachable = true; + + // Push each phi value to the value stack. + for phi in frame.phis() { + if phi.count_incoming() != 0 { + state.push1(phi.as_basic_value()); + } else { + let basic_ty = phi.as_basic_value().get_type(); + let placeholder_value = match basic_ty { + BasicTypeEnum::IntType(int_ty) => { + int_ty.const_int(0, false).as_basic_value_enum() + } + BasicTypeEnum::FloatType(float_ty) => { + float_ty.const_float(0.0).as_basic_value_enum() + } + _ => unimplemented!(), + }; + state.push1(placeholder_value); + phi.as_instruction().erase_from_basic_block(); + } + } + } + Operator::Return => { + let frame = state.outermost_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + builder.build_unconditional_branch(frame.br_dest()); + + let phis = frame.phis().to_vec(); + + for phi in phis.iter() { + let arg = state.pop1()?; + phi.add_incoming(&[(&arg, ¤t_block)]); + } + + state.reachable = false; + } + + Operator::Unreachable => { + // Emit an unreachable instruction. + // If llvm cannot prove that this is never touched, + // it will emit a `ud2` instruction on x86_64 arches. + + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_unreachable], + "throw", + ); + builder.build_unreachable(); + + state.reachable = false; + } + + /*************************** + * Basic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#basic-instructions + ***************************/ + Operator::Nop => { + // Do nothing. + } + Operator::Drop => { + state.pop1()?; + } + + // Generate const values. + Operator::I32Const { value } => { + let i = intrinsics.i32_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::I64Const { value } => { + let i = intrinsics.i64_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::F32Const { value } => { + let bits = intrinsics.i32_ty.const_int(value.bits() as u64, false); + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), "const_space"); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, "i32_space"); + builder.build_store(i32_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + Operator::F64Const { value } => { + let bits = intrinsics.i64_ty.const_int(value.bits(), false); + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), "const_space"); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, "i32_space"); + builder.build_store(i64_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + + // Operate on locals. + Operator::GetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = builder.build_load(pointer_value, &state.var_name()); + state.push1(v); + } + Operator::SetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.pop1()?; + builder.build_store(pointer_value, v); + } + Operator::TeeLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.peek1()?; + builder.build_store(pointer_value, v); + } + + Operator::GetGlobal { global_index } => { + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Const { value } => { + state.push1(value); + } + GlobalCache::Mut { ptr_to_value } => { + let value = builder.build_load(ptr_to_value, "global_value"); + state.push1(value); + } + } + } + Operator::SetGlobal { global_index } => { + let value = state.pop1()?; + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Mut { ptr_to_value } => { + builder.build_store(ptr_to_value, value); + } + GlobalCache::Const { value: _ } => { + unreachable!("cannot set non-mutable globals") + } + } + } + + Operator::Select => { + let (v1, v2, cond) = state.pop3()?; + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_select(cond_value, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::Call { function_index } => { + let func_index = FuncIndex::new(function_index as usize); + let sigindex = info.func_assoc[func_index]; + let llvm_sig = signatures[sigindex]; + let func_sig = &info.signatures[sigindex]; + + let call_site = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + let params: Vec<_> = [ctx.basic()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr = ctx.local_func(local_func_index, llvm_sig); + + 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 params: Vec<_> = [ctx_ptr.as_basic_value_enum()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic); + + let func_ptr = builder.build_pointer_cast( + func_ptr_untyped, + func_ptr_ty, + "typed_func_ptr", + ); + + builder.build_call(func_ptr, ¶ms, &state.var_name()) + } + }; + + state.popn(func_sig.params().len())?; + + if let Some(basic_value) = call_site.try_as_basic_value().left() { + match func_sig.returns().len() { + 1 => state.push1(basic_value), + count @ _ => { + // This is a multi-value return. + let struct_value = basic_value.into_struct_value(); + for i in 0..(count as u32) { + let value = builder + .build_extract_value(struct_value, i, &state.var_name()) + .unwrap(); + state.push1(value); + } + } + } + } + } + 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 func_index = state.pop1()?.into_int_value(); + + // We assume the table has the `anyfunc` element type. + let casted_table_base = builder.build_pointer_cast( + table_base, + intrinsics.anyfunc_ty.ptr_type(AddressSpace::Generic), + "casted_table_base", + ); + + let anyfunc_struct_ptr = unsafe { + builder.build_in_bounds_gep( + casted_table_base, + &[func_index], + "anyfunc_struct_ptr", + ) + }; + + // Load things from the anyfunc data structure. + let (func_ptr, ctx_ptr, found_dynamic_sigindex) = unsafe { + ( + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr"), + "func_ptr", + ) + .into_pointer_value(), + builder.build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"), + "ctx_ptr", + ), + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"), + "sigindex", + ) + .into_int_value(), + ) + }; + + let truncated_table_bounds = builder.build_int_truncate( + table_bound, + intrinsics.i32_ty, + "truncated_table_bounds", + ); + + // First, check if the index is outside of the table bounds. + let index_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + func_index, + truncated_table_bounds, + "index_in_bounds", + ); + + let index_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + index_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "index_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( + index_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + // Next, check if the signature id is correct. + + let sigindices_equal = builder.build_int_compare( + IntPredicate::EQ, + expected_dynamic_sigindex, + found_dynamic_sigindex, + "sigindices_equal", + ); + + // Tell llvm that `expected_dynamic_sigindex` should equal `found_dynamic_sigindex`. + let sigindices_equal = builder + .build_call( + intrinsics.expect_i1, + &[ + sigindices_equal.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "sigindices_equal_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let continue_block = context.append_basic_block(&function, "continue_block"); + let sigindices_notequal_block = + context.append_basic_block(&function, "sigindices_notequal_block"); + builder.build_conditional_branch( + sigindices_equal, + &continue_block, + &sigindices_notequal_block, + ); + + builder.position_at_end(&sigindices_notequal_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_sig], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); + + let wasmer_fn_sig = &info.signatures[sig_index]; + let fn_ty = signatures[sig_index]; + + let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?; + + let args: Vec<_> = std::iter::once(ctx_ptr) + .chain(pushed_args.into_iter()) + .collect(); + + let typed_func_ptr = builder.build_pointer_cast( + func_ptr, + fn_ty.ptr_type(AddressSpace::Generic), + "typed_func_ptr", + ); + + let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call"); + + match wasmer_fn_sig.returns() { + [] => {} + [_] => { + let value = call_site.try_as_basic_value().left().unwrap(); + state.push1(value); + } + _ => unimplemented!("multi-value returns"), + } + } + + /*************************** + * Integer Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions + ***************************/ + Operator::I32Add | Operator::I64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Sub | Operator::I64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Mul | Operator::I64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivS | Operator::I64DivS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero_or_overflow(builder, intrinsics, context, &function, v1, v2); + + let res = builder.build_int_signed_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivU | Operator::I64DivU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemS | Operator::I64RemS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_signed_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemU | Operator::I64RemU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32And | Operator::I64And => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_and(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Or | Operator::I64Or => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_or(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Xor | Operator::I64Xor => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_xor(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Shl | Operator::I64Shl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_left_shift(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32ShrS | Operator::I64ShrS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, true, &state.var_name()); + state.push1(res); + } + Operator::I32ShrU | Operator::I64ShrU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, false, &state.var_name()); + state.push1(res); + } + Operator::I32Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i64_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions + ***************************/ + Operator::F32Add | Operator::F64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sub | Operator::F64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Mul | Operator::F64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Div | Operator::F64Div => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Neg | Operator::F64Neg => { + let input = state.pop1()?.into_float_value(); + let res = builder.build_float_neg(input, &state.var_name()); + state.push1(res); + } + Operator::F32Copysign => { + let (mag, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f32, &[mag, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Copysign => { + let (msg, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f64, &[msg, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + + /*************************** + * Integer Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions + ***************************/ + Operator::I32Eq | Operator::I64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32Ne | Operator::I64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtS | Operator::I64LtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtU | Operator::I64LtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeS | Operator::I64LeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeU | Operator::I64LeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtS | Operator::I64GtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtU | Operator::I64GtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeS | Operator::I64GeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeU | Operator::I64GeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-comparison-instructions + ***************************/ + Operator::F32Eq | Operator::F64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ne | Operator::F64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Lt | Operator::F64Lt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Le | Operator::F64Le => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Gt | Operator::F64Gt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ge | Operator::F64Ge => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Conversion instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#conversion-instructions + ***************************/ + Operator::I32WrapI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_truncate(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendSI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_s_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendUI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_z_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483904.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483649.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSSatF32 | Operator::I32TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223373136366403584.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223372036854777856.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSSatF32 | Operator::I64TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUSatF32 | Operator::I32TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUSatF32 | Operator::I64TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32DemoteF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_trunc(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64PromoteF32 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_ext(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertSI32 | Operator::F64ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertUI32 | Operator::F32ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertUI32 | Operator::F64ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32ReinterpretF32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i32_ty.as_basic_type_enum(), &state.var_name()); + let f32_space = + builder.build_pointer_cast(space, intrinsics.f32_ptr_ty, &state.var_name()); + builder.build_store(f32_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::I64ReinterpretF64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i64_ty.as_basic_type_enum(), &state.var_name()); + let f64_space = + builder.build_pointer_cast(space, intrinsics.f64_ptr_ty, &state.var_name()); + builder.build_store(f64_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::F32ReinterpretI32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), &state.var_name()); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, &state.var_name()); + builder.build_store(i32_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + Operator::F64ReinterpretI64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), &state.var_name()); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, &state.var_name()); + builder.build_store(i64_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + + /*************************** + * Sign-extension operators. + * https://github.com/WebAssembly/sign-extension-ops/blob/master/proposals/sign-extension-ops/Overview.md + ***************************/ + Operator::I32Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I32Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend32S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + + /*************************** + * Load and Store instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions + ***************************/ + Operator::I32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::I64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + + Operator::I32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::I64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + + Operator::I32Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I64Store32 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + + Operator::MemoryGrow { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = match memory_index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_grow_dynamic_local, + MemoryType::Static => intrinsics.memory_grow_static_local, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_grow_dynamic_import, + MemoryType::Static => intrinsics.memory_grow_static_import, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let delta = state.pop1()?; + + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const, delta], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + Operator::MemorySize { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = match memory_index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_size_dynamic_local, + MemoryType::Static => intrinsics.memory_size_static_local, + MemoryType::SharedStatic => intrinsics.memory_size_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_size_dynamic_import, + MemoryType::Static => intrinsics.memory_size_static_import, + MemoryType::SharedStatic => intrinsics.memory_size_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + op @ _ => { + unimplemented!("{:?}", op); + } + } + } + + let results = state.popn_save(func_sig.returns().len())?; + + match results.as_slice() { + [] => { + builder.build_return(None); + } + [one_value] => { + builder.build_return(Some(one_value)); + } + _ => { + // 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") + } + } + + 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, + } + + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_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 (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, + ), + }; + + builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); + + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, + ) + }; + + 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 + } + 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())) +} diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs new file mode 100644 index 000000000..1b367f5d4 --- /dev/null +++ b/lib/llvm-backend/src/intrinsics.rs @@ -0,0 +1,757 @@ +use hashbrown::HashMap; +use inkwell::{ + builder::Builder, + context::Context, + module::Module, + types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType}, + values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue}, + AddressSpace, +}; +use std::marker::PhantomData; +use wasmer_runtime_core::{ + memory::MemoryType, + module::ModuleInfo, + structures::TypedIndex, + types::{ + GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, +}; + +fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { + match ty { + Type::I32 => intrinsics.i32_ptr_ty, + Type::I64 => intrinsics.i64_ptr_ty, + Type::F32 => intrinsics.f32_ptr_ty, + Type::F64 => intrinsics.f64_ptr_ty, + } +} + +pub struct Intrinsics { + pub ctlz_i32: FunctionValue, + pub ctlz_i64: FunctionValue, + + pub cttz_i32: FunctionValue, + pub cttz_i64: FunctionValue, + + pub ctpop_i32: FunctionValue, + pub ctpop_i64: FunctionValue, + + pub sqrt_f32: FunctionValue, + pub sqrt_f64: FunctionValue, + + pub minimum_f32: FunctionValue, + pub minimum_f64: FunctionValue, + + pub maximum_f32: FunctionValue, + pub maximum_f64: FunctionValue, + + pub ceil_f32: FunctionValue, + pub ceil_f64: FunctionValue, + + pub floor_f32: FunctionValue, + pub floor_f64: FunctionValue, + + pub trunc_f32: FunctionValue, + pub trunc_f64: FunctionValue, + + pub nearbyint_f32: FunctionValue, + pub nearbyint_f64: FunctionValue, + + pub fabs_f32: FunctionValue, + pub fabs_f64: FunctionValue, + + pub copysign_f32: FunctionValue, + pub copysign_f64: FunctionValue, + + pub expect_i1: FunctionValue, + pub trap: FunctionValue, + + pub void_ty: VoidType, + pub i1_ty: IntType, + pub i8_ty: IntType, + pub i16_ty: IntType, + pub i32_ty: IntType, + pub i64_ty: IntType, + pub f32_ty: FloatType, + pub f64_ty: FloatType, + + pub i8_ptr_ty: PointerType, + pub i16_ptr_ty: PointerType, + pub i32_ptr_ty: PointerType, + pub i64_ptr_ty: PointerType, + pub f32_ptr_ty: PointerType, + pub f64_ptr_ty: PointerType, + + pub anyfunc_ty: StructType, + + pub i1_zero: IntValue, + pub i32_zero: IntValue, + pub i64_zero: IntValue, + pub f32_zero: FloatValue, + pub f64_zero: FloatValue, + + pub trap_unreachable: BasicValueEnum, + pub trap_call_indirect_sig: BasicValueEnum, + pub trap_call_indirect_oob: BasicValueEnum, + pub trap_memory_oob: BasicValueEnum, + pub trap_illegal_arithmetic: BasicValueEnum, + + // VM intrinsics. + pub memory_grow_dynamic_local: FunctionValue, + pub memory_grow_static_local: FunctionValue, + pub memory_grow_shared_local: FunctionValue, + pub memory_grow_dynamic_import: FunctionValue, + pub memory_grow_static_import: FunctionValue, + pub memory_grow_shared_import: FunctionValue, + + pub memory_size_dynamic_local: FunctionValue, + pub memory_size_static_local: FunctionValue, + pub memory_size_shared_local: FunctionValue, + pub memory_size_dynamic_import: FunctionValue, + pub memory_size_static_import: FunctionValue, + pub memory_size_shared_import: FunctionValue, + + pub throw_trap: FunctionValue, + + pub ctx_ptr_ty: PointerType, +} + +impl Intrinsics { + pub fn declare(module: &Module, context: &Context) -> Self { + let void_ty = context.void_type(); + let i1_ty = context.bool_type(); + let i8_ty = context.i8_type(); + let i16_ty = context.i16_type(); + let i32_ty = context.i32_type(); + let i64_ty = context.i64_type(); + let f32_ty = context.f32_type(); + let f64_ty = context.f64_type(); + + let i8_ptr_ty = i8_ty.ptr_type(AddressSpace::Generic); + let i16_ptr_ty = i16_ty.ptr_type(AddressSpace::Generic); + let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic); + let i64_ptr_ty = i64_ty.ptr_type(AddressSpace::Generic); + let f32_ptr_ty = f32_ty.ptr_type(AddressSpace::Generic); + let f64_ptr_ty = f64_ty.ptr_type(AddressSpace::Generic); + + let i1_zero = i1_ty.const_int(0, false); + let i32_zero = i32_ty.const_int(0, false); + let i64_zero = i64_ty.const_int(0, false); + let f32_zero = f32_ty.const_float(0.0); + let f64_zero = f64_ty.const_float(0.0); + + let i1_ty_basic = i1_ty.as_basic_type_enum(); + let i32_ty_basic = i32_ty.as_basic_type_enum(); + let i64_ty_basic = i64_ty.as_basic_type_enum(); + let f32_ty_basic = f32_ty.as_basic_type_enum(); + let f64_ty_basic = f64_ty.as_basic_type_enum(); + let i8_ptr_ty_basic = i8_ptr_ty.as_basic_type_enum(); + + let ctx_ty = context.opaque_struct_type("ctx"); + let ctx_ptr_ty = ctx_ty.ptr_type(AddressSpace::Generic); + + let local_memory_ty = + context.struct_type(&[i8_ptr_ty_basic, i64_ty_basic, i8_ptr_ty_basic], false); + let local_table_ty = local_memory_ty; + let local_global_ty = i64_ty; + let imported_func_ty = + context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false); + let sigindex_ty = i32_ty; + let local_function_ty = i8_ptr_ty; + + let anyfunc_ty = context.struct_type( + &[ + i8_ptr_ty_basic, + ctx_ptr_ty.as_basic_type_enum(), + sigindex_ty.as_basic_type_enum(), + ], + false, + ); + + ctx_ty.set_body( + &[ + local_memory_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_table_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_global_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_memory_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_table_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_global_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + imported_func_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + sigindex_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_function_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + ], + false, + ); + + let ret_i32_take_i32_i1 = i32_ty.fn_type(&[i32_ty_basic, i1_ty_basic], false); + let ret_i64_take_i64_i1 = i64_ty.fn_type(&[i64_ty_basic, i1_ty_basic], false); + + let ret_i32_take_i32 = i32_ty.fn_type(&[i32_ty_basic], false); + let ret_i64_take_i64 = i64_ty.fn_type(&[i64_ty_basic], false); + + let ret_f32_take_f32 = f32_ty.fn_type(&[f32_ty_basic], false); + let ret_f64_take_f64 = f64_ty.fn_type(&[f64_ty_basic], false); + + let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false); + let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false); + + let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type( + &[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic], + false, + ); + let ret_i32_take_ctx_i32 = + i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false); + + let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false); + + Self { + ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), + ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), + + cttz_i32: module.add_function("llvm.cttz.i32", ret_i32_take_i32_i1, None), + cttz_i64: module.add_function("llvm.cttz.i64", ret_i64_take_i64_i1, None), + + ctpop_i32: module.add_function("llvm.ctpop.i32", ret_i32_take_i32, None), + ctpop_i64: module.add_function("llvm.ctpop.i64", ret_i64_take_i64, None), + + sqrt_f32: module.add_function("llvm.sqrt.f32", ret_f32_take_f32, None), + sqrt_f64: module.add_function("llvm.sqrt.f64", ret_f64_take_f64, None), + + minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None), + minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None), + + maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None), + maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None), + + ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None), + ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None), + + floor_f32: module.add_function("llvm.floor.f32", ret_f32_take_f32, None), + floor_f64: module.add_function("llvm.floor.f64", ret_f64_take_f64, None), + + trunc_f32: module.add_function("llvm.trunc.f32", ret_f32_take_f32, None), + trunc_f64: module.add_function("llvm.trunc.f64", ret_f64_take_f64, None), + + nearbyint_f32: module.add_function("llvm.nearbyint.f32", ret_f32_take_f32, None), + nearbyint_f64: module.add_function("llvm.nearbyint.f64", ret_f64_take_f64, None), + + fabs_f32: module.add_function("llvm.fabs.f32", ret_f32_take_f32, None), + fabs_f64: module.add_function("llvm.fabs.f64", ret_f64_take_f64, None), + + copysign_f32: module.add_function("llvm.copysign.f32", ret_f32_take_f32_f32, None), + copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None), + + expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None), + trap: module.add_function("llvm.trap", void_ty.fn_type(&[], false), None), + + void_ty, + i1_ty, + i8_ty, + i16_ty, + i32_ty, + i64_ty, + f32_ty, + f64_ty, + + i8_ptr_ty, + i16_ptr_ty, + i32_ptr_ty, + i64_ptr_ty, + f32_ptr_ty, + f64_ptr_ty, + + anyfunc_ty, + + i1_zero, + i32_zero, + i64_zero, + f32_zero, + f64_zero, + + trap_unreachable: i32_zero.as_basic_value_enum(), + trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(), + trap_call_indirect_oob: i32_ty.const_int(3, false).as_basic_value_enum(), + trap_memory_oob: i32_ty.const_int(2, false).as_basic_value_enum(), + trap_illegal_arithmetic: i32_ty.const_int(4, false).as_basic_value_enum(), + + // VM intrinsics. + memory_grow_dynamic_local: module.add_function( + "vm.memory.grow.dynamic.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_static_local: module.add_function( + "vm.memory.grow.static.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_shared_local: module.add_function( + "vm.memory.grow.shared.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_dynamic_import: module.add_function( + "vm.memory.grow.dynamic.import", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_static_import: module.add_function( + "vm.memory.grow.static.import", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_shared_import: module.add_function( + "vm.memory.grow.shared.import", + ret_i32_take_ctx_i32_i32, + None, + ), + + memory_size_dynamic_local: module.add_function( + "vm.memory.size.dynamic.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_static_local: module.add_function( + "vm.memory.size.static.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_shared_local: module.add_function( + "vm.memory.size.shared.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_dynamic_import: module.add_function( + "vm.memory.size.dynamic.import", + ret_i32_take_ctx_i32, + None, + ), + memory_size_static_import: module.add_function( + "vm.memory.size.static.import", + ret_i32_take_ctx_i32, + None, + ), + memory_size_shared_import: module.add_function( + "vm.memory.size.shared.import", + ret_i32_take_ctx_i32, + None, + ), + throw_trap: module.add_function( + "vm.exception.trap", + void_ty.fn_type(&[i32_ty_basic], false), + None, + ), + 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_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)] +pub enum MemoryCache { + /// The memory moves around. + Dynamic { + ptr_to_base_ptr: PointerValue, + ptr_to_bounds: PointerValue, + }, + /// The memory is always in the same place. + Static { + base_ptr: PointerValue, + bounds: IntValue, + }, +} + +struct TableCache { + ptr_to_base_ptr: PointerValue, + ptr_to_bounds: PointerValue, +} + +#[derive(Clone, Copy)] +pub enum GlobalCache { + Mut { ptr_to_value: PointerValue }, + Const { value: BasicValueEnum }, +} + +struct ImportedFuncCache { + func_ptr: PointerValue, + ctx_ptr: PointerValue, +} + +pub struct CtxType<'a> { + ctx_ptr_value: PointerValue, + + builder: &'a Builder, + intrinsics: &'a Intrinsics, + info: &'a ModuleInfo, + cache_builder: Builder, + + cached_memories: HashMap, + cached_tables: HashMap, + cached_sigindices: HashMap, + cached_globals: HashMap, + cached_imported_functions: HashMap, + + _phantom: PhantomData<&'a FunctionValue>, +} + +impl<'a> CtxType<'a> { + pub fn basic(&self) -> BasicValueEnum { + self.ctx_ptr_value.as_basic_value_enum() + } + + pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache { + let (cached_memories, info, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_memories, + self.info, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + *cached_memories.entry(index).or_insert_with(|| { + let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 0, "memory_array_ptr_ptr") + }, + local_mem_index.index() as u64, + info.memories[local_mem_index].memory_type(), + ), + LocalOrImport::Import(import_mem_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 3, "memory_array_ptr_ptr") + }, + import_mem_index.index() as u64, + info.imported_memories[import_mem_index].1.memory_type(), + ), + }; + + let memory_array_ptr = cache_builder + .build_load(memory_array_ptr_ptr, "memory_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let memory_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep( + memory_array_ptr, + &[const_index], + "memory_ptr_ptr", + ) + }; + let memory_ptr = cache_builder + .build_load(memory_ptr_ptr, "memory_ptr") + .into_pointer_value(); + + let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { + ( + cache_builder.build_struct_gep(memory_ptr, 0, "base_ptr"), + cache_builder.build_struct_gep(memory_ptr, 1, "bounds_ptr"), + ) + }; + + match memory_type { + MemoryType::Dynamic => MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + }, + MemoryType::Static | MemoryType::SharedStatic => MemoryCache::Static { + base_ptr: cache_builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(), + bounds: cache_builder + .build_load(ptr_to_bounds, "bounds") + .into_int_value(), + }, + } + }) + } + + pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { + let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_tables, + self.builder, + self.info, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + let TableCache { + ptr_to_base_ptr, + ptr_to_bounds, + } = *cached_tables.entry(index).or_insert_with(|| { + let (table_array_ptr_ptr, index) = match index.local_or_import(info) { + LocalOrImport::Local(local_table_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 1, "table_array_ptr_ptr") + }, + local_table_index.index() as u64, + ), + LocalOrImport::Import(import_table_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 4, "table_array_ptr_ptr") + }, + import_table_index.index() as u64, + ), + }; + + let table_array_ptr = cache_builder + .build_load(table_array_ptr_ptr, "table_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let table_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr") + }; + let table_ptr = cache_builder + .build_load(table_ptr_ptr, "table_ptr") + .into_pointer_value(); + + let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { + ( + cache_builder.build_struct_gep(table_ptr, 0, "base_ptr"), + cache_builder.build_struct_gep(table_ptr, 1, "bounds_ptr"), + ) + }; + + TableCache { + ptr_to_base_ptr, + ptr_to_bounds, + } + }); + + ( + builder + .build_load(ptr_to_base_ptr, "base_ptr") + .into_pointer_value(), + builder.build_load(ptr_to_bounds, "bounds").into_int_value(), + ) + } + + 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 + .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( + local_func_array_ptr, + &[self + .intrinsics + .i32_ty + .const_int(index.index() as u64, false)], + "local_func_ptr_ptr", + ) + }; + let local_func_ptr = self + .builder + .build_load(local_func_ptr_ptr, "local_func_ptr") + .into_pointer_value(); + self.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, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_sigindices, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + *cached_sigindices.entry(index).or_insert_with(|| { + let sigindex_array_ptr_ptr = unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 7, "sigindex_array_ptr_ptr") + }; + let sigindex_array_ptr = cache_builder + .build_load(sigindex_array_ptr_ptr, "sigindex_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false); + + let sigindex_ptr = unsafe { + cache_builder.build_in_bounds_gep( + sigindex_array_ptr, + &[const_index], + "sigindex_ptr", + ) + }; + + cache_builder + .build_load(sigindex_ptr, "sigindex") + .into_int_value() + }) + } + + pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { + let (cached_globals, ctx_ptr_value, info, intrinsics, cache_builder) = ( + &mut self.cached_globals, + self.ctx_ptr_value, + self.info, + self.intrinsics, + &self.cache_builder, + ); + + *cached_globals.entry(index).or_insert_with(|| { + let (globals_array_ptr_ptr, index, mutable, wasmer_ty) = + match index.local_or_import(info) { + LocalOrImport::Local(local_global_index) => { + let desc = info.globals[local_global_index].desc; + ( + unsafe { + cache_builder.build_struct_gep( + ctx_ptr_value, + 2, + "globals_array_ptr_ptr", + ) + }, + local_global_index.index() as u64, + desc.mutable, + desc.ty, + ) + } + LocalOrImport::Import(import_global_index) => { + let desc = info.imported_globals[import_global_index].1; + ( + unsafe { + cache_builder.build_struct_gep( + ctx_ptr_value, + 5, + "globals_array_ptr_ptr", + ) + }, + import_global_index.index() as u64, + desc.mutable, + desc.ty, + ) + } + }; + + let llvm_ptr_ty = type_to_llvm_ptr(intrinsics, wasmer_ty); + + let global_array_ptr = cache_builder + .build_load(globals_array_ptr_ptr, "global_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let global_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep( + global_array_ptr, + &[const_index], + "global_ptr_ptr", + ) + }; + let global_ptr = cache_builder + .build_load(global_ptr_ptr, "global_ptr") + .into_pointer_value(); + + let global_ptr_typed = + cache_builder.build_pointer_cast(global_ptr, llvm_ptr_ty, "global_ptr_typed"); + + if mutable { + GlobalCache::Mut { + ptr_to_value: global_ptr_typed, + } + } else { + GlobalCache::Const { + value: cache_builder.build_load(global_ptr_typed, "global_value"), + } + } + }) + } + + pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { + let (cached_imported_functions, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_imported_functions, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + let imported_func_cache = cached_imported_functions.entry(index).or_insert_with(|| { + let func_array_ptr_ptr = unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 6, "imported_func_array_ptr_ptr") + }; + let func_array_ptr = cache_builder + .build_load(func_array_ptr_ptr, "func_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false); + let imported_func_ptr = unsafe { + cache_builder.build_in_bounds_gep( + func_array_ptr, + &[const_index], + "imported_func_ptr", + ) + }; + let (func_ptr_ptr, ctx_ptr_ptr) = unsafe { + ( + cache_builder.build_struct_gep(imported_func_ptr, 0, "func_ptr_ptr"), + cache_builder.build_struct_gep(imported_func_ptr, 1, "ctx_ptr_ptr"), + ) + }; + + let func_ptr = cache_builder + .build_load(func_ptr_ptr, "func_ptr") + .into_pointer_value(); + let ctx_ptr = cache_builder + .build_load(ctx_ptr_ptr, "ctx_ptr") + .into_pointer_value(); + + ImportedFuncCache { func_ptr, ctx_ptr } + }); + + (imported_func_cache.func_ptr, imported_func_cache.ctx_ptr) + } +} diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs new file mode 100644 index 000000000..6ce8139c3 --- /dev/null +++ b/lib/llvm-backend/src/lib.rs @@ -0,0 +1,126 @@ +#![cfg_attr(nightly, feature(unwind_attributes))] + +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; +mod platform; +mod read_info; +mod state; +mod trampolines; + +pub struct LLVMCompiler { + _private: (), +} + +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, cache_gen) = backend::LLVMBackend::new(module, intrinsics); + + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), + + info, + }) + } + + unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + backend::LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; + + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), + + info, + }) + } +} + +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::{ + backend::RunnableModule, structures::TypedIndex, types::LocalFuncIndex, vm, + }; + // 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, _) = 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); + } +} diff --git a/lib/llvm-backend/src/platform/mod.rs b/lib/llvm-backend/src/platform/mod.rs new file mode 100644 index 000000000..01b81b022 --- /dev/null +++ b/lib/llvm-backend/src/platform/mod.rs @@ -0,0 +1,7 @@ +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use self::unix::*; + +#[cfg(target_family = "windows")] +compile_error!("windows not yet supported for the llvm-based compiler backend"); diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs new file mode 100644 index 000000000..12894f6bc --- /dev/null +++ b/lib/llvm-backend/src/platform/unix.rs @@ -0,0 +1,70 @@ +use libc::{c_void, siginfo_t}; +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. +/// +/// This is a pretty direct port of llvm's fde handling code: +/// https://llvm.org/doxygen/RTDyldMemoryManager_8cpp_source.html. +#[allow(clippy::cast_ptr_alignment)] +#[cfg(target_os = "macos")] +pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + unsafe fn process_fde(entry: *mut u8, visitor: extern "C" fn(*mut u8)) -> *mut u8 { + let mut p = entry; + let length = (p as *const u32).read_unaligned(); + p = p.add(4); + let offset = (p as *const u32).read_unaligned(); + + if offset != 0 { + visitor(entry); + } + p.add(length as usize) + } + + let mut p = addr; + let end = p.add(size); + + loop { + if p >= end { + break; + } + + p = process_fde(p, visitor); + } +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + visitor(addr); +} + +extern "C" { + #[cfg_attr(nightly, unwind(allowed))] + fn throw_trap(ty: i32) -> !; +} + +pub unsafe fn install_signal_handler() { + let sa = SigAction::new( + SigHandler::SigAction(signal_trap_handler), + SaFlags::SA_ONSTACK | SaFlags::SA_SIGINFO, + SigSet::empty(), + ); + 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, +) { + 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. + throw_trap(2); + } +} diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs new file mode 100644 index 000000000..5ebfa5868 --- /dev/null +++ b/lib/llvm-backend/src/read_info.rs @@ -0,0 +1,350 @@ +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 { .. } => {} + } + } +} + +pub fn 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, + }); + } + _ => { + return Err(BinaryReaderError { + message: "that type is not supported as a wasmer type", + offset: -1isize as usize, + }); + } + }) +} + +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/state.rs b/lib/llvm-backend/src/state.rs new file mode 100644 index 000000000..47da54a6d --- /dev/null +++ b/lib/llvm-backend/src/state.rs @@ -0,0 +1,244 @@ +use inkwell::{ + basic_block::BasicBlock, + values::{BasicValue, BasicValueEnum, PhiValue}, +}; +use smallvec::SmallVec; +use std::cell::Cell; +use wasmparser::BinaryReaderError; + +#[derive(Debug)] +pub enum ControlFrame { + Block { + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + }, + Loop { + body: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + }, + IfElse { + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + if_else_state: IfElseState, + }, +} + +#[derive(Debug)] +pub enum IfElseState { + If, + Else, +} + +impl ControlFrame { + pub fn code_after(&self) -> &BasicBlock { + match self { + ControlFrame::Block { ref next, .. } + | ControlFrame::Loop { ref next, .. } + | ControlFrame::IfElse { ref next, .. } => next, + } + } + + pub fn br_dest(&self) -> &BasicBlock { + match self { + ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next, + ControlFrame::Loop { ref body, .. } => body, + } + } + + pub fn phis(&self) -> &[PhiValue] { + match self { + ControlFrame::Block { ref phis, .. } + | ControlFrame::Loop { ref phis, .. } + | ControlFrame::IfElse { ref phis, .. } => phis.as_slice(), + } + } + + pub fn is_loop(&self) -> bool { + match self { + ControlFrame::Loop { .. } => true, + _ => false, + } + } +} + +#[derive(Debug)] +pub struct State { + stack: Vec, + control_stack: Vec, + value_counter: Cell, + + pub reachable: bool, +} + +impl State { + pub fn new() -> Self { + Self { + stack: vec![], + control_stack: vec![], + value_counter: Cell::new(0), + reachable: true, + } + } + + pub fn reset_stack(&mut self, frame: &ControlFrame) { + let stack_size_snapshot = match frame { + ControlFrame::Block { + stack_size_snapshot, + .. + } + | ControlFrame::Loop { + stack_size_snapshot, + .. + } + | ControlFrame::IfElse { + stack_size_snapshot, + .. + } => *stack_size_snapshot, + }; + self.stack.truncate(stack_size_snapshot); + } + + pub fn outermost_frame(&self) -> Result<&ControlFrame, BinaryReaderError> { + self.control_stack.get(0).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame, BinaryReaderError> { + let index = self.control_stack.len() - 1 - (depth as usize); + self.control_stack.get(index).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn frame_at_depth_mut( + &mut self, + depth: u32, + ) -> Result<&mut ControlFrame, BinaryReaderError> { + let index = self.control_stack.len() - 1 - (depth as usize); + self.control_stack.get_mut(index).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn pop_frame(&mut self) -> Result { + self.control_stack.pop().ok_or(BinaryReaderError { + message: "cannot pop from control stack", + offset: -1isize as usize, + }) + } + + pub fn var_name(&self) -> String { + let counter = self.value_counter.get(); + let s = format!("s{}", counter); + self.value_counter.set(counter + 1); + s + } + + pub fn push1(&mut self, value: T) { + self.stack.push(value.as_basic_value_enum()) + } + + pub fn pop1(&mut self) -> Result { + self.stack.pop().ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn pop2(&mut self) -> Result<(BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2)) + } + + pub fn pop3( + &mut self, + ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v3 = self.pop1()?; + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2, v3)) + } + + pub fn peek1(&self) -> Result { + self.stack + .get(self.stack.len() - 1) + .ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + .map(|v| *v) + } + + pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> { + self.stack + .get(self.stack.len() - n..) + .ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn popn_save(&mut self, n: usize) -> Result, BinaryReaderError> { + let v = self.peekn(n)?.to_vec(); + self.popn(n)?; + Ok(v) + } + + pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> { + if self.stack.len() < n { + return Err(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }); + } + + let new_len = self.stack.len() - n; + self.stack.truncate(new_len); + Ok(()) + } + + pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + self.control_stack.push(ControlFrame::Block { + next, + phis, + stack_size_snapshot: self.stack.len(), + }); + } + + pub fn push_loop(&mut self, body: BasicBlock, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + self.control_stack.push(ControlFrame::Loop { + body, + next, + phis, + stack_size_snapshot: self.stack.len(), + }); + } + + pub fn push_if( + &mut self, + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + ) { + self.control_stack.push(ControlFrame::IfElse { + if_then, + if_else, + next, + phis, + stack_size_snapshot: self.stack.len(), + if_else_state: IfElseState::If, + }); + } +} diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs new file mode 100644 index 000000000..23677618b --- /dev/null +++ b/lib/llvm-backend/src/trampolines.rs @@ -0,0 +1,111 @@ +use crate::intrinsics::Intrinsics; +use inkwell::{ + builder::Builder, + context::Context, + module::{Linkage, Module}, + types::{BasicType, FunctionType}, + values::FunctionValue, + AddressSpace, +}; +use wasmer_runtime_core::{ + module::ModuleInfo, + structures::{SliceMap, TypedIndex}, + types::{FuncSig, SigIndex, Type}, +}; + +pub fn generate_trampolines( + info: &ModuleInfo, + signatures: &SliceMap, + module: &Module, + context: &Context, + builder: &Builder, + intrinsics: &Intrinsics, +) { + for (sig_index, sig) in info.signatures.iter() { + let func_type = signatures[sig_index]; + + let trampoline_sig = intrinsics.void_ty.fn_type( + &[ + intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr + func_type + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), // func ptr + intrinsics.i64_ptr_ty.as_basic_type_enum(), // args ptr + intrinsics.i64_ptr_ty.as_basic_type_enum(), // returns ptr + ], + false, + ); + + let trampoline_func = module.add_function( + &format!("trmp{}", sig_index.index()), + trampoline_sig, + Some(Linkage::External), + ); + + generate_trampoline(trampoline_func, sig, context, builder, intrinsics); + } +} + +fn generate_trampoline( + trampoline_func: FunctionValue, + func_sig: &FuncSig, + context: &Context, + builder: &Builder, + intrinsics: &Intrinsics, +) { + let entry_block = context.append_basic_block(&trampoline_func, "entry"); + builder.position_at_end(&entry_block); + + let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice() + { + &[vmctx_ptr, func_ptr, args_ptr, returns_ptr] => ( + vmctx_ptr, + func_ptr.into_pointer_value(), + args_ptr.into_pointer_value(), + returns_ptr.into_pointer_value(), + ), + _ => unimplemented!(), + }; + + let cast_ptr_ty = |wasmer_ty| match wasmer_ty { + Type::I32 => intrinsics.i32_ptr_ty, + Type::I64 => intrinsics.i64_ptr_ty, + Type::F32 => intrinsics.f32_ptr_ty, + Type::F64 => intrinsics.f64_ptr_ty, + }; + + let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1); + args_vec.push(vmctx_ptr); + + for (i, param_ty) in func_sig.params().iter().enumerate() { + let index = intrinsics.i32_ty.const_int(i as _, false); + let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") }; + + let casted_pointer_type = cast_ptr_ty(*param_ty); + + let typed_item_pointer = + builder.build_pointer_cast(item_pointer, casted_pointer_type, "typed_arg_pointer"); + + let arg = builder.build_load(typed_item_pointer, "arg"); + args_vec.push(arg); + } + + let call_site = builder.build_call(func_ptr, &args_vec, "call"); + + match func_sig.returns() { + &[] => {} + &[one_ret] => { + let ret_ptr_type = cast_ptr_ty(one_ret); + + let typed_ret_ptr = + builder.build_pointer_cast(returns_ptr, ret_ptr_type, "typed_ret_ptr"); + builder.build_store( + typed_ret_ptr, + call_site.try_as_basic_value().left().unwrap(), + ); + } + _ => unimplemented!("multi-value returns"), + } + + builder.build_return(None); +} diff --git a/lib/runtime-abi/Cargo.toml b/lib/runtime-abi/Cargo.toml new file mode 100644 index 000000000..459c08de2 --- /dev/null +++ b/lib/runtime-abi/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "wasmer-runtime-abi" +version = "0.3.0" +description = "Wasmer runtime core library" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[dependencies] +libc = "0.2.50" +wasmer-runtime-core = { path = "../runtime-core" } +hashbrown = "0.1" +failure = "0.1" +tar = "0.4" +wasmparser = "0.23.0" +zstd = "0.4" + +[target.'cfg(unix)'.dependencies.zbox] +git = "https://github.com/wasmerio/zbox" +branch = "bundle-libsodium" +features = ["libsodium-bundled"] + +[dev-dependencies] +tempdir = "0.3" + +[features] +debug = [] diff --git a/lib/runtime-abi/README.md b/lib/runtime-abi/README.md new file mode 100644 index 000000000..fe906c2dd --- /dev/null +++ b/lib/runtime-abi/README.md @@ -0,0 +1,23 @@ +# runtime-abi + +This crate has ABI functions (like syscalls) and extensions to the runtime for enabling ABIs (e.g. virtual filesystem). + +## Virtual Filesystem (experimental) + +The virtual filesystem allows the runtime to read bundled wasm data as if they were files. Data that is stored in a +custom section compressed with [zstd][1] compression and archived with [tar][2] will be exposed as files and mounted +in the `/` root. + +The only current supported operation is the `read` syscall. + +The virtual filesystem is not enabled by default. Build with `--features vfs` to use it. + +[Zbox][3] is a virtual filesystem that depends on [libsodium][4]. See [installation instructions][5] for libsodium here. One can +statically link libsodium with the [instructions][6] on Zbox's readme. + +[1]: https://facebook.github.io/zstd/ +[2]: https://www.gnu.org/software/tar/ +[3]: https://zbox.io/ +[4]: https://download.libsodium.org/doc/ +[5]: https://download.libsodium.org/doc/installation +[6]: https://github.com/zboxfs/zbox#static-linking-with-libsodium diff --git a/lib/runtime-abi/src/lib.rs b/lib/runtime-abi/src/lib.rs new file mode 100644 index 000000000..2f0ee8c51 --- /dev/null +++ b/lib/runtime-abi/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(not(target_os = "windows"))] +#[macro_use] +extern crate failure; + +#[cfg(not(target_os = "windows"))] +pub mod vfs; diff --git a/lib/runtime-abi/src/vfs/device_file.rs b/lib/runtime-abi/src/vfs/device_file.rs new file mode 100644 index 000000000..3013d32b2 --- /dev/null +++ b/lib/runtime-abi/src/vfs/device_file.rs @@ -0,0 +1,111 @@ +use crate::vfs::file_like::{FileLike, Metadata}; +use failure::Error; +use std::io; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl FileLike for Stdin { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdin"); + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stdin { + fn write(&mut self, _buf: &[u8]) -> Result { + unimplemented!() + } + + fn flush(&mut self) -> Result<(), io::Error> { + unimplemented!() + } +} + +impl io::Seek for Stdin { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} + +impl FileLike for Stdout { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stdout"); + } +} + +impl io::Read for Stdout { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> Result { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle.write(buf) + } + + fn flush(&mut self) -> Result<(), io::Error> { + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle.flush() + } +} + +impl io::Seek for Stdout { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} + +impl FileLike for Stderr { + fn metadata(&self) -> Result { + unimplemented!() + } + + fn set_file_len(&mut self, _len: usize) -> Result<(), failure::Error> { + panic!("Cannot set length of stderr"); + } +} + +impl io::Read for Stderr { + fn read(&mut self, _buf: &mut [u8]) -> Result { + unimplemented!() + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> Result { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + handle.write(buf) + } + + fn flush(&mut self) -> Result<(), io::Error> { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + handle.flush() + } +} + +impl io::Seek for Stderr { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + unimplemented!() + } +} diff --git a/lib/runtime-abi/src/vfs/file_like.rs b/lib/runtime-abi/src/vfs/file_like.rs new file mode 100644 index 000000000..4b9b8771c --- /dev/null +++ b/lib/runtime-abi/src/vfs/file_like.rs @@ -0,0 +1,21 @@ +pub type Fd = isize; + +#[derive(Debug)] +pub struct Metadata { + pub len: usize, + pub is_file: bool, +} + +pub trait FileLike: std::io::Write + std::io::Read + std::io::Seek { + // get metadata + fn metadata(&self) -> Result; + + // write + // fn write_file(&mut self, buf: &[u8]) -> Result; + + // read + // fn read_file(&mut self, buf: &mut [u8]) -> Result; + + // set_file_len + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error>; +} diff --git a/lib/runtime-abi/src/vfs/mod.rs b/lib/runtime-abi/src/vfs/mod.rs new file mode 100644 index 000000000..0a152b5b3 --- /dev/null +++ b/lib/runtime-abi/src/vfs/mod.rs @@ -0,0 +1,5 @@ +pub mod device_file; +pub mod file_like; +pub mod vfs; +pub mod vfs_header; +pub mod virtual_file; diff --git a/lib/runtime-abi/src/vfs/vfs.rs b/lib/runtime-abi/src/vfs/vfs.rs new file mode 100644 index 000000000..5cb7a0997 --- /dev/null +++ b/lib/runtime-abi/src/vfs/vfs.rs @@ -0,0 +1,170 @@ +use crate::vfs::file_like::FileLike; +use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType}; +use crate::vfs::virtual_file::VirtualFile; +use hashbrown::HashMap; +use std::cell::RefCell; +use std::io; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use tar::EntryType; +use zbox::{init_env, OpenOptions, Repo, RepoOpener}; + +pub struct Vfs { + repo: Repo, + device_files: HashMap>>, +} + +impl Vfs { + /// Like `VfsBacking::from_tar_bytes` except it also decompresses from the zstd format. + pub fn from_tar_zstd_bytes(tar_bytes: Reader) -> Result { + let result = zstd::decode_all(tar_bytes); + let decompressed_data = result.unwrap(); + Self::from_tar_bytes(&decompressed_data[..]) + } + + /// Match on the type of the compressed-archive and select the correct unpack method + pub fn from_compressed_bytes(compressed_data_slice: &[u8]) -> Result { + let data_bytes = &compressed_data_slice[4..]; + match header_from_bytes(compressed_data_slice)? { + (_, CompressionType::ZSTD, ArchiveType::TAR) => Self::from_tar_zstd_bytes(data_bytes), + (_, CompressionType::NONE, ArchiveType::TAR) => Self::from_tar_bytes(data_bytes), + } + } + + /// Create a vfs from raw bytes in tar format + pub fn from_tar_bytes(tar_bytes: Reader) -> Result { + init_env(); + let mut repo = RepoOpener::new() + .create(true) + .open("mem://wasmer_fs", "") + .unwrap(); + let _errors = tar::Archive::new(tar_bytes) + .entries()? + .map(|entry| { + let mut entry: tar::Entry = entry?; + let path = entry.path()?; + let path = convert_to_absolute_path(path); + let _result = match (entry.header().entry_type(), path.parent()) { + (EntryType::Regular, Some(parent)) => { + if let Err(e) = repo.create_dir_all(parent) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + let mut file = repo.create_file(&path)?; + if entry.header().size().unwrap_or(0) > 0 { + io::copy(&mut entry, &mut file)?; + file.finish()?; + } + } + (EntryType::Directory, _) => { + if let Err(e) = repo.create_dir_all(path) { + if e == zbox::Error::AlreadyExists || e == zbox::Error::IsRoot { + } else { + return Err(VfsAggregateError::ZboxError(e)); + } + } else { + } + } + _ => return Err(VfsAggregateError::UnsupportedFileType), + }; + Ok(()) + }) + .collect::>>(); + + // let import_errors = errors.iter().filter_map(|e| e.err()).collect::>(); + + let vfs = Self { + repo, + device_files: HashMap::new(), + // import_errors: vec![], + }; + Ok(vfs) + } + + pub fn new() -> Result<(Self, Vec), failure::Error> { + init_env(); + let repo = RepoOpener::new() + .create(true) + .open("mem://wasmer_fs", "") + .unwrap(); + Ok(( + Vfs { + repo, + device_files: HashMap::new(), + }, + vec![], + )) + } + + pub fn open_file>(&mut self, path: P) -> Option>> { + init_env(); + let path = convert_to_absolute_path(path); + if let Ok(file) = OpenOptions::new().write(true).open(&mut self.repo, &path) { + Some(Rc::new(RefCell::new(VirtualFile::new(file)))) + } else if let Some(dev_file) = self.device_files.get(&path) { + Some(dev_file.clone()) + } else { + None + } + } + + pub fn make_dir>(&mut self, path: P) { + self.repo.create_dir_all(path).unwrap(); + } + + pub fn create_device_file>(&mut self, path: P, file: Rc>) { + self.device_files.insert(path.as_ref().to_path_buf(), file); + } +} + +fn convert_to_absolute_path>(path: P) -> PathBuf { + let path = path.as_ref(); + if path.is_relative() { + std::path::PathBuf::from("/").join(path) + } else { + path.to_path_buf() + } +} + +pub type Handle = i32; +#[derive(Debug, Fail)] +pub enum VfsError { + #[fail(display = "File with file descriptor \"{}\" does not exist.", _0)] + FileWithFileDescriptorNotExist(Handle), + #[fail(display = "File descriptor does not exist.")] + FileDescriptorNotExist(Handle), + #[fail(display = "Source file descriptor does not exist.")] + SourceFileDescriptorDoesNotExist, + #[fail(display = "Target file descriptor already exists.")] + TargetFileDescriptorAlreadyExists, + #[fail(display = "Could not get a mutable reference to the file because it is in use.")] + CouldNotGetMutableReferenceToFile, +} + +#[derive(Debug, Fail)] +pub enum VfsAggregateError { + #[fail(display = "Entry error.")] + EntryError(std::io::Error), + #[fail(display = "IO error.")] + IoError(std::io::Error), + #[fail(display = "Zbox error.")] + ZboxError(zbox::Error), + #[fail(display = "Unsupported file type.")] + UnsupportedFileType, +} + +impl std::convert::From for VfsAggregateError { + fn from(error: std::io::Error) -> VfsAggregateError { + VfsAggregateError::EntryError(error) + } +} + +impl std::convert::From for VfsAggregateError { + fn from(error: zbox::Error) -> VfsAggregateError { + VfsAggregateError::ZboxError(error) + } +} diff --git a/lib/runtime-abi/src/vfs/vfs_header.rs b/lib/runtime-abi/src/vfs/vfs_header.rs new file mode 100644 index 000000000..c90c658b0 --- /dev/null +++ b/lib/runtime-abi/src/vfs/vfs_header.rs @@ -0,0 +1,57 @@ +/// Represents the version of this header schema. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum HeaderVersion { + Version1 = 1, +} + +/// Represents the compression type of the file data. Only Zstd or no-compression is supported. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum CompressionType { + NONE = 0, + ZSTD = 1, +} + +/// Represents the type of archive. The only supported archive is the Tar format. +#[repr(u8)] +#[derive(Debug, PartialEq)] +pub enum ArchiveType { + TAR = 0, +} + +// extract the header data from bytes +pub fn header_from_bytes( + bytes: &[u8], +) -> Result<(HeaderVersion, CompressionType, ArchiveType), HeaderError> { + if let Some(bytes) = bytes.get(..4) { + let version = match bytes[0] { + 1 => HeaderVersion::Version1, + x => return Err(HeaderError::UnknownHeaderVersion(x)), + }; + let compression_type = match bytes[1] { + 0 => CompressionType::NONE, + 1 => CompressionType::ZSTD, + x => return Err(HeaderError::UnknownCompressionType(x)), + }; + let archive_type = match bytes[2] { + 0 => ArchiveType::TAR, + x => return Err(HeaderError::UnknownArchiveType(x)), + }; + Ok((version, compression_type, archive_type)) + } else { + Err(HeaderError::HeaderTooSmall) + } +} + +#[derive(Debug, Fail)] +pub enum HeaderError { + #[fail(display = "The version is not supported: \"{}\"", _0)] + UnknownHeaderVersion(u8), + #[fail(display = "The compression type is unknown: \"{}\"", _0)] + UnknownCompressionType(u8), + #[fail(display = "The archive type is unknown: \"{}\"", _0)] + UnknownArchiveType(u8), + #[fail(display = "The header is too small.")] + HeaderTooSmall, +} diff --git a/lib/runtime-abi/src/vfs/virtual_file.rs b/lib/runtime-abi/src/vfs/virtual_file.rs new file mode 100644 index 000000000..cd72231a1 --- /dev/null +++ b/lib/runtime-abi/src/vfs/virtual_file.rs @@ -0,0 +1,51 @@ +use crate::vfs::file_like::{FileLike, Metadata}; +use failure::Error; +use std::io; + +pub struct VirtualFile(zbox::File); + +impl VirtualFile { + pub fn new(file: zbox::File) -> Self { + VirtualFile(file) + } +} + +impl FileLike for VirtualFile { + fn metadata(&self) -> Result { + self.0 + .metadata() + .map(|m| Metadata { + len: m.content_len(), + is_file: m.is_file(), + }) + .map_err(|e: zbox::Error| e.into()) + } + + fn set_file_len(&mut self, len: usize) -> Result<(), failure::Error> { + self.0.set_len(len).map_err(|e| e.into()) + } +} + +impl io::Write for VirtualFile { + fn write(&mut self, buf: &[u8]) -> Result { + let result = self.0.write(buf)?; + self.0.finish().unwrap(); + Ok(result) + } + + fn flush(&mut self) -> Result<(), io::Error> { + self.0.flush() + } +} + +impl io::Read for VirtualFile { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.0.read(buf) + } +} + +impl io::Seek for VirtualFile { + fn seek(&mut self, pos: io::SeekFrom) -> Result { + self.0.seek(pos) + } +} diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 692c03cd7..881c37fcb 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,25 +1,30 @@ [package] name = "wasmer-runtime-c-api" -version = "0.1.4" -description = "Wasmer c-api library" +version = "0.3.0" +description = "Wasmer C API library" license = "MIT" authors = ["The Wasmer Engineering Team "] repository = "https://github.com/wasmerio/wasmer" edition = "2018" readme = "README.md" +[lib] +crate-type = ["cdylib", "rlib", "staticlib"] + [dependencies] -wasmer-runtime = { path = "../runtime", version = "0.1.2" } -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } libc = "0.2" -[lib] -crate-type = ["cdylib"] +[dependencies.wasmer-runtime] +path = "../runtime" +version = "0.3.0" -[build-dependencies] -cbindgen = { version = "0.7.1", optional = true } +[dependencies.wasmer-runtime-core] +path = "../runtime-core" +version = "0.3.0" [features] -generate-c-api-headers = ["cbindgen"] - +debug = ["wasmer-runtime/debug"] +llvm = ["wasmer-runtime/llvm"] +[build-dependencies] +cbindgen = "0.8" diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md index 02ba9a705..1e741cfd1 100644 --- a/lib/runtime-c-api/README.md +++ b/lib/runtime-c-api/README.md @@ -1,10 +1,132 @@ +

+ + Wasmer logo + +

+ +

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

+ # Wasmer Runtime C API -## Generating header files -Run `make capi` from wasmer project root directory +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). -## Running tests -The tests can be run via `cargo test`, E.g. `cargo test -p wasmer-runtime-c-api -- --nocapture` +This crate exposes a C and a C++ API for the Wasmer runtime. -*Running manually* -`cmake . && make && make test` from the `lib/runtime-c-api/tests` directory +# Usage + +The C and C++ header files can be found in the source tree of this +crate, respectively [`wasmer.h`][wasmer_h] and +[`wasmer.hh`][wasmer_hh]. They are automatically generated, and always +up-to-date in this repository. + +Here is a simple example to use the C API: + +```c +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the Wasm file bytes. + FILE *file = fopen("sum.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + // Prepare the imports. + wasmer_import_t imports[] = {}; + + // Instantiate! + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0); + + assert(instantiation_result == WASMER_OK); + + // Let's call a function. + // Start by preparing the arguments. + + // Value of argument #1 is `7i32`. + wasmer_value_t argument_one; + argument_one.tag = WASM_I32; + argument_one.value.I32 = 7; + + // Value of argument #2 is `8i32`. + wasmer_value_t argument_two; + argument_two.tag = WASM_I32; + argument_two.value.I32 = 8; + + // Prepare the arguments. + wasmer_value_t arguments[] = {argument_one, argument_two}; + + // Prepare the return value. + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + + // Call the `sum` function with the prepared arguments and the return value. + wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1); + + // Let's display the result. + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + + // `sum(7, 8) == 15`. + assert(results[0].value.I32 == 15); + assert(call_result == WASMER_OK); + + wasmer_instance_destroy(instance); + + return 0; +} +``` + +# Testing + +The tests can be run via `cargo test`, such as: + +```sh +$ cargo test -- --nocapture +``` + +To run tests manually, enter the `lib/runtime-c-api/tests` directory +and run the following commands: + +```sh +$ cmake . +$ make +$ make test +``` + + +# License + +Wasmer is primarily distributed under the terms of the [MIT +license][mit-license] ([LICENSE][license]). + + +[wasmer_h]: ./wasmer.h +[wasmer_hh]: ./wasmer.hh +[mit-license]: http://opensource.org/licenses/MIT +[license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs index f78c5cf69..1db19a197 100644 --- a/lib/runtime-c-api/build.rs +++ b/lib/runtime-c-api/build.rs @@ -1,39 +1,52 @@ -#[cfg(feature = "generate-c-api-headers")] extern crate cbindgen; -use std::env; - -static CAPI_ENV_VAR: &str = "WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS"; +use cbindgen::{Builder, Language}; +use std::{env, fs, path::PathBuf}; fn main() { - if env::var(CAPI_ENV_VAR).unwrap_or("0".to_string()) == "1" { - build(); - } -} - -#[cfg(feature = "generate-c-api-headers")] -fn build() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let mut crate_wasmer_header_file = PathBuf::from(&crate_dir); + crate_wasmer_header_file.push("wasmer"); - use cbindgen::Language; - cbindgen::Builder::new() + let out_dir = env::var("OUT_DIR").unwrap(); + let mut out_wasmer_header_file = PathBuf::from(&out_dir); + out_wasmer_header_file.push("wasmer"); + + // Generate the C bindings in the `OUT_DIR`. + out_wasmer_header_file.set_extension("h"); + Builder::new() .with_crate(crate_dir.clone()) .with_language(Language::C) .with_include_guard("WASMER_H") .generate() .expect("Unable to generate C bindings") - .write_to_file("wasmer.h"); + .write_to_file(out_wasmer_header_file.as_path()); - cbindgen::Builder::new() + // Generate the C++ bindings in the `OUT_DIR`. + out_wasmer_header_file.set_extension("hh"); + Builder::new() .with_crate(crate_dir) .with_language(Language::Cxx) .with_include_guard("WASMER_H") .generate() .expect("Unable to generate C++ bindings") - .write_to_file("wasmer.hh"); -} + .write_to_file(out_wasmer_header_file.as_path()); -#[cfg(not(feature = "generate-c-api-headers"))] -fn build() { - panic!("environment var set to generate wasmer c API headers but generate-c-api-headers feature not enabled") + // Copy the generated C bindings from `OUT_DIR` to + // `CARGO_MANIFEST_DIR`. + crate_wasmer_header_file.set_extension("h"); + out_wasmer_header_file.set_extension("h"); + fs::copy( + out_wasmer_header_file.as_path(), + crate_wasmer_header_file.as_path(), + ) + .expect("Unable to copy the generated C bindings"); + + // Copy the generated C++ bindings from `OUT_DIR` to + // `CARGO_MANIFEST_DIR`. + crate_wasmer_header_file.set_extension("h"); + crate_wasmer_header_file.set_extension("hh"); + out_wasmer_header_file.set_extension("hh"); + fs::copy(out_wasmer_header_file, crate_wasmer_header_file) + .expect("Unable to copy the generated C++ bindings"); } diff --git a/lib/runtime-c-api/src/error.rs b/lib/runtime-c-api/src/error.rs new file mode 100644 index 000000000..5980c79a9 --- /dev/null +++ b/lib/runtime-c-api/src/error.rs @@ -0,0 +1,102 @@ +//! Errors. + +use libc::{c_char, c_int}; +use std::cell::RefCell; +use std::error::Error; +use std::fmt::{self, Display, Formatter}; +use std::ptr; +use std::slice; + +thread_local! { + static LAST_ERROR: RefCell>> = RefCell::new(None); +} + +pub(crate) fn update_last_error(err: E) { + LAST_ERROR.with(|prev| { + *prev.borrow_mut() = Some(Box::new(err)); + }); +} + +/// Retrieve the most recent error, clearing it in the process. +pub(crate) fn take_last_error() -> Option> { + LAST_ERROR.with(|prev| prev.borrow_mut().take()) +} + +/// Gets the length in bytes of the last error. +/// This can be used to dynamically allocate a buffer with the correct number of +/// bytes needed to store a message. +/// +/// # Example +/// +/// ```c +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// ``` +#[no_mangle] +pub extern "C" fn wasmer_last_error_length() -> c_int { + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => err.to_string().len() as c_int + 1, + None => 0, + }) +} + +/// Stores the last error message into the provided buffer up to the given `length`. +/// The `length` parameter must be large enough to store the last error message. +/// +/// Returns the length of the string in bytes. +/// Returns `-1` if an error occurs. +/// +/// # Example +/// +/// ```c +/// int error_len = wasmer_last_error_length(); +/// char *error_str = malloc(error_len); +/// wasmer_last_error_message(error_str, error_len); +/// printf("Error str: `%s`\n", error_str); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: c_int) -> c_int { + if buffer.is_null() { + // buffer pointer is null + return -1; + } + + let last_error = match take_last_error() { + Some(err) => err, + None => return 0, + }; + + let error_message = last_error.to_string(); + + 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 + return -1; + } + + ptr::copy_nonoverlapping( + error_message.as_ptr(), + buffer.as_mut_ptr(), + error_message.len(), + ); + + // Add a trailing null so people using the string as a `char *` don't + // accidentally read into garbage. + buffer[error_message.len()] = 0; + + error_message.len() as c_int +} + +#[derive(Debug)] +pub(crate) struct CApiError { + pub(crate) msg: String, +} + +impl Display for CApiError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", &self.msg) + } +} + +impl Error for CApiError {} diff --git a/lib/runtime-c-api/src/export.rs b/lib/runtime-c-api/src/export.rs new file mode 100644 index 000000000..6bcae32f2 --- /dev/null +++ b/lib/runtime-c-api/src/export.rs @@ -0,0 +1,440 @@ +//! Wasm exports. + +use crate::{ + error::{update_last_error, CApiError}, + global::wasmer_global_t, + import::wasmer_import_func_t, + memory::wasmer_memory_t, + module::wasmer_module_t, + table::wasmer_table_t, + value::{wasmer_value, wasmer_value_t, wasmer_value_tag}, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t}; +use std::{ptr, slice}; +use wasmer_runtime::{Instance, Memory, Module, Value}; +use wasmer_runtime_core::{export::Export, module::ExportIndex}; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_exports_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_func_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptors_t; + +#[repr(C)] +#[derive(Clone, Copy)] +pub union wasmer_import_export_value { + pub func: *const wasmer_import_func_t, + pub table: *const wasmer_table_t, + pub memory: *const wasmer_memory_t, + pub global: *const wasmer_global_t, +} + +#[allow(non_camel_case_types)] +#[repr(u32)] +#[derive(Clone)] +pub enum wasmer_import_export_kind { + WASM_FUNCTION, + WASM_GLOBAL, + WASM_MEMORY, + WASM_TABLE, +} + +/// Gets export descriptors for the given module +/// +/// The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors( + module: *const wasmer_module_t, + export_descriptors: *mut *mut wasmer_export_descriptors_t, +) { + let module = &*(module as *const Module); + + let named_export_descriptors: Box = Box::new(NamedExportDescriptors( + module.info().exports.iter().map(|e| e.into()).collect(), + )); + *export_descriptors = + Box::into_raw(named_export_descriptors) as *mut wasmer_export_descriptors_t; +} + +pub struct NamedExportDescriptors(Vec); + +/// Frees the memory for the given export descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_export_descriptors_destroy( + export_descriptors: *mut wasmer_export_descriptors_t, +) { + if !export_descriptors.is_null() { + unsafe { Box::from_raw(export_descriptors as *mut NamedExportDescriptors) }; + } +} + +/// Gets the length of the export descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors_len( + exports: *mut wasmer_export_descriptors_t, +) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedExportDescriptors)).0.len() as c_int +} + +/// Gets export descriptor by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors_get( + export_descriptors: *mut wasmer_export_descriptors_t, + idx: c_int, +) -> *mut wasmer_export_descriptor_t { + if export_descriptors.is_null() { + return ptr::null_mut(); + } + let named_export_descriptors = &mut *(export_descriptors as *mut NamedExportDescriptors); + &mut (*named_export_descriptors).0[idx as usize] as *mut NamedExportDescriptor + as *mut wasmer_export_descriptor_t +} + +/// Gets name for the export descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_descriptor_name( + export_descriptor: *mut wasmer_export_descriptor_t, +) -> wasmer_byte_array { + let named_export_descriptor = &*(export_descriptor as *mut NamedExportDescriptor); + wasmer_byte_array { + bytes: named_export_descriptor.name.as_ptr(), + bytes_len: named_export_descriptor.name.len() as u32, + } +} + +/// Gets export descriptor kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_descriptor_kind( + export: *mut wasmer_export_descriptor_t, +) -> wasmer_import_export_kind { + let named_export_descriptor = &*(export as *mut NamedExportDescriptor); + named_export_descriptor.kind.clone() +} + +pub(crate) struct NamedExports(pub Vec); + +/// Frees the memory for the given exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) { + if !exports.is_null() { + unsafe { Box::from_raw(exports as *mut NamedExports) }; + } +} + +/// Gets the length of the exports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_len(exports: *mut wasmer_exports_t) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedExports)).0.len() as c_int +} + +/// Gets wasmer_export by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_exports_get( + exports: *mut wasmer_exports_t, + idx: c_int, +) -> *mut wasmer_export_t { + if exports.is_null() { + return ptr::null_mut(); + } + let named_exports = &mut *(exports as *mut NamedExports); + &mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t +} + +/// Gets wasmer_export kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_kind( + export: *mut wasmer_export_t, +) -> wasmer_import_export_kind { + let named_export = &*(export as *mut NamedExport); + match named_export.export { + Export::Table(_) => wasmer_import_export_kind::WASM_TABLE, + Export::Function { .. } => wasmer_import_export_kind::WASM_FUNCTION, + Export::Global(_) => wasmer_import_export_kind::WASM_GLOBAL, + Export::Memory(_) => wasmer_import_export_kind::WASM_MEMORY, + } +} + +/// Sets the result parameter to the arity of the params of the wasmer_export_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_func_params_arity( + func: *const wasmer_export_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + *result = signature.params().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_params_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the params buffer to the parameter types of the given wasmer_export_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_func_params( + func: *const wasmer_export_func_t, + params: *mut wasmer_value_tag, + params_len: c_int, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + let params: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(params, params_len as usize); + for (i, item) in signature.params().iter().enumerate() { + params[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_params".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the returns buffer to the parameter types of the given wasmer_export_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_func_returns( + func: *const wasmer_export_func_t, + returns: *mut wasmer_value_tag, + returns_len: c_int, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + let returns: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(returns, returns_len as usize); + for (i, item) in signature.returns().iter().enumerate() { + returns[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_returns".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the result parameter to the arity of the returns of the wasmer_export_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_func_returns_arity( + func: *const wasmer_export_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + *result = signature.returns().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_results_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Gets export func from export +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_to_func( + export: *const wasmer_export_t, +) -> *const wasmer_export_func_t { + export as *const wasmer_export_func_t +} + +/// Gets a memory pointer from an export pointer. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_to_memory( + export: *const wasmer_export_t, + memory: *mut *mut wasmer_memory_t, +) -> wasmer_result_t { + let named_export = &*(export as *const NamedExport); + let export = &named_export.export; + + if let Export::Memory(exported_memory) = export { + *memory = exported_memory as *const Memory as *mut wasmer_memory_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "cannot cast the `wasmer_export_t` pointer to a `wasmer_memory_t` \ + pointer because it does not represent a memory export." + .to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Gets name from wasmer_export +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_name(export: *mut wasmer_export_t) -> wasmer_byte_array { + let named_export = &*(export as *mut NamedExport); + wasmer_byte_array { + bytes: named_export.name.as_ptr(), + bytes_len: named_export.name.len() as u32, + } +} + +/// Calls a `func` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_func_call( + func: *const wasmer_export_func_t, + params: *const wasmer_value_t, + params_len: c_int, + results: *mut wasmer_value_t, + results_len: c_int, +) -> wasmer_result_t { + if func.is_null() { + update_last_error(CApiError { + msg: "func ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if params.is_null() { + update_last_error(CApiError { + msg: "params ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + + let named_export = &*(func as *mut NamedExport); + + let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); + + let instance = &*named_export.instance; + let result = instance.call(&named_export.name, ¶ms[..]); + match result { + Ok(results_vec) => { + if !results_vec.is_empty() { + let ret = match results_vec[0] { + Value::I32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32: x }, + }, + Value::I64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64: x }, + }, + Value::F32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32: x }, + }, + Value::F64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64: x }, + }, + }; + results[0] = ret; + } + wasmer_result_t::WASMER_OK + } + Err(err) => { + update_last_error(err); + wasmer_result_t::WASMER_ERROR + } + } +} + +impl From<(&std::string::String, &ExportIndex)> for NamedExportDescriptor { + fn from((name, export_index): (&String, &ExportIndex)) -> Self { + let kind = match *export_index { + ExportIndex::Memory(_) => wasmer_import_export_kind::WASM_MEMORY, + ExportIndex::Global(_) => wasmer_import_export_kind::WASM_GLOBAL, + ExportIndex::Table(_) => wasmer_import_export_kind::WASM_TABLE, + ExportIndex::Func(_) => wasmer_import_export_kind::WASM_FUNCTION, + }; + NamedExportDescriptor { + name: name.clone(), + kind, + } + } +} + +pub(crate) struct NamedExport { + pub(crate) name: String, + pub(crate) export: Export, + pub(crate) instance: *mut Instance, +} + +pub(crate) struct NamedExportDescriptor { + name: String, + kind: wasmer_import_export_kind, +} diff --git a/lib/runtime-c-api/src/global.rs b/lib/runtime-c-api/src/global.rs new file mode 100644 index 000000000..2e4ea645f --- /dev/null +++ b/lib/runtime-c-api/src/global.rs @@ -0,0 +1,70 @@ +//! Wasm global. + +use crate::value::{wasmer_value_t, wasmer_value_tag}; +use wasmer_runtime::Global; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_descriptor_t { + mutable: bool, + kind: wasmer_value_tag, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_global_t; + +/// Creates a new Global and returns a pointer to it. +/// The caller owns the object and should call `wasmer_global_destroy` to free it. +#[no_mangle] +pub unsafe extern "C" fn wasmer_global_new( + value: wasmer_value_t, + mutable: bool, +) -> *mut wasmer_global_t { + let global = if mutable { + Global::new_mutable(value.into()) + } else { + Global::new(value.into()) + }; + Box::into_raw(Box::new(global)) as *mut wasmer_global_t +} + +/// Gets the value stored by the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_get(global: *mut wasmer_global_t) -> wasmer_value_t { + let global = unsafe { &*(global as *mut Global) }; + let value: wasmer_value_t = global.get().into(); + value +} + +/// Sets the value stored by the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_set(global: *mut wasmer_global_t, value: wasmer_value_t) { + let global = unsafe { &*(global as *mut Global) }; + global.set(value.into()); +} + +/// Returns a descriptor (type, mutability) of the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_get_descriptor( + global: *mut wasmer_global_t, +) -> wasmer_global_descriptor_t { + let global = unsafe { &*(global as *mut Global) }; + let descriptor = global.descriptor(); + wasmer_global_descriptor_t { + mutable: descriptor.mutable, + kind: descriptor.ty.into(), + } +} + +/// Frees memory for the given Global +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_global_destroy(global: *mut wasmer_global_t) { + if !global.is_null() { + unsafe { Box::from_raw(global as *mut Global) }; + } +} diff --git a/lib/runtime-c-api/src/import.rs b/lib/runtime-c-api/src/import.rs new file mode 100644 index 000000000..cc3598078 --- /dev/null +++ b/lib/runtime-c-api/src/import.rs @@ -0,0 +1,358 @@ +//! Wasm imports. + +use crate::{ + error::{update_last_error, CApiError}, + export::{wasmer_import_export_kind, wasmer_import_export_value}, + module::wasmer_module_t, + value::wasmer_value_tag, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t}; +use std::{ffi::c_void, ptr, slice, sync::Arc}; +use wasmer_runtime::Module; +use wasmer_runtime_core::{ + export::{Context, Export, FuncPointer}, + module::ImportName, + types::{FuncSig, Type}, +}; + +#[repr(C)] +pub struct wasmer_import_t { + pub module_name: wasmer_byte_array, + pub import_name: wasmer_byte_array, + pub tag: wasmer_import_export_kind, + pub value: wasmer_import_export_value, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_func_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_descriptors_t; + +/// Gets import descriptors for the given module +/// +/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors( + module: *const wasmer_module_t, + import_descriptors: *mut *mut wasmer_import_descriptors_t, +) { + let module = &*(module as *const Module); + let total_imports = module.info().imported_functions.len() + + module.info().imported_tables.len() + + module.info().imported_globals.len() + + module.info().imported_memories.len(); + let mut descriptors: Vec = Vec::with_capacity(total_imports); + + for ( + _index, + ImportName { + namespace_index, + name_index, + }, + ) in &module.info().imported_functions + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_FUNCTION, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_tables + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_TABLE, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_globals + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_GLOBAL, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_memories + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_MEMORY, + }); + } + + let named_import_descriptors: Box = + Box::new(NamedImportDescriptors(descriptors)); + *import_descriptors = + Box::into_raw(named_import_descriptors) as *mut wasmer_import_descriptors_t; +} + +pub struct NamedImportDescriptors(Vec); + +/// Frees the memory for the given import descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_import_descriptors_destroy( + import_descriptors: *mut wasmer_import_descriptors_t, +) { + if !import_descriptors.is_null() { + unsafe { Box::from_raw(import_descriptors as *mut NamedImportDescriptors) }; + } +} + +/// Gets the length of the import descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors_len( + exports: *mut wasmer_import_descriptors_t, +) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedImportDescriptors)).0.len() as c_int +} + +/// Gets import descriptor by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors_get( + import_descriptors: *mut wasmer_import_descriptors_t, + idx: c_int, +) -> *mut wasmer_import_descriptor_t { + if import_descriptors.is_null() { + return ptr::null_mut(); + } + let named_import_descriptors = &mut *(import_descriptors as *mut NamedImportDescriptors); + &mut (*named_import_descriptors).0[idx as usize] as *mut NamedImportDescriptor + as *mut wasmer_import_descriptor_t +} + +/// Gets name for the import descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_name( + import_descriptor: *mut wasmer_import_descriptor_t, +) -> wasmer_byte_array { + let named_import_descriptor = &*(import_descriptor as *mut NamedImportDescriptor); + wasmer_byte_array { + bytes: named_import_descriptor.name.as_ptr(), + bytes_len: named_import_descriptor.name.len() as u32, + } +} + +/// Gets module name for the import descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_module_name( + import_descriptor: *mut wasmer_import_descriptor_t, +) -> wasmer_byte_array { + let named_import_descriptor = &*(import_descriptor as *mut NamedImportDescriptor); + wasmer_byte_array { + bytes: named_import_descriptor.module.as_ptr(), + bytes_len: named_import_descriptor.module.len() as u32, + } +} + +/// Gets export descriptor kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_kind( + export: *mut wasmer_import_descriptor_t, +) -> wasmer_import_export_kind { + let named_import_descriptor = &*(export as *mut NamedImportDescriptor); + named_import_descriptor.kind.clone() +} + +/// Sets the result parameter to the arity of the params of the wasmer_import_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_func_params_arity( + func: *const wasmer_import_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + *result = signature.params().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_params_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Creates new func +/// +/// The caller owns the object and should call `wasmer_import_func_destroy` to free it. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_func_new( + func: extern "C" fn(data: *mut c_void), + params: *const wasmer_value_tag, + params_len: c_int, + returns: *const wasmer_value_tag, + returns_len: c_int, +) -> *mut wasmer_import_func_t { + let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + let returns: &[wasmer_value_tag] = slice::from_raw_parts(returns, returns_len as usize); + let returns: Vec = returns.iter().cloned().map(|x| x.into()).collect(); + + let export = Box::new(Export::Function { + func: FuncPointer::new(func as _), + ctx: Context::Internal, + signature: Arc::new(FuncSig::new(params, returns)), + }); + Box::into_raw(export) as *mut wasmer_import_func_t +} + +/// Sets the params buffer to the parameter types of the given wasmer_import_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_func_params( + func: *const wasmer_import_func_t, + params: *mut wasmer_value_tag, + params_len: c_int, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + let params: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(params, params_len as usize); + for (i, item) in signature.params().iter().enumerate() { + params[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_params".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the returns buffer to the parameter types of the given wasmer_import_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_func_returns( + func: *const wasmer_import_func_t, + returns: *mut wasmer_value_tag, + returns_len: c_int, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + let returns: &mut [wasmer_value_tag] = + slice::from_raw_parts_mut(returns, returns_len as usize); + for (i, item) in signature.returns().iter().enumerate() { + returns[i] = item.into(); + } + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_returns".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the result parameter to the arity of the returns of the wasmer_import_func_t +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_func_returns_arity( + func: *const wasmer_import_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + *result = signature.returns().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_results_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Frees memory for the given Func +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) { + if !func.is_null() { + unsafe { Box::from_raw(func as *mut Export) }; + } +} + +struct NamedImportDescriptor { + module: String, + name: String, + kind: wasmer_import_export_kind, +} diff --git a/lib/runtime-c-api/src/instance.rs b/lib/runtime-c-api/src/instance.rs new file mode 100644 index 000000000..a9d7d5555 --- /dev/null +++ b/lib/runtime-c-api/src/instance.rs @@ -0,0 +1,256 @@ +//! Wasm instance. + +use crate::{ + error::{update_last_error, CApiError}, + export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports}, + import::wasmer_import_t, + memory::wasmer_memory_t, + value::{wasmer_value, wasmer_value_t, wasmer_value_tag}, + wasmer_result_t, +}; +use libc::{c_char, c_int, c_void, uint32_t, uint8_t}; +use std::{collections::HashMap, ffi::CStr, slice}; +use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Table, Value}; +use wasmer_runtime_core::{export::Export, import::Namespace}; + +#[repr(C)] +pub struct wasmer_instance_t; + +#[repr(C)] +pub struct wasmer_instance_context_t; + +/// Creates a new Instance from the given wasm bytes and imports. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instantiate( + instance: *mut *mut wasmer_instance_t, + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, + imports: *mut wasmer_import_t, + imports_len: c_int, +) -> wasmer_result_t { + if wasm_bytes.is_null() { + update_last_error(CApiError { + msg: "wasm bytes ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); + let mut import_object = ImportObject::new(); + let mut namespaces = HashMap::new(); + for import in imports { + let module_name = slice::from_raw_parts( + import.module_name.bytes, + import.module_name.bytes_len as usize, + ); + let module_name = if let Ok(s) = std::str::from_utf8(module_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting module name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + let import_name = slice::from_raw_parts( + import.import_name.bytes, + import.import_name.bytes_len as usize, + ); + let import_name = if let Ok(s) = std::str::from_utf8(import_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting import_name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new); + + let export = match import.tag { + wasmer_import_export_kind::WASM_MEMORY => { + let mem = import.value.memory as *mut Memory; + Export::Memory((&*mem).clone()) + } + wasmer_import_export_kind::WASM_FUNCTION => { + let func_export = import.value.func as *mut Export; + (&*func_export).clone() + } + wasmer_import_export_kind::WASM_GLOBAL => { + let global = import.value.global as *mut Global; + Export::Global((&*global).clone()) + } + wasmer_import_export_kind::WASM_TABLE => { + let table = import.value.table as *mut Table; + Export::Table((&*table).clone()) + } + }; + namespace.insert(import_name, export); + } + for (module_name, namespace) in namespaces.into_iter() { + import_object.register(module_name, namespace); + } + + let bytes: &[u8] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize); + let result = wasmer_runtime::instantiate(bytes, &import_object); + let new_instance = match result { + Ok(instance) => instance, + Err(_error) => { + // TODO the trait bound `wasmer_runtime::error::Error: std::error::Error` is not satisfied + //update_last_error(error); + update_last_error(CApiError { + msg: "error instantiating".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + }; + *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t; + wasmer_result_t::WASMER_OK +} + +/// Calls an instances exported function by `name` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_call( + instance: *mut wasmer_instance_t, + name: *const c_char, + params: *const wasmer_value_t, + params_len: c_int, + results: *mut wasmer_value_t, + results_len: c_int, +) -> wasmer_result_t { + if instance.is_null() { + update_last_error(CApiError { + msg: "instance ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if name.is_null() { + update_last_error(CApiError { + msg: "name ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + if params.is_null() { + update_last_error(CApiError { + msg: "params ptr is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); + let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); + + let func_name_c = CStr::from_ptr(name); + let func_name_r = func_name_c.to_str().unwrap(); + + let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); + let result = (&*(instance as *mut Instance)).call(func_name_r, ¶ms[..]); + + match result { + Ok(results_vec) => { + if !results_vec.is_empty() { + let ret = match results_vec[0] { + Value::I32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32: x }, + }, + Value::I64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64: x }, + }, + Value::F32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32: x }, + }, + Value::F64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64: x }, + }, + }; + results[0] = ret; + } + wasmer_result_t::WASMER_OK + } + Err(err) => { + update_last_error(err); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Gets Exports for the given instance +/// +/// The caller owns the object and should call `wasmer_exports_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_exports( + instance: *mut wasmer_instance_t, + exports: *mut *mut wasmer_exports_t, +) { + let instance_ref = &mut *(instance as *mut Instance); + let mut exports_vec: Vec = Vec::with_capacity(instance_ref.exports().count()); + for (name, export) in instance_ref.exports() { + exports_vec.push(NamedExport { + name: name.clone(), + export: export.clone(), + instance: instance as *mut Instance, + }); + } + let named_exports: Box = Box::new(NamedExports(exports_vec)); + *exports = Box::into_raw(named_exports) as *mut wasmer_exports_t; +} + +/// Sets the `data` field of the instance context. This context will be +/// passed to all imported function for instance. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_data_set( + instance: *mut wasmer_instance_t, + data_ptr: *mut c_void, +) { + let instance_ref = unsafe { &mut *(instance as *mut Instance) }; + instance_ref.context_mut().data = data_ptr; +} + +/// Gets the memory within the context at the index `memory_idx`. +/// The index is always 0 until multiple memories are supported. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_memory( + ctx: *const wasmer_instance_context_t, + _memory_idx: uint32_t, +) -> *const wasmer_memory_t { + let ctx = unsafe { &*(ctx as *const Ctx) }; + let memory = ctx.memory(0); + memory as *const Memory as *const wasmer_memory_t +} + +/// Gets the `data` field within the context. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_data_get( + ctx: *const wasmer_instance_context_t, +) -> *mut c_void { + let ctx = unsafe { &*(ctx as *const Ctx) }; + ctx.data +} + +/// Frees memory for the given Instance +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_destroy(instance: *mut wasmer_instance_t) { + if !instance.is_null() { + unsafe { Box::from_raw(instance as *mut Instance) }; + } +} diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index f52076d28..aa6455f60 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -1,89 +1,25 @@ extern crate wasmer_runtime; extern crate wasmer_runtime_core; -use libc::{c_char, c_int, int32_t, int64_t, uint32_t, uint8_t}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::error::Error; -use std::ffi::CStr; -use std::fmt; -use std::slice; -use std::sync::Arc; -use std::{ffi::c_void, ptr}; -use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Module, Table, Value}; -use wasmer_runtime_core::export::{Context, Export, FuncPointer}; -use wasmer_runtime_core::import::Namespace; -use wasmer_runtime_core::types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type}; -use wasmer_runtime_core::units::{Bytes, Pages}; +use libc::{uint32_t, uint8_t}; + +pub mod error; +pub mod export; +pub mod global; +pub mod import; +pub mod instance; +pub mod memory; +pub mod module; +pub mod table; +pub mod value; #[allow(non_camel_case_types)] -pub struct wasmer_import_object_t(); - -#[allow(non_camel_case_types)] -pub struct wasmer_module_t(); - -#[allow(non_camel_case_types)] -pub struct wasmer_instance_t(); - -#[allow(non_camel_case_types)] -pub struct wasmer_instance_context_t(); - -#[allow(non_camel_case_types)] -#[no_mangle] #[repr(C)] pub enum wasmer_result_t { WASMER_OK = 1, WASMER_ERROR = 2, } -#[repr(u32)] -#[derive(Clone)] -pub enum wasmer_value_tag { - WASM_I32, - WASM_I64, - WASM_F32, - WASM_F64, -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub union wasmer_value { - I32: int32_t, - I64: int64_t, - F32: f32, - F64: f64, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_value_t { - tag: wasmer_value_tag, - value: wasmer_value, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_global_descriptor_t { - mutable: bool, - kind: wasmer_value_tag, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_memory_t(); - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_table_t(); - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_func_t(); - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_global_t(); - #[repr(C)] pub struct wasmer_limits_t { pub min: uint32_t, @@ -96,1080 +32,8 @@ pub struct wasmer_limit_option_t { pub some: uint32_t, } -#[repr(C)] -pub struct wasmer_func_signature { - pub params: *const wasmer_value_tag, - pub params_len: c_int, - pub returns: *const wasmer_value_tag, - pub returns_len: c_int, -} - -#[repr(C)] -pub struct wasmer_import_t { - module_name: wasmer_byte_array, - import_name: wasmer_byte_array, - tag: wasmer_import_export_kind, - value: wasmer_import_export_value, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_export_t; - -#[repr(C)] -#[derive(Clone)] -pub struct wasmer_exports_t; - -#[repr(u32)] -#[derive(Clone)] -pub enum wasmer_import_export_kind { - WASM_FUNCTION, - WASM_GLOBAL, - WASM_MEMORY, - WASM_TABLE, -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub union wasmer_import_export_value { - func: *const wasmer_func_t, - table: *const wasmer_table_t, - memory: *const wasmer_memory_t, - global: *const wasmer_global_t, -} - #[repr(C)] pub struct wasmer_byte_array { bytes: *const uint8_t, bytes_len: uint32_t, } - -/// Returns true for valid wasm bytes and false for invalid bytes -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_validate( - wasm_bytes: *mut uint8_t, - wasm_bytes_len: uint32_t, -) -> bool { - if wasm_bytes.is_null() { - return false; - } - let bytes: &[u8] = - unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; - wasmer_runtime_core::validate(bytes) -} - -/// Creates a new Memory for the given descriptor and initializes the given -/// pointer to pointer to a pointer to the new memory. -/// -/// The caller owns the object and should call `wasmer_memory_destroy` to free it. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -pub unsafe extern "C" fn wasmer_memory_new( - mut memory: *mut *mut wasmer_memory_t, - limits: wasmer_limits_t, -) -> wasmer_result_t { - let max = if limits.max.has_some { - Some(Pages(limits.max.some)) - } else { - None - }; - let desc = MemoryDescriptor { - minimum: Pages(limits.min), - maximum: max, - shared: false, - }; - let result = Memory::new(desc); - let new_memory = match result { - Ok(memory) => memory, - Err(error) => { - update_last_error(error); - return wasmer_result_t::WASMER_ERROR; - } - }; - unsafe { *memory = Box::into_raw(Box::new(new_memory)) as *mut wasmer_memory_t }; - wasmer_result_t::WASMER_OK -} - -/// Grows a Memory by the given number of pages. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_memory_grow( - memory: *mut wasmer_memory_t, - delta: uint32_t, -) -> wasmer_result_t { - let memory = unsafe { &*(memory as *mut Memory) }; - let delta_result = memory.grow(Pages(delta)); - match delta_result { - Ok(_) => wasmer_result_t::WASMER_OK, - Err(grow_error) => { - update_last_error(grow_error); - wasmer_result_t::WASMER_ERROR - } - } -} - -/// Returns the current length in pages of the given memory -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_memory_length(memory: *mut wasmer_memory_t) -> uint32_t { - let memory = unsafe { &*(memory as *mut Memory) }; - let Pages(len) = memory.size(); - len -} - -/// Creates a new Table for the given descriptor and initializes the given -/// pointer to pointer to a pointer to the new Table. -/// -/// The caller owns the object and should call `wasmer_table_destroy` to free it. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -pub unsafe extern "C" fn wasmer_table_new( - mut table: *mut *mut wasmer_table_t, - limits: wasmer_limits_t, -) -> wasmer_result_t { - let max = if limits.max.has_some { - Some(limits.max.some) - } else { - None - }; - let desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: limits.min, - maximum: max, - }; - let result = Table::new(desc); - let new_table = match result { - Ok(table) => table, - Err(error) => { - update_last_error(error); - return wasmer_result_t::WASMER_ERROR; - } - }; - unsafe { *table = Box::into_raw(Box::new(new_table)) as *mut wasmer_table_t }; - wasmer_result_t::WASMER_OK -} - -/// Grows a Table by the given number of elements. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_table_grow( - table: *mut wasmer_table_t, - delta: uint32_t, -) -> wasmer_result_t { - let table = unsafe { &*(table as *mut Table) }; - let delta_result = table.grow(delta); - match delta_result { - Ok(_) => wasmer_result_t::WASMER_OK, - Err(grow_error) => { - update_last_error(grow_error); - wasmer_result_t::WASMER_ERROR - } - } -} - -/// Returns the current length of the given Table -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_table_length(table: *mut wasmer_table_t) -> uint32_t { - let table = unsafe { &*(table as *mut Table) }; - let len = table.size(); - len -} - -/// Frees memory for the given Table -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_table_destroy(table: *mut wasmer_table_t) { - if !table.is_null() { - drop(unsafe { Box::from_raw(table as *mut Table) }); - } -} - -/// Creates a new Global and returns a pointer to it. -/// The caller owns the object and should call `wasmer_global_destroy` to free it. -#[no_mangle] -pub unsafe extern "C" fn wasmer_global_new( - value: wasmer_value_t, - mutable: bool, -) -> *mut wasmer_global_t { - let global = if mutable { - Global::new_mutable(value.into()) - } else { - Global::new(value.into()) - }; - unsafe { Box::into_raw(Box::new(global)) as *mut wasmer_global_t } -} - -/// Gets the value stored by the given Global -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_global_get(global: *mut wasmer_global_t) -> wasmer_value_t { - let global = unsafe { &*(global as *mut Global) }; - let value: wasmer_value_t = global.get().into(); - value -} - -/// Sets the value stored by the given Global -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_global_set(global: *mut wasmer_global_t, value: wasmer_value_t) { - let global = unsafe { &*(global as *mut Global) }; - global.set(value.into()); -} - -/// Returns a descriptor (type, mutability) of the given Global -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_global_get_descriptor( - global: *mut wasmer_global_t, -) -> wasmer_global_descriptor_t { - let global = unsafe { &*(global as *mut Global) }; - let descriptor = global.descriptor(); - let desc = wasmer_global_descriptor_t { - mutable: descriptor.mutable, - kind: descriptor.ty.into(), - }; - desc -} - -/// Frees memory for the given Global -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_global_destroy(global: *mut wasmer_global_t) { - if !global.is_null() { - drop(unsafe { Box::from_raw(global as *mut Global) }); - } -} - -/// Frees memory for the given Memory -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) { - if !memory.is_null() { - drop(unsafe { Box::from_raw(memory as *mut Memory) }); - } -} - -/// Creates a new Module from the given wasm bytes. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_compile( - mut module: *mut *mut wasmer_module_t, - wasm_bytes: *mut uint8_t, - wasm_bytes_len: uint32_t, -) -> wasmer_result_t { - let bytes: &[u8] = - unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; - let result = wasmer_runtime::compile(bytes); - let new_module = match result { - Ok(instance) => instance, - Err(error) => { - update_last_error(error); - return wasmer_result_t::WASMER_ERROR; - } - }; - unsafe { *module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t }; - wasmer_result_t::WASMER_OK -} - -/// Creates a new Instance from the given module and imports. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_module_instantiate( - module: *mut wasmer_module_t, - mut instance: *mut *mut wasmer_instance_t, - imports: *mut wasmer_import_t, - imports_len: c_int, -) -> wasmer_result_t { - let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); - let mut import_object = ImportObject::new(); - let mut namespaces = HashMap::new(); - for import in imports { - let module_name = slice::from_raw_parts( - import.module_name.bytes, - import.module_name.bytes_len as usize, - ); - let module_name = if let Ok(s) = std::str::from_utf8(module_name) { - s - } else { - update_last_error(CApiError { - msg: "error converting module name to string".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - }; - let import_name = slice::from_raw_parts( - import.import_name.bytes, - import.import_name.bytes_len as usize, - ); - let import_name = if let Ok(s) = std::str::from_utf8(import_name) { - s - } else { - update_last_error(CApiError { - msg: "error converting import_name to string".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - }; - - let namespace = namespaces - .entry(module_name) - .or_insert_with(|| Namespace::new()); - - let export = match import.tag { - wasmer_import_export_kind::WASM_MEMORY => import.value.memory as *mut Export, - wasmer_import_export_kind::WASM_FUNCTION => import.value.func as *mut Export, - wasmer_import_export_kind::WASM_GLOBAL => import.value.global as *mut Export, - wasmer_import_export_kind::WASM_TABLE => import.value.table as *mut Export, - }; - namespace.insert(import_name, unsafe { (&*export).clone() }); - } - for (module_name, namespace) in namespaces.into_iter() { - import_object.register(module_name, namespace); - } - - let module = unsafe { &*(module as *mut Module) }; - let new_instance = if let Ok(res) = module.instantiate(&import_object) { - res - } else { - update_last_error(CApiError { - msg: "error instantiating from module".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - }; - unsafe { *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t }; - wasmer_result_t::WASMER_OK -} - -/// Frees memory for the given Module -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_module_destroy(module: *mut wasmer_module_t) { - if !module.is_null() { - drop(unsafe { Box::from_raw(module as *mut Module) }); - } -} - -/// Creates a new Instance from the given wasm bytes and imports. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_instantiate( - mut instance: *mut *mut wasmer_instance_t, - wasm_bytes: *mut uint8_t, - wasm_bytes_len: uint32_t, - imports: *mut wasmer_import_t, - imports_len: c_int, -) -> wasmer_result_t { - if wasm_bytes.is_null() { - update_last_error(CApiError { - msg: "wasm bytes ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); - let mut import_object = ImportObject::new(); - let mut namespaces = HashMap::new(); - for import in imports { - let module_name = slice::from_raw_parts( - import.module_name.bytes, - import.module_name.bytes_len as usize, - ); - let module_name = if let Ok(s) = std::str::from_utf8(module_name) { - s - } else { - update_last_error(CApiError { - msg: "error converting module name to string".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - }; - let import_name = slice::from_raw_parts( - import.import_name.bytes, - import.import_name.bytes_len as usize, - ); - let import_name = if let Ok(s) = std::str::from_utf8(import_name) { - s - } else { - update_last_error(CApiError { - msg: "error converting import_name to string".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - }; - - let namespace = namespaces - .entry(module_name) - .or_insert_with(|| Namespace::new()); - - let export = match import.tag { - wasmer_import_export_kind::WASM_MEMORY => import.value.memory as *mut Export, - wasmer_import_export_kind::WASM_FUNCTION => import.value.func as *mut Export, - wasmer_import_export_kind::WASM_GLOBAL => import.value.global as *mut Export, - wasmer_import_export_kind::WASM_TABLE => import.value.table as *mut Export, - }; - namespace.insert(import_name, unsafe { (&*export).clone() }); - } - for (module_name, namespace) in namespaces.into_iter() { - import_object.register(module_name, namespace); - } - - let bytes: &[u8] = - unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) }; - let result = wasmer_runtime::instantiate(bytes, &import_object); - let new_instance = match result { - Ok(instance) => instance, - Err(error) => { - // TODO the trait bound `wasmer_runtime::error::Error: std::error::Error` is not satisfied - //update_last_error(error); - update_last_error(CApiError { - msg: "error instantiating".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - }; - unsafe { *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t }; - wasmer_result_t::WASMER_OK -} - -/// Calls an instances exported function by `name` with the provided parameters. -/// Results are set using the provided `results` pointer. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_instance_call( - instance: *mut wasmer_instance_t, - name: *const c_char, - params: *const wasmer_value_t, - params_len: c_int, - results: *mut wasmer_value_t, - results_len: c_int, -) -> wasmer_result_t { - if instance.is_null() { - update_last_error(CApiError { - msg: "instance ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - if name.is_null() { - update_last_error(CApiError { - msg: "name ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - if params.is_null() { - update_last_error(CApiError { - msg: "params ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - - let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); - let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); - - let func_name_c = unsafe { CStr::from_ptr(name) }; - let func_name_r = func_name_c.to_str().unwrap(); - - let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); - let result = (&*(instance as *mut Instance)).call(func_name_r, ¶ms[..]); - - match result { - Ok(results_vec) => { - if results_vec.len() > 0 { - let ret = match results_vec[0] { - Value::I32(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_I32, - value: wasmer_value { I32: x }, - }, - Value::I64(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_I64, - value: wasmer_value { I64: x }, - }, - Value::F32(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_F32, - value: wasmer_value { F32: x }, - }, - Value::F64(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_F64, - value: wasmer_value { F64: x }, - }, - }; - results[0] = ret; - } - wasmer_result_t::WASMER_OK - } - Err(err) => { - update_last_error(err); - wasmer_result_t::WASMER_ERROR - } - } -} - -/// Gets Exports for the given instance -/// -/// The caller owns the object and should call `wasmer_exports_destroy` to free it. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_instance_exports( - instance: *mut wasmer_instance_t, - exports: *mut *mut wasmer_exports_t, -) { - let mut instance = unsafe { &mut *(instance as *mut Instance) }; - let named_exports: Box = - Box::new(NamedExports(instance.exports().map(|e| e.into()).collect())); - unsafe { *exports = Box::into_raw(named_exports) as *mut wasmer_exports_t }; -} - -pub struct NamedExports(Vec); - -/// Frees the memory for the given exports -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) { - if !exports.is_null() { - drop(unsafe { Box::from_raw(exports as *mut NamedExports) }); - } -} - -/// Gets the length of the exports -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_exports_len(exports: *mut wasmer_exports_t) -> c_int { - if exports.is_null() { - return 0; - } - (*(exports as *mut NamedExports)).0.len() as c_int -} - -/// Gets wasmer_export by index -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_exports_get( - exports: *mut wasmer_exports_t, - idx: c_int, -) -> *mut wasmer_export_t { - if exports.is_null() { - return ptr::null_mut(); - } - let mut named_exports = unsafe { &mut *(exports as *mut NamedExports) }; - let ptr = &mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t; - ptr -} - -/// Gets wasmer_export kind -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_export_kind( - export: *mut wasmer_export_t, -) -> wasmer_import_export_kind { - let named_export = &*(export as *mut NamedExport); - match named_export.export { - Export::Table(_) => wasmer_import_export_kind::WASM_TABLE, - Export::Function { .. } => wasmer_import_export_kind::WASM_FUNCTION, - Export::Global(_) => wasmer_import_export_kind::WASM_GLOBAL, - Export::Memory(_) => wasmer_import_export_kind::WASM_MEMORY, - } -} - -/// Creates new func -/// -/// The caller owns the object and should call `wasmer_func_destroy` to free it. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_func_new( - func: extern "C" fn(data: *mut c_void), - params: *const wasmer_value_tag, - params_len: c_int, - returns: *const wasmer_value_tag, - returns_len: c_int, -) -> *const wasmer_func_t { - let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize); - let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); - let returns: &[wasmer_value_tag] = slice::from_raw_parts(returns, returns_len as usize); - let returns: Vec = returns.iter().cloned().map(|x| x.into()).collect(); - - let export = Box::new(Export::Function { - func: unsafe { FuncPointer::new(func as _) }, - ctx: Context::Internal, - signature: Arc::new(FuncSig::new(params, returns)), - }); - Box::into_raw(export) as *mut wasmer_func_t -} - -/// Sets the result parameter to the arity of the params of the wasmer_func_t -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_func_params_arity( - func: *mut wasmer_func_t, - result: *mut uint32_t, -) -> wasmer_result_t { - let mut export = unsafe { &mut *(func as *mut Export) }; - let result = if let Export::Function { ref signature, .. } = *export { - unsafe { *result = signature.params().len() as uint32_t }; - wasmer_result_t::WASMER_OK - } else { - update_last_error(CApiError { - msg: "func ptr error in wasmer_func_params_arity".to_string(), - }); - wasmer_result_t::WASMER_ERROR - }; - result -} - -/// Sets the params buffer to the parameter types of the given wasmer_func_t -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_func_params( - func: *mut wasmer_func_t, - params: *mut wasmer_value_tag, - params_len: c_int, -) -> wasmer_result_t { - let mut export = unsafe { &mut *(func as *mut Export) }; - let result = if let Export::Function { ref signature, .. } = *export { - let params: &mut [wasmer_value_tag] = - slice::from_raw_parts_mut(params, params_len as usize); - for (i, item) in signature.params().iter().enumerate() { - params[i] = item.into(); - } - wasmer_result_t::WASMER_OK - } else { - update_last_error(CApiError { - msg: "func ptr error in wasmer_func_params".to_string(), - }); - wasmer_result_t::WASMER_ERROR - }; - result -} - -/// Sets the returns buffer to the parameter types of the given wasmer_func_t -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_func_returns( - func: *mut wasmer_func_t, - returns: *mut wasmer_value_tag, - returns_len: c_int, -) -> wasmer_result_t { - let mut export = unsafe { &mut *(func as *mut Export) }; - let result = if let Export::Function { ref signature, .. } = *export { - let returns: &mut [wasmer_value_tag] = - slice::from_raw_parts_mut(returns, returns_len as usize); - for (i, item) in signature.returns().iter().enumerate() { - returns[i] = item.into(); - } - wasmer_result_t::WASMER_OK - } else { - update_last_error(CApiError { - msg: "func ptr error in wasmer_func_returns".to_string(), - }); - wasmer_result_t::WASMER_ERROR - }; - result -} - -/// Sets the result parameter to the arity of the returns of the wasmer_func_t -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_func_returns_arity( - func: *mut wasmer_func_t, - result: *mut uint32_t, -) -> wasmer_result_t { - let mut export = unsafe { &*(func as *mut Export) }; - let result = if let Export::Function { ref signature, .. } = *export { - unsafe { *result = signature.returns().len() as uint32_t }; - wasmer_result_t::WASMER_OK - } else { - update_last_error(CApiError { - msg: "func ptr error in wasmer_func_results_arity".to_string(), - }); - wasmer_result_t::WASMER_ERROR - }; - result -} - -/// Frees memory for the given Func -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_func_destroy(func: *mut wasmer_func_t) { - if !func.is_null() { - drop(unsafe { Box::from_raw(func as *mut Export) }); - } -} - -/// Gets func from wasm_export -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_export_to_func( - export: *mut wasmer_export_t, -) -> *const wasmer_func_t { - let named_export = &*(export as *mut NamedExport); - &named_export.export as *const Export as *const wasmer_func_t -} - -/// Gets name from wasmer_export -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_export_name(export: *mut wasmer_export_t) -> wasmer_byte_array { - let named_export = &*(export as *mut NamedExport); - wasmer_byte_array { - bytes: named_export.name.as_ptr(), - bytes_len: named_export.name.len() as u32, - } -} - -/// Calls a `func` with the provided parameters. -/// Results are set using the provided `results` pointer. -/// -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn wasmer_func_call( - func: *mut wasmer_func_t, - params: *const wasmer_value_t, - params_len: c_int, - results: *mut wasmer_value_t, - results_len: c_int, -) -> wasmer_result_t { - if func.is_null() { - update_last_error(CApiError { - msg: "func ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - if params.is_null() { - update_last_error(CApiError { - msg: "params ptr is null".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - - let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize); - let params: Vec = params.iter().cloned().map(|x| x.into()).collect(); - - let export_func = unsafe { &*(func as *mut Export) }; - - let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); - // TODO implement func.call - update_last_error(CApiError { - msg: "wasmer_func_call not yet implemented".to_string(), - }); - wasmer_result_t::WASMER_ERROR - // let result = instance.call(func_name_r, ¶ms[..]); - // Box::into_raw(export_func); - // match result { - // Ok(results_vec) => { - // if results_vec.len() > 0 { - // let ret = match results_vec[0] { - // Value::I32(x) => wasmer_value_t { - // tag: wasmer_value_tag::WASM_I32, - // value: wasmer_value { I32: x }, - // }, - // Value::I64(x) => wasmer_value_t { - // tag: wasmer_value_tag::WASM_I64, - // value: wasmer_value { I64: x }, - // }, - // Value::F32(x) => wasmer_value_t { - // tag: wasmer_value_tag::WASM_F32, - // value: wasmer_value { F32: x }, - // }, - // Value::F64(x) => wasmer_value_t { - // tag: wasmer_value_tag::WASM_F64, - // value: wasmer_value { F64: x }, - // }, - // }; - // results[0] = ret; - // } - // wasmer_result_t::WASMER_OK - // } - // Err(err) => { - // update_last_error(err); - // wasmer_result_t::WASMER_ERROR - // } - // } -} - -/// Gets the memory within the context at the index `memory_idx`. -/// The index is always 0 until multiple memories are supported. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_instance_context_memory( - ctx: *mut wasmer_instance_context_t, - memory_idx: uint32_t, -) -> *const wasmer_memory_t { - let mut ctx = unsafe { &mut *(ctx as *mut Ctx) }; - let memory = ctx.memory(0); - memory as *const Memory as *const wasmer_memory_t -} - -/// Gets the start pointer to the bytes within a Memory -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_memory_data(mem: *mut wasmer_memory_t) -> *mut uint8_t { - let memory = mem as *mut Memory; - use std::cell::Cell; - unsafe { ((*memory).view::()[..]).as_ptr() as *mut Cell as *mut u8 } -} - -/// Gets the size in bytes of a Memory -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_memory_data_length(mem: *mut wasmer_memory_t) -> uint32_t { - let memory = mem as *mut Memory; - let Bytes(len) = unsafe { (*memory).size().bytes() }; - len as uint32_t -} - -/// Frees memory for the given Instance -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn wasmer_instance_destroy(instance: *mut wasmer_instance_t) { - if !instance.is_null() { - drop(unsafe { Box::from_raw(instance as *mut Instance) }); - } -} - -impl From for Value { - fn from(v: wasmer_value_t) -> Self { - unsafe { - match v { - wasmer_value_t { - tag: WASM_I32, - value: wasmer_value { I32 }, - } => Value::I32(I32), - wasmer_value_t { - tag: WASM_I64, - value: wasmer_value { I64 }, - } => Value::I64(I64), - wasmer_value_t { - tag: WASM_F32, - value: wasmer_value { F32 }, - } => Value::F32(F32), - wasmer_value_t { - tag: WASM_F64, - value: wasmer_value { F64 }, - } => Value::F64(F64), - _ => panic!("not implemented"), - } - } - } -} - -impl From for wasmer_value_t { - fn from(val: Value) -> Self { - match val { - Value::I32(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_I32, - value: wasmer_value { I32: x }, - }, - Value::I64(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_I64, - value: wasmer_value { I64: x }, - }, - Value::F32(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_F32, - value: wasmer_value { F32: x }, - }, - Value::F64(x) => wasmer_value_t { - tag: wasmer_value_tag::WASM_F64, - value: wasmer_value { F64: x }, - }, - } - } -} - -impl From for wasmer_value_tag { - fn from(ty: Type) -> Self { - match ty { - Type::I32 => wasmer_value_tag::WASM_I32, - Type::I64 => wasmer_value_tag::WASM_I64, - Type::F32 => wasmer_value_tag::WASM_F32, - Type::F64 => wasmer_value_tag::WASM_F64, - _ => panic!("not implemented"), - } - } -} - -impl From for Type { - fn from(v: wasmer_value_tag) -> Self { - unsafe { - match v { - wasmer_value_tag::WASM_I32 => Type::I32, - wasmer_value_tag::WASM_I64 => Type::I64, - wasmer_value_tag::WASM_F32 => Type::F32, - wasmer_value_tag::WASM_F64 => Type::F64, - _ => panic!("not implemented"), - } - } - } -} - -impl From<(std::string::String, wasmer_runtime_core::export::Export)> for NamedExport { - fn from((name, export): (String, Export)) -> Self { - NamedExport { name, export } - } -} - -impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag { - fn from(ty: &Type) -> Self { - match *ty { - Type::I32 => wasmer_value_tag::WASM_I32, - Type::I64 => wasmer_value_tag::WASM_I64, - Type::F32 => wasmer_value_tag::WASM_F32, - Type::F64 => wasmer_value_tag::WASM_F64, - } - } -} - -// Error reporting - -thread_local! { - static LAST_ERROR: RefCell>> = RefCell::new(None); -} - -fn update_last_error(err: E) { - LAST_ERROR.with(|prev| { - *prev.borrow_mut() = Some(Box::new(err)); - }); -} - -/// Retrieve the most recent error, clearing it in the process. -fn take_last_error() -> Option> { - LAST_ERROR.with(|prev| prev.borrow_mut().take()) -} - -/// Gets the length in bytes of the last error. -/// This can be used to dynamically allocate a buffer with the correct number of -/// bytes needed to store a message. -/// -/// # Example -/// ``` -/// int error_len = wasmer_last_error_length(); -/// char *error_str = malloc(error_len); -/// ``` -#[no_mangle] -pub extern "C" fn wasmer_last_error_length() -> c_int { - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(ref err) => err.to_string().len() as c_int + 1, - None => 0, - }) -} - -/// Stores the last error message into the provided buffer up to the given `length`. -/// The `length` parameter must be large enough to store the last error message. -/// -/// Returns the length of the string in bytes. -/// Returns `-1` if an error occurs. -/// -/// # Example -/// ``` -/// int error_len = wasmer_last_error_length(); -/// char *error_str = malloc(error_len); -/// wasmer_last_error_message(error_str, error_len); -/// printf("Error str: `%s`\n", error_str); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: c_int) -> c_int { - if buffer.is_null() { - // buffer pointer is null - return -1; - } - - let last_error = match take_last_error() { - Some(err) => err, - None => return 0, - }; - - let error_message = last_error.to_string(); - - 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 - return -1; - } - - ptr::copy_nonoverlapping( - error_message.as_ptr(), - buffer.as_mut_ptr(), - error_message.len(), - ); - - // Add a trailing null so people using the string as a `char *` don't - // accidentally read into garbage. - buffer[error_message.len()] = 0; - - error_message.len() as c_int -} - -#[derive(Debug)] -struct CApiError { - msg: String, -} - -impl fmt::Display for CApiError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", &self.msg) - } -} - -impl Error for CApiError {} - -struct NamedExport { - name: String, - export: Export, -} diff --git a/lib/runtime-c-api/src/memory.rs b/lib/runtime-c-api/src/memory.rs new file mode 100644 index 000000000..f90965586 --- /dev/null +++ b/lib/runtime-c-api/src/memory.rs @@ -0,0 +1,108 @@ +//! Wasm memory.o + +use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; +use libc::{uint32_t, uint8_t}; +use std::cell::Cell; +use wasmer_runtime::Memory; +use wasmer_runtime_core::{ + types::MemoryDescriptor, + units::{Bytes, Pages}, +}; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_memory_t; + +/// Creates a new Memory for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new memory. +/// +/// The caller owns the object and should call `wasmer_memory_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +pub unsafe extern "C" fn wasmer_memory_new( + memory: *mut *mut wasmer_memory_t, + limits: wasmer_limits_t, +) -> wasmer_result_t { + let max = if limits.max.has_some { + Some(Pages(limits.max.some)) + } else { + None + }; + let desc = MemoryDescriptor { + minimum: Pages(limits.min), + maximum: max, + shared: false, + }; + let result = Memory::new(desc); + let new_memory = match result { + Ok(memory) => memory, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + *memory = Box::into_raw(Box::new(new_memory)) as *mut wasmer_memory_t; + wasmer_result_t::WASMER_OK +} + +/// Grows a Memory by the given number of pages. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_grow( + memory: *mut wasmer_memory_t, + delta: uint32_t, +) -> wasmer_result_t { + let memory = unsafe { &*(memory as *mut Memory) }; + let delta_result = memory.grow(Pages(delta)); + match delta_result { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(grow_error) => { + update_last_error(grow_error); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Returns the current length in pages of the given memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_length(memory: *const wasmer_memory_t) -> uint32_t { + let memory = unsafe { &*(memory as *const Memory) }; + let Pages(len) = memory.size(); + len +} + +/// Gets the start pointer to the bytes within a Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_data(mem: *const wasmer_memory_t) -> *mut uint8_t { + let memory = unsafe { &*(mem as *const Memory) }; + memory.view::()[..].as_ptr() as *mut Cell as *mut u8 +} + +/// Gets the size in bytes of a Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_data_length(mem: *mut wasmer_memory_t) -> uint32_t { + let memory = mem as *mut Memory; + let Bytes(len) = unsafe { (*memory).size().bytes() }; + len as uint32_t +} + +/// Frees memory for the given Memory +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) { + if !memory.is_null() { + unsafe { Box::from_raw(memory as *mut Memory) }; + } +} diff --git a/lib/runtime-c-api/src/module.rs b/lib/runtime-c-api/src/module.rs new file mode 100644 index 000000000..42d9815a6 --- /dev/null +++ b/lib/runtime-c-api/src/module.rs @@ -0,0 +1,289 @@ +//! Wasm module. + +use crate::{ + error::{update_last_error, CApiError}, + export::wasmer_import_export_kind, + import::wasmer_import_t, + instance::wasmer_instance_t, + wasmer_byte_array, wasmer_result_t, +}; +use libc::{c_int, uint32_t, uint8_t}; +use std::{collections::HashMap, slice}; +use wasmer_runtime::{compile, default_compiler, Global, ImportObject, Memory, Module, Table}; +use wasmer_runtime_core::{cache::Artifact, export::Export, import::Namespace, load_cache_with}; + +#[repr(C)] +pub struct wasmer_module_t; + +#[repr(C)] +pub struct wasmer_serialized_module_t; + +/// Creates a new Module from the given wasm bytes. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_compile( + module: *mut *mut wasmer_module_t, + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, +) -> wasmer_result_t { + let bytes: &[u8] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize); + let result = compile(bytes); + let new_module = match result { + Ok(instance) => instance, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + *module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t; + wasmer_result_t::WASMER_OK +} + +/// Returns true for valid wasm bytes and false for invalid bytes +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_validate( + wasm_bytes: *const uint8_t, + wasm_bytes_len: uint32_t, +) -> bool { + if wasm_bytes.is_null() { + return false; + } + let bytes: &[u8] = slice::from_raw_parts(wasm_bytes, wasm_bytes_len as usize); + + wasmer_runtime_core::validate(bytes) +} + +/// Creates a new Instance from the given module and imports. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_instantiate( + module: *const wasmer_module_t, + instance: *mut *mut wasmer_instance_t, + imports: *mut wasmer_import_t, + imports_len: c_int, +) -> wasmer_result_t { + let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); + let mut import_object = ImportObject::new(); + let mut namespaces = HashMap::new(); + for import in imports { + let module_name = slice::from_raw_parts( + import.module_name.bytes, + import.module_name.bytes_len as usize, + ); + let module_name = if let Ok(s) = std::str::from_utf8(module_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting module name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + let import_name = slice::from_raw_parts( + import.import_name.bytes, + import.import_name.bytes_len as usize, + ); + let import_name = if let Ok(s) = std::str::from_utf8(import_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting import_name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + let namespace = namespaces.entry(module_name).or_insert_with(Namespace::new); + + let export = match import.tag { + wasmer_import_export_kind::WASM_MEMORY => { + let mem = import.value.memory as *mut Memory; + Export::Memory((&*mem).clone()) + } + wasmer_import_export_kind::WASM_FUNCTION => { + let func_export = import.value.func as *mut Export; + (&*func_export).clone() + } + wasmer_import_export_kind::WASM_GLOBAL => { + let global = import.value.global as *mut Global; + Export::Global((&*global).clone()) + } + wasmer_import_export_kind::WASM_TABLE => { + let table = import.value.table as *mut Table; + Export::Table((&*table).clone()) + } + }; + namespace.insert(import_name, export); + } + for (module_name, namespace) in namespaces.into_iter() { + import_object.register(module_name, namespace); + } + + let module = &*(module as *const Module); + let new_instance = if let Ok(res) = module.instantiate(&import_object) { + res + } else { + update_last_error(CApiError { + msg: "error instantiating from module".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t; + wasmer_result_t::WASMER_OK +} + +/// Serialize the given Module. +/// +/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_serialize( + serialized_module: *mut *mut wasmer_serialized_module_t, + module: *const wasmer_module_t, +) -> wasmer_result_t { + let module = &*(module as *const Module); + + match module.cache() { + Ok(artifact) => match artifact.serialize() { + Ok(serialized_artifact) => { + *serialized_module = Box::into_raw(Box::new(serialized_artifact)) as _; + + wasmer_result_t::WASMER_OK + } + Err(_) => { + update_last_error(CApiError { + msg: "Failed to serialize the module artifact".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + }, + Err(_) => { + update_last_error(CApiError { + msg: "Failed to serialize the module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Get bytes of the serialized module. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_serialized_module_bytes( + serialized_module: *const wasmer_serialized_module_t, +) -> wasmer_byte_array { + let serialized_module = &*(serialized_module as *const &[u8]); + + wasmer_byte_array { + bytes: serialized_module.as_ptr(), + bytes_len: serialized_module.len() as u32, + } +} + +/// Transform a sequence of bytes into a serialized module. +/// +/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_serialized_module_from_bytes( + serialized_module: *mut *mut wasmer_serialized_module_t, + serialized_module_bytes: *const uint8_t, + serialized_module_bytes_length: uint32_t, +) -> wasmer_result_t { + if serialized_module.is_null() { + update_last_error(CApiError { + msg: "`serialized_module_bytes` pointer is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let serialized_module_bytes: &[u8] = slice::from_raw_parts( + serialized_module_bytes, + serialized_module_bytes_length as usize, + ); + + *serialized_module = Box::into_raw(Box::new(serialized_module_bytes)) as _; + wasmer_result_t::WASMER_OK +} + +/// Deserialize the given serialized module. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_module_deserialize( + module: *mut *mut wasmer_module_t, + serialized_module: *const wasmer_serialized_module_t, +) -> wasmer_result_t { + if serialized_module.is_null() { + update_last_error(CApiError { + msg: "`serialized_module` pointer is null".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + + let serialized_module: &[u8] = &*(serialized_module as *const &[u8]); + + match Artifact::deserialize(serialized_module) { + Ok(artifact) => match load_cache_with(artifact, default_compiler()) { + Ok(deserialized_module) => { + *module = Box::into_raw(Box::new(deserialized_module)) as _; + wasmer_result_t::WASMER_OK + } + Err(_) => { + update_last_error(CApiError { + msg: "Failed to compile the serialized module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + }, + Err(_) => { + update_last_error(CApiError { + msg: "Failed to deserialize the module".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Frees memory for the given serialized Module. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_serialized_module_destroy( + serialized_module: *mut wasmer_serialized_module_t, +) { + if !serialized_module.is_null() { + unsafe { Box::from_raw(serialized_module as *mut &[u8]) }; + } +} + +/// Frees memory for the given Module +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_module_destroy(module: *mut wasmer_module_t) { + if !module.is_null() { + unsafe { Box::from_raw(module as *mut Module) }; + } +} diff --git a/lib/runtime-c-api/src/table.rs b/lib/runtime-c-api/src/table.rs new file mode 100644 index 000000000..84d3b794f --- /dev/null +++ b/lib/runtime-c-api/src/table.rs @@ -0,0 +1,86 @@ +//! Wasm tables. + +use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; +use libc::uint32_t; +use wasmer_runtime::Table; +use wasmer_runtime_core::types::{ElementType, TableDescriptor}; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_table_t; + +/// Creates a new Table for the given descriptor and initializes the given +/// pointer to pointer to a pointer to the new Table. +/// +/// The caller owns the object and should call `wasmer_table_destroy` to free it. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[no_mangle] +pub unsafe extern "C" fn wasmer_table_new( + table: *mut *mut wasmer_table_t, + limits: wasmer_limits_t, +) -> wasmer_result_t { + let max = if limits.max.has_some { + Some(limits.max.some) + } else { + None + }; + let desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: limits.min, + maximum: max, + }; + let result = Table::new(desc); + let new_table = match result { + Ok(table) => table, + Err(error) => { + update_last_error(error); + return wasmer_result_t::WASMER_ERROR; + } + }; + *table = Box::into_raw(Box::new(new_table)) as *mut wasmer_table_t; + wasmer_result_t::WASMER_OK +} + +/// Grows a Table by the given number of elements. +/// +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_grow( + table: *mut wasmer_table_t, + delta: uint32_t, +) -> wasmer_result_t { + let table = unsafe { &*(table as *mut Table) }; + let delta_result = table.grow(delta); + match delta_result { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(grow_error) => { + update_last_error(grow_error); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Returns the current length of the given Table +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_length(table: *mut wasmer_table_t) -> uint32_t { + let table = unsafe { &*(table as *mut Table) }; + table.size() +} + +/// Frees memory for the given Table +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_table_destroy(table: *mut wasmer_table_t) { + if !table.is_null() { + unsafe { Box::from_raw(table as *mut Table) }; + } +} diff --git a/lib/runtime-c-api/src/value.rs b/lib/runtime-c-api/src/value.rs new file mode 100644 index 000000000..c0a3e64d8 --- /dev/null +++ b/lib/runtime-c-api/src/value.rs @@ -0,0 +1,119 @@ +//! Wasm values. + +use libc::{int32_t, int64_t}; +use wasmer_runtime::Value; +use wasmer_runtime_core::types::Type; + +#[allow(non_camel_case_types)] +#[repr(u32)] +#[derive(Clone)] +pub enum wasmer_value_tag { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(non_snake_case)] +pub union wasmer_value { + pub I32: int32_t, + pub I64: int64_t, + pub F32: f32, + pub F64: f64, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_value_t { + pub tag: wasmer_value_tag, + pub value: wasmer_value, +} + +impl From for Value { + fn from(v: wasmer_value_t) -> Self { + unsafe { + #[allow(unreachable_patterns, non_snake_case)] + match v { + wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32 }, + } => Value::I32(I32), + wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64 }, + } => Value::I64(I64), + wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32 }, + } => Value::F32(F32), + wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64 }, + } => Value::F64(F64), + _ => panic!("not implemented"), + } + } + } +} + +impl From for wasmer_value_t { + fn from(val: Value) -> Self { + match val { + Value::I32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32: x }, + }, + Value::I64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64: x }, + }, + Value::F32(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32: x }, + }, + Value::F64(x) => wasmer_value_t { + tag: wasmer_value_tag::WASM_F64, + value: wasmer_value { F64: x }, + }, + } + } +} + +impl From for wasmer_value_tag { + fn from(ty: Type) -> Self { + #[allow(unreachable_patterns)] + match ty { + Type::I32 => wasmer_value_tag::WASM_I32, + Type::I64 => wasmer_value_tag::WASM_I64, + Type::F32 => wasmer_value_tag::WASM_F32, + Type::F64 => wasmer_value_tag::WASM_F64, + _ => panic!("not implemented"), + } + } +} + +impl From for Type { + fn from(v: wasmer_value_tag) -> Self { + #[allow(unreachable_patterns)] + match v { + wasmer_value_tag::WASM_I32 => Type::I32, + wasmer_value_tag::WASM_I64 => Type::I64, + wasmer_value_tag::WASM_F32 => Type::F32, + wasmer_value_tag::WASM_F64 => Type::F64, + _ => panic!("not implemented"), + } + } +} + +impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag { + fn from(ty: &Type) -> Self { + match *ty { + Type::I32 => wasmer_value_tag::WASM_I32, + Type::I64 => wasmer_value_tag::WASM_I64, + Type::F32 => wasmer_value_tag::WASM_F32, + Type::F64 => wasmer_value_tag::WASM_F64, + } + } +} diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index 6ea57c28d..ed3b6ed6e 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -9,12 +9,17 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps -test-globals +rust-build +test-exported-memory test-exports -test-instantiate +test-globals test-import-function +test-imports +test-instantiate test-memory test-module +test-module-exports +test-module-imports +test-module-serialize test-tables -test-validate -rust-build \ No newline at end of file +test-validate \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 788ee1c6a..ef9804f01 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -1,14 +1,19 @@ cmake_minimum_required (VERSION 2.6) -project (WasmerCApiTests) +project (WasmerRuntimeCApiTests) +add_executable(test-exported-memory test-exported-memory.c) add_executable(test-exports test-exports.c) add_executable(test-globals test-globals.c) -add_executable(test-instantiate test-instantiate.c) add_executable(test-import-function test-import-function.c) +add_executable(test-imports test-imports.c) +add_executable(test-instantiate test-instantiate.c) add_executable(test-memory test-memory.c) add_executable(test-module test-module.c) -add_executable(test-validate test-validate.c) +add_executable(test-module-exports test-module-exports.c) +add_executable(test-module-imports test-module-imports.c) +add_executable(test-module-serialize test-module-serialize.c) add_executable(test-tables test-tables.c) +add_executable(test-validate test-validate.c) find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll @@ -19,30 +24,66 @@ if(NOT WASMER_LIB) message(FATAL_ERROR "wasmer library not found") endif() -target_link_libraries(test-exports - general ${WASMER_LIB}) -target_link_libraries(test-globals - general ${WASMER_LIB}) -target_link_libraries(test-instantiate - general ${WASMER_LIB}) -target_link_libraries(test-import-function - general ${WASMER_LIB}) -target_link_libraries(test-memory - general ${WASMER_LIB}) -target_link_libraries(test-module - general ${WASMER_LIB}) -target_link_libraries(test-validate - general ${WASMER_LIB}) -target_link_libraries(test-tables - general ${WASMER_LIB}) - enable_testing() + +set( + COMPILER_OPTIONS + # Clang or gcc + $<$,$>: + "-Werror" > + # MSVC + $<$: + "/WX" > +) + +target_link_libraries(test-exported-memory general ${WASMER_LIB}) +target_compile_options(test-exported-memory PRIVATE ${COMPILER_OPTIONS}) +add_test(test-exported-memory test-exported-memory) + +target_link_libraries(test-exports general ${WASMER_LIB}) +target_compile_options(test-exports PRIVATE ${COMPILER_OPTIONS}) add_test(test-exports test-exports) + +target_link_libraries(test-globals general ${WASMER_LIB}) +target_compile_options(test-globals PRIVATE ${COMPILER_OPTIONS}) add_test(test-globals test-globals) -add_test(test-instantiate test-instantiate) + +target_link_libraries(test-import-function general ${WASMER_LIB}) +target_compile_options(test-import-function PRIVATE ${COMPILER_OPTIONS}) add_test(test-import-function test-import-function) + +target_link_libraries(test-imports general ${WASMER_LIB}) +target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-imports test-imports) + +target_link_libraries(test-instantiate general ${WASMER_LIB}) +target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-instantiate test-instantiate) + +target_link_libraries(test-memory general ${WASMER_LIB}) +target_compile_options(test-memory PRIVATE ${COMPILER_OPTIONS}) add_test(test-memory test-memory) + +target_link_libraries(test-module general ${WASMER_LIB}) +target_compile_options(test-module PRIVATE ${COMPILER_OPTIONS}) add_test(test-module test-module) -add_test(test-validate test-validate) + +target_link_libraries(test-module-exports general ${WASMER_LIB}) +target_compile_options(test-module-exports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-exports test-module-exports) + +target_link_libraries(test-module-imports general ${WASMER_LIB}) +target_compile_options(test-module-imports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-imports test-module-imports) + +target_link_libraries(test-module-serialize general ${WASMER_LIB}) +target_compile_options(test-module-serialize PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-serialize test-module-serialize) + +target_link_libraries(test-tables general ${WASMER_LIB}) +target_compile_options(test-tables PRIVATE ${COMPILER_OPTIONS}) add_test(test-tables test-tables) +target_link_libraries(test-validate general ${WASMER_LIB}) +target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-validate test-validate) diff --git a/lib/runtime-c-api/tests/assets/hello_wasm.wasm b/lib/runtime-c-api/tests/assets/hello_wasm.wasm new file mode 100644 index 000000000..b2287be03 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/hello_wasm.wasm differ diff --git a/lib/runtime-c-api/tests/assets/return_hello.rs b/lib/runtime-c-api/tests/assets/return_hello.rs new file mode 100644 index 000000000..39e770193 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/return_hello.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn return_hello() -> *const u8 { + b"Hello, World!\0"[..].as_ptr() +} diff --git a/lib/runtime-c-api/tests/assets/return_hello.wasm b/lib/runtime-c-api/tests/assets/return_hello.wasm new file mode 100644 index 000000000..a7f79c376 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/return_hello.wasm differ diff --git a/lib/runtime-c-api/tests/sum.wasm b/lib/runtime-c-api/tests/assets/sum.wasm similarity index 100% rename from lib/runtime-c-api/tests/sum.wasm rename to lib/runtime-c-api/tests/assets/sum.wasm diff --git a/lib/runtime-c-api/tests/wasm_sample_app.wasm b/lib/runtime-c-api/tests/assets/wasm_sample_app.wasm similarity index 100% rename from lib/runtime-c-api/tests/wasm_sample_app.wasm rename to lib/runtime-c-api/tests/assets/wasm_sample_app.wasm diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs index b605da49e..4349133a7 100644 --- a/lib/runtime-c-api/tests/runtime_c_api_tests.rs +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -3,37 +3,47 @@ use std::process::Command; #[test] fn test_c_api() { let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests"); + run_command("cmake", project_tests_dir, Some(".")); - run_command("make", project_tests_dir, None); + run_command("make", project_tests_dir, Some("-Wdev -Werror=dev")); run_command("make", project_tests_dir, Some("test")); } fn run_command(command_str: &str, dir: &str, arg: Option<&str>) { println!("Running command: `{}` arg: {:?}", command_str, arg); + let mut command = Command::new(command_str); + if let Some(a) = arg { command.arg(a); } + command.current_dir(dir); + let result = command.output(); + match result { Ok(r) => { println!("output:"); + if let Some(code) = r.status.code() { println!("status: {}", code); } else { println!("status: None"); } + println!("stdout:"); println!("{}", String::from_utf8_lossy(&r.stdout[..])); println!("stderr:"); println!("{}", String::from_utf8_lossy(&r.stderr[..])); + if r.status.success() { assert!(true) } else { panic!("Command failed with exit status: {:?}", r.status); } } + Err(e) => panic!("Command failed: {}", e), } } diff --git a/lib/runtime-c-api/tests/test-exported-memory.c b/lib/runtime-c-api/tests/test-exported-memory.c new file mode 100644 index 000000000..455de0402 --- /dev/null +++ b/lib/runtime-c-api/tests/test-exported-memory.c @@ -0,0 +1,73 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/return_hello.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + // Instantiate the module. + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + // Call the `return_hello` function. + wasmer_value_t params[] = {}; + wasmer_value_t result; + wasmer_value_t results[] = {result}; + + wasmer_result_t call_result = wasmer_instance_call(instance, "return_hello", params, 0, results, 1); + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + assert(call_result == WASMER_OK); + assert(results[0].value.I32 == 1048576); + + // Get all exports. + wasmer_exports_t *exports = NULL; + wasmer_instance_exports(instance, &exports); + + int export_length = wasmer_exports_len(exports); + printf("exports_length: %d\n", export_length); + assert(export_length == 5); + + // Get the `memory` export. + wasmer_export_t *export = wasmer_exports_get(exports, 1); + wasmer_import_export_kind kind = wasmer_export_kind(export); + assert(kind == WASM_MEMORY); + + wasmer_byte_array export_name = wasmer_export_name(export); + printf("export_name: `%.*s`\n", export_name.bytes_len, export_name.bytes); + + // Cast the export into a memory. + wasmer_memory_t *memory; + wasmer_result_t export_to_memory_result = wasmer_export_to_memory(export, &memory); + printf("Export to memory result: %d\n", export_to_memory_result); + printf("Memory pointer: %p\n", memory); + assert(export_to_memory_result == WASMER_OK); + + uint32_t memory_length = wasmer_memory_length(memory); + assert(memory_length == 17); + + // Read the data from the memory. + uint8_t *memory_data = wasmer_memory_data(memory); + uint8_t *returned_string = memory_data + results[0].value.I32; + + printf("Returned string from Wasm: %s\n", returned_string); + assert(strcmp("Hello, World!", (const char *) returned_string) == 0); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + + return 0; +} diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c index 3f458ebab..951c4f3a4 100644 --- a/lib/runtime-c-api/tests/test-exports.c +++ b/lib/runtime-c-api/tests/test-exports.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -17,21 +17,21 @@ int main() wasmer_import_t imports[] = {}; wasmer_instance_t *instance = NULL; wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0); - printf("Compile result: %d\n", compile_result); + printf("Compile result: %d\n", compile_result); assert(compile_result == WASMER_OK); wasmer_exports_t *exports = NULL; wasmer_instance_exports(instance, &exports); int exports_len = wasmer_exports_len(exports); - printf("exports_len: %d\n", exports_len); + printf("exports_len: %d\n", exports_len); assert(exports_len == 1); wasmer_export_t *export = wasmer_exports_get(exports, 0); wasmer_import_export_kind kind = wasmer_export_kind(export); assert(kind == WASM_FUNCTION); - wasmer_func_t *func = wasmer_export_to_func(export); + const wasmer_export_func_t *func = wasmer_export_to_func(export); wasmer_byte_array name_bytes = wasmer_export_name(export); assert(name_bytes.bytes_len == 3); @@ -42,40 +42,40 @@ int main() } uint32_t params_arity; - wasmer_func_params_arity(func, ¶ms_arity); + wasmer_export_func_params_arity(func, ¶ms_arity); assert(params_arity == 2); wasmer_value_tag *params_sig = malloc(sizeof(wasmer_value_tag) * params_arity); - wasmer_func_params(func, params_sig , params_arity); + wasmer_export_func_params(func, params_sig , params_arity); assert(params_sig[0] == WASM_I32); assert(params_sig[1] == WASM_I32); free(params_sig); uint32_t returns_arity; - wasmer_func_returns_arity(func, &returns_arity); + wasmer_export_func_returns_arity(func, &returns_arity); assert(returns_arity == 1); wasmer_value_tag *returns_sig = malloc(sizeof(wasmer_value_tag) * returns_arity); - wasmer_func_returns(func, returns_sig , returns_arity); + wasmer_export_func_returns(func, returns_sig , returns_arity); assert(returns_sig[0] == WASM_I32); free(returns_sig); -// wasmer_value_t param_one; -// param_one.tag = WASM_I32; -// param_one.value.I32 = 7; -// wasmer_value_t param_two; -// param_two.tag = WASM_I32; -// param_two.value.I32 = 8; -// wasmer_value_t params[] = {param_one, param_two}; -// wasmer_value_t result_one; -// wasmer_value_t results[] = {result_one}; -// -// wasmer_result_t call_result = wasmer_func_call(func, params, 2, results, 1); -// printf("Call result: %d\n", call_result); -// printf("Result: %d\n", results[0].value.I32); -// assert(results[0].value.I32 == 15); -// assert(call_result == WASMER_OK); + wasmer_value_t param_one; + param_one.tag = WASM_I32; + param_one.value.I32 = 7; + wasmer_value_t param_two; + param_two.tag = WASM_I32; + param_two.value.I32 = 8; + wasmer_value_t params[] = {param_one, param_two}; + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + + wasmer_result_t call_result = wasmer_export_func_call(func, params, 2, results, 1); + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + assert(results[0].value.I32 == 15); + assert(call_result == WASMER_OK); printf("Destroy instance\n"); @@ -83,4 +83,4 @@ int main() printf("Destroy exports\n"); wasmer_exports_destroy(exports); return 0; -} \ No newline at end of file +} diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c index e7caed874..92e3e821d 100644 --- a/lib/runtime-c-api/tests/test-import-function.c +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -4,14 +4,19 @@ #include #include -static print_str_called = false; -static memory_len = 0; -static ptr_len = 0; +static bool print_str_called = false; +static int memory_len = 0; +static int ptr_len = 0; static char actual_str[14] = {}; +static int actual_context_data_value = 0; + +typedef struct { + int value; +} context_data; void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) { - wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); uint32_t mem_len = wasmer_memory_length(memory); uint8_t *mem_bytes = wasmer_memory_data(memory); for (int32_t idx = 0; idx < len; idx++) @@ -23,6 +28,8 @@ void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) print_str_called = true; memory_len = mem_len; ptr_len = len; + + actual_context_data_value = ((context_data *) wasmer_instance_context_data_get(ctx))->value; } int main() @@ -31,16 +38,16 @@ int main() wasmer_value_tag returns_sig[] = {}; printf("Creating new func\n"); - wasmer_func_t *func = wasmer_func_new(print_str, params_sig, 2, returns_sig, 0); + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); wasmer_import_t import; char *module_name = "env"; wasmer_byte_array module_name_bytes; - module_name_bytes.bytes = module_name; + module_name_bytes.bytes = (const uint8_t *) module_name; module_name_bytes.bytes_len = strlen(module_name); char *import_name = "print_str"; wasmer_byte_array import_name_bytes; - import_name_bytes.bytes = import_name; + import_name_bytes.bytes = (const uint8_t *) import_name; import_name_bytes.bytes_len = strlen(import_name); import.module_name = module_name_bytes; @@ -50,7 +57,7 @@ int main() wasmer_import_t imports[] = {import}; // Read the wasm file bytes - FILE *file = fopen("wasm_sample_app.wasm", "r"); + FILE *file = fopen("assets/wasm_sample_app.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -65,6 +72,11 @@ int main() assert(compile_result == WASMER_OK); + context_data* context_data = malloc(sizeof(context_data)); + int context_data_value = 42; + context_data->value = context_data_value; + wasmer_instance_context_data_set(instance, context_data); + wasmer_value_t params[] = {}; wasmer_value_t results[] = {}; wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0); @@ -82,10 +94,12 @@ int main() assert(memory_len == 17); assert(ptr_len == 13); assert(0 == strcmp(actual_str, "Hello, World!")); + assert(context_data_value == actual_context_data_value); printf("Destroying func\n"); - wasmer_func_destroy(func); + wasmer_import_func_destroy(func); printf("Destroy instance\n"); wasmer_instance_destroy(instance); + free(context_data); return 0; -} \ No newline at end of file +} diff --git a/lib/runtime-c-api/tests/test-imports.c b/lib/runtime-c-api/tests/test-imports.c new file mode 100644 index 000000000..4713719ed --- /dev/null +++ b/lib/runtime-c-api/tests/test-imports.c @@ -0,0 +1,155 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +bool static print_str_called = false; + +// Host function that will be imported into the Web Assembly Instance +void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + print_str_called = true; + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + uint32_t mem_len = wasmer_memory_length(memory); + uint8_t *mem_bytes = wasmer_memory_data(memory); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +int main() +{ + // Create a new func to hold the parameter and signature + // of our `print_str` host function + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); + + // Create module name for our imports + // represented in bytes for UTF-8 compatability + const char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + // Define a function import + const char *import_name = "_print_str"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + wasmer_import_t func_import; + func_import.module_name = module_name_bytes; + func_import.import_name = import_name_bytes; + func_import.tag = WASM_FUNCTION; + func_import.value.func = func; + + // Define a memory import + const char *import_memory_name = "memory"; + wasmer_byte_array import_memory_name_bytes; + import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; + import_memory_name_bytes.bytes_len = strlen(import_memory_name); + wasmer_import_t memory_import; + memory_import.module_name = module_name_bytes; + memory_import.import_name = import_memory_name_bytes; + memory_import.tag = WASM_MEMORY; + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 256; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 256; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + if (memory_result != WASMER_OK) + { + print_wasmer_error(); + } + memory_import.value.memory = memory; + + // Define a global import + const char *import_global_name = "__memory_base"; + wasmer_byte_array import_global_name_bytes; + import_global_name_bytes.bytes = (const uint8_t *) import_global_name; + import_global_name_bytes.bytes_len = strlen(import_global_name); + wasmer_import_t global_import; + global_import.module_name = module_name_bytes; + global_import.import_name = import_global_name_bytes; + global_import.tag = WASM_GLOBAL; + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 1024; + wasmer_global_t *global = wasmer_global_new(val, false); + global_import.value.global = global; + + // Define a table import + const char *import_table_name = "table"; + wasmer_byte_array import_table_name_bytes; + import_table_name_bytes.bytes = (const uint8_t *) import_table_name; + import_table_name_bytes.bytes_len = strlen(import_table_name); + wasmer_import_t table_import; + table_import.module_name = module_name_bytes; + table_import.import_name = import_table_name_bytes; + table_import.tag = WASM_TABLE; + wasmer_table_t *table = NULL; + wasmer_limits_t table_descriptor; + table_descriptor.min = 256; + wasmer_limit_option_t table_max; + table_max.has_some = true; + table_max.some = 256; + table_descriptor.max = table_max; + wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); + if (table_result != WASMER_OK) + { + print_wasmer_error(); + } + table_import.value.table = table; + + // Define an array containing our imports + wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; + + // Read the wasm file bytes + FILE *file = fopen("assets/hello_wasm.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + // Creates a WebAssembly Instance from wasm bytes and imports + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 4); + printf("Compile result: %d\n", compile_result); + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(compile_result == WASMER_OK); + + // Call the exported "hello_wasm" function of our instance + wasmer_value_t params[] = {}; + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1); + printf("Call result: %d\n", call_result); + assert(call_result == WASMER_OK); + assert(print_str_called); + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_import_func_destroy(func); + wasmer_global_destroy(global); + wasmer_memory_destroy(memory); + wasmer_table_destroy(table); + wasmer_instance_destroy(instance); + + return 0; +} diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c index 2a675de12..20332623e 100644 --- a/lib/runtime-c-api/tests/test-instantiate.c +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -2,11 +2,12 @@ #include "../wasmer.h" #include #include +#include int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -53,4 +54,4 @@ int main() printf("Destroy instance\n"); wasmer_instance_destroy(instance); return 0; -} \ No newline at end of file +} diff --git a/lib/runtime-c-api/tests/test-memory.c b/lib/runtime-c-api/tests/test-memory.c index 64e028693..9adf9215c 100644 --- a/lib/runtime-c-api/tests/test-memory.c +++ b/lib/runtime-c-api/tests/test-memory.c @@ -2,6 +2,7 @@ #include "../wasmer.h" #include #include +#include int main() { diff --git a/lib/runtime-c-api/tests/test-module-exports.c b/lib/runtime-c-api/tests/test-module-exports.c new file mode 100644 index 000000000..f290ef45b --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-exports.c @@ -0,0 +1,53 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/sum.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_instantiate(module, &instance, imports, 0); + printf("Instantiate result: %d\n", compile_result); + assert(instantiate_result == WASMER_OK); + + wasmer_export_descriptors_t *exports = NULL; + wasmer_export_descriptors(module, &exports); + + int exports_len = wasmer_export_descriptors_len(exports); + printf("exports_len: %d\n", exports_len); + assert(exports_len == 1); + + wasmer_export_descriptor_t *export = wasmer_export_descriptors_get(exports, 0); + + wasmer_import_export_kind kind = wasmer_export_descriptor_kind(export); + assert(kind == WASM_FUNCTION); + + wasmer_byte_array name_bytes = wasmer_export_descriptor_name(export); + assert(name_bytes.bytes_len == 3); + char expected[] = {'s', 'u', 'm'}; + for(int idx = 0; idx < 3; idx++){ + printf("%c\n", name_bytes.bytes[idx]); + assert(name_bytes.bytes[idx] == expected[idx]); + } + + printf("Destroy module\n"); + wasmer_module_destroy(module); + printf("Destroy exports\n"); + wasmer_export_descriptors_destroy(exports); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module-imports.c b/lib/runtime-c-api/tests/test-module-imports.c new file mode 100644 index 000000000..532005928 --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-imports.c @@ -0,0 +1,56 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/wasm_sample_app.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_import_descriptors_t *imports = NULL; + wasmer_import_descriptors(module, &imports); + + int imports_len = wasmer_import_descriptors_len(imports); + printf("imports_len: %d\n", imports_len); + assert(imports_len == 1); + + wasmer_import_descriptor_t *import = wasmer_import_descriptors_get(imports, 0); + + wasmer_import_export_kind kind = wasmer_import_descriptor_kind(import); + assert(kind == WASM_FUNCTION); + + wasmer_byte_array name_bytes = wasmer_import_descriptor_name(import); + assert(name_bytes.bytes_len == 9); + char expected[] = {'p', 'r', 'i', 'n', 't', '_', 's', 't', 'r'}; + + for(int idx = 0; idx < 9; idx++){ + printf("%c\n", name_bytes.bytes[idx]); + assert(name_bytes.bytes[idx] == expected[idx]); + } + + wasmer_byte_array module_name_bytes = wasmer_import_descriptor_module_name(import); + assert(module_name_bytes.bytes_len == 3); + char module_expected[] = {'e', 'n', 'v'}; + for(int idx = 0; idx < 3; idx++){ + printf("%c\n", module_name_bytes.bytes[idx]); + assert(module_name_bytes.bytes[idx] == module_expected[idx]); + } + + printf("Destroy module\n"); + wasmer_module_destroy(module); + printf("Destroy imports\n"); + wasmer_import_descriptors_destroy(imports); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module-serialize.c b/lib/runtime-c-api/tests/test-module-serialize.c new file mode 100644 index 000000000..b98418646 --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-serialize.c @@ -0,0 +1,93 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // Read the wasm file bytes + FILE *file = fopen("assets/sum.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module_one = NULL; + wasmer_result_t compile_result = wasmer_compile(&module_one, bytes, len); + printf("Compile result: %d\n", compile_result); + assert(compile_result == WASMER_OK); + + wasmer_serialized_module_t *serialized_module = NULL; + wasmer_result_t serialize_result = wasmer_module_serialize(&serialized_module, module_one); + printf("Serialize result: %d\n", serialize_result); + assert(serialize_result == WASMER_OK); + + wasmer_byte_array serialized_module_bytes = wasmer_serialized_module_bytes(serialized_module); + printf("Serialized module pointer: %p\n", serialized_module_bytes.bytes); + printf("Serialized module length: %d\n", serialized_module_bytes.bytes_len); + assert(serialized_module_bytes.bytes != NULL); + assert(serialized_module_bytes.bytes_len > 8); + assert(serialized_module_bytes.bytes[0] == 'W'); + assert(serialized_module_bytes.bytes[1] == 'A'); + assert(serialized_module_bytes.bytes[2] == 'S'); + assert(serialized_module_bytes.bytes[3] == 'M'); + assert(serialized_module_bytes.bytes[4] == 'E'); + assert(serialized_module_bytes.bytes[5] == 'R'); + + wasmer_module_t *module_two = NULL; + wasmer_result_t unserialize_result = wasmer_module_deserialize(&module_two, serialized_module); + assert(unserialize_result == WASMER_OK); + + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_instantiate(module_two, &instance, imports, 0); + printf("Instantiate result: %d\n", compile_result); + assert(instantiate_result == WASMER_OK); + + wasmer_value_t param_one; + param_one.tag = WASM_I32; + param_one.value.I32 = 7; + wasmer_value_t param_two; + param_two.tag = WASM_I32; + param_two.value.I32 = 8; + wasmer_value_t params[] = {param_one, param_two}; + + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + + wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1); + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + assert(results[0].value.I32 == 15); + assert(call_result == WASMER_OK); + + wasmer_serialized_module_t *serialized_module_two = NULL; + wasmer_result_t serialized_module_from_bytes_result = wasmer_serialized_module_from_bytes( + &serialized_module_two, + serialized_module_bytes.bytes, + serialized_module_bytes.bytes_len + ); + assert(serialized_module_from_bytes_result == WASMER_OK); + + wasmer_module_t *module_three = NULL; + wasmer_result_t unserialized_result_two = wasmer_module_deserialize(&module_three, serialized_module_two); + assert(unserialized_result_two == WASMER_OK); + + wasmer_instance_t *instance_two = NULL; + wasmer_result_t instantiate_result_two = wasmer_module_instantiate(module_three, &instance, imports, 0); + assert(instantiate_result_two == WASMER_OK); + + printf("Destroy the serialized module\n"); + wasmer_serialized_module_destroy(serialized_module); + wasmer_serialized_module_destroy(serialized_module_two); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + + printf("Destroy modules\n"); + wasmer_module_destroy(module_one); + wasmer_module_destroy(module_two); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module.c b/lib/runtime-c-api/tests/test-module.c index 062caf5b8..a3f21f6a1 100644 --- a/lib/runtime-c-api/tests/test-module.c +++ b/lib/runtime-c-api/tests/test-module.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); @@ -48,4 +48,4 @@ int main() printf("Destroy module\n"); wasmer_module_destroy(module); return 0; -} \ No newline at end of file +} diff --git a/lib/runtime-c-api/tests/test-validate.c b/lib/runtime-c-api/tests/test-validate.c index 689cf50f2..8ce135d82 100644 --- a/lib/runtime-c-api/tests/test-validate.c +++ b/lib/runtime-c-api/tests/test-validate.c @@ -6,7 +6,7 @@ int main() { // Read the wasm file bytes - FILE *file = fopen("sum.wasm", "r"); + FILE *file = fopen("assets/sum.wasm", "r"); fseek(file, 0, SEEK_END); long len = ftell(file); uint8_t *bytes = malloc(len); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 6c0494e01..d11deab11 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -27,15 +27,13 @@ enum wasmer_value_tag { }; typedef uint32_t wasmer_value_tag; -typedef struct wasmer_instance_context_t wasmer_instance_context_t; +typedef struct { -typedef struct wasmer_instance_t wasmer_instance_t; - -typedef struct wasmer_module_t wasmer_module_t; +} wasmer_module_t; typedef struct { -} wasmer_export_t; +} wasmer_export_descriptor_t; typedef struct { const uint8_t *bytes; @@ -44,11 +42,11 @@ typedef struct { typedef struct { -} wasmer_func_t; +} wasmer_export_descriptors_t; typedef struct { -} wasmer_exports_t; +} wasmer_export_func_t; typedef union { int32_t I32; @@ -64,6 +62,18 @@ typedef struct { typedef struct { +} wasmer_export_t; + +typedef struct { + +} wasmer_memory_t; + +typedef struct { + +} wasmer_exports_t; + +typedef struct { + } wasmer_global_t; typedef struct { @@ -73,14 +83,30 @@ typedef struct { typedef struct { -} wasmer_memory_t; +} wasmer_import_descriptor_t; + +typedef struct { + +} wasmer_import_descriptors_t; + +typedef struct { + +} wasmer_import_func_t; + +typedef struct { + +} wasmer_instance_t; + +typedef struct { + +} wasmer_instance_context_t; typedef struct { } wasmer_table_t; typedef union { - const wasmer_func_t *func; + const wasmer_import_func_t *func; const wasmer_table_t *table; const wasmer_memory_t *memory; const wasmer_global_t *global; @@ -103,6 +129,10 @@ typedef struct { wasmer_limit_option_t max; } wasmer_limits_t; +typedef struct { + +} wasmer_serialized_module_t; + /** * Creates a new Module from the given wasm bytes. * Returns `wasmer_result_t::WASMER_OK` upon success. @@ -113,6 +143,89 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +/** + * Gets export descriptor kind + */ +wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); + +/** + * Gets name for the export descriptor + */ +wasmer_byte_array wasmer_export_descriptor_name(wasmer_export_descriptor_t *export_descriptor); + +/** + * Gets export descriptors for the given module + * The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. + */ +void wasmer_export_descriptors(const wasmer_module_t *module, + wasmer_export_descriptors_t **export_descriptors); + +/** + * Frees the memory for the given export descriptors + */ +void wasmer_export_descriptors_destroy(wasmer_export_descriptors_t *export_descriptors); + +/** + * Gets export descriptor by index + */ +wasmer_export_descriptor_t *wasmer_export_descriptors_get(wasmer_export_descriptors_t *export_descriptors, + int idx); + +/** + * Gets the length of the export descriptors + */ +int wasmer_export_descriptors_len(wasmer_export_descriptors_t *exports); + +/** + * Calls a `func` with the provided parameters. + * Results are set using the provided `results` pointer. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_func_call(const wasmer_export_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/** + * Sets the params buffer to the parameter types of the given wasmer_export_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_func_params(const wasmer_export_func_t *func, + wasmer_value_tag *params, + int params_len); + +/** + * Sets the result parameter to the arity of the params of the wasmer_export_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_func_params_arity(const wasmer_export_func_t *func, uint32_t *result); + +/** + * Sets the returns buffer to the parameter types of the given wasmer_export_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_func_returns(const wasmer_export_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the result parameter to the arity of the returns of the wasmer_export_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_func_returns_arity(const wasmer_export_func_t *func, + uint32_t *result); + /** * Gets wasmer_export kind */ @@ -124,9 +237,17 @@ wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_); wasmer_byte_array wasmer_export_name(wasmer_export_t *export_); /** - * Gets func from wasm_export + * Gets export func from export */ -const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_); +const wasmer_export_func_t *wasmer_export_to_func(const wasmer_export_t *export_); + +/** + * Gets a memory pointer from an export pointer. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_export_to_memory(const wasmer_export_t *export_, wasmer_memory_t **memory); /** * Frees the memory for the given exports @@ -143,68 +264,6 @@ wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx); */ int wasmer_exports_len(wasmer_exports_t *exports); -/** - * Calls a `func` with the provided parameters. - * Results are set using the provided `results` pointer. - * Returns `wasmer_result_t::WASMER_OK` upon success. - * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` - * and `wasmer_last_error_message` to get an error message. - */ -wasmer_result_t wasmer_func_call(wasmer_func_t *func, - const wasmer_value_t *params, - int params_len, - wasmer_value_t *results, - int results_len); - -/** - * Frees memory for the given Func - */ -void wasmer_func_destroy(wasmer_func_t *func); - -/** - * Creates new func - * The caller owns the object and should call `wasmer_func_destroy` to free it. - */ -const wasmer_func_t *wasmer_func_new(void (*func)(void *data), - const wasmer_value_tag *params, - int params_len, - const wasmer_value_tag *returns, - int returns_len); - -/** - * Sets the params buffer to the parameter types of the given wasmer_func_t - * Returns `wasmer_result_t::WASMER_OK` upon success. - * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` - * and `wasmer_last_error_message` to get an error message. - */ -wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len); - -/** - * Sets the result parameter to the arity of the params of the wasmer_func_t - * Returns `wasmer_result_t::WASMER_OK` upon success. - * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` - * and `wasmer_last_error_message` to get an error message. - */ -wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result); - -/** - * Sets the returns buffer to the parameter types of the given wasmer_func_t - * Returns `wasmer_result_t::WASMER_OK` upon success. - * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` - * and `wasmer_last_error_message` to get an error message. - */ -wasmer_result_t wasmer_func_returns(wasmer_func_t *func, - wasmer_value_tag *returns, - int returns_len); - -/** - * Sets the result parameter to the arity of the returns of the wasmer_func_t - * Returns `wasmer_result_t::WASMER_OK` upon success. - * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` - * and `wasmer_last_error_message` to get an error message. - */ -wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result); - /** * Frees memory for the given Global */ @@ -231,6 +290,96 @@ wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_); */ void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value); +/** + * Gets export descriptor kind + */ +wasmer_import_export_kind wasmer_import_descriptor_kind(wasmer_import_descriptor_t *export_); + +/** + * Gets module name for the import descriptor + */ +wasmer_byte_array wasmer_import_descriptor_module_name(wasmer_import_descriptor_t *import_descriptor); + +/** + * Gets name for the import descriptor + */ +wasmer_byte_array wasmer_import_descriptor_name(wasmer_import_descriptor_t *import_descriptor); + +/** + * Gets import descriptors for the given module + * The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. + */ +void wasmer_import_descriptors(const wasmer_module_t *module, + wasmer_import_descriptors_t **import_descriptors); + +/** + * Frees the memory for the given import descriptors + */ +void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descriptors); + +/** + * Gets import descriptor by index + */ +wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, + int idx); + +/** + * Gets the length of the import descriptors + */ +int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); + +/** + * Frees memory for the given Func + */ +void wasmer_import_func_destroy(wasmer_import_func_t *func); + +/** + * Creates new func + * The caller owns the object and should call `wasmer_import_func_destroy` to free it. + */ +wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data), + const wasmer_value_tag *params, + int params_len, + const wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the params buffer to the parameter types of the given wasmer_import_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func, + wasmer_value_tag *params, + int params_len); + +/** + * Sets the result parameter to the arity of the params of the wasmer_import_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_import_func_params_arity(const wasmer_import_func_t *func, uint32_t *result); + +/** + * Sets the returns buffer to the parameter types of the given wasmer_import_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the result parameter to the arity of the returns of the wasmer_import_func_t + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func, + uint32_t *result); + /** * Calls an instances exported function by `name` with the provided parameters. * Results are set using the provided `results` pointer. @@ -245,12 +394,23 @@ wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance, wasmer_value_t *results, int results_len); +/** + * Gets the `data` field within the context. + */ +void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); + +/** + * Sets the `data` field of the instance context. This context will be + * passed to all imported function for instance. + */ +void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr); + /** * Gets the memory within the context at the index `memory_idx`. * The index is always 0 until multiple memories are supported. */ -const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx, - uint32_t memory_idx); +const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_context_t *ctx, + uint32_t _memory_idx); /** * Frees memory for the given Instance @@ -280,7 +440,7 @@ wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance, * This can be used to dynamically allocate a buffer with the correct number of * bytes needed to store a message. * # Example - * ``` + * ```c * int error_len = wasmer_last_error_length(); * char *error_str = malloc(error_len); * ``` @@ -293,7 +453,7 @@ int wasmer_last_error_length(void); * Returns the length of the string in bytes. * Returns `-1` if an error occurs. * # Example - * ``` + * ```c * int error_len = wasmer_last_error_length(); * char *error_str = malloc(error_len); * wasmer_last_error_message(error_str, error_len); @@ -305,7 +465,7 @@ int wasmer_last_error_message(char *buffer, int length); /** * Gets the start pointer to the bytes within a Memory */ -uint8_t *wasmer_memory_data(wasmer_memory_t *mem); +uint8_t *wasmer_memory_data(const wasmer_memory_t *mem); /** * Gets the size in bytes of a Memory @@ -328,7 +488,7 @@ wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta); /** * Returns the current length in pages of the given memory */ -uint32_t wasmer_memory_length(wasmer_memory_t *memory); +uint32_t wasmer_memory_length(const wasmer_memory_t *memory); /** * Creates a new Memory for the given descriptor and initializes the given @@ -340,6 +500,15 @@ uint32_t wasmer_memory_length(wasmer_memory_t *memory); */ wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); +/** + * Deserialize the given serialized module. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module, + const wasmer_serialized_module_t *serialized_module); + /** * Frees memory for the given Module */ @@ -351,11 +520,42 @@ void wasmer_module_destroy(wasmer_module_t *module); * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` * and `wasmer_last_error_message` to get an error message. */ -wasmer_result_t wasmer_module_instantiate(wasmer_module_t *module, +wasmer_result_t wasmer_module_instantiate(const wasmer_module_t *module, wasmer_instance_t **instance, wasmer_import_t *imports, int imports_len); +/** + * Serialize the given Module. + * The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_module_serialize(wasmer_serialized_module_t **serialized_module, + const wasmer_module_t *module); + +/** + * Get bytes of the serialized module. + */ +wasmer_byte_array wasmer_serialized_module_bytes(const wasmer_serialized_module_t *serialized_module); + +/** + * Frees memory for the given serialized Module. + */ +void wasmer_serialized_module_destroy(wasmer_serialized_module_t *serialized_module); + +/** + * Transform a sequence of bytes into a serialized module. + * The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. + * Returns `wasmer_result_t::WASMER_OK` upon success. + * Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` + * and `wasmer_last_error_message` to get an error message. + */ +wasmer_result_t wasmer_serialized_module_from_bytes(wasmer_serialized_module_t **serialized_module, + const uint8_t *serialized_module_bytes, + uint32_t serialized_module_bytes_length); + /** * Frees memory for the given Table */ @@ -387,6 +587,6 @@ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits) /** * Returns true for valid wasm bytes and false for invalid bytes */ -bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); #endif /* WASMER_H */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 2bb55de45..7b22732d0 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -24,13 +24,11 @@ enum class wasmer_value_tag : uint32_t { WASM_F64, }; -struct wasmer_instance_context_t; +struct wasmer_module_t { -struct wasmer_instance_t; +}; -struct wasmer_module_t; - -struct wasmer_export_t { +struct wasmer_export_descriptor_t { }; @@ -39,11 +37,11 @@ struct wasmer_byte_array { uint32_t bytes_len; }; -struct wasmer_func_t { +struct wasmer_export_descriptors_t { }; -struct wasmer_exports_t { +struct wasmer_export_func_t { }; @@ -59,6 +57,18 @@ struct wasmer_value_t { wasmer_value value; }; +struct wasmer_export_t { + +}; + +struct wasmer_memory_t { + +}; + +struct wasmer_exports_t { + +}; + struct wasmer_global_t { }; @@ -68,7 +78,23 @@ struct wasmer_global_descriptor_t { wasmer_value_tag kind; }; -struct wasmer_memory_t { +struct wasmer_import_descriptor_t { + +}; + +struct wasmer_import_descriptors_t { + +}; + +struct wasmer_import_func_t { + +}; + +struct wasmer_instance_t { + +}; + +struct wasmer_instance_context_t { }; @@ -77,7 +103,7 @@ struct wasmer_table_t { }; union wasmer_import_export_value { - const wasmer_func_t *func; + const wasmer_import_func_t *func; const wasmer_table_t *table; const wasmer_memory_t *memory; const wasmer_global_t *global; @@ -100,6 +126,10 @@ struct wasmer_limits_t { wasmer_limit_option_t max; }; +struct wasmer_serialized_module_t { + +}; + extern "C" { /// Creates a new Module from the given wasm bytes. @@ -110,14 +140,81 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +/// Gets export descriptor kind +wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); + +/// Gets name for the export descriptor +wasmer_byte_array wasmer_export_descriptor_name(wasmer_export_descriptor_t *export_descriptor); + +/// Gets export descriptors for the given module +/// The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. +void wasmer_export_descriptors(const wasmer_module_t *module, + wasmer_export_descriptors_t **export_descriptors); + +/// Frees the memory for the given export descriptors +void wasmer_export_descriptors_destroy(wasmer_export_descriptors_t *export_descriptors); + +/// Gets export descriptor by index +wasmer_export_descriptor_t *wasmer_export_descriptors_get(wasmer_export_descriptors_t *export_descriptors, + int idx); + +/// Gets the length of the export descriptors +int wasmer_export_descriptors_len(wasmer_export_descriptors_t *exports); + +/// Calls a `func` with the provided parameters. +/// Results are set using the provided `results` pointer. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_func_call(const wasmer_export_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/// Sets the params buffer to the parameter types of the given wasmer_export_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_func_params(const wasmer_export_func_t *func, + wasmer_value_tag *params, + int params_len); + +/// Sets the result parameter to the arity of the params of the wasmer_export_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_func_params_arity(const wasmer_export_func_t *func, uint32_t *result); + +/// Sets the returns buffer to the parameter types of the given wasmer_export_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_func_returns(const wasmer_export_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/// Sets the result parameter to the arity of the returns of the wasmer_export_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_func_returns_arity(const wasmer_export_func_t *func, + uint32_t *result); + /// Gets wasmer_export kind wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_); /// Gets name from wasmer_export wasmer_byte_array wasmer_export_name(wasmer_export_t *export_); -/// Gets func from wasm_export -const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_); +/// Gets export func from export +const wasmer_export_func_t *wasmer_export_to_func(const wasmer_export_t *export_); + +/// Gets a memory pointer from an export pointer. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_export_to_memory(const wasmer_export_t *export_, wasmer_memory_t **memory); /// Frees the memory for the given exports void wasmer_exports_destroy(wasmer_exports_t *exports); @@ -128,54 +225,6 @@ wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx); /// Gets the length of the exports int wasmer_exports_len(wasmer_exports_t *exports); -/// Calls a `func` with the provided parameters. -/// Results are set using the provided `results` pointer. -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_func_call(wasmer_func_t *func, - const wasmer_value_t *params, - int params_len, - wasmer_value_t *results, - int results_len); - -/// Frees memory for the given Func -void wasmer_func_destroy(wasmer_func_t *func); - -/// Creates new func -/// The caller owns the object and should call `wasmer_func_destroy` to free it. -const wasmer_func_t *wasmer_func_new(void (*func)(void *data), - const wasmer_value_tag *params, - int params_len, - const wasmer_value_tag *returns, - int returns_len); - -/// Sets the params buffer to the parameter types of the given wasmer_func_t -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len); - -/// Sets the result parameter to the arity of the params of the wasmer_func_t -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result); - -/// Sets the returns buffer to the parameter types of the given wasmer_func_t -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_func_returns(wasmer_func_t *func, - wasmer_value_tag *returns, - int returns_len); - -/// Sets the result parameter to the arity of the returns of the wasmer_func_t -/// Returns `wasmer_result_t::WASMER_OK` upon success. -/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` -/// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result); - /// Frees memory for the given Global void wasmer_global_destroy(wasmer_global_t *global); @@ -192,6 +241,70 @@ wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_); /// Sets the value stored by the given Global void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value); +/// Gets export descriptor kind +wasmer_import_export_kind wasmer_import_descriptor_kind(wasmer_import_descriptor_t *export_); + +/// Gets module name for the import descriptor +wasmer_byte_array wasmer_import_descriptor_module_name(wasmer_import_descriptor_t *import_descriptor); + +/// Gets name for the import descriptor +wasmer_byte_array wasmer_import_descriptor_name(wasmer_import_descriptor_t *import_descriptor); + +/// Gets import descriptors for the given module +/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. +void wasmer_import_descriptors(const wasmer_module_t *module, + wasmer_import_descriptors_t **import_descriptors); + +/// Frees the memory for the given import descriptors +void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descriptors); + +/// Gets import descriptor by index +wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, + int idx); + +/// Gets the length of the import descriptors +int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); + +/// Frees memory for the given Func +void wasmer_import_func_destroy(wasmer_import_func_t *func); + +/// Creates new func +/// The caller owns the object and should call `wasmer_import_func_destroy` to free it. +wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data), + const wasmer_value_tag *params, + int params_len, + const wasmer_value_tag *returns, + int returns_len); + +/// Sets the params buffer to the parameter types of the given wasmer_import_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func, + wasmer_value_tag *params, + int params_len); + +/// Sets the result parameter to the arity of the params of the wasmer_import_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_import_func_params_arity(const wasmer_import_func_t *func, uint32_t *result); + +/// Sets the returns buffer to the parameter types of the given wasmer_import_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/// Sets the result parameter to the arity of the returns of the wasmer_import_func_t +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func, + uint32_t *result); + /// Calls an instances exported function by `name` with the provided parameters. /// Results are set using the provided `results` pointer. /// Returns `wasmer_result_t::WASMER_OK` upon success. @@ -204,10 +317,17 @@ wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance, wasmer_value_t *results, int results_len); +/// Gets the `data` field within the context. +void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); + +/// Sets the `data` field of the instance context. This context will be +/// passed to all imported function for instance. +void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr); + /// Gets the memory within the context at the index `memory_idx`. /// The index is always 0 until multiple memories are supported. -const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx, - uint32_t memory_idx); +const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_context_t *ctx, + uint32_t _memory_idx); /// Frees memory for the given Instance void wasmer_instance_destroy(wasmer_instance_t *instance); @@ -230,7 +350,7 @@ wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance, /// This can be used to dynamically allocate a buffer with the correct number of /// bytes needed to store a message. /// # Example -/// ``` +/// ```c /// int error_len = wasmer_last_error_length(); /// char *error_str = malloc(error_len); /// ``` @@ -241,7 +361,7 @@ int wasmer_last_error_length(); /// Returns the length of the string in bytes. /// Returns `-1` if an error occurs. /// # Example -/// ``` +/// ```c /// int error_len = wasmer_last_error_length(); /// char *error_str = malloc(error_len); /// wasmer_last_error_message(error_str, error_len); @@ -250,7 +370,7 @@ int wasmer_last_error_length(); int wasmer_last_error_message(char *buffer, int length); /// Gets the start pointer to the bytes within a Memory -uint8_t *wasmer_memory_data(wasmer_memory_t *mem); +uint8_t *wasmer_memory_data(const wasmer_memory_t *mem); /// Gets the size in bytes of a Memory uint32_t wasmer_memory_data_length(wasmer_memory_t *mem); @@ -265,7 +385,7 @@ void wasmer_memory_destroy(wasmer_memory_t *memory); wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta); /// Returns the current length in pages of the given memory -uint32_t wasmer_memory_length(wasmer_memory_t *memory); +uint32_t wasmer_memory_length(const wasmer_memory_t *memory); /// Creates a new Memory for the given descriptor and initializes the given /// pointer to pointer to a pointer to the new memory. @@ -275,6 +395,13 @@ uint32_t wasmer_memory_length(wasmer_memory_t *memory); /// and `wasmer_last_error_message` to get an error message. wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits); +/// Deserialize the given serialized module. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module, + const wasmer_serialized_module_t *serialized_module); + /// Frees memory for the given Module void wasmer_module_destroy(wasmer_module_t *module); @@ -282,11 +409,34 @@ void wasmer_module_destroy(wasmer_module_t *module); /// Returns `wasmer_result_t::WASMER_OK` upon success. /// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` /// and `wasmer_last_error_message` to get an error message. -wasmer_result_t wasmer_module_instantiate(wasmer_module_t *module, +wasmer_result_t wasmer_module_instantiate(const wasmer_module_t *module, wasmer_instance_t **instance, wasmer_import_t *imports, int imports_len); +/// Serialize the given Module. +/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_module_serialize(wasmer_serialized_module_t **serialized_module, + const wasmer_module_t *module); + +/// Get bytes of the serialized module. +wasmer_byte_array wasmer_serialized_module_bytes(const wasmer_serialized_module_t *serialized_module); + +/// Frees memory for the given serialized Module. +void wasmer_serialized_module_destroy(wasmer_serialized_module_t *serialized_module); + +/// Transform a sequence of bytes into a serialized module. +/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it. +/// Returns `wasmer_result_t::WASMER_OK` upon success. +/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length` +/// and `wasmer_last_error_message` to get an error message. +wasmer_result_t wasmer_serialized_module_from_bytes(wasmer_serialized_module_t **serialized_module, + const uint8_t *serialized_module_bytes, + uint32_t serialized_module_bytes_length); + /// Frees memory for the given Table void wasmer_table_destroy(wasmer_table_t *table); @@ -308,7 +458,7 @@ uint32_t wasmer_table_length(wasmer_table_t *table); wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); /// Returns true for valid wasm bytes and false for invalid bytes -bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); } // extern "C" diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index cd0648613..e311be8fb 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.1.2" +version = "0.3.0" 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.48" +libc = "0.2.49" hex = "0.3.2" +smallvec = "0.6.9" # Dependencies for caching. [dependencies.serde] @@ -29,8 +30,8 @@ version = "1.0" version = "0.10" [dependencies.serde-bench] version = "0.0.7" -[dependencies.meowhash] -version = "0.1.2" +[dependencies.blake2b_simd] +version = "0.4.1" [dependencies.digest] version = "0.8.0" [dependencies.hashbrown] @@ -41,9 +42,11 @@ features = ["serde"] winapi = { version = "0.3", features = ["memoryapi"] } [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" } 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 new file mode 100644 index 000000000..38071e31c --- /dev/null +++ b/lib/runtime-core/build.rs @@ -0,0 +1,31 @@ +use blake2b_simd::blake2bp; +use std::{env, fs, io::Write, path::PathBuf}; + +const WASMER_VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +fn main() { + let mut state = blake2bp::State::new(); + state.update(WASMER_VERSION.as_bytes()); + + let hasher = state.finalize(); + let hash_string = hasher.to_hex().as_str().to_owned(); + + let crate_dir = env::var("OUT_DIR").unwrap(); + let wasmer_version_hash_file = { + let mut path = PathBuf::from(&crate_dir); + path.push("wasmer_version_hash.txt"); + path + }; + + let mut f_out = fs::File::create(wasmer_version_hash_file) + .expect("Could not create file for wasmer hash value"); + + 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 e88383800..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, }; @@ -12,7 +11,9 @@ use crate::{ module::ModuleInfo, sys::Memory, }; -use std::ptr::NonNull; +use std::{any::Any, ptr::NonNull}; + +use hashbrown::HashMap; pub mod sys { pub use crate::sys::*; @@ -22,6 +23,8 @@ pub use crate::sig_registry::SigRegistry; #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Backend { Cranelift, + Singlepass, + LLVM, } /// This type cannot be constructed from @@ -36,64 +39,49 @@ impl Token { } } +/// Configuration data for the compiler +pub struct CompilerConfig { + /// Symbol information generated from emscripten; used for more detailed debug messages + pub symbol_map: Option>, +} + +impl Default for CompilerConfig { + fn default() -> CompilerConfig { + CompilerConfig { symbol_map: None } + } +} + pub trait Compiler { /// Compiles a `Module` from WebAssembly binary format. /// The `CompileToken` parameter ensures that this can only /// be called from inside the runtime. - fn compile(&self, wasm: &[u8], _: Token) -> CompileResult; + fn compile( + &self, + wasm: &[u8], + comp_conf: CompilerConfig, + _: Token, + ) -> CompileResult; 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( - &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult>; - - fn get_early_trapper(&self) -> Box; -} - -pub trait UserTrapper { - unsafe fn do_early_trap(&self, msg: String) -> !; -} - -pub trait FuncResolver: Send + Sync { +pub trait RunnableModule: Send + Sync { /// This returns a pointer to the function designated by the `local_func_index` /// parameter. - fn get( + fn get_func( &self, - module: &ModuleInner, + info: &ModuleInfo, local_func_index: LocalFuncIndex, ) -> Option>; + + /// 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; + + unsafe fn do_early_trap(&self, data: Box) -> !; } 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 ef2556895..b79180ad6 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -4,17 +4,18 @@ use crate::{ global::Global, import::ImportObject, memory::Memory, - module::{ImportName, ModuleInner}, + module::{ImportName, ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::{BoxedMap, Map, SliceMap, TypedIndex}, table::Table, types::{ ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, - Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, LocalTableIndex, Value, + Initializer, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, + LocalTableIndex, SigIndex, Value, }, vm, }; -use std::{slice, sync::Arc}; +use std::slice; #[derive(Debug)] pub struct LocalBacking { @@ -25,6 +26,9 @@ pub struct LocalBacking { pub(crate) vm_memories: BoxedMap, pub(crate) vm_tables: BoxedMap, pub(crate) vm_globals: BoxedMap, + + pub(crate) dynamic_sigindices: BoxedMap, + pub(crate) local_functions: BoxedMap, } // impl LocalBacking { @@ -47,6 +51,9 @@ impl LocalBacking { let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx); let vm_globals = Self::finalize_globals(&mut globals); + let dynamic_sigindices = Self::generate_sigindices(&module.info); + let local_functions = Self::generate_local_functions(module); + Self { memories, tables, @@ -55,9 +62,37 @@ impl LocalBacking { vm_memories, vm_tables, vm_globals, + + dynamic_sigindices, + local_functions, } } + fn generate_local_functions(module: &ModuleInner) -> BoxedMap { + (0..module.info.func_assoc.len() - module.info.imported_functions.len()) + .map(|index| { + module + .runnable_module + .get_func(&module.info, LocalFuncIndex::new(index)) + .unwrap() + .as_ptr() as *const _ + }) + .collect::>() + .into_boxed_map() + } + + fn generate_sigindices(info: &ModuleInfo) -> BoxedMap { + info.signatures + .iter() + .map(|(_, signature)| { + let signature = SigRegistry.lookup_signature_ref(signature); + let sig_index = SigRegistry.lookup_sig_index(signature); + vm::SigId(sig_index.index() as u32) + }) + .collect::>() + .into_boxed_map() + } + fn generate_memories(module: &ModuleInner) -> BoxedMap { let mut memories = Map::with_capacity(module.info.memories.len()); for (_, &desc) in &module.info.memories { @@ -172,16 +207,17 @@ impl LocalBacking { table.anyfunc_direct_access_mut(|elements| { for (i, &func_index) in init.elements.iter().enumerate() { let sig_index = module.info.func_assoc[func_index]; - let signature = &module.info.signatures[sig_index]; - let sig_id = vm::SigId( - SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32, - ); + // let signature = &module.info.signatures[sig_index]; + let signature = SigRegistry + .lookup_signature_ref(&module.info.signatures[sig_index]); + let sig_id = + vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); 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, @@ -210,16 +246,17 @@ impl LocalBacking { table.anyfunc_direct_access_mut(|elements| { for (i, &func_index) in init.elements.iter().enumerate() { let sig_index = module.info.func_assoc[func_index]; - let signature = &module.info.signatures[sig_index]; - let sig_id = vm::SigId( - SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32, - ); + let signature = SigRegistry + .lookup_signature_ref(&module.info.signatures[sig_index]); + // let signature = &module.info.signatures[sig_index]; + let sig_id = + vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); 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, @@ -379,7 +416,7 @@ fn import_functions( ctx, signature, }) => { - if *expected_sig == signature { + if *expected_sig == *signature { functions.push(vm::ImportedFunc { func: func.inner(), vmctx: match ctx { @@ -391,8 +428,8 @@ fn import_functions( link_errors.push(LinkError::IncorrectImportSignature { namespace: namespace.to_string(), name: name.to_string(), - expected: expected_sig.clone(), - found: signature.clone(), + expected: (*expected_sig).clone(), + found: (*signature).clone(), }); } } diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 4859bd390..93e14b0f9 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -2,8 +2,7 @@ use crate::{ module::{Module, ModuleInfo}, sys::Memory, }; -use digest::Digest; -use meowhash::MeowHasher; +use blake2b_simd::blake2bp; use std::{fmt, io, mem, slice}; #[derive(Debug)] @@ -45,7 +44,12 @@ impl WasmHash { pub fn generate(wasm: &[u8]) -> Self { let mut first_part = [0u8; 32]; let mut second_part = [0u8; 32]; - let generic_array = MeowHasher::digest(wasm); + + let mut state = blake2bp::State::new(); + state.update(wasm); + + let hasher = state.finalize(); + let generic_array = hasher.as_bytes(); first_part.copy_from_slice(&generic_array[0..32]); second_part.copy_from_slice(&generic_array[32..64]); @@ -203,3 +207,7 @@ pub trait Cache { fn load(&self, key: WasmHash) -> Result; fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>; } + +/// A unique ID generated from the version of Wasmer for use with cache versioning +pub const WASMER_VERSION_HASH: &'static str = + include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt")); diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 1f9f60c61..090214834 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,9 +1,6 @@ -use crate::sys::Memory; -use crate::types::{ - FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type, -}; +use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value}; use core::borrow::Borrow; -use std::sync::Arc; +use std::any::Any; pub type Result = std::result::Result; pub type CompileResult = std::result::Result; @@ -11,6 +8,7 @@ pub type LinkResult = std::result::Result>; pub type RuntimeResult = std::result::Result; pub type CallResult = std::result::Result; 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 @@ -57,8 +55,8 @@ pub enum LinkError { IncorrectImportSignature { namespace: String, name: String, - expected: Arc, - found: Arc, + expected: FuncSig, + found: FuncSig, }, ImportNotFound { namespace: String, @@ -121,28 +119,10 @@ impl std::error::Error for LinkError {} /// The main way to do this is `Instance.call`. /// /// Comparing two `RuntimeError`s always evaluates to false. -#[derive(Debug, Clone)] pub enum RuntimeError { - OutOfBoundsAccess { - memory: MemoryIndex, - addr: Option, - }, - TableOutOfBounds { - table: TableIndex, - }, - IndirectCallSignature { - table: TableIndex, - }, - IndirectCallToNull { - table: TableIndex, - }, - IllegalArithmeticOperation, - User { - msg: String, - }, - Unknown { - msg: String, - }, + Trap { msg: Box }, + Exception { data: Box<[Value]> }, + Panic { data: Box }, } impl PartialEq for RuntimeError { @@ -154,34 +134,33 @@ impl PartialEq for RuntimeError { impl std::fmt::Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - RuntimeError::IndirectCallSignature { table } => write!( - f, - "Indirect call signature error with Table Index \"{:?}\"", - table - ), - RuntimeError::IndirectCallToNull { table } => { - write!(f, "Indirect call to null with table index \"{:?}\"", table) + RuntimeError::Trap { ref msg } => { + write!(f, "WebAssembly trap occured during runtime: {}", msg) } - RuntimeError::IllegalArithmeticOperation => write!(f, "Illegal arithmetic operation"), - RuntimeError::OutOfBoundsAccess { memory, addr } => match addr { - Some(addr) => write!( - f, - "Out-of-bounds access with memory index {:?} and address {}", - memory, addr - ), - None => write!(f, "Out-of-bounds access with memory index {:?}", memory), - }, - RuntimeError::TableOutOfBounds { table } => { - write!(f, "Table out of bounds with table index \"{:?}\"", table) + RuntimeError::Exception { ref data } => { + write!(f, "Uncaught WebAssembly exception: {:?}", data) } - RuntimeError::Unknown { msg } => { - write!(f, "Unknown runtime error with message: \"{}\"", msg) + RuntimeError::Panic { data } => { + let msg = if let Some(s) = data.downcast_ref::() { + s + } else if let Some(s) = data.downcast_ref::<&str>() { + s + } else { + "user-defined, opaque" + }; + + write!(f, "{}", msg) } - RuntimeError::User { msg } => write!(f, "User runtime error with message: \"{}\"", msg), } } } +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 @@ -190,16 +169,9 @@ impl std::error::Error for RuntimeError {} /// Comparing two `ResolveError`s always evaluates to false. #[derive(Debug, Clone)] pub enum ResolveError { - Signature { - expected: Arc, - found: Vec, - }, - ExportNotFound { - name: String, - }, - ExportWrongType { - name: String, - }, + Signature { expected: FuncSig, found: Vec }, + ExportNotFound { name: String }, + ExportWrongType { name: String }, } impl PartialEq for ResolveError { @@ -240,7 +212,6 @@ impl std::error::Error for ResolveError {} /// be the `CallError::Runtime(RuntimeError)` variant. /// /// Comparing two `CallError`s always evaluates to false. -#[derive(Debug, Clone)] pub enum CallError { Resolve(ResolveError), Runtime(RuntimeError), @@ -261,6 +232,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, @@ -299,7 +279,7 @@ impl std::error::Error for CreationError {} /// of a webassembly module. /// /// Comparing two `Error`s always evaluates to false. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Error { CompileError(CompileError), LinkError(Vec), @@ -365,7 +345,24 @@ impl From for CallError { impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self) + match self { + Error::CompileError(err) => write!(f, "compile error: {}", err), + Error::LinkError(errs) => { + if errs.len() == 1 { + write!(f, "link error: {}", errs[0]) + } else { + write!(f, "{} link errors:", errs.len())?; + for (i, err) in errs.iter().enumerate() { + write!(f, " ({} of {}) {}", i + 1, errs.len(), err)?; + } + Ok(()) + } + } + Error::RuntimeError(err) => write!(f, "runtime error: {}", err), + Error::ResolveError(err) => write!(f, "resolve error: {}", err), + Error::CallError(err) => write!(f, "call error: {}", err), + Error::CreationError(err) => write!(f, "creation error: {}", err), + } } } @@ -472,3 +469,14 @@ impl Into for MemoryProtectionError { GrowError::CouldNotProtectMemory(self) } } + +#[derive(Debug)] +pub enum ParseError { + BinaryReadError, +} + +impl From for ParseError { + fn from(_: wasmparser::BinaryReaderError) -> Self { + ParseError::BinaryReadError + } +} 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 7073a5ac1..14465deed 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -1,8 +1,16 @@ use crate::export::Export; use hashbrown::{hash_map::Entry, HashMap}; +use std::collections::VecDeque; +use std::{ + cell::{Ref, RefCell}, + ffi::c_void, + rc::Rc, +}; pub trait LikeNamespace { fn get_export(&self, name: &str) -> Option; + fn get_exports(&self) -> Vec<(String, Export)>; + fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>; } pub trait IsExport { @@ -37,17 +45,33 @@ impl IsExport for Export { /// } /// ``` pub struct ImportObject { - map: HashMap>, + map: Rc>>>, + state_creator: Option (*mut c_void, fn(*mut c_void))>>, } impl ImportObject { /// Create a new `ImportObject`. pub fn new() -> Self { Self { - map: HashMap::new(), + 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: @@ -67,7 +91,9 @@ impl ImportObject { S: Into, N: LikeNamespace + 'static, { - match self.map.entry(name.into()) { + let mut map = self.map.borrow_mut(); + + match map.entry(name.into()) { Entry::Vacant(empty) => { empty.insert(Box::new(namespace)); None @@ -76,8 +102,68 @@ impl ImportObject { } } - pub fn get_namespace(&self, namespace: &str) -> Option<&(dyn LikeNamespace + 'static)> { - self.map.get(namespace).map(|namespace| &**namespace) + pub fn get_namespace(&self, namespace: &str) -> Option> { + let map_ref = self.map.borrow(); + + if map_ref.contains_key(namespace) { + Some(Ref::map(map_ref, |map| &*map[namespace])) + } else { + None + } + } + + pub fn clone_ref(&self) -> Self { + Self { + map: Rc::clone(&self.map), + state_creator: self.state_creator.clone(), + } + } + + fn get_objects(&self) -> VecDeque<(String, String, Export)> { + let mut out = VecDeque::new(); + for (name, ns) in self.map.borrow().iter() { + for (id, exp) in ns.get_exports() { + out.push_back((name.clone(), id, exp)); + } + } + out + } +} + +pub struct ImportObjectIterator { + elements: VecDeque<(String, String, Export)>, +} + +impl Iterator for ImportObjectIterator { + type Item = (String, String, Export); + fn next(&mut self) -> Option { + self.elements.pop_front() + } +} + +impl IntoIterator for ImportObject { + type IntoIter = ImportObjectIterator; + type Item = (String, String, Export); + + fn into_iter(self) -> Self::IntoIter { + ImportObjectIterator { + elements: self.get_objects(), + } + } +} + +impl Extend<(String, String, Export)> for ImportObject { + fn extend>(&mut self, iter: T) { + let mut map = self.map.borrow_mut(); + for (ns, id, exp) in iter.into_iter() { + if let Some(like_ns) = map.get_mut(&ns) { + like_ns.maybe_insert(&id, exp); + } else { + let mut new_ns = Namespace::new(); + new_ns.insert(id, exp); + map.insert(ns, Box::new(new_ns)); + } + } } } @@ -105,4 +191,76 @@ impl LikeNamespace for Namespace { fn get_export(&self, name: &str) -> Option { self.map.get(name).map(|is_export| is_export.to_export()) } + + fn get_exports(&self) -> Vec<(String, Export)> { + self.map + .iter() + .map(|(k, v)| (k.clone(), v.to_export())) + .collect() + } + + fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()> { + self.map.insert(name.to_owned(), Box::new(export)); + Some(()) + } +} + +#[cfg(test)] +mod test { + use crate::export::Export; + use crate::global::Global; + use crate::types::Value; + + #[test] + fn extending_works() { + let mut imports1 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(0)), + }, + }; + + let imports2 = imports! { + "dog" => { + "small" => Global::new(Value::I32(2)), + }, + "cat" => { + "small" => Global::new(Value::I32(3)), + }, + }; + + imports1.extend(imports2); + + let cat_ns = imports1.get_namespace("cat").unwrap(); + assert!(cat_ns.get_export("small").is_some()); + + let dog_ns = imports1.get_namespace("dog").unwrap(); + assert!(dog_ns.get_export("happy").is_some()); + assert!(dog_ns.get_export("small").is_some()); + } + + #[test] + fn extending_conflict_overwrites() { + let mut imports1 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(0)), + }, + }; + + let imports2 = imports! { + "dog" => { + "happy" => Global::new(Value::I32(4)), + }, + }; + + imports1.extend(imports2); + let dog_ns = imports1.get_namespace("dog").unwrap(); + + assert!( + if let Export::Global(happy_dog_global) = dog_ns.get_export("happy").unwrap() { + happy_dog_global.get() == Value::I32(4) + } else { + false + } + ); + } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index a634b21e5..db9a2847f 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,18 +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)] @@ -38,6 +40,8 @@ impl Drop for InstanceInner { pub struct Instance { module: Arc, inner: Box, + #[allow(dead_code)] + import_object: ImportObject, } impl Instance { @@ -60,13 +64,64 @@ 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 { module, inner }; + let instance = Instance { + module, + inner, + import_object: imports.clone_ref(), + }; 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) @@ -91,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, @@ -112,11 +167,12 @@ impl Instance { .func_assoc .get(*func_index) .expect("broken invariant, incorrect func index"); - let signature = &self.module.info.signatures[sig_index]; + let signature = + SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]); if signature.params() != Args::types() || signature.returns() != Rets::types() { Err(ResolveError::Signature { - expected: Arc::clone(&signature), + expected: (*signature).clone(), found: Args::types().to_vec(), })?; } @@ -128,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 { @@ -183,7 +245,8 @@ impl Instance { .func_assoc .get(*func_index) .expect("broken invariant, incorrect func index"); - let signature = Arc::clone(&self.module.info.signatures[sig_index]); + let signature = + SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]); Ok(DynFunc { signature, @@ -221,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 @@ -240,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 @@ -261,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. @@ -271,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, @@ -358,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 _, @@ -374,13 +410,10 @@ impl InstanceInner { } }; - let signature = &module.info.signatures[sig_index]; + let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]); + // let signature = &module.info.signatures[sig_index]; - ( - unsafe { FuncPointer::new(func_ptr) }, - ctx, - Arc::clone(signature), - ) + (unsafe { FuncPointer::new(func_ptr) }, ctx, signature) } fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory { @@ -421,6 +454,136 @@ impl LikeNamespace for Instance { Some(self.inner.get_export_from_index(&self.module, export_index)) } + + fn get_exports(&self) -> Vec<(String, Export)> { + unimplemented!("Use the exports method instead"); + } + + fn maybe_insert(&mut self, _name: &str, _export: Export) -> Option<()> { + None + } +} + +#[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 success = invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut trap_info, + invoke_env, + ); + + if success { + Ok(()) + } 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. @@ -454,33 +617,20 @@ impl<'a> DynFunc<'a> { /// # Ok(()) /// # } /// ``` - pub fn call(&mut 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(), - })? - } + pub fn call(&self, params: &[Value]) -> CallResult> { + 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 { @@ -491,8 +641,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) => { diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 663e17534..a43a4c63a 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(nightly, feature(unwind_attributes))] + #[cfg(test)] #[macro_use] extern crate field_offset; @@ -23,7 +25,7 @@ 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 +38,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)] @@ -68,7 +70,24 @@ pub fn compile_with( ) -> CompileResult { let token = backend::Token::generate(); compiler - .compile(wasm, token) + .compile(wasm, Default::default(), token) + .map(|mut inner| { + let inner_info: &mut crate::module::ModuleInfo = &mut inner.info; + inner_info.import_custom_sections(wasm).unwrap(); + module::Module::new(Arc::new(inner)) + }) +} + +/// The same as `compile_with` but changes the compiler behavior +/// with the values in the `CompilerConfig` +pub fn compile_with_config( + wasm: &[u8], + compiler: &dyn backend::Compiler, + compiler_config: backend::CompilerConfig, +) -> CompileResult { + let token = backend::Token::generate(); + compiler + .compile(wasm, compiler_config, token) .map(|inner| module::Module::new(Arc::new(inner))) } @@ -76,13 +95,18 @@ pub fn compile_with( /// 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 a7bdff1b0..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 }}; } @@ -75,3 +103,15 @@ macro_rules! __imports_internal { $ns }; } + +#[macro_export] +#[doc(hidden)] +macro_rules! namespace { + ( $( $imp_name:expr => $import_item:expr, )* ) => {{ + let mut ns = $crate::import::Namespace::new(); + $( + ns.insert($imp_name, $import_item); + )* + ns + }}; +} diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 4a9dccba7..6f532b6d4 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -284,6 +284,7 @@ impl Clone for UnsharedMemory { } pub struct SharedMemory { + #[allow(dead_code)] desc: MemoryDescriptor, } diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 43c9be9dc..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, @@ -51,11 +48,34 @@ pub struct ModuleInfo { pub start_func: Option, pub func_assoc: Map, - pub signatures: Map>, + pub signatures: Map, pub backend: Backend, pub namespace_table: StringTable, pub name_table: StringTable, + + /// Symbol information from emscripten + pub em_symbol_map: Option>, + + pub custom_sections: HashMap>, +} + +impl ModuleInfo { + pub fn import_custom_sections(&mut self, wasm: &[u8]) -> crate::error::ParseResult<()> { + let mut parser = wasmparser::ModuleReader::new(wasm)?; + while !parser.eof() { + let section = parser.read()?; + if let wasmparser::SectionCode::Custom { name, kind: _ } = section.code { + let mut reader = section.get_binary_reader(); + let len = reader.bytes_remaining(); + let bytes = reader.read_bytes(len)?; + let data = bytes.to_vec(); + let name = name.to_string(); + self.custom_sections.insert(name, data); + } + } + Ok(()) + } } /// A compiled WebAssembly module. @@ -71,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 } } @@ -105,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/sig_registry.rs b/lib/runtime-core/src/sig_registry.rs index 77c9a0d3c..1f6d87b4f 100644 --- a/lib/runtime-core/src/sig_registry.rs +++ b/lib/runtime-core/src/sig_registry.rs @@ -49,4 +49,20 @@ impl SigRegistry { let global = (*GLOBAL_SIG_REGISTRY).read(); Arc::clone(&global.sig_assoc[sig_index]) } + + pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc { + let mut global = (*GLOBAL_SIG_REGISTRY).write(); + let global = &mut *global; + + let func_table = &mut global.func_table; + let sig_assoc = &mut global.sig_assoc; + + if func_table.contains_key(func_sig) { + Arc::clone(&sig_assoc[func_table[func_sig]]) + } else { + let arc = Arc::new(func_sig.clone()); + func_table.insert(Arc::clone(&arc), sig_assoc.push(Arc::clone(&arc))); + arc + } + } } diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index d7177c427..add5b0257 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -56,6 +56,10 @@ where pub fn into_boxed_map(self) -> BoxedMap { BoxedMap::new(self.elems.into_boxed_slice()) } + + pub fn into_vec(self) -> Vec { + self.elems + } } impl Map diff --git a/lib/runtime-core/src/sys/unix/memory.rs b/lib/runtime-core/src/sys/unix/memory.rs index 338024d09..9ffb663eb 100644 --- a/lib/runtime-core/src/sys/unix/memory.rs +++ b/lib/runtime-core/src/sys/unix/memory.rs @@ -245,6 +245,7 @@ pub enum Protect { Read, ReadWrite, ReadExec, + ReadWriteExec, } impl Protect { @@ -254,6 +255,7 @@ impl Protect { Protect::Read => 1, Protect::ReadWrite => 1 | 2, Protect::ReadExec => 1 | 4, + Protect::ReadWriteExec => 1 | 2 | 4, } } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 6e59d0d4a..59c610773 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,29 +1,109 @@ use crate::{ - backend::UserTrapper, error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, Type, WasmExternType}, - vm::Ctx, + vm::{self, Ctx}, +}; +use std::{ + any::Any, + ffi::c_void, + fmt, + marker::PhantomData, + mem, panic, + ptr::{self, NonNull}, + sync::Arc, }; -use std::{cell::UnsafeCell, fmt, 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, + 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 +113,21 @@ 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; + fn report(self) -> Result>; } impl TrapEarly for Rets where Rets: WasmTypeList, { - fn report(self) -> Result { + fn report(self) -> Result> { Ok(self) } } @@ -55,10 +135,10 @@ where impl TrapEarly for Result where Rets: WasmTypeList, - E: fmt::Debug, + E: Any, { - fn report(self) -> Result { - self.map_err(|err| format!("Error: {:?}", err)) + fn report(self) -> Result> { + self.map_err(|err| Box::new(err) as Box) } } @@ -71,19 +151,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 +177,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 +195,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() @@ -124,6 +211,13 @@ where impl WasmTypeList for (A,) { type CStruct = S1; + type RetArray = [u64; 1]; + fn from_ret_array(array: Self::RetArray) -> Self { + (WasmExternType::from_bits(array[0]),) + } + fn empty_ret_array() -> Self::RetArray { + [0u64] + } fn from_c_struct(c_struct: Self::CStruct) -> Self { let S1(a) = c_struct; (a,) @@ -137,19 +231,46 @@ impl WasmTypeList for (A,) { &[A::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 { + // type Trampoline = extern "C" fn(*mut Ctx, *const c_void, *const u64, *mut u64); + // type Invoke = extern "C" fn(Trampoline, *mut Ctx, *const c_void, *const u64, *mut u64, &mut WasmTrapInfo) -> bool; + let (a,) = self; - f(ctx, a) + let args = [a.to_bits()]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + + if (wasm.invoke)( + wasm.trampoline, + ctx, + f, + args.as_ptr(), + rets.as_mut().as_mut_ptr(), + &mut trap, + wasm.invoke_env, + ) { + Ok(Rets::from_ret_array(rets)) + } else { + Err(trap) + } } } -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) }.map_err(|e| { + RuntimeError::Trap { + msg: e.to_string().into(), + } + }) } } @@ -160,6 +281,15 @@ macro_rules! impl_traits { 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_bits($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; @@ -174,66 +304,84 @@ macro_rules! impl_traits { &[$( $x::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 { + // type Trampoline = extern "C" fn(*mut Ctx, *const c_void, *const u64, *mut u64); + // type Invoke = extern "C" fn(Trampoline, *mut Ctx, *const c_void, *const u64, *mut u64, &mut WasmTrapInfo) -> bool; + #[allow(unused_parens)] let ( $( $x ),* ) = self; - let c_struct = f(ctx $( ,$x )*); - Rets::from_c_struct(c_struct) + let args = [ $( $x.to_bits() ),* ]; + let mut rets = Rets::empty_ret_array(); + let mut trap = WasmTrapInfo::Unknown; + + if (wasm.invoke)(wasm.trampoline, ctx, f, args.as_ptr(), rets.as_mut().as_mut_ptr(), &mut trap, wasm.invoke_env) { + Ok(Rets::from_ret_array(rets)) + } else { + Err(trap) + } + + // let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); + // #[allow(unused_parens)] + // let ( $( $x ),* ) = self; + // let c_struct = f(ctx $( ,$x )*); + // Rets::from_c_struct(c_struct) } } 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`."); + /// 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 )* ) -> Rets::CStruct { let f: FN = unsafe { mem::transmute_copy(&()) }; - let msg = match panic::catch_unwind(panic::AssertUnwindSafe(|| { - f( ctx $( ,$x )* ).report() + let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| { + let res = f( ctx $( ,$x )* ).report(); + res })) { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => err, - Err(err) => { - if let Some(s) = err.downcast_ref::<&str>() { - s.to_string() - } else if let Some(s) = err.downcast_ref::() { - s.clone() - } else { - "a panic occurred, but no additional information is available".to_string() - } - }, + Err(err) => err, }; unsafe { - if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) { - early_trapper.do_early_trap(msg) - } 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) }.map_err(|e| { + RuntimeError::Trap { + msg: e.to_string().into(), + } + }) } } }; } +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); @@ -248,14 +396,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..79ef5da7f 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)] @@ -76,24 +76,99 @@ where Self: Sized, { const TYPE: Type; + fn to_bits(self) -> u64; + fn from_bits(n: u64) -> Self; +} + +unsafe impl WasmExternType for i8 { + const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } +} +unsafe impl WasmExternType for u8 { + const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } +} +unsafe impl WasmExternType for i16 { + const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } +} +unsafe impl WasmExternType for u16 { + const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for i32 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u32 { const TYPE: Type = Type::I32; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for i64 { const TYPE: Type = Type::I64; + fn to_bits(self) -> u64 { + self as u64 + } + fn from_bits(n: u64) -> Self { + n as _ + } } unsafe impl WasmExternType for u64 { const TYPE: Type = Type::I64; + fn to_bits(self) -> u64 { + self + } + fn from_bits(n: u64) -> Self { + n + } } unsafe impl WasmExternType for f32 { const TYPE: Type = Type::F32; + fn to_bits(self) -> u64 { + self.to_bits() as u64 + } + fn from_bits(n: u64) -> Self { + f32::from_bits(n as u32) + } } unsafe impl WasmExternType for f64 { const TYPE: Type = Type::F64; + fn to_bits(self) -> u64 { + self.to_bits() + } + fn from_bits(n: u64) -> Self { + f64::from_bits(n) + } } // pub trait IntegerAtomic @@ -113,34 +188,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 +205,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 +245,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, 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 71400af70..a1e493f8c 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -7,39 +7,60 @@ use crate::{ }; use std::{ffi::c_void, mem, ptr}; +use hashbrown::HashMap; + /// The context of the currently running WebAssembly instance. /// /// #[derive(Debug)] #[repr(C)] pub struct Ctx { - /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. - pub(crate) memories: *mut *mut LocalMemory, + // `internal` must be the first field of `Ctx`. + pub(crate) internal: InternalCtx, - /// 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, + pub(crate) local_functions: *const *const Func, local_backing: *mut LocalBacking, import_backing: *mut ImportBacking, - module: *const ModuleInner, + pub(crate) module: *const ModuleInner, pub data: *mut c_void, - pub data_finalizer: Option, + pub data_finalizer: Option, +} + +/// The internal context of the currently running WebAssembly instance. +/// +/// +#[doc(hidden)] +#[derive(Debug)] +#[repr(C)] +pub struct InternalCtx { + /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. + pub memories: *mut *mut LocalMemory, + + /// A pointer to an array of locally-defined tables, indexed by `TableIndex`. + pub tables: *mut *mut LocalTable, + + /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. + pub globals: *mut *mut LocalGlobal, + + /// A pointer to an array of imported memories, indexed by `MemoryIndex, + pub imported_memories: *mut *mut LocalMemory, + + /// A pointer to an array of imported tables, indexed by `TableIndex`. + pub imported_tables: *mut *mut LocalTable, + + /// A pointer to an array of imported globals, indexed by `GlobalIndex`. + pub imported_globals: *mut *mut LocalGlobal, + + /// A pointer to an array of imported functions, indexed by `FuncIndex`. + pub imported_funcs: *mut ImportedFunc, + + /// A pointer to an array of signature ids. Conceptually, this maps + /// from a static, module-local signature id to a runtime-global + /// signature id. This is used to allow call-indirect to other + /// modules safely. + pub dynamic_sigindices: *const SigId, } impl Ctx { @@ -50,14 +71,19 @@ impl Ctx { module: &ModuleInner, ) -> Self { Self { - memories: local_backing.vm_memories.as_mut_ptr(), - tables: local_backing.vm_tables.as_mut_ptr(), - globals: local_backing.vm_globals.as_mut_ptr(), + internal: InternalCtx { + memories: local_backing.vm_memories.as_mut_ptr(), + tables: local_backing.vm_tables.as_mut_ptr(), + globals: local_backing.vm_globals.as_mut_ptr(), - imported_memories: import_backing.vm_memories.as_mut_ptr(), - imported_tables: import_backing.vm_tables.as_mut_ptr(), - imported_globals: import_backing.vm_globals.as_mut_ptr(), - imported_funcs: import_backing.vm_functions.as_mut_ptr(), + imported_memories: import_backing.vm_memories.as_mut_ptr(), + imported_tables: import_backing.vm_tables.as_mut_ptr(), + imported_globals: import_backing.vm_globals.as_mut_ptr(), + imported_funcs: import_backing.vm_functions.as_mut_ptr(), + + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + }, + local_functions: local_backing.local_functions.as_ptr(), local_backing, import_backing, @@ -74,17 +100,22 @@ 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 { - memories: local_backing.vm_memories.as_mut_ptr(), - tables: local_backing.vm_tables.as_mut_ptr(), - globals: local_backing.vm_globals.as_mut_ptr(), + internal: InternalCtx { + memories: local_backing.vm_memories.as_mut_ptr(), + tables: local_backing.vm_tables.as_mut_ptr(), + globals: local_backing.vm_globals.as_mut_ptr(), - imported_memories: import_backing.vm_memories.as_mut_ptr(), - imported_tables: import_backing.vm_tables.as_mut_ptr(), - imported_globals: import_backing.vm_globals.as_mut_ptr(), - imported_funcs: import_backing.vm_functions.as_mut_ptr(), + imported_memories: import_backing.vm_memories.as_mut_ptr(), + imported_tables: import_backing.vm_tables.as_mut_ptr(), + imported_globals: import_backing.vm_globals.as_mut_ptr(), + imported_funcs: import_backing.vm_functions.as_mut_ptr(), + + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + }, + local_functions: local_backing.local_functions.as_ptr(), local_backing, import_backing, @@ -127,6 +158,11 @@ impl Ctx { }, } } + + /// Gives access to the emscripten symbol map, used for debugging + pub unsafe fn borrow_symbol_map(&self) -> &Option> { + &(*self.module).info.em_symbol_map + } } #[doc(hidden)] @@ -163,6 +199,10 @@ impl Ctx { pub fn offset_signatures() -> u8 { 7 * (mem::size_of::() as u8) } + + pub fn offset_local_functions() -> u8 { + 8 * (mem::size_of::() as u8) + } } enum InnerFunc {} @@ -315,43 +355,50 @@ impl Anyfunc { #[cfg(test)] mod vm_offset_tests { - use super::{Anyfunc, Ctx, ImportedFunc, LocalGlobal, LocalMemory, LocalTable}; + use super::{Anyfunc, Ctx, ImportedFunc, InternalCtx, LocalGlobal, LocalMemory, LocalTable}; #[test] fn vmctx() { + assert_eq!(0usize, offset_of!(Ctx => internal).get_byte_offset(),); + assert_eq!( Ctx::offset_memories() as usize, - offset_of!(Ctx => memories).get_byte_offset(), + offset_of!(InternalCtx => memories).get_byte_offset(), ); assert_eq!( Ctx::offset_tables() as usize, - offset_of!(Ctx => tables).get_byte_offset(), + offset_of!(InternalCtx => tables).get_byte_offset(), ); assert_eq!( Ctx::offset_globals() as usize, - offset_of!(Ctx => globals).get_byte_offset(), + offset_of!(InternalCtx => globals).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_memories() as usize, - offset_of!(Ctx => imported_memories).get_byte_offset(), + offset_of!(InternalCtx => imported_memories).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_tables() as usize, - offset_of!(Ctx => imported_tables).get_byte_offset(), + offset_of!(InternalCtx => imported_tables).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_globals() as usize, - offset_of!(Ctx => imported_globals).get_byte_offset(), + offset_of!(InternalCtx => imported_globals).get_byte_offset(), ); assert_eq!( Ctx::offset_imported_funcs() as usize, - offset_of!(Ctx => imported_funcs).get_byte_offset(), + offset_of!(InternalCtx => imported_funcs).get_byte_offset(), + ); + + assert_eq!( + Ctx::offset_local_functions() as usize, + offset_of!(Ctx => local_functions).get_byte_offset(), ); } @@ -434,7 +481,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); @@ -458,6 +505,9 @@ mod vm_ctx_tests { vm_memories: Map::new().into_boxed_map(), vm_tables: Map::new().into_boxed_map(), vm_globals: Map::new().into_boxed_map(), + + dynamic_sigindices: Map::new().into_boxed_map(), + local_functions: Map::new().into_boxed_map(), }; let mut import_backing = ImportBacking { memories: Map::new().into_boxed_map(), @@ -494,52 +544,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::cache::{Error as CacheError, WasmHash}; - use crate::error::RuntimeResult; - use crate::types::{FuncIndex, LocalFuncIndex, Value}; + use crate::backend::{sys::Memory, Backend, CacheGen, RunnableModule}; + use crate::cache::Error as CacheError; + 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(), @@ -565,6 +601,10 @@ mod vm_ctx_tests { namespace_table: StringTable::new(), name_table: StringTable::new(), + + em_symbol_map: None, + + custom_sections: HashMap::new(), }, } } diff --git a/lib/runtime-core/src/vmcalls.rs b/lib/runtime-core/src/vmcalls.rs index b428fb24e..4126024bf 100644 --- a/lib/runtime-core/src/vmcalls.rs +++ b/lib/runtime-core/src/vmcalls.rs @@ -17,7 +17,7 @@ pub unsafe extern "C" fn local_static_memory_grow( memory_index: LocalMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -30,7 +30,7 @@ pub unsafe extern "C" fn local_static_memory_size( ctx: &vm::Ctx, memory_index: LocalMemoryIndex, ) -> Pages { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; (*memory).size() @@ -41,7 +41,7 @@ pub unsafe extern "C" fn local_dynamic_memory_grow( memory_index: LocalMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -54,7 +54,7 @@ pub unsafe extern "C" fn local_dynamic_memory_size( ctx: &vm::Ctx, memory_index: LocalMemoryIndex, ) -> Pages { - let local_memory = *ctx.memories.add(memory_index.index()); + let local_memory = *ctx.internal.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; (*memory).size() @@ -69,7 +69,10 @@ pub unsafe extern "C" fn imported_static_memory_grow( import_memory_index: ImportedMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.imported_memories.add(import_memory_index.index()); + let local_memory = *ctx + .internal + .imported_memories + .add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -82,7 +85,10 @@ pub unsafe extern "C" fn imported_static_memory_size( ctx: &vm::Ctx, import_memory_index: ImportedMemoryIndex, ) -> Pages { - let local_memory = *ctx.imported_memories.add(import_memory_index.index()); + let local_memory = *ctx + .internal + .imported_memories + .add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; (*memory).size() @@ -93,7 +99,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_grow( memory_index: ImportedMemoryIndex, delta: Pages, ) -> i32 { - let local_memory = *ctx.imported_memories.add(memory_index.index()); + let local_memory = *ctx.internal.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; match (*memory).grow(delta, &mut *local_memory) { @@ -106,7 +112,7 @@ pub unsafe extern "C" fn imported_dynamic_memory_size( ctx: &vm::Ctx, memory_index: ImportedMemoryIndex, ) -> Pages { - let local_memory = *ctx.imported_memories.add(memory_index.index()); + let local_memory = *ctx.internal.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; (*memory).size() diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 979fae140..678d6861c 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.1.4" +version = "0.3.0" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,24 +9,36 @@ edition = "2018" readme = "README.md" [dependencies] +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } lazy_static = "1.2.0" memmap = "0.7.0" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.1.2" +version = "0.3.0" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.1.2" +version = "0.3.0" +optional = true [dev-dependencies] tempfile = "3.0.7" criterion = "0.2" +wabt = "0.7.4" + +[target.'cfg(not(windows))'.dependencies.wasmer-llvm-backend] +path = "../llvm-backend" +optional = true [features] +default = ["default-compiler"] +default-compiler = ["wasmer-clif-backend"] +cache = ["default-compiler"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] +llvm = ["wasmer-llvm-backend"] +singlepass = ["wasmer-singlepass-backend"] [[bench]] name = "nginx" -harness = false \ No newline at end of file +harness = false diff --git a/lib/runtime/README.md b/lib/runtime/README.md index bd9bb0f0c..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] = &[ @@ -51,7 +80,7 @@ fn main() -> error::Result<()> { // We're not importing anything, so make an empty import object. let import_object = imports! {}; - let mut instance = instantiate(WASM, import_object)?; + let mut instance = instantiate(WASM, &import_object)?; let values = instance .func("add_one")? @@ -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 new file mode 100644 index 000000000..a8f27b4c0 --- /dev/null +++ b/lib/runtime/examples/call.rs @@ -0,0 +1,67 @@ +use wasmer_runtime::{compile, error, 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) + i32.const 42 + i32.const 0 + i32.div_u + ) + (export "dbz" (func $dbz)) + ) +"#; + +// static WAT2: &'static str = r#" +// (module +// (type $t0 (func (param i32))) +// (type $t1 (func)) +// (func $print_i32 (export "print_i32") (type $t0) (param $lhs i32)) +// (func $print (export "print") (type $t1)) +// (table $table (export "table") 10 20 anyfunc) +// (memory $memory (export "memory") 1 2) +// (global $global_i32 (export "global_i32") i32 (i32.const 666))) +// "#; + +fn get_wasm() -> Vec { + wat2wasm(WAT).unwrap() +} + +fn foobar(_ctx: &mut Ctx) -> i32 { + 42 +} + +fn do_panic(_ctx: &mut Ctx) -> Result { + Err("error".to_string()) +} + +fn main() -> Result<(), error::Error> { + let wasm = get_wasm(); + + let module = compile(&wasm)?; + + // let import_module = compile(&wat2wasm(WAT2).unwrap())?; + // let import_instance = import_module.instantiate(&imports! {})?; + + // let imports = imports! { + // "spectest" => import_instance, + // }; + + println!("instantiating"); + let instance = module.instantiate(&imports! { + "env" => { + "do_panic" => Func::new(do_panic), + }, + })?; + + let foo: Func<(), i32> = instance.func("dbz")?; + + let result = foo.call(); + + println!("result: {:?}", result); + + Ok(()) +} diff --git a/lib/runtime/src/cache.rs b/lib/runtime/src/cache.rs index 6ebbf1017..4b2768bc6 100644 --- a/lib/runtime/src/cache.rs +++ b/lib/runtime/src/cache.rs @@ -7,7 +7,7 @@ use std::{ }; use wasmer_runtime_core::cache::Error as CacheError; -pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash}; +pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash, WASMER_VERSION_HASH}; /// Representation of a directory that contains compiled wasm artifacts. /// @@ -40,12 +40,17 @@ pub struct FileSystemCache { impl FileSystemCache { /// Construct a new `FileSystemCache` around the specified directory. + /// The contents of the cache are stored in sub-versioned directories. /// /// # Note: /// This method is unsafe because there's no way to ensure the artifacts /// stored in this cache haven't been corrupted or tampered with. pub unsafe fn new>(path: P) -> io::Result { - let path: PathBuf = path.into(); + let path: PathBuf = { + let mut path = path.into(); + path.push(WASMER_VERSION_HASH); + path + }; if path.exists() { let metadata = path.metadata()?; diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index c7e3b3826..1836bb206 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -74,6 +74,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 +96,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 { @@ -110,7 +113,7 @@ pub mod units { pub mod cache; -use wasmer_runtime_core::backend::Compiler; +use wasmer_runtime_core::backend::{Compiler, CompilerConfig}; /// Compile WebAssembly binary code into a [`Module`]. /// This function is useful if it is necessary to @@ -129,6 +132,25 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult { wasmer_runtime_core::compile_with(&wasm[..], default_compiler()) } +/// The same as `compile` but takes a `CompilerConfig` for the purpose of +/// changing the compiler's behavior +pub fn compile_with_config( + wasm: &[u8], + compiler_config: CompilerConfig, +) -> error::CompileResult { + 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`]. /// @@ -152,12 +174,21 @@ pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result &'static dyn Compiler { +/// Get a single instance of the default compiler to use. +pub fn default_compiler() -> &'static dyn Compiler { use lazy_static::lazy_static; - use wasmer_clif_backend::CraneliftCompiler; + + #[cfg(feature = "llvm")] + use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; + + #[cfg(feature = "singlepass")] + use wasmer_singlepass_backend::SinglePassCompiler as DefaultCompiler; + + #[cfg(not(any(feature = "llvm", feature = "singlepass")))] + use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; lazy_static! { - static ref DEFAULT_COMPILER: CraneliftCompiler = { CraneliftCompiler::new() }; + static ref DEFAULT_COMPILER: DefaultCompiler = { DefaultCompiler::new() }; } &*DEFAULT_COMPILER as &dyn Compiler diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml new file mode 100644 index 000000000..51fa61319 --- /dev/null +++ b/lib/singlepass-backend/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasmer-singlepass-backend" +version = "0.3.0" +repository = "https://github.com/wasmerio/wasmer" +description = "Wasmer runtime single pass compiler backend" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmparser = "0.28.0" +dynasm = "0.3.1" +dynasmrt = "0.3.1" +lazy_static = "1.2.0" +byteorder = "1" +nix = "0.13.0" +libc = "0.2.49" +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.rs b/lib/singlepass-backend/src/codegen.rs new file mode 100644 index 000000000..a5bfbf594 --- /dev/null +++ b/lib/singlepass-backend/src/codegen.rs @@ -0,0 +1,51 @@ +use wasmer_runtime_core::{ + backend::RunnableModule, + 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>; + + /// Creates a new function and returns the function-scope code generator for it. + fn next_function(&mut self) -> Result<&mut FCG, CodegenError>; + fn finalize(self, module_info: &ModuleInfo) -> Result; + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError>; + + /// Sets function signatures. + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError>; + + /// Adds an import function. + fn feed_import_function(&mut self) -> Result<(), CodegenError>; +} + +/// The function-scope code generator trait. +pub trait FunctionCodeGenerator { + /// Sets the return type. + fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError>; + + /// Adds a parameter to the function. + fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError>; + + /// Adds `n` locals to the function. + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError>; + + /// Called before the first call to `feed_opcode`. + fn begin_body(&mut self) -> Result<(), CodegenError>; + + /// Called for each operator. + fn feed_opcode(&mut self, op: &Operator, module_info: &ModuleInfo) -> Result<(), CodegenError>; + + /// Finalizes the function. + fn finalize(&mut self) -> Result<(), CodegenError>; +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: &'static str, +} diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs new file mode 100644 index 000000000..10f9f16af --- /dev/null +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -0,0 +1,4127 @@ +#![allow(clippy::forget_copy)] // Used by dynasm. + +use super::codegen::*; +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::RunnableModule, + memory::MemoryType, + module::ModuleInfo, + 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>>, + 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>, + 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, + num_params_plus_one: Option>, + ) -> bool { + 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(); + 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(_) => false, + } + } + + 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(); + } +} + +impl X64ModuleCodeGenerator { + pub 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, + } + } +} + +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(), + + assembler: Some(assembler), + function_labels: Some(function_labels), + br_table_data: Some(br_table_data), + 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 { + 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 _)); + } + + Ok(X64ExecutionContext { + code: output, + functions: self.functions, + signatures: self.signatures.as_ref().unwrap().clone(), + _br_table_data: br_table_data, + func_import_count: self.func_import_count, + function_pointers: out_labels, + }) + } + + 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(()) + } +} + +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() { + 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) -> 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); + + 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_opcode(&mut self, op: &Operator, module_info: &ModuleInfo) -> Result<(), CodegenError> { + //println!("{:?} {}", op, self.value_stack.len()); + 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.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(); + 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..8a949f8a5 --- /dev/null +++ b/lib/singlepass-backend/src/emitter_x64.rs @@ -0,0 +1,950 @@ +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); +} + +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!(), + } + } +} diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs new file mode 100644 index 000000000..7ecd029e4 --- /dev/null +++ b/lib/singlepass-backend/src/lib.rs @@ -0,0 +1,91 @@ +#![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; +mod codegen_x64; +mod emitter_x64; +mod machine; +mod parse; +mod protect_unix; + +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) -> Result<(Box<[u8]>, Memory), CacheError> { + Err(CacheError::Unknown( + "the singlepass 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::Singlepass, &mut mcg, &compiler_config)?; + let exec_context = mcg.finalize(&info)?; + Ok(ModuleInner { + cache_gen: Box::new(Placeholder), + runnable_module: Box::new(exec_context), + info: info, + }) + } + + unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { + Err(CacheError::Unknown( + "the singlepass 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/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/singlepass-backend/src/parse.rs b/lib/singlepass-backend/src/parse.rs new file mode 100644 index 000000000..e02b4b8c1 --- /dev/null +++ b/lib/singlepass-backend/src/parse.rs @@ -0,0 +1,393 @@ +use crate::codegen::{CodegenError, FunctionCodeGenerator, ModuleCodeGenerator}; +use hashbrown::HashMap; +use wasmer_runtime_core::{ + backend::{Backend, CompilerConfig, RunnableModule}, + 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) + } +} + +pub fn read_module< + MCG: ModuleCodeGenerator, + FCG: FunctionCodeGenerator, + RM: RunnableModule, +>( + wasm: &[u8], + backend: Backend, + mcg: &mut MCG, + 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()?; + } + 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())?; + mcg.feed_function_signatures(info.func_assoc.clone())?; + mcg.check_precondition(&info)?; + } + + let fcg = mcg.next_function()?; + 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))?; + } + for param in sig.params() { + fcg.feed_param(type_to_wp_type(*param))?; + } + + 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)?; + } + } + ParserState::CodeOperator(ref op) => { + if !body_begun { + body_begun = true; + fcg.begin_body()?; + } + fcg.feed_opcode(op, &info)?; + } + ParserState::EndFunctionBody => break, + _ => unreachable!(), + } + } + fcg.finalize()?; + } + 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 = chunk.to_vec(); + } + 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/singlepass-backend/src/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs new file mode 100644 index 000000000..918df5346 --- /dev/null +++ b/lib/singlepass-backend/src/protect_unix.rs @@ -0,0 +1,208 @@ +//! Installing signal handlers allows us to handle traps and out-of-bounds memory +//! accesses that occur when runniing webassembly. +//! +//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 +//! +//! When a WebAssembly module triggers any traps, we perform recovery here. +//! +//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling +//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here +//! unless you have memory unsafety elsewhere in your code. +//! +use libc::{c_int, c_void, siginfo_t}; +use nix::sys::signal::{ + sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, +}; +use std::any::Any; +use std::cell::{Cell, UnsafeCell}; +use std::ptr; +use std::sync::Once; +use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; + +extern "C" fn signal_trap_handler( + signum: ::nix::libc::c_int, + siginfo: *mut siginfo_t, + ucontext: *mut c_void, +) { + unsafe { + do_unwind(signum, siginfo as _, ucontext); + } +} + +extern "C" { + pub fn setjmp(env: *mut c_void) -> c_int; + fn longjmp(env: *mut c_void, val: c_int) -> !; +} + +pub unsafe fn install_sighandler() { + let sa = SigAction::new( + SigHandler::SigAction(signal_trap_handler), + SaFlags::SA_ONSTACK, + SigSet::empty(), + ); + sigaction(SIGFPE, &sa).unwrap(); + sigaction(SIGILL, &sa).unwrap(); + sigaction(SIGSEGV, &sa).unwrap(); + sigaction(SIGBUS, &sa).unwrap(); +} + +const SETJMP_BUFFER_LEN: usize = 27; +pub static SIGHANDLER_INIT: Once = Once::new(); + +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 unsafe fn trigger_trap() -> ! { + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + + longjmp(jmp_buf as *mut c_void, 0) +} + +pub fn call_protected(f: impl FnOnce() -> T) -> RuntimeResult { + unsafe { + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + let prev_jmp_buf = *jmp_buf; + + SIGHANDLER_INIT.call_once(|| { + install_sighandler(); + }); + + let signum = setjmp(jmp_buf as *mut _); + if signum != 0 { + *jmp_buf = prev_jmp_buf; + + if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(RuntimeError::Panic { 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(), + } + .into()) + } + } else { + let ret = f(); // TODO: Switch stack? + *jmp_buf = prev_jmp_buf; + Ok(ret) + } + } +} + +/// Unwinds to last protected_call. +pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! { + // Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.) + // itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should + // temporarily disable the signal handlers to debug it. + + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + if *jmp_buf == [0; SETJMP_BUFFER_LEN] { + ::std::process::abort(); + } + + CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext))); + + longjmp(jmp_buf as *mut ::nix::libc::c_void, signum) +} + +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +unsafe fn get_faulting_addr_and_ip( + siginfo: *const c_void, + ucontext: *const c_void, +) -> (*const c_void, *const c_void) { + use libc::{ucontext_t, RIP}; + + #[allow(dead_code)] + #[repr(C)] + struct siginfo_t { + si_signo: i32, + si_errno: i32, + si_code: i32, + si_addr: u64, + // ... + } + + let siginfo = siginfo as *const siginfo_t; + let si_addr = (*siginfo).si_addr; + + let ucontext = ucontext as *const ucontext_t; + let rip = (*ucontext).uc_mcontext.gregs[RIP as usize]; + + (si_addr as _, rip as _) +} + +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +unsafe fn get_faulting_addr_and_ip( + siginfo: *const c_void, + ucontext: *const c_void, +) -> (*const c_void, *const c_void) { + #[allow(dead_code)] + #[repr(C)] + struct ucontext_t { + uc_onstack: u32, + uc_sigmask: u32, + uc_stack: libc::stack_t, + uc_link: *const ucontext_t, + uc_mcsize: u64, + uc_mcontext: *const mcontext_t, + } + #[repr(C)] + struct exception_state { + trapno: u16, + cpu: u16, + err: u32, + faultvaddr: u64, + } + #[repr(C)] + struct regs { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + rdi: u64, + rsi: u64, + rbp: u64, + rsp: u64, + r8: u64, + r9: u64, + r10: u64, + r11: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + rip: u64, + rflags: u64, + cs: u64, + fs: u64, + gs: u64, + } + #[allow(dead_code)] + #[repr(C)] + struct mcontext_t { + es: exception_state, + ss: regs, + // ... + } + + let siginfo = siginfo as *const siginfo_t; + let si_addr = (*siginfo).si_addr; + + let ucontext = ucontext as *const ucontext_t; + let rip = (*(*ucontext).uc_mcontext).ss.rip; + + (si_addr, rip as _) +} diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index f69782b40..390e7ebe9 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.1.2" +version = "0.3.0" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,20 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } [build-dependencies] wabt = "0.7.2" [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" } wabt = "0.7.2" [features] default = ["fast-tests"] -fast-tests = [] \ No newline at end of file +fast-tests = [] +clif = [] +llvm = ["wasmer-llvm-backend"] +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 54d9404d6..10fb45678 100644 --- a/lib/spectests/build/spectests.rs +++ b/lib/spectests/build/spectests.rs @@ -77,12 +77,12 @@ const TESTS: &[&str] = &[ static COMMON: &'static str = r##" use std::{{f32, f64}}; use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::import::ImportObject; use wasmer_runtime_core::types::Value; use wasmer_runtime_core::{{Instance, module::Module}}; use wasmer_runtime_core::error::Result; use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::backend::Compiler; static IMPORT_MODULE: &str = r#" (module @@ -95,9 +95,34 @@ static IMPORT_MODULE: &str = r#" (global $global_i32 (export "global_i32") i32 (i32.const 666))) "#; +#[cfg(feature = "clif")] +fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +#[cfg(feature = "llvm")] +fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} + +#[cfg(feature = "singlepass")] +fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] +fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + pub fn generate_imports() -> ImportObject { let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new()) + let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()) .expect("WASM can't be compiled"); let instance = module .instantiate(&ImportObject::new()) @@ -358,7 +383,7 @@ fn test_module_{}() {{ let module_str = \"{}\"; println!(\"{{}}\", module_str); let wasm_binary = wat2wasm(module_str.as_bytes()).expect(\"WAST not valid or malformed\"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new()).expect(\"WASM can't be compiled\"); + let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()).expect(\"WASM can't be compiled\"); module.instantiate(&generate_imports()).expect(\"WASM can't be instantiated\") }}\n", self.last_module, @@ -381,7 +406,7 @@ fn test_module_{}() {{ "#[test] fn {}_assert_invalid() {{ let wasm_binary = {:?}; - let module = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new()); + let module = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler()); assert!(module.is_err(), \"WASM should not compile as is invalid\"); }}\n", command_name, @@ -512,7 +537,7 @@ fn {}_assert_invalid() {{ "#[test] fn {}_assert_malformed() {{ let wasm_binary = {:?}; - let compilation = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new()); + let compilation = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler()); assert!(compilation.is_err(), \"WASM should not compile as is malformed\"); }}\n", command_name, @@ -568,7 +593,7 @@ fn {}_assert_malformed() {{ let assertion = if expected.len() > 0 && is_nan(&expected[0]) { format!( "let expected = {expected_result}; - if let {return_type_destructure} = result.clone().unwrap().first().unwrap() {{ + if let {return_type_destructure} = result.as_ref().unwrap().first().unwrap() {{ assert!((*result as {return_type}).is_nan()); assert_eq!((*result as {return_type}).is_sign_positive(), (expected as {return_type}).is_sign_positive()); }} else {{ diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index 029cec1ac..357adb5f7 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -1,6 +1,6 @@ use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::{ + backend::Compiler, error, global::Global, memory::Memory, @@ -10,12 +10,37 @@ use wasmer_runtime_core::{ units::Pages, }; +#[cfg(feature = "clif")] +fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +#[cfg(feature = "llvm")] +fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} + +#[cfg(feature = "singlepass")] +fn get_compiler() -> impl Compiler { + use wasmer_singlepass_backend::SinglePassCompiler; + SinglePassCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))] +fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm"); fn main() -> error::Result<()> { let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new())?; + let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler())?; let memory = Memory::new(MemoryDescriptor { minimum: Pages(1), @@ -50,7 +75,7 @@ fn main() -> error::Result<()> { "env" => inner_instance, }; - let outer_module = wasmer_runtime_core::compile_with(EXAMPLE_WASM, &CraneliftCompiler::new())?; + let outer_module = wasmer_runtime_core::compile_with(EXAMPLE_WASM, &get_compiler())?; let outer_instance = outer_module.instantiate(&outer_imports)?; let ret = outer_instance.call("main", &[Value::I32(42)])?; println!("ret: {:?}", ret); diff --git a/lib/spectests/tests/semantics.rs b/lib/spectests/tests/semantics.rs index c364dca29..0bffa9dca 100644 --- a/lib/spectests/tests/semantics.rs +++ b/lib/spectests/tests/semantics.rs @@ -31,7 +31,7 @@ mod tests { match result { Err(err) => match err { - CallError::Runtime(RuntimeError::Unknown { msg }) => { + CallError::Runtime(RuntimeError::Trap { msg }) => { assert!(!msg.contains("segmentation violation")); assert!(!msg.contains("bus error")); } diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml new file mode 100644 index 000000000..87224d529 --- /dev/null +++ b/lib/wasi/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "wasmer-wasi" +version = "0.3.0" +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.3.0" } +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" + +[dependencies.zbox] +git = "https://github.com/wasmerio/zbox" +branch = "bundle-libsodium" +features = ["libsodium-bundled"] diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs new file mode 100644 index 000000000..c08428dbe --- /dev/null +++ b/lib/wasi/src/lib.rs @@ -0,0 +1,94 @@ +#[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}; + +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..85a582df0 --- /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::{Type, 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 { + const TYPE: Type = Type::I32; + + fn to_bits(self) -> u64 { + self.offset as u64 + } + fn from_bits(n: u64) -> 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..813250c57 --- /dev/null +++ b/lib/wasi/src/state.rs @@ -0,0 +1,439 @@ +// 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}, + time::SystemTime, +}; +use wasmer_runtime_core::debug; +use zbox::init_env as zbox_init_env; + +pub const MAX_SYMLINKS: usize = 100; + +#[derive(Debug)] +pub enum WasiFile { + #[allow(dead_code)] + ZboxFile(zbox::File), + HostFile(fs::File), +} + +impl Write for WasiFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + WasiFile::ZboxFile(zbf) => zbf.write(buf), + WasiFile::HostFile(hf) => hf.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + WasiFile::ZboxFile(zbf) => zbf.flush(), + WasiFile::HostFile(hf) => hf.flush(), + } + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match self { + WasiFile::ZboxFile(zbf) => zbf.write_all(buf), + WasiFile::HostFile(hf) => hf.write_all(buf), + } + } + + fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> { + match self { + WasiFile::ZboxFile(zbf) => zbf.write_fmt(fmt), + WasiFile::HostFile(hf) => hf.write_fmt(fmt), + } + } +} + +impl Read for WasiFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + WasiFile::ZboxFile(zbf) => zbf.read(buf), + WasiFile::HostFile(hf) => hf.read(buf), + } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + match self { + WasiFile::ZboxFile(zbf) => zbf.read_to_end(buf), + WasiFile::HostFile(hf) => hf.read_to_end(buf), + } + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + match self { + WasiFile::ZboxFile(zbf) => zbf.read_to_string(buf), + WasiFile::HostFile(hf) => hf.read_to_string(buf), + } + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + match self { + WasiFile::ZboxFile(zbf) => zbf.read_exact(buf), + WasiFile::HostFile(hf) => hf.read_exact(buf), + } + } +} + +impl Seek for WasiFile { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match self { + WasiFile::ZboxFile(zbf) => zbf.seek(pos), + 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 { + handle: WasiFile, + /// 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_files: &[String]) -> Result { + debug!("wasi::fs::init"); + zbox_init_env(); + debug!("wasi::fs::repo"); + /*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 file in preopened_files { + debug!("Attempting to preopen {}", &file); + // TODO: think about this + let default_rights = 0x1FFFFFFF; // all rights + let cur_file: fs::File = fs::OpenOptions::new() + .read(true) + .open(file) + .expect("Could not find file"); + let cur_file_metadata = cur_file.metadata().unwrap(); + let kind = if cur_file_metadata.is_dir() { + Kind::Dir { + handle: WasiFile::HostFile(cur_file), + entries: Default::default(), + } + } else { + return Err(format!( + "WASI only supports pre-opened directories right now; found \"{}\"", + file + )); + }; + // TODO: handle nested pats in `file` + let inode_val = + InodeVal::from_file_metadata(&cur_file_metadata, file.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..a436444fb --- /dev/null +++ b/lib/wasi/src/syscalls/mod.rs @@ -0,0 +1,1498 @@ +#![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}, +}; +use rand::{thread_rng, Rng}; +use std::cell::Cell; +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!() +} + +/// ### `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!() +} + +/// ### `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!(); + } + + 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!(); + } + + __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_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!(), + 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!(), + 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!() + } 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_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!() +} + +/// ### `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!(), + 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"); + // check __WASI_RIGHT_PATH_CREATE_DIRECTORY + unimplemented!() +} + +/// ### `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_vec = wasi_try!(::std::str::from_utf8(unsafe { + &*(wasi_try!(path.deref(memory, 0, path_len)) as *const [_] as *const [u8]) + }) + .map_err(|_| __WASI_EINVAL)) + .split('/') + .map(|str| str.to_string()) + .collect::>(); + let buf_cell = wasi_try!(buf.deref(memory)); + + // find the inode by traversing the path + let mut inode = root_dir.inode; + 'outer: for segment in path_vec { + // loop to traverse symlinks + // TODO: proper cycle detection + let mut sym_count = 0; + loop { + match &state.fs.inodes[inode].kind { + Kind::Dir { entries, .. } => { + if let Some(entry) = entries.get(&segment) { + inode = entry.clone(); + continue 'outer; + } else { + return __WASI_ENOENT; + } + } + Kind::Symlink { forwarded } => { + sym_count += 1; + inode = forwarded.clone(); + if sym_count > MAX_SYMLINKS { + return __WASI_ELOOP; + } + } + _ => { + return __WASI_ENOTDIR; + } + } + } + } + + let stat = state.fs.inodes[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!() +} + +/// ### `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!() +} + +/// ### `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) { + 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 + + let cur_dir = wasi_try!(open_options + .open(&cumulative_path) + .map_err(|_| __WASI_EINVAL)); + + // TODO: refactor and reuse + let cur_file_metadata = cur_dir.metadata().unwrap(); + let kind = if cur_file_metadata.is_dir() { + Kind::Dir { + handle: WasiFile::HostFile(cur_dir), + 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!() +} +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!() +} +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!() +} +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!() +} +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!() +} +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!() +} +pub fn proc_exit(ctx: &mut Ctx, rval: __wasi_exitcode_t) -> Result<(), &'static str> { + debug!("wasi::proc_exit, {}", rval); + Err("Instance exited") +} +pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t { + debug!("wasi::proc_raise"); + unimplemented!() +} + +/// ### `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!() +} +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!() +} +pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { + debug!("wasi::sock_shutdown"); + unimplemented!() +} diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs new file mode 100644 index 000000000..c030df312 --- /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 = u32; +pub const __WASI_ESUCCESS: u32 = 0; +pub const __WASI_E2BIG: u32 = 1; +pub const __WASI_EACCES: u32 = 2; +pub const __WASI_EADDRINUSE: u32 = 3; +pub const __WASI_EADDRNOTAVAIL: u32 = 4; +pub const __WASI_EAFNOSUPPORT: u32 = 5; +pub const __WASI_EAGAIN: u32 = 6; +pub const __WASI_EALREADY: u32 = 7; +pub const __WASI_EBADF: u32 = 8; +pub const __WASI_EBADMSG: u32 = 9; +pub const __WASI_EBUSY: u32 = 10; +pub const __WASI_ECANCELED: u32 = 11; +pub const __WASI_ECHILD: u32 = 12; +pub const __WASI_ECONNABORTED: u32 = 13; +pub const __WASI_ECONNREFUSED: u32 = 14; +pub const __WASI_ECONNRESET: u32 = 15; +pub const __WASI_EDEADLK: u32 = 16; +pub const __WASI_EDESTADDRREQ: u32 = 17; +pub const __WASI_EDOM: u32 = 18; +pub const __WASI_EDQUOT: u32 = 19; +pub const __WASI_EEXIST: u32 = 20; +pub const __WASI_EFAULT: u32 = 21; +pub const __WASI_EFBIG: u32 = 22; +pub const __WASI_EHOSTUNREACH: u32 = 23; +pub const __WASI_EIDRM: u32 = 24; +pub const __WASI_EILSEQ: u32 = 25; +pub const __WASI_EINPROGRESS: u32 = 26; +pub const __WASI_EINTR: u32 = 27; +pub const __WASI_EINVAL: u32 = 28; +pub const __WASI_EIO: u32 = 29; +pub const __WASI_EISCONN: u32 = 30; +pub const __WASI_EISDIR: u32 = 31; +pub const __WASI_ELOOP: u32 = 32; +pub const __WASI_EMFILE: u32 = 33; +pub const __WASI_EMLINK: u32 = 34; +pub const __WASI_EMSGSIZE: u32 = 35; +pub const __WASI_EMULTIHOP: u32 = 36; +pub const __WASI_ENAMETOOLONG: u32 = 37; +pub const __WASI_ENETDOWN: u32 = 38; +pub const __WASI_ENETRESET: u32 = 39; +pub const __WASI_ENETUNREACH: u32 = 40; +pub const __WASI_ENFILE: u32 = 41; +pub const __WASI_ENOBUFS: u32 = 42; +pub const __WASI_ENODEV: u32 = 43; +pub const __WASI_ENOENT: u32 = 44; +pub const __WASI_ENOEXEC: u32 = 45; +pub const __WASI_ENOLCK: u32 = 46; +pub const __WASI_ENOLINK: u32 = 47; +pub const __WASI_ENOMEM: u32 = 48; +pub const __WASI_ENOMSG: u32 = 49; +pub const __WASI_ENOPROTOOPT: u32 = 50; +pub const __WASI_ENOSPC: u32 = 51; +pub const __WASI_ENOSYS: u32 = 52; +pub const __WASI_ENOTCONN: u32 = 53; +pub const __WASI_ENOTDIR: u32 = 54; +pub const __WASI_ENOTEMPTY: u32 = 55; +pub const __WASI_ENOTRECOVERABLE: u32 = 56; +pub const __WASI_ENOTSOCK: u32 = 57; +pub const __WASI_ENOTSUP: u32 = 58; +pub const __WASI_ENOTTY: u32 = 59; +pub const __WASI_ENXIO: u32 = 60; +pub const __WASI_EOVERFLOW: u32 = 61; +pub const __WASI_EOWNERDEAD: u32 = 62; +pub const __WASI_EPERM: u32 = 63; +pub const __WASI_EPIPE: u32 = 64; +pub const __WASI_EPROTO: u32 = 65; +pub const __WASI_EPROTONOSUPPORT: u32 = 66; +pub const __WASI_EPROTOTYPE: u32 = 67; +pub const __WASI_ERANGE: u32 = 68; +pub const __WASI_EROFS: u32 = 69; +pub const __WASI_ESPIPE: u32 = 70; +pub const __WASI_ESRCH: u32 = 71; +pub const __WASI_ESTALE: u32 = 72; +pub const __WASI_ETIMEDOUT: u32 = 73; +pub const __WASI_ETXTBSY: u32 = 74; +pub const __WASI_EXDEV: u32 = 75; +pub const __WASI_ENOTCAPABLE: u32 = 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 = u32; +pub const __WASI_EVENT_FD_READWRITE_HANGUP: u32 = 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 = u32; +pub const __WASI_FDFLAG_APPEND: u32 = 1 << 0; +pub const __WASI_FDFLAG_DSYNC: u32 = 1 << 1; +pub const __WASI_FDFLAG_NONBLOCK: u32 = 1 << 2; +pub const __WASI_FDFLAG_RSYNC: u32 = 1 << 3; +pub const __WASI_FDFLAG_SYNC: u32 = 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 = u32; +pub const __WASI_FILESTAT_SET_ATIM: u32 = 1 << 0; +pub const __WASI_FILESTAT_SET_ATIM_NOW: u32 = 1 << 1; +pub const __WASI_FILESTAT_SET_MTIM: u32 = 1 << 2; +pub const __WASI_FILESTAT_SET_MTIM_NOW: u32 = 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 = u32; +pub const __WASI_O_CREAT: u32 = 1 << 0; +pub const __WASI_O_DIRECTORY: u32 = 1 << 1; +pub const __WASI_O_EXCL: u32 = 1 << 2; +pub const __WASI_O_TRUNC: u32 = 1 << 3; + +pub type __wasi_riflags_t = u32; +pub const __WASI_SOCK_RECV_PEEK: u32 = 1 << 0; +pub const __WASI_SOCK_RECV_WAITALL: u32 = 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 = u32; +pub const __WASI_SOCK_RECV_DATA_TRUNCATED: u32 = 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 = u32; + +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 = u32; +pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: u32 = 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 043dc3c59..c11d497bd 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.0.1" +version = "0.3.0" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,9 +8,9 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } -libc = "0.2.48" +libc = "0.2.49" [build-dependencies] cmake = "0.1.35" 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/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c index 3284034cc..3c7990ec2 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.c +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -10,6 +10,7 @@ __declspec(thread) DWORD64 caughtInstructionPointer; __declspec(thread) PVOID savedStackPointer; __declspec(thread) BOOL exceptionHandlerInstalled = FALSE; __declspec(thread) BOOL alreadyHandlingException = FALSE; +__declspec(thread) PVOID handle; void longjmpOutOfHere() { longjmp(jmpBuf, 1); @@ -38,6 +39,14 @@ exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { return EXCEPTION_CONTINUE_EXECUTION; } +static void removeExceptionHandler() { + if (exceptionHandlerInstalled == FALSE) { + return; + } + RemoveVectoredExceptionHandler(handle); + exceptionHandlerInstalled = FALSE; +} + uint8_t callProtected(trampoline_t trampoline, const struct wasmer_instance_context_t* ctx, const struct func_t* func, @@ -48,7 +57,7 @@ uint8_t callProtected(trampoline_t trampoline, // install exception handler if (exceptionHandlerInstalled == FALSE) { exceptionHandlerInstalled = TRUE; - AddVectoredExceptionHandler(CALL_FIRST, exceptionHandler); + handle = AddVectoredExceptionHandler(CALL_FIRST, exceptionHandler); } // jmp jmp jmp! @@ -58,17 +67,20 @@ uint8_t callProtected(trampoline_t trampoline, savedStackPointer = get_callee_frame_address(); trampoline(ctx, func, param_vec, return_vec); out_result->code = 0; - out_result->exceptionAddress = 0; - out_result->instructionPointer = 0; + out_result->exception_address = 0; + out_result->instruction_pointer = 0; + + removeExceptionHandler(); return TRUE; } out_result->code = (uint64_t)signum; - out_result->exceptionAddress = (uint64_t)caughtExceptionAddress; - out_result->instructionPointer = caughtInstructionPointer; + out_result->exception_address = (uint64_t)caughtExceptionAddress; + out_result->instruction_pointer = caughtInstructionPointer; caughtExceptionAddress = 0; caughtInstructionPointer = 0; + removeExceptionHandler(); return FALSE; } diff --git a/lib/win-exception-handler/exception_handling/exception_handling.h b/lib/win-exception-handler/exception_handling/exception_handling.h index cd5472149..f00227600 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.h +++ b/lib/win-exception-handler/exception_handling/exception_handling.h @@ -10,8 +10,8 @@ typedef void(*trampoline_t)(struct wasmer_instance_context_t*, const struct fun struct call_protected_result_t { uint64_t code; - uint64_t exceptionAddress; - uint64_t instructionPointer; + uint64_t exception_address; + uint64_t instruction_pointer; }; uint8_t callProtected( diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index 04c3c7f2e..ea36333ab 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -1,14 +1,15 @@ 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)] pub struct CallProtectedData { pub code: u64, - pub exceptionAddress: u64, - pub instructionPointer: u64, + pub exception_address: u64, + pub instruction_pointer: u64, } extern "C" { @@ -16,7 +17,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,14 +27,14 @@ 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 { let mut out_result = CallProtectedData { code: 0, - exceptionAddress: 0, - instructionPointer: 0, + exception_address: 0, + instruction_pointer: 0, }; let result = unsafe { __call_protected( diff --git a/scripts/install_lib_sodium.sh b/scripts/install_lib_sodium.sh new file mode 100755 index 000000000..51e46c57c --- /dev/null +++ b/scripts/install_lib_sodium.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +curl -O https://download.libsodium.org/libsodium/releases/libsodium-1.0.17.tar.gz +tar xf libsodium-1.0.17.tar.gz +cd libsodium-1.0.17/ +./configure +make && make check +sudo make install diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 373db7f92..b9606a225 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,20 +1,44 @@ extern crate structopt; use std::env; -use std::fs; -use std::fs::File; +use std::fs::{read_to_string, File}; 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}; -use wasmer_runtime::error::CacheError; +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}; +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 +52,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, @@ -35,9 +63,6 @@ enum CLIOptions { #[derive(Debug, StructOpt)] struct Run { - #[structopt(short = "d", long = "debug")] - debug: bool, - // Disable the cache #[structopt(long = "disable-cache")] disable_cache: bool, @@ -46,20 +71,78 @@ 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, + + /// 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, } +#[allow(dead_code)] +#[derive(Debug)] +enum Backend { + Cranelift, + Singlepass, + LLVM, +} + +impl Backend { + pub fn variants() -> &'static [&'static str] { + &["singlepass", "cranelift", "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)] enum Cache { + /// Clear the cache #[structopt(name = "clean")] Clean, + /// Display the location of the cache #[structopt(name = "dir")] 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(); @@ -77,6 +160,7 @@ fn get_cache_dir() -> PathBuf { // We use a temporal directory for saving cache files let mut temp_dir = env::temp_dir(); temp_dir.push("wasmer"); + temp_dir.push(WASMER_VERSION_HASH); temp_dir } } @@ -84,6 +168,12 @@ fn get_cache_dir() -> PathBuf { /// Execute a wasm/wat file fn execute_wasm(options: &Run) -> Result<(), String> { + // force disable caching on windows + #[cfg(target_os = "windows")] + let disable_cache = true; + #[cfg(not(target_os = "windows"))] + let disable_cache = options.disable_cache; + let wasm_path = &options.path; let mut wasm_binary: Vec = read_file_contents(wasm_path).map_err(|err| { @@ -94,12 +184,66 @@ fn execute_wasm(options: &Run) -> Result<(), String> { ) })?; + let em_symbol_map = if let Some(em_symbol_map_path) = options.em_symbol_map.clone() { + let em_symbol_map_content: String = read_to_string(&em_symbol_map_path) + .map_err(|err| { + format!( + "Can't read symbol map file {}: {}", + em_symbol_map_path.as_os_str().to_string_lossy(), + err, + ) + })? + .to_owned(); + let mut em_symbol_map = HashMap::new(); + for line in em_symbol_map_content.lines() { + let mut split = line.split(':'); + let num_str = if let Some(ns) = split.next() { + ns + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + }; + let num: u32 = num_str.parse::().map_err(|err| { + format!( + "Failed to parse {} as a number in symbol map: {}", + num_str, err + ) + })?; + let name_str: String = if let Some(name_str) = split.next() { + name_str + } else { + return Err(format!( + "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" + )); + } + .to_owned(); + + em_symbol_map.insert(num, name_str); + } + Some(em_symbol_map) + } else { + None + }; + if !utils::is_wasm_binary(&wasm_binary) { wasm_binary = wabt::wat2wasm(wasm_binary) .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; } - let module = if !options.disable_cache { + 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 // We generate a hash for the given binary, so we can use it as key @@ -124,35 +268,69 @@ fn execute_wasm(options: &Run) -> Result<(), String> { module } Err(_) => { - let module = webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))?; + 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 + cache.store(hash, module.clone()).unwrap_or_default(); - // We save the module into a cache file - cache.store(hash, module.clone()).unwrap(); module } }; module } else { - webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))? + 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 + let (abi, import_object, _em_globals) = 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 ) - } else if wasmer_golang::is_golang_module(&module) { - (InstanceABI::Go, wasmer_golang::generate_golang_env(), None) } else { - ( - InstanceABI::Go, - wasmer_runtime_core::import::ImportObject::new(), - None, - ) + if cfg!(feature = "wasi") && wasmer_wasi::is_wasi_module(&module) { + ( + InstanceABI::WASI, + 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(), + ), + None, + ) + } else { + ( + InstanceABI::None, + wasmer_runtime_core::import::ImportObject::new(), + None, + ) + } }; let mut instance = module @@ -162,7 +340,12 @@ fn execute_wasm(options: &Run) -> Result<(), String> { webassembly::run_instance( &module, &mut instance, - options.path.to_str().unwrap(), + abi, + if let Some(cn) = &options.command_name { + cn + } else { + options.path.to_str().unwrap() + }, options.args.iter().map(|arg| arg.as_str()).collect(), ) .map_err(|e| format!("{:?}", e))?; @@ -180,6 +363,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 { @@ -190,15 +409,26 @@ fn main() { CLIOptions::SelfUpdate => { println!("Self update is not supported on Windows. Use install instructions on the Wasmer homepage: https://wasmer.io"); } + #[cfg(not(target_os = "windows"))] CLIOptions::Cache(cache) => match cache { Cache::Clean => { + use std::fs; let cache_dir = get_cache_dir(); - fs::remove_dir_all(cache_dir.clone()).expect("Can't remove cache dir"); - fs::create_dir(cache_dir.clone()).expect("Can't create cache dir"); + if cache_dir.exists() { + fs::remove_dir_all(cache_dir.clone()).expect("Can't remove cache dir"); + } + fs::create_dir_all(cache_dir.clone()).expect("Can't create cache dir"); } Cache::Dir => { 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 04850eb7a..f844eb4da 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -11,7 +11,7 @@ ChangesEnvironment=yes OutputBaseFilename=WasmerInstaller [Files] -Source: "..\target\release\wasmer.exe"; DestDir: "{app}\bin" +Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" [Code] const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; diff --git a/src/webassembly.rs b/src/webassembly.rs index b178e1814..354136ead 100644 --- a/src/webassembly.rs +++ b/src/webassembly.rs @@ -1,12 +1,13 @@ use std::panic; +pub use wasmer_runtime::compile_with_config_with; use wasmer_runtime::{ self as runtime, error::{CallResult, Result}, ImportObject, Instance, Module, }; +use wasmer_runtime_core::types::Value; -use wasmer_emscripten::{is_emscripten_module, run_emscripten_instance}; -use wasmer_golang::{is_golang_module, run_golang_instance}; +use wasmer_emscripten::run_emscripten_instance; pub struct ResultObject { /// A webassembly::Module object representing the compiled WebAssembly module. @@ -20,7 +21,7 @@ pub struct ResultObject { #[derive(PartialEq)] pub enum InstanceABI { Emscripten, - Go, + WASI, None, } @@ -77,21 +78,39 @@ pub fn compile(buffer_source: &[u8]) -> Result { Ok(module) } +// /// The same as `compile` but takes a `CompilerConfig` for the purpose of +// /// changing the compiler's behavior +// pub fn compile_with_config_with( +// 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, + abi: InstanceABI, path: &str, args: Vec<&str>, ) -> CallResult<()> { - if is_emscripten_module(&module) { - run_emscripten_instance(module, instance, path, args)?; - } else if is_golang_module(&module) { - run_golang_instance(module, instance, path, args)?; - } else { - instance.call("main", &[])?; - }; - + match abi { + InstanceABI::Emscripten => { + run_emscripten_instance(module, instance, path, args)?; + } + InstanceABI::WASI => { + instance.call("_start", &[])?; + } + InstanceABI::None => { + 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..df7f38952 --- /dev/null +++ b/update_version_numbers.sh @@ -0,0 +1,17 @@ +PREVIOUS_VERSION='0.2.1' +NEXT_VERSION='0.3.0' + +# 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