mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-23 13:41:32 +00:00
Merge remote-tracking branch 'origin/master' into feature/llvm-osr
This commit is contained in:
@ -18,24 +18,22 @@ environment:
|
||||
cache:
|
||||
- 'C:\Users\appveyor\.cargo'
|
||||
- target
|
||||
- wapm-cli-target
|
||||
|
||||
install:
|
||||
# # 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 LLVM
|
||||
- mkdir C:\projects\deps
|
||||
- cd C:\projects\deps
|
||||
- appveyor DownloadFile https://github.com/wasmerio/windows-llvm-build/releases/download/v8.0.0/llvm-8.0.0-install.zip -FileName llvm-8.0.0-install.zip
|
||||
- 7z x llvm-8.0.0-install.zip
|
||||
- C:\projects\deps\llvm-8.0.0-install\bin\llvm-config.exe --version
|
||||
- set "LLVM_SYS_80_PREFIX=C:\projects\deps\llvm-8.0.0-install"
|
||||
- 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
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init.exe -yv --default-host %target%
|
||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- rustup default stable-%target%
|
||||
- rustup update
|
||||
- rustc -vV
|
||||
@ -49,25 +47,17 @@ install:
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
build_script:
|
||||
- cargo build --release --verbose
|
||||
- git submodule init
|
||||
- git submodule update
|
||||
# Cache wapm cli target in dir above to prevent breaking git submodule on windows
|
||||
- if not exist wapm-cli-target mkdir wapm-cli-target
|
||||
- move wapm-cli-target wapm-cli
|
||||
- cd wapm-cli
|
||||
- rename wapm-cli-target target
|
||||
- cd ..
|
||||
- cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
|
||||
- cd wapm-cli
|
||||
- cd ..
|
||||
- xcopy wapm-cli\target wapm-cli-target\ /i /y
|
||||
- cargo build --release --verbose --features backend-llvm
|
||||
- cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml
|
||||
|
||||
test_script:
|
||||
- cargo test --manifest-path lib/spectests/Cargo.toml --features clif
|
||||
- cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture
|
||||
|
||||
before_deploy:
|
||||
- appveyor PushArtifact target\release\wasmer_runtime_c_api.dll
|
||||
- git submodule init
|
||||
- git submodule update
|
||||
- cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
|
||||
- cd ./src/installer
|
||||
- iscc wasmer.iss
|
||||
- copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
|
||||
|
@ -1,14 +1,14 @@
|
||||
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/
|
||||
LLVM_SYS_80_PREFIX: /home/circleci/project/clang+llvm-8.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
|
||||
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
tar xf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
@ -75,9 +75,6 @@ jobs:
|
||||
make cranelift
|
||||
make llvm
|
||||
make test-rest
|
||||
- run:
|
||||
name: Release
|
||||
command: make release-fast
|
||||
- run:
|
||||
name: Integration Tests
|
||||
command: make integration-tests
|
||||
@ -115,9 +112,9 @@ jobs:
|
||||
name: Check
|
||||
command: |
|
||||
make check
|
||||
- run:
|
||||
name: Release
|
||||
command: make release-fast
|
||||
make compile-bench-singlepass
|
||||
make compile-bench-llvm
|
||||
# TODO: add compile-bench-clif when it works
|
||||
- run:
|
||||
name: Integration Tests
|
||||
command: make integration-tests
|
||||
@ -129,6 +126,26 @@ jobs:
|
||||
- target/release/deps
|
||||
key: v8-test-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||
|
||||
test-rust-example:
|
||||
docker:
|
||||
- image: circleci/rust:latest
|
||||
<<: *run_with_build_env_vars
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Check Wasmer Rust example"
|
||||
command: |
|
||||
git clone https://github.com/wasmerio/wasmer-rust-example
|
||||
rustup default stable
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cd wasmer-rust-example
|
||||
cd wasm-sample-app
|
||||
cargo build --release
|
||||
cd ..
|
||||
sed -i 's/wasmer-runtime.*/wasmer-runtime = \{ path = "..\/lib\/runtime" \}/g' Cargo.toml
|
||||
cargo run
|
||||
cargo test
|
||||
|
||||
test-macos:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
@ -149,8 +166,8 @@ jobs:
|
||||
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
|
||||
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
|
||||
tar xf clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
|
||||
- run:
|
||||
name: Install Rust
|
||||
command: |
|
||||
@ -162,7 +179,7 @@ jobs:
|
||||
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/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.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
|
||||
@ -171,18 +188,14 @@ jobs:
|
||||
name: Check
|
||||
command: |
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
|
||||
make check
|
||||
- run:
|
||||
name: Release
|
||||
command: |
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
make release-fast
|
||||
- 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/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
|
||||
make integration-tests
|
||||
- save_cache:
|
||||
paths:
|
||||
@ -213,27 +226,27 @@ jobs:
|
||||
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
|
||||
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
tar xf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
|
||||
- run: rustup default nightly-2019-06-10
|
||||
- run:
|
||||
name: Tests
|
||||
command: |
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
make test
|
||||
- run:
|
||||
name: Release Build
|
||||
command: |
|
||||
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/"
|
||||
make release
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
|
||||
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
|
||||
make build-install
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh)
|
||||
- run:
|
||||
name: Dynamic library
|
||||
command: |
|
||||
@ -275,8 +288,8 @@ jobs:
|
||||
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
|
||||
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
|
||||
tar xf clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
|
||||
- run:
|
||||
name: Install Rust
|
||||
command: |
|
||||
@ -288,7 +301,7 @@ jobs:
|
||||
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/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.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
|
||||
@ -299,12 +312,12 @@ jobs:
|
||||
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/"
|
||||
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
|
||||
make release
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
|
||||
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
|
||||
mkdir -p artifacts
|
||||
make build-install
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
|
||||
cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh)
|
||||
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
|
||||
# echo "${VERSION}" >> artifacts/version
|
||||
- run:
|
||||
@ -344,6 +357,8 @@ jobs:
|
||||
VERSION=$(cat ./artifacts/version)
|
||||
# VERSION_TAG=${CIRCLE_TAG}
|
||||
VERSION_TAG=$(cat ./artifacts/git_version)
|
||||
LATEST_VERSION_PUBLISHED_ON_CRATES=$(curl -s https://raw.githubusercontent.com/rust-lang/crates.io-index/master/wa/sm/wasmer-runtime | tail -n 1 | sed 's/.*"vers":"\([^"]*\)".*/\1/')
|
||||
if ( [ $VERSION_TAG -ne $LATEST_VERSION_PUBLISHED_ON_CRATES ] ) then { echo "Could not detect version published to crates.io; make sure we've published the crates before publishing the Wasmer binary"; exit 1; } else { true; } fi
|
||||
rm ./artifacts/version
|
||||
rm ./artifacts/git_version
|
||||
# VERSION_TAG=$(git describe --exact-match --tags)
|
||||
@ -382,6 +397,12 @@ workflows:
|
||||
only:
|
||||
- trying
|
||||
- staging
|
||||
- test-rust-example:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- trying
|
||||
- staging
|
||||
- test-macos:
|
||||
filters:
|
||||
branches:
|
||||
|
39
.github/CODEOWNERS
vendored
Normal file
39
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
src/ @syrusakbary @MarkMcCaskey
|
||||
|
||||
# Backends
|
||||
lib/singlepass-backend @losfair @nlewycky
|
||||
lib/clif-backend @nlewycky @bjfish
|
||||
lib/llvm-backend @nlewycky @losfair
|
||||
|
||||
# Runtime
|
||||
lib/runtime-core @Hywan @bjfish
|
||||
lib/runtime-abi @MarkMcCaskey
|
||||
lib/runtime @MarkMcCaskey @Hywan @bjfish
|
||||
lib/runtime-c-api @bjfish @Hywan
|
||||
lib/win-exception-handler @bjfish @losfair
|
||||
lib/middleware-common @bjfish @losfair
|
||||
|
||||
# Frontend integrations
|
||||
|
||||
## Emscripten
|
||||
lib/emscripten @MarkMcCaskey @syrusakbary
|
||||
lib/emscripten-tests @MarkMcCaskey @syrusakbary
|
||||
|
||||
## WASI
|
||||
lib/wasi @MarkMcCaskey
|
||||
lib/wasi-tests @MarkMcCaskey
|
||||
|
||||
## Spectests
|
||||
lib/spectests @syrusakbary @MarkMcCaskey @nlewycky
|
||||
|
||||
# Kernel
|
||||
lib/kernel-loader @losfair
|
||||
lib/kernel-net @losfair
|
||||
|
||||
# Examples
|
||||
examples @syrusakbary
|
||||
|
||||
# Documentation
|
||||
docs @syrusakbary
|
||||
|
46
CHANGELOG.md
46
CHANGELOG.md
@ -6,26 +6,64 @@ Blocks of changes will separated by version increments.
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
## 0.5.6
|
||||
Special thanks to @YaronWittenstein @penberg for their contributions.
|
||||
|
||||
- [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted
|
||||
- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting
|
||||
- [#608](https://github.com/wasmerio/wasmer/issues/608) Implement wasi syscalls `fd_allocate`, `fd_sync`, `fd_pread`, `path_link`, `path_filestat_set_times`; update WASI fs API in a WIP way; reduce coupling of WASI code to host filesystem; make debug messages from WASI more readable; improve rights-checking when calling syscalls; implement reference counting on inodes; misc bug fixes and improvements
|
||||
- [#616](https://github.com/wasmerio/wasmer/issues/616) Create the import object separately from instance instantiation in `runtime-c-api`
|
||||
- [#620](https://github.com/wasmerio/wasmer/issues/620) Replace one `throw()` with `noexcept` in llvm backend
|
||||
- [#618](https://github.com/wasmerio/wasmer/issues/618) Implement `InternalEvent::Breakpoint` in the llvm backend to allow metering in llvm
|
||||
- [#615](https://github.com/wasmerio/wasmer/issues/615) Eliminate `FunctionEnvironment` construction in `feed_event()` speeding up to 70% of compilation in clif
|
||||
- [#609](https://github.com/wasmerio/wasmer/issues/609) Update dependencies
|
||||
- [#602](https://github.com/wasmerio/wasmer/issues/602) C api extract instance context from instance
|
||||
- [#590](https://github.com/wasmerio/wasmer/issues/590) Error visibility changes in wasmer-c-api
|
||||
- [#589](https://github.com/wasmerio/wasmer/issues/589) Make `wasmer_byte_array` fields `public` in wasmer-c-api
|
||||
|
||||
## 0.6.0 - 2019-07-31
|
||||
- [#603](https://github.com/wasmerio/wasmer/pull/603) Update Wapm-cli, bump version numbers
|
||||
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
|
||||
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
|
||||
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
|
||||
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.
|
||||
Add `default-backend-singlepass`, `default-backend-llvm`, and `default-backend-cranelift` features to `wasmer-runtime`
|
||||
to control the `default_compiler()` function (this is a breaking change). Add `compiler_for_backend` function in `wasmer-runtime`
|
||||
- [#561](https://github.com/wasmerio/wasmer/pull/561) Call the `data_finalizer` field on the `Ctx`
|
||||
- [#576](https://github.com/wasmerio/wasmer/pull/576) fix `Drop` of uninit `Ctx`
|
||||
- [#542](https://github.com/wasmerio/wasmer/pull/542) Add SIMD support to Wasmer (LLVM backend only)
|
||||
- Updates LLVM to version 8.0
|
||||
|
||||
## 0.5.7 - 2019-07-23
|
||||
- [#575](https://github.com/wasmerio/wasmer/pull/575) Prepare for release; update wapm to 0.3.6
|
||||
- [#555](https://github.com/wasmerio/wasmer/pull/555) WASI filesystem rewrite. Major improvements
|
||||
- adds virtual root showing all preopened directories
|
||||
- improved sandboxing and code-reuse
|
||||
- symlinks work in a lot more situations
|
||||
- many misc. improvements to most syscalls touching the filesystem
|
||||
|
||||
## 0.5.6 - 2019-07-16
|
||||
- [#565](https://github.com/wasmerio/wasmer/pull/565) Update wapm and bump version to 0.5.6
|
||||
- [#563](https://github.com/wasmerio/wasmer/pull/563) Improve wasi testing infrastructure
|
||||
- fixes arg parsing from comments & fixes the mapdir test to have the native code doing the same thing as the WASI code
|
||||
- makes wasitests-generate output stdout/stderr by default & adds function to print stdout and stderr for a command if it fails
|
||||
- compiles wasm with size optimizations & strips generated wasm with wasm-strip
|
||||
- [#554](https://github.com/wasmerio/wasmer/pull/554) Finish implementation of `wasi::fd_seek`, fix bug in filestat
|
||||
- [#550](https://github.com/wasmerio/wasmer/pull/550) Fix singlepass compilation error with `imul` instruction
|
||||
|
||||
## 0.5.5
|
||||
|
||||
## 0.5.5 - 2019-07-10
|
||||
- [#541](https://github.com/wasmerio/wasmer/pull/541) Fix dependency graph by making separate test crates; ABI implementations should not depend on compilers. Add Cranelift fork as git submodule of clif-backend
|
||||
- [#537](https://github.com/wasmerio/wasmer/pull/537) Add hidden flag (`--cache-key`) to use prehashed key into the compiled wasm cache and change compiler backend-specific caching to use directories
|
||||
- [#536](https://github.com/wasmerio/wasmer/pull/536) ~Update cache to use compiler backend name in cache key~
|
||||
|
||||
## 0.5.4
|
||||
## 0.5.4 - 2019-07-06
|
||||
- [#529](https://github.com/wasmerio/wasmer/pull/529) Updates the Wasm Interface library, which is used by wapm, with bug fixes and error message improvements
|
||||
|
||||
## 0.5.3
|
||||
## 0.5.3 - 2019-07-03
|
||||
- [#523](https://github.com/wasmerio/wasmer/pull/523) Update wapm version to fix bug related to signed packages in the global namespace and locally-stored public keys
|
||||
|
||||
## 0.5.2 - 2019-07-02
|
||||
- [#516](https://github.com/wasmerio/wasmer/pull/516) Add workaround for singlepass miscompilation on GetLocal
|
||||
- [#521](https://github.com/wasmerio/wasmer/pull/521) Update Wapm-cli, bump version numbers
|
||||
- [#518](https://github.com/wasmerio/wasmer/pull/518) Update Cranelift and WasmParser
|
||||
- [#514](https://github.com/wasmerio/wasmer/pull/514) [#519](https://github.com/wasmerio/wasmer/pull/519) Improved Emscripten network related calls, added a null check to `WasmPtr`
|
||||
|
813
Cargo.lock
generated
813
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
34
Cargo.toml
34
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
@ -19,11 +19,10 @@ include = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.3.1"
|
||||
byteorder = "1.3.2"
|
||||
errno = "0.2.4"
|
||||
structopt = "0.2.11"
|
||||
wabt = "0.7.2"
|
||||
hashbrown = "0.1.8"
|
||||
structopt = "0.2.18"
|
||||
wabt = "0.9.1"
|
||||
wasmer-clif-backend = { path = "lib/clif-backend" }
|
||||
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
|
||||
wasmer-middleware-common = { path = "lib/middleware-common" }
|
||||
@ -61,20 +60,35 @@ members = [
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
wabt = "0.7.2"
|
||||
glob = "0.2.11"
|
||||
wabt = "0.9.1"
|
||||
glob = "0.3.0"
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
[features]
|
||||
default = ["fast-tests", "wasi"]
|
||||
default = ["fast-tests", "wasi", "backend-cranelift"]
|
||||
"loader-kernel" = ["wasmer-kernel-loader"]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
trace = ["wasmer-runtime-core/trace"]
|
||||
extra-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", "wasmer-runtime-core/backend-llvm"]
|
||||
"backend-singlepass" = ["wasmer-singlepass-backend", "wasmer-runtime-core/backend-singlepass"]
|
||||
backend-cranelift = [
|
||||
"wasmer-runtime-core/backend-cranelift",
|
||||
"wasmer-runtime/cranelift",
|
||||
"wasmer-middleware-common/clif"
|
||||
]
|
||||
backend-llvm = [
|
||||
"wasmer-llvm-backend",
|
||||
"wasmer-runtime-core/backend-llvm",
|
||||
"wasmer-runtime/llvm",
|
||||
"wasmer-middleware-common/llvm"
|
||||
]
|
||||
backend-singlepass = [
|
||||
"wasmer-singlepass-backend",
|
||||
"wasmer-runtime-core/backend-singlepass",
|
||||
"wasmer-runtime/singlepass",
|
||||
"wasmer-middleware-common/singlepass"
|
||||
]
|
||||
wasi = ["wasmer-wasi"]
|
||||
managed = ["backend-singlepass", "wasmer-runtime-core/managed"]
|
||||
# vfs = ["wasmer-runtime-abi"]
|
||||
|
@ -3,9 +3,9 @@ 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 \
|
||||
RUN curl -SL https://releases.llvm.org/8.0.0/clang+llvm-8.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/
|
||||
ENV LLVM_SYS_80_PREFIX /home/circleci/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/
|
||||
|
||||
FROM wasmer-build-env AS wasmer-debug-env
|
||||
RUN sudo apt-get update && \
|
||||
|
13
Dockerfile.build
Normal file
13
Dockerfile.build
Normal file
@ -0,0 +1,13 @@
|
||||
FROM ubuntu:19.04
|
||||
|
||||
ARG RUST_TOOLCHAIN="nightly"
|
||||
|
||||
ENV CARGO_HOME=/usr/local/rust
|
||||
ENV RUSTUP_HOME=/usr/local/rust
|
||||
ENV PATH="$PATH:$CARGO_HOME/bin"
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install sudo strace curl cmake pkg-config python libssl-dev llvm-dev libz-dev gnuplot-nox \
|
||||
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
|
||||
&& echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
|
||||
&& curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN"
|
48
Makefile
48
Makefile
@ -7,21 +7,27 @@ generate-spectests:
|
||||
generate-emtests:
|
||||
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten-tests --release
|
||||
|
||||
generate-wasitests:
|
||||
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv
|
||||
generate-wasitests: wasitests-setup
|
||||
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv \
|
||||
&& echo "formatting" \
|
||||
&& cargo fmt
|
||||
|
||||
spectests-generate: generate-spectests
|
||||
emtests-generate: generate-emtests
|
||||
wasitests-generate: generate-wasitests
|
||||
|
||||
generate: generate-spectests generate-emtests generate-wasitests
|
||||
|
||||
|
||||
# Spectests
|
||||
spectests-singlepass:
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture
|
||||
|
||||
spectests-cranelift:
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture
|
||||
|
||||
spectests-llvm:
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features llvm
|
||||
cargo test --manifest-path lib/spectests/Cargo.toml --release --features llvm -- --nocapture
|
||||
|
||||
spectests: spectests-singlepass spectests-cranelift spectests-llvm
|
||||
|
||||
@ -56,13 +62,17 @@ middleware: middleware-singlepass middleware-cranelift middleware-llvm
|
||||
|
||||
|
||||
# Wasitests
|
||||
wasitests-singlepass:
|
||||
wasitests-setup:
|
||||
rm -rf lib/wasi-tests/wasitests/test_fs/temp
|
||||
mkdir -p lib/wasi-tests/wasitests/test_fs/temp
|
||||
|
||||
wasitests-singlepass: wasitests-setup
|
||||
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features singlepass -- --test-threads=1
|
||||
|
||||
wasitests-cranelift:
|
||||
wasitests-cranelift: wasitests-setup
|
||||
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1
|
||||
|
||||
wasitests-llvm:
|
||||
wasitests-llvm: wasitests-setup
|
||||
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features llvm -- --test-threads=1
|
||||
|
||||
wasitests-unit:
|
||||
@ -78,7 +88,7 @@ singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasite
|
||||
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
|
||||
cargo test -p wasmer-clif-backend --release
|
||||
|
||||
llvm: spectests-llvm emtests-llvm middleware-llvm wasitests-llvm
|
||||
llvm: spectests-llvm emtests-llvm wasitests-llvm
|
||||
cargo test -p wasmer-llvm-backend --release
|
||||
|
||||
|
||||
@ -98,7 +108,7 @@ test: spectests emtests middleware wasitests circleci-clean test-rest
|
||||
|
||||
|
||||
# Integration tests
|
||||
integration-tests: release-fast
|
||||
integration-tests: release-clif
|
||||
echo "Running Integration Tests"
|
||||
./integration_tests/lua/test.sh
|
||||
./integration_tests/nginx/test.sh
|
||||
@ -124,7 +134,7 @@ release:
|
||||
cargo build --release --features backend-singlepass,backend-llvm,loader-kernel
|
||||
|
||||
# Only one backend (cranelift)
|
||||
release-fast:
|
||||
release-clif:
|
||||
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
|
||||
# brew install mingw-w64
|
||||
cargo build --release
|
||||
@ -135,8 +145,20 @@ release-singlepass:
|
||||
release-llvm:
|
||||
cargo build --release --features backend-llvm
|
||||
|
||||
bench:
|
||||
cargo bench --all
|
||||
bench-singlepass:
|
||||
cargo bench --all --no-default-features --features "backend-singlepass"
|
||||
bench-clif:
|
||||
cargo bench --all --no-default-features --features "backend-cranelift"
|
||||
bench-llvm:
|
||||
cargo bench --all --no-default-features --features "backend-llvm"
|
||||
|
||||
# compile but don't run the benchmarks
|
||||
compile-bench-singlepass:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-singlepass"
|
||||
compile-bench-clif:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-cranelift"
|
||||
compile-bench-llvm:
|
||||
cargo bench --all --no-run --no-default-features --features "backend-llvm"
|
||||
|
||||
|
||||
# Build utils
|
||||
|
81
README.md
81
README.md
@ -14,6 +14,9 @@
|
||||
<a href="https://spectrum.chat/wasmer">
|
||||
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
|
||||
</a>
|
||||
<a href="https://twitter.com/wasmerio">
|
||||
<img alt="Follow @wasmerio on Twitter" src="https://img.shields.io/twitter/follow/wasmerio?label=%40wasmerio&style=social">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Introduction
|
||||
@ -26,10 +29,13 @@ Install Wasmer with:
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
```
|
||||
|
||||
> Note: *Wasmer is also available on Windows. Download the [`WasmerInstaller.exe` from the Github Releases](https://github.com/wasmerio/wasmer/releases) page.*
|
||||
|
||||
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)
|
||||
* [**🦀 Rust**](https://github.com/wasmerio/wasmer-rust-example)
|
||||
* [**🔗 C/C++**](https://github.com/wasmerio/wasmer-c-api)
|
||||
* [**#️⃣ C#**](https://github.com/migueldeicaza/WasmerSharp)
|
||||
* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm)
|
||||
* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm)
|
||||
* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm)
|
||||
@ -45,21 +51,14 @@ Once installed, you will be able to run any WebAssembly files (_including Lua, P
|
||||
```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
|
||||
```
|
||||
|
||||
*You can find more `wasm/wat` examples in the [examples](./examples) directory.*
|
||||
|
||||
#### With WAPM
|
||||
|
||||
Installing Wasmer through `wasmer.io` includes
|
||||
[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager.
|
||||
[`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/).
|
||||
|
||||
Wapm allows you to easily download, run, and distribute WebAssembly binaries.
|
||||
|
||||
@ -86,7 +85,7 @@ Wasmer is structured into different directories:
|
||||
|
||||
Building Wasmer requires [rustup](https://rustup.rs/).
|
||||
|
||||
To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
|
||||
To build Wasmer on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
|
||||
then follow the onscreen instructions.
|
||||
|
||||
To build on other systems, run:
|
||||
@ -99,10 +98,9 @@ curl https://sh.rustup.rs -sSf | sh
|
||||
|
||||
Please select your operating system:
|
||||
|
||||
- [macOS](#macos)
|
||||
- [Debian-based Linuxes](#debian-based-linuxes)
|
||||
- [FreeBSD](#freebsd)
|
||||
- [Microsoft Windows](#windows-msvc)
|
||||
<details>
|
||||
<summary><b>macOS</b></summary>
|
||||
<p>
|
||||
|
||||
#### macOS
|
||||
|
||||
@ -118,22 +116,41 @@ Or, in case you have [MacPorts](https://www.macports.org/install.php):
|
||||
sudo port install cmake
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Debian-based Linuxes</b></summary>
|
||||
<p>
|
||||
|
||||
#### Debian-based Linuxes
|
||||
|
||||
```sh
|
||||
sudo apt install cmake pkg-config libssl-dev
|
||||
```
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>FreeBSD</b></summary>
|
||||
<p>
|
||||
|
||||
#### FreeBSD
|
||||
|
||||
```sh
|
||||
pkg install cmake
|
||||
```
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Windows</b></summary>
|
||||
<p>
|
||||
|
||||
#### 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.
|
||||
Windows support is _experimental_. WASI is fully supported, but Emscripten support is on the works (this means
|
||||
nginx and Lua do not work on Windows - you can track the progress on [this issue](https://github.com/wasmerio/wasmer/issues/176)).
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15)
|
||||
|
||||
@ -147,7 +164,9 @@ nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmer
|
||||
|
||||
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)
|
||||
6. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe)
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Building
|
||||
|
||||
@ -165,8 +184,14 @@ git clone https://github.com/wasmerio/wasmer.git
|
||||
cd wasmer
|
||||
|
||||
# install tools
|
||||
# make sure that `python` is accessible.
|
||||
make install
|
||||
make release-clif # To build with cranelift (default)
|
||||
|
||||
make release-llvm # To build with llvm support
|
||||
|
||||
make release-singlepass # To build with singlepass support
|
||||
|
||||
# or
|
||||
make release # To build with singlepass, cranelift and llvm support
|
||||
```
|
||||
|
||||
## Testing
|
||||
@ -204,7 +229,10 @@ Each integration can be tested separately:
|
||||
Benchmarks can be run with:
|
||||
|
||||
```sh
|
||||
make bench
|
||||
make bench-[backend]
|
||||
|
||||
# for example
|
||||
make bench-singlepass
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
@ -216,13 +244,12 @@ Below are some of the goals of this project (in order of priority):
|
||||
- [x] It should be 100% compatible with the [WebAssembly spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests)
|
||||
- [x] It should be fast _(partially achieved)_
|
||||
- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0)
|
||||
- [ ] Support Emscripten calls _(in the works)_
|
||||
- [ ] Support Rust ABI calls
|
||||
- [ ] Support Go ABI calls
|
||||
- [x] Support Emscripten calls _(in the works)_
|
||||
- [ ] Support Go js ABI calls
|
||||
|
||||
## Architecture
|
||||
|
||||
If you would like to know how Wasmer works under the hood, please see [ARCHITECTURE.md](./ARCHITECTURE.md).
|
||||
If you would like to know how Wasmer works under the hood, please see [docs/architecture.md](./docs/architecture.md).
|
||||
|
||||
## License
|
||||
|
||||
|
@ -3,8 +3,9 @@ status = [
|
||||
"ci/circleci: test",
|
||||
"ci/circleci: test-macos",
|
||||
"ci/circleci: test-stable",
|
||||
"ci/circleci: test-rust-example",
|
||||
"continuous-integration/appveyor/branch"
|
||||
]
|
||||
required_approvals = 1
|
||||
timeout_sec = 900
|
||||
timeout_sec = 7200
|
||||
delete_merged_branches = true
|
||||
|
79
build
Executable file
79
build
Executable file
@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Wasmer build tool
|
||||
#
|
||||
# This is a script to build Wasmer in a Docker sandbox.
|
||||
#
|
||||
# To use the script, first make sure Docker is installed. Then build the
|
||||
# sandbox image with:
|
||||
#
|
||||
# docker build --file Dockerfile.build --tag wasmer-build .
|
||||
#
|
||||
# After the sandbox image is built successfully, you can run commands in it
|
||||
# with this script.
|
||||
#
|
||||
# For example, to build Wasmer, run:
|
||||
#
|
||||
# ./build make
|
||||
#
|
||||
# To test Wasmer, run:
|
||||
#
|
||||
# ./build make test
|
||||
#
|
||||
# and so on.
|
||||
|
||||
docker_hostname="wasmer-build"
|
||||
|
||||
docker_img="wasmer-build"
|
||||
|
||||
docker_workdir="/wasmer"
|
||||
|
||||
docker_args=(
|
||||
#
|
||||
# General config.
|
||||
#
|
||||
--hostname=${docker_hostname}
|
||||
--interactive
|
||||
--network=host
|
||||
--rm
|
||||
--tty
|
||||
|
||||
#
|
||||
# User and group config.
|
||||
#
|
||||
# Use the same user and group permissions as host to make integration
|
||||
# between host and container simple.
|
||||
#
|
||||
--user "$(id --user):$(id --group)"
|
||||
--volume "/etc/group:/etc/group:ro"
|
||||
--volume "/etc/passwd:/etc/passwd:ro"
|
||||
--volume "/etc/shadow:/etc/shadow:ro"
|
||||
|
||||
#
|
||||
# Time zone config.
|
||||
#
|
||||
# Use the same time zone as the host.
|
||||
#
|
||||
--volume "/etc/localtime:/etc/localtime:ro"
|
||||
|
||||
#
|
||||
# Linux capabilities.
|
||||
#
|
||||
# Add SYS_PTRACE capability to the container so that people can run
|
||||
# `strace'.
|
||||
#
|
||||
--cap-add SYS_PTRACE
|
||||
|
||||
#
|
||||
# Source directory.
|
||||
#
|
||||
--workdir=${docker_workdir}
|
||||
--volume "$(pwd):${docker_workdir}:z"
|
||||
|
||||
#
|
||||
# Environment variables.
|
||||
#
|
||||
--env "CARGO_HOME=${docker_workdir}/.cargo"
|
||||
)
|
||||
|
||||
docker run ${docker_args[@]} ${docker_img} $*
|
11
examples/no_abi_simple_plugin.rs
Normal file
11
examples/no_abi_simple_plugin.rs
Normal file
@ -0,0 +1,11 @@
|
||||
extern "C" {
|
||||
fn it_works() -> i32;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn plugin_entrypoint(n: i32) -> i32 {
|
||||
let result = unsafe { it_works() };
|
||||
result + n
|
||||
}
|
||||
|
||||
fn main() {}
|
BIN
examples/no_abi_simple_plugin.wasm
Executable file
BIN
examples/no_abi_simple_plugin.wasm
Executable file
Binary file not shown.
BIN
examples/particle-repel-simd.wasm
Executable file
BIN
examples/particle-repel-simd.wasm
Executable file
Binary file not shown.
BIN
examples/particle-repel.wasm
Executable file
BIN
examples/particle-repel.wasm
Executable file
Binary file not shown.
Binary file not shown.
@ -1,6 +1,10 @@
|
||||
use wasmer_runtime::{func, imports, instantiate};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_wasi::generate_import_object;
|
||||
use wasmer_wasi::{
|
||||
generate_import_object,
|
||||
state::{self, WasiFile},
|
||||
types,
|
||||
};
|
||||
|
||||
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
|
||||
|
||||
@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
|
||||
5
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoggingWrapper {
|
||||
pub wasm_module_name: String,
|
||||
}
|
||||
|
||||
// std io trait boiler plate so we can implement WasiFile
|
||||
// LoggingWrapper is a write-only type so we just want to immediately
|
||||
// fail when reading or Seeking
|
||||
impl std::io::Read for LoggingWrapper {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"can not read from logging wrapper",
|
||||
))
|
||||
}
|
||||
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"can not read from logging wrapper",
|
||||
))
|
||||
}
|
||||
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"can not read from logging wrapper",
|
||||
))
|
||||
}
|
||||
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"can not read from logging wrapper",
|
||||
))
|
||||
}
|
||||
}
|
||||
impl std::io::Seek for LoggingWrapper {
|
||||
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"can not seek logging wrapper",
|
||||
))
|
||||
}
|
||||
}
|
||||
impl std::io::Write for LoggingWrapper {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let stdout = std::io::stdout();
|
||||
let mut out = stdout.lock();
|
||||
out.write(b"[")?;
|
||||
out.write(self.wasm_module_name.as_bytes())?;
|
||||
out.write(b"]: ")?;
|
||||
out.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
std::io::stdout().flush()
|
||||
}
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
let stdout = std::io::stdout();
|
||||
let mut out = stdout.lock();
|
||||
out.write(b"[")?;
|
||||
out.write(self.wasm_module_name.as_bytes())?;
|
||||
out.write(b"]: ")?;
|
||||
out.write_all(buf)
|
||||
}
|
||||
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
|
||||
let stdout = std::io::stdout();
|
||||
let mut out = stdout.lock();
|
||||
out.write(b"[")?;
|
||||
out.write(self.wasm_module_name.as_bytes())?;
|
||||
out.write(b"]: ")?;
|
||||
out.write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
|
||||
impl WasiFile for LoggingWrapper {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn created_time(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the program when it wants to set itself up
|
||||
fn initialize(ctx: &mut Ctx) {
|
||||
let state = unsafe { state::get_wasi_state(ctx) };
|
||||
let wasi_file_inner = LoggingWrapper {
|
||||
wasm_module_name: "example module name".to_string(),
|
||||
};
|
||||
// swap stdout with our new wasifile
|
||||
let _old_stdout = state
|
||||
.fs
|
||||
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Load the plugin data
|
||||
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
|
||||
@ -27,8 +132,10 @@ fn main() {
|
||||
// The WASI imports object contains all required import functions for a WASI module to run.
|
||||
// Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run.
|
||||
base_imports.extend(custom_imports);
|
||||
let instance =
|
||||
let mut instance =
|
||||
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
|
||||
// set up logging by replacing stdout
|
||||
initialize(instance.context_mut());
|
||||
|
||||
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
|
||||
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
|
||||
|
@ -33,6 +33,19 @@ INFO: seed corpus: files: 8 min: 1b max: 1b total: 8b rss: 133Mb
|
||||
```
|
||||
It will continue to generate random inputs forever, until it finds a bug or is terminated. The testcases for bugs it finds go into `fuzz/artifacts/simple_instantiate` and you can rerun the fuzzer on a single input by passing it on the command line `cargo fuzz run simple_instantiate my_testcase.wasm`.
|
||||
|
||||
## Seeding the corpus, optional
|
||||
|
||||
The fuzzer works best when it has examples of small Wasm files to start with. Using `wast2json` from [wabt](https://github.com/WebAssembly/wabt), we can easily produce `.wasm` files out of the WebAssembly spec tests.
|
||||
|
||||
```sh
|
||||
mkdir spec-test-corpus
|
||||
for i in lib/spectests/spectests/*.wast; do wast2json $i -o spec-test-corpus/$(basename $i).json; done
|
||||
mv spec-test-corpus/*.wasm fuzz/corpus/simple_instantiate/
|
||||
rm -r spec-test-corpus
|
||||
```
|
||||
|
||||
The corpus directory is created on the first run of the fuzzer. If it doesn't exist, run it first and then seed the corpus. The fuzzer will pick up new files added to the corpus while it is running.
|
||||
|
||||
## Trophy case
|
||||
|
||||
- [x] https://github.com/wasmerio/wasmer/issues/558
|
||||
|
82
install.sh
82
install.sh
@ -35,6 +35,11 @@ dim="\e[2m"
|
||||
# Warning: Remove this on the public repo
|
||||
RELEASES_URL="https://github.com/wasmerio/wasmer/releases"
|
||||
|
||||
WASMER_VERBOSE="verbose"
|
||||
if [ -z "$WASMER_INSTALL_LOG" ]; then
|
||||
WASMER_INSTALL_LOG="$WASMER_VERBOSE"
|
||||
fi
|
||||
|
||||
wasmer_download_json() {
|
||||
url="$2"
|
||||
|
||||
@ -66,9 +71,19 @@ wasmer_download_file() {
|
||||
|
||||
# echo "Fetching $url.."
|
||||
if test -x "$(command -v curl)"; then
|
||||
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
|
||||
code=$(curl --progress-bar -w '%{http_code}' -L "$url" -o "$destination")
|
||||
printf "\033[K\n\033[1A"
|
||||
else
|
||||
code=$(curl -s -w '%{http_code}' -L "$url" -o "$destination")
|
||||
fi
|
||||
elif test -x "$(command -v wget)"; then
|
||||
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
|
||||
code=$(wget --show-progress --progress=bar:force:noscroll -q -O "$destination" --server-response "$url" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
|
||||
printf "\033[K\n\033[1A";
|
||||
else
|
||||
code=$(wget --quiet -O "$destination" --server-response "$url" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
|
||||
fi
|
||||
else
|
||||
printf "$red> Neither curl nor wget was available to perform http requests.$reset\n"
|
||||
exit 1
|
||||
@ -130,7 +145,7 @@ wasmer_detect_profile() {
|
||||
wasmer_link() {
|
||||
printf "$cyan> Adding to bash profile...$reset\n"
|
||||
WASMER_PROFILE="$(wasmer_detect_profile)"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
|
||||
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\"\n"
|
||||
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n"
|
||||
|
||||
# We create the wasmer.sh file
|
||||
@ -152,19 +167,22 @@ wasmer_link() {
|
||||
# fi
|
||||
fi
|
||||
printf "\033[1A$cyan> Adding to bash profile... ✓$reset\n"
|
||||
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
|
||||
printf "${dim}Note: We've added the following to your $WASMER_PROFILE\n"
|
||||
echo "If this isn't the profile of your current shell then please add the following to your correct profile:"
|
||||
echo "If you have a different profile please add the following:"
|
||||
printf "$LOAD_STR$reset\n"
|
||||
fi
|
||||
|
||||
version=`$INSTALL_DIRECTORY/bin/wasmer --version` || (
|
||||
printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n"
|
||||
exit 1;
|
||||
)
|
||||
|
||||
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n"
|
||||
printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n"
|
||||
echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`."
|
||||
echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)"
|
||||
printf "$green> Successfully installed $version!\n"
|
||||
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
|
||||
printf "${reset}${dim}wasmer & wapm will be available the next time you open the terminal.\n"
|
||||
printf "${reset}${dim}If you want to have the commands available now please execute:\n${reset}source $INSTALL_DIRECTORY/wasmer.sh$reset\n"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@ -230,33 +248,36 @@ initOS() {
|
||||
|
||||
wasmer_install() {
|
||||
magenta1="${reset}\033[34;1m"
|
||||
magenta2="${reset}\033[34m"
|
||||
magenta3="${reset}\033[34;2m"
|
||||
magenta2=""
|
||||
magenta3=""
|
||||
|
||||
if which wasmer >/dev/null; then
|
||||
printf "${reset}Updating wasmer$reset\n"
|
||||
printf "${reset}Updating Wasmer and WAPM$reset\n"
|
||||
else
|
||||
printf "${reset}Installing Wasmer!$reset\n"
|
||||
printf "${reset}Installing Wasmer and WAPM!$reset\n"
|
||||
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
|
||||
printf "
|
||||
${magenta1} ${magenta2} ${magenta3}###${reset}
|
||||
${magenta1} ${magenta2} ${magenta3}#####${reset}
|
||||
${magenta1} ${magenta2}### ${magenta3}######${reset}
|
||||
${magenta1} ${magenta2}###### ${magenta3}#############${reset}
|
||||
${magenta1}# ${magenta2}####### ${magenta3}##############${reset}
|
||||
${magenta1}##### ${magenta2}#############${magenta3}#########${reset}
|
||||
${magenta1}######${magenta2}###############${magenta3}#######${reset}
|
||||
${magenta1}############${magenta2}#########${magenta3}#######${reset}
|
||||
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
|
||||
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
|
||||
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
|
||||
${magenta1}##############${magenta2}#######${magenta3} ###${reset}
|
||||
${magenta1}##############${magenta2}#######
|
||||
${magenta1}###########${magenta2} ###
|
||||
${magenta1}########${magenta2}
|
||||
${magenta1}####${reset}
|
||||
|
||||
${magenta1} ww
|
||||
${magenta1} wwwww
|
||||
${magenta1} ww wwwwww w
|
||||
${magenta1} wwwww wwwwwwwww
|
||||
${magenta1}ww wwwwww w wwwwwww
|
||||
${magenta1}wwwww wwwwwwwwww wwwww
|
||||
${magenta1}wwwwww w wwwwwww wwwww
|
||||
${magenta1}wwwwwwwwwwwwww wwwww wwwww
|
||||
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
|
||||
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
|
||||
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
|
||||
${magenta1}wwwwwwwwwwwwwww wwwww wwww
|
||||
${magenta1}wwwwwwwwwwwwwww wwwww
|
||||
${magenta1} wwwwwwwwwwww wwww
|
||||
${magenta1} wwwwwwww
|
||||
${magenta1} wwww
|
||||
${reset}
|
||||
"
|
||||
fi
|
||||
fi
|
||||
|
||||
# if [ -d "$INSTALL_DIRECTORY" ]; then
|
||||
# if which wasmer; then
|
||||
# local latest_url
|
||||
@ -419,7 +440,7 @@ wasmer_download() {
|
||||
printf "$cyan> Downloading $WASMER_RELEASE_TAG release...$reset\n"
|
||||
wasmer_download_file "$BINARY_URL" "$DOWNLOAD_FILE"
|
||||
# echo -en "\b\b"
|
||||
printf "\033[2A$cyan> Downloading $WASMER_RELEASE_TAG release... ✓$reset\033[K\n"
|
||||
printf "\033[1A$cyan> Downloading $WASMER_RELEASE_TAG release... ✓$reset\n"
|
||||
printf "\033[K\n\033[1A"
|
||||
# printf "\033[1A$cyan> Downloaded$reset\033[K\n"
|
||||
# echo "Setting executable permissions."
|
||||
@ -431,9 +452,12 @@ wasmer_download() {
|
||||
|
||||
# echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME"
|
||||
|
||||
printf "$cyan> Unpacking contents...$reset\n"
|
||||
|
||||
mkdir -p $INSTALL_DIRECTORY
|
||||
# Untar the wasmer contents in the install directory
|
||||
tar -C $INSTALL_DIRECTORY -zxvf $DOWNLOAD_FILE
|
||||
tar -C $INSTALL_DIRECTORY -zxf $DOWNLOAD_FILE
|
||||
printf "\033[1A$cyan> Unpacking contents... ✓$reset\n"
|
||||
}
|
||||
|
||||
wasmer_verify_or_quit() {
|
||||
|
@ -1,40 +1,41 @@
|
||||
[package]
|
||||
name = "wasmer-clif-backend"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer runtime Cranelift compiler backend"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
cranelift-native = { version = "0.31" }
|
||||
cranelift-codegen = { version = "0.31" }
|
||||
cranelift-entity = { version = "0.31" }
|
||||
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.31" }
|
||||
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.31" }
|
||||
hashbrown = "0.1"
|
||||
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.33" }
|
||||
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.33" }
|
||||
target-lexicon = "0.4.0"
|
||||
wasmparser = "0.32.1"
|
||||
byteorder = "1"
|
||||
nix = "0.14.0"
|
||||
libc = "0.2.49"
|
||||
rayon = "1.0"
|
||||
wasmparser = "0.35.1"
|
||||
byteorder = "1.3.2"
|
||||
nix = "0.14.1"
|
||||
libc = "0.2.60"
|
||||
rayon = "1.1.0"
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
features = ["rc"]
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
[dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
version = "0.11.1"
|
||||
[dependencies.serde-bench]
|
||||
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.5.6" }
|
||||
winapi = { version = "0.3.7", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.6.0" }
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
@ -28,4 +28,26 @@ 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.
|
||||
This crate represents the Cranelift backend integration for Wasmer.
|
||||
|
||||
## Usage
|
||||
|
||||
### Usage in Wasmer Standalone
|
||||
|
||||
If you are using the `wasmer` CLI, you can specify the backend with:
|
||||
|
||||
```bash
|
||||
wasmer run program.wasm --backend=cranelift
|
||||
```
|
||||
|
||||
### Usage in Wasmer Embedded
|
||||
|
||||
If you are using Wasmer Embedded, you can specify
|
||||
the Cranelift backend to the [`compile_with` function](https://docs.rs/wasmer-runtime-core/*/wasmer_runtime_core/fn.compile_with.html):
|
||||
|
||||
```rust
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
|
||||
// ...
|
||||
let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new());
|
||||
```
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::relocation::{ExternalRelocation, TrapSink};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{sys::Memory, CacheGen},
|
||||
|
@ -33,7 +33,7 @@ use wasmer_runtime_core::{
|
||||
use wasmparser::Type as WpType;
|
||||
|
||||
pub struct CraneliftModuleCodeGenerator {
|
||||
isa: Box<isa::TargetIsa>,
|
||||
isa: Box<dyn isa::TargetIsa>,
|
||||
signatures: Option<Arc<Map<SigIndex, FuncSig>>>,
|
||||
pub clif_signatures: Map<SigIndex, ir::Signature>,
|
||||
function_signatures: Option<Arc<Map<FuncIndex, SigIndex>>>,
|
||||
@ -89,10 +89,12 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
|
||||
func,
|
||||
func_translator,
|
||||
next_local: 0,
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
position: Position::default(),
|
||||
func_env: FunctionEnvironment {
|
||||
module_info: Arc::clone(&module_info),
|
||||
target_config: self.isa.frontend_config().clone(),
|
||||
position: Position::default(),
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty");
|
||||
@ -304,8 +306,15 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
|
||||
|
||||
let trampolines = Arc::new(Trampolines::new(&*self.isa, module_info));
|
||||
|
||||
let signatures_empty = Map::new();
|
||||
let signatures = if self.signatures.is_some() {
|
||||
&self.signatures.as_ref().unwrap()
|
||||
} else {
|
||||
&signatures_empty
|
||||
};
|
||||
|
||||
let (func_resolver, backend_cache) = func_resolver_builder.finalize(
|
||||
&self.signatures.as_ref().unwrap(),
|
||||
signatures,
|
||||
Arc::clone(&trampolines),
|
||||
handler_data.clone(),
|
||||
)?;
|
||||
@ -384,10 +393,8 @@ pub struct CraneliftFunctionCodeGenerator {
|
||||
func: Function,
|
||||
func_translator: FuncTranslator,
|
||||
next_local: usize,
|
||||
pub clif_signatures: Map<SigIndex, ir::Signature>,
|
||||
module_info: Arc<RwLock<ModuleInfo>>,
|
||||
target_config: isa::TargetFrontendConfig,
|
||||
position: Position,
|
||||
func_env: FunctionEnvironment,
|
||||
}
|
||||
|
||||
pub struct FunctionEnvironment {
|
||||
@ -1131,11 +1138,6 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
|
||||
//let builder = self.builder.as_mut().unwrap();
|
||||
//let func_environment = FuncEnv::new();
|
||||
//let state = TranslationState::new();
|
||||
let mut function_environment = FunctionEnvironment {
|
||||
module_info: Arc::clone(&self.module_info),
|
||||
target_config: self.target_config.clone(),
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
};
|
||||
|
||||
if self.func_translator.state.control_stack.is_empty() {
|
||||
return Ok(());
|
||||
@ -1147,7 +1149,7 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
|
||||
&mut self.position,
|
||||
);
|
||||
let state = &mut self.func_translator.state;
|
||||
translate_operator(op, &mut builder, state, &mut function_environment)?;
|
||||
translate_operator(op, &mut builder, state, &mut self.func_env)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
mod cache;
|
||||
mod code;
|
||||
mod libcalls;
|
||||
@ -21,7 +26,7 @@ extern crate serde_derive;
|
||||
extern crate rayon;
|
||||
extern crate serde;
|
||||
|
||||
fn get_isa() -> Box<isa::TargetIsa> {
|
||||
fn get_isa() -> Box<dyn isa::TargetIsa> {
|
||||
let flags = {
|
||||
let mut builder = settings::builder();
|
||||
builder.set("opt_level", "best").unwrap();
|
||||
|
@ -107,7 +107,8 @@ impl From<Converter<ir::Type>> for Type {
|
||||
ir::types::I64 => Type::I64,
|
||||
ir::types::F32 => Type::F32,
|
||||
ir::types::F64 => Type::F64,
|
||||
_ => panic!("unsupported wasm type"),
|
||||
ir::types::I32X4 => Type::V128,
|
||||
_ => unimplemented!("unsupported wasm type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,6 +120,7 @@ impl From<Converter<Type>> for ir::Type {
|
||||
Type::I64 => ir::types::I64,
|
||||
Type::F32 => ir::types::F32,
|
||||
Type::F64 => ir::types::F64,
|
||||
Type::V128 => ir::types::I32X4,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,6 +132,7 @@ impl From<Converter<Type>> for ir::AbiParam {
|
||||
Type::I64 => ir::AbiParam::new(ir::types::I64),
|
||||
Type::F32 => ir::AbiParam::new(ir::types::F32),
|
||||
Type::F64 => ir::AbiParam::new(ir::types::F64),
|
||||
Type::V128 => ir::AbiParam::new(ir::types::I32X4),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ impl FuncResolverBuilder {
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
isa: &isa::TargetIsa,
|
||||
isa: &dyn isa::TargetIsa,
|
||||
function_bodies: Map<LocalFuncIndex, ir::Function>,
|
||||
info: &ModuleInfo,
|
||||
) -> CompileResult<(Self, HandlerData)> {
|
||||
|
@ -5,7 +5,7 @@ use cranelift_codegen::{
|
||||
ir::{self, InstBuilder},
|
||||
isa, Context,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::{iter, mem, ptr::NonNull};
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::{Memory, Protect},
|
||||
@ -66,7 +66,7 @@ impl Trampolines {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
|
||||
pub fn new(isa: &dyn isa::TargetIsa, module: &ModuleInfo) -> Self {
|
||||
let func_index_iter = module
|
||||
.exports
|
||||
.values()
|
||||
@ -204,6 +204,7 @@ fn wasm_ty_to_clif(ty: Type) -> ir::types::Type {
|
||||
Type::I64 => ir::types::I64,
|
||||
Type::F32 => ir::types::F32,
|
||||
Type::F64 => ir::types::F64,
|
||||
Type::V128 => ir::types::I32X4,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-dev-utils"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,4 +8,4 @@ edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten-tests"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Tests for our Emscripten implementation"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -9,18 +9,18 @@ publish = false
|
||||
build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-emscripten = { path = "../emscripten", version = "0.5.6" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.5.6" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.5.6", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.5.6", optional = true }
|
||||
wasmer-emscripten = { path = "../emscripten", version = "0.6.0" }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.7.2"
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.5.6"}
|
||||
wabt = "0.9.1"
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.6.0"}
|
||||
|
||||
[build-dependencies]
|
||||
glob = "0.2.11"
|
||||
glob = "0.3.0"
|
||||
|
||||
[features]
|
||||
clif = []
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-emscripten"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer runtime emscripten implementation library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,15 +8,14 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
hashbrown = "0.1"
|
||||
lazy_static = "1.2.0"
|
||||
libc = "0.2.49"
|
||||
time = "0.1.41"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
|
||||
byteorder = "1.3.2"
|
||||
lazy_static = "1.3.0"
|
||||
libc = "0.2.60"
|
||||
time = "0.1.42"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rand = "0.6"
|
||||
rand = "0.7.0"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
1
lib/emscripten/src/env/mod.rs
vendored
1
lib/emscripten/src/env/mod.rs
vendored
@ -30,6 +30,7 @@ pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[warn(dead_code)]
|
||||
pub fn call_malloc_with_cast<T: Copy, Ty>(ctx: &mut Ctx, size: u32) -> WasmPtr<T, Ty> {
|
||||
WasmPtr::new(call_malloc(ctx, size))
|
||||
}
|
||||
|
21
lib/emscripten/src/env/unix/mod.rs
vendored
21
lib/emscripten/src/env/unix/mod.rs
vendored
@ -196,18 +196,15 @@ pub fn _getaddrinfo(
|
||||
|
||||
let hints = hints_ptr.deref(memory).map(|hints_memory| {
|
||||
let hints_guest = hints_memory.get();
|
||||
unsafe {
|
||||
let mut hints_native: addrinfo = std::mem::uninitialized();
|
||||
hints_native.ai_flags = hints_guest.ai_flags;
|
||||
hints_native.ai_family = hints_guest.ai_family;
|
||||
hints_native.ai_socktype = hints_guest.ai_socktype;
|
||||
hints_native.ai_protocol = hints_guest.ai_protocol;
|
||||
hints_native.ai_addrlen = 0;
|
||||
hints_native.ai_addr = std::ptr::null_mut();
|
||||
hints_native.ai_canonname = std::ptr::null_mut();
|
||||
hints_native.ai_next = std::ptr::null_mut();
|
||||
|
||||
hints_native
|
||||
addrinfo {
|
||||
ai_flags: hints_guest.ai_flags,
|
||||
ai_family: hints_guest.ai_family,
|
||||
ai_socktype: hints_guest.ai_socktype,
|
||||
ai_protocol: hints_guest.ai_protocol,
|
||||
ai_addrlen: 0,
|
||||
ai_addr: std::ptr::null_mut(),
|
||||
ai_canonname: std::ptr::null_mut(),
|
||||
ai_next: std::ptr::null_mut(),
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::{f64, ffi::c_void};
|
||||
use wasmer_runtime_core::{
|
||||
@ -25,11 +30,11 @@ use wasmer_runtime_core::{
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use ::libc::DIR as libcDIR;
|
||||
use ::libc::DIR as LibcDir;
|
||||
|
||||
// We use a placeholder for windows
|
||||
#[cfg(not(unix))]
|
||||
type libcDIR = u8;
|
||||
type LibcDir = u8;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@ -93,7 +98,7 @@ pub struct EmscriptenData<'a> {
|
||||
pub memset: Option<Func<'a, (u32, u32, u32), u32>>,
|
||||
pub stack_alloc: Option<Func<'a, u32, u32>>,
|
||||
pub jumps: Vec<UnsafeCell<[u32; 27]>>,
|
||||
pub opened_dirs: HashMap<i32, Box<*mut libcDIR>>,
|
||||
pub opened_dirs: HashMap<i32, Box<*mut LibcDir>>,
|
||||
|
||||
pub dyn_call_i: Option<Func<'a, i32, i32>>,
|
||||
pub dyn_call_ii: Option<Func<'a, (i32, i32), i32>>,
|
||||
|
@ -71,7 +71,7 @@ pub fn sbrk(ctx: &mut Ctx, increment: i32) -> i32 {
|
||||
debug!("emscripten::sbrk");
|
||||
// let old_dynamic_top = 0;
|
||||
// let new_dynamic_top = 0;
|
||||
let mut globals = get_emscripten_data(ctx).globals;
|
||||
let globals = get_emscripten_data(ctx).globals;
|
||||
let dynamictop_ptr = (globals.dynamictop_ptr) as usize;
|
||||
let old_dynamic_top = ctx.memory(0).view::<u32>()[dynamictop_ptr].get() as i32;
|
||||
let new_dynamic_top: i32 = old_dynamic_top + increment;
|
||||
|
@ -617,13 +617,13 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
// let mut address_len_addr: socklen_t = 0;
|
||||
|
||||
let (fd, host_address) = unsafe {
|
||||
let mut host_address: sockaddr = std::mem::uninitialized();
|
||||
let fd = accept(socket, &mut host_address, address_len_addr);
|
||||
|
||||
(fd, host_address)
|
||||
let mut host_address: sockaddr = sockaddr {
|
||||
sa_family: Default::default(),
|
||||
sa_data: Default::default(),
|
||||
#[cfg(target_os = "macos")]
|
||||
sa_len: Default::default(),
|
||||
};
|
||||
|
||||
let fd = unsafe { accept(socket, &mut host_address, address_len_addr) };
|
||||
let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
|
||||
address_addr.sa_family = host_address.sa_family as _;
|
||||
@ -651,15 +651,18 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
let address_len_addr =
|
||||
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
|
||||
let (ret, sock_addr_host) = unsafe {
|
||||
// read host data into new var
|
||||
let mut address: sockaddr = std::mem::uninitialized();
|
||||
let ret = getsockname(
|
||||
let mut sock_addr_host: sockaddr = sockaddr {
|
||||
sa_family: Default::default(),
|
||||
sa_data: Default::default(),
|
||||
#[cfg(target_os = "macos")]
|
||||
sa_len: Default::default(),
|
||||
};
|
||||
let ret = unsafe {
|
||||
getsockname(
|
||||
socket,
|
||||
&mut address as *mut sockaddr,
|
||||
&mut sock_addr_host as *mut sockaddr,
|
||||
address_len_addr as *mut u32,
|
||||
);
|
||||
(ret, address)
|
||||
)
|
||||
};
|
||||
// translate from host data into emscripten data
|
||||
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
|
||||
|
@ -34,11 +34,19 @@ pub fn is_emscripten_module(module: &Module) -> bool {
|
||||
}
|
||||
|
||||
pub fn get_emscripten_table_size(module: &Module) -> (u32, Option<u32>) {
|
||||
assert!(
|
||||
module.info().imported_tables.len() > 0,
|
||||
"Emscripten requires at least one imported table"
|
||||
);
|
||||
let (_, table) = &module.info().imported_tables[ImportedTableIndex::new(0)];
|
||||
(table.minimum, table.maximum)
|
||||
}
|
||||
|
||||
pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option<Pages>, bool) {
|
||||
assert!(
|
||||
module.info().imported_tables.len() > 0,
|
||||
"Emscripten requires at least one imported memory"
|
||||
);
|
||||
let (_, memory) = &module.info().imported_memories[ImportedMemoryIndex::new(0)];
|
||||
(memory.minimum, memory.maximum, memory.shared)
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
wasmer-runtime-core = { path = "../runtime-core" }
|
||||
|
@ -75,7 +75,7 @@ impl Loader for KernelLoader {
|
||||
if module.imported_globals.len() > 0 {
|
||||
return Err("imported globals are not supported".into());
|
||||
}
|
||||
let globals: Vec<u64> = unsafe {
|
||||
let globals: Vec<u128> = unsafe {
|
||||
let globals: &[*mut LocalGlobal] =
|
||||
::std::slice::from_raw_parts(ctx.globals, module.globals.len());
|
||||
globals.iter().map(|x| (**x).data).collect()
|
||||
@ -138,11 +138,11 @@ pub struct KernelInstance {
|
||||
|
||||
impl Instance for KernelInstance {
|
||||
type Error = String;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, String> {
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, String> {
|
||||
if args.len() != self.param_counts[id] {
|
||||
return Err("param count mismatch".into());
|
||||
}
|
||||
let args: Vec<u64> = args.iter().map(|x| x.to_u64()).collect();
|
||||
let args: Vec<u128> = args.iter().map(|x| x.to_u128()).collect();
|
||||
|
||||
let ret = self
|
||||
.context
|
||||
|
@ -54,7 +54,7 @@ struct LoadCodeRequest {
|
||||
memory_max: u32,
|
||||
table: *const TableEntryRequest,
|
||||
table_count: u32,
|
||||
globals: *const u64,
|
||||
globals: *const u128,
|
||||
global_count: u32,
|
||||
|
||||
imported_funcs: *const ImportRequest,
|
||||
@ -67,7 +67,7 @@ struct LoadCodeRequest {
|
||||
#[repr(C)]
|
||||
struct RunCodeRequest {
|
||||
entry_offset: u32,
|
||||
params: *const u64,
|
||||
params: *const u128,
|
||||
param_count: u32,
|
||||
result: *mut RunCodeResult,
|
||||
}
|
||||
@ -75,7 +75,7 @@ struct RunCodeRequest {
|
||||
#[repr(C)]
|
||||
struct RunCodeResult {
|
||||
success: u32,
|
||||
retval: u64,
|
||||
retval: u128,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -108,7 +108,7 @@ pub struct LoadProfile<'a> {
|
||||
pub code: &'a [u8],
|
||||
pub memory: Option<&'a [u8]>,
|
||||
pub memory_max: usize,
|
||||
pub globals: &'a [u64],
|
||||
pub globals: &'a [u128],
|
||||
pub imports: &'a [ImportInfo],
|
||||
pub dynamic_sigindices: &'a [u32],
|
||||
pub table: Option<&'a [TableEntryRequest]>,
|
||||
@ -121,7 +121,7 @@ pub struct ImportInfo {
|
||||
|
||||
pub struct RunProfile<'a> {
|
||||
pub entry_offset: u32,
|
||||
pub params: &'a [u64],
|
||||
pub params: &'a [u128],
|
||||
}
|
||||
|
||||
pub struct ServiceContext {
|
||||
@ -181,7 +181,7 @@ impl ServiceContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u64> {
|
||||
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u128> {
|
||||
let mut result: RunCodeResult = unsafe { ::std::mem::zeroed() };
|
||||
let mut req = RunCodeRequest {
|
||||
entry_offset: run.entry_offset,
|
||||
|
@ -219,7 +219,7 @@ fn __get_async_io_payload<
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SockaddrIn {
|
||||
sin_family: u16, // e.g. AF_INET
|
||||
sin_port: u16, // e.g. htons(3490)
|
||||
@ -315,7 +315,7 @@ impl Tcp4Listener {
|
||||
self.fd,
|
||||
EpollDirection::In,
|
||||
move |fd| -> Result<Arc<TcpStream>, i32> {
|
||||
let mut incoming_sa: SockaddrIn = unsafe { ::std::mem::uninitialized() };
|
||||
let mut incoming_sa: SockaddrIn = SockaddrIn::default();
|
||||
let mut real_len: usize = ::std::mem::size_of::<SockaddrIn>();
|
||||
let conn = unsafe { _accept4(fd, &mut incoming_sa, &mut real_len, O_NONBLOCK) };
|
||||
if conn >= 0 {
|
||||
|
@ -1,30 +1,40 @@
|
||||
[package]
|
||||
name = "wasmer-llvm-backend"
|
||||
version = "0.5.6"
|
||||
authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"]
|
||||
version = "0.6.0"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
|
||||
inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" }
|
||||
wasmparser = "0.32.1"
|
||||
hashbrown = "0.1.8"
|
||||
smallvec = "0.6.8"
|
||||
goblin = "0.0.20"
|
||||
libc = "0.2.49"
|
||||
nix = "0.14.0"
|
||||
capstone = { version = "0.5.0", optional = true }
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
wasmparser = "0.35.1"
|
||||
smallvec = "0.6.10"
|
||||
goblin = "0.0.24"
|
||||
libc = "0.2.60"
|
||||
capstone = { version = "0.6.0", optional = true }
|
||||
byteorder = "1"
|
||||
|
||||
[dependencies.inkwell]
|
||||
git = "https://github.com/wasmerio/inkwell"
|
||||
branch = "llvm8-0"
|
||||
default-features = false
|
||||
features = ["llvm8-0", "target-x86"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = "0.14.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.7", features = ["memoryapi"] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
lazy_static = "1.2.0"
|
||||
regex = "1.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.2.1"
|
||||
semver = "0.9"
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.7.4"
|
||||
wabt = "0.9.1"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
@ -28,4 +28,26 @@ 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.
|
||||
This crate represents the LLVM backend integration for Wasmer.
|
||||
|
||||
## Usage
|
||||
|
||||
### Usage in Wasmer Standalone
|
||||
|
||||
If you are using the `wasmer` CLI, you can specify the backend with:
|
||||
|
||||
```bash
|
||||
wasmer run program.wasm --backend=llvm
|
||||
```
|
||||
|
||||
### Usage in Wasmer Embedded
|
||||
|
||||
If you are using Wasmer Embedded, you can specify
|
||||
the LLVM backend to the [`compile_with` function](https://docs.rs/wasmer-runtime-core/*/wasmer_runtime_core/fn.compile_with.html):
|
||||
|
||||
```rust
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
|
||||
// ...
|
||||
let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &LLVMCompiler::new());
|
||||
```
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! 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)
|
||||
//! (https://bitbucket.org/tari/llvm-sys.rs/raw/94361c1083a88f439b9d24c59b2d2831517413d7/build.rs)
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
@ -10,23 +10,54 @@ use std::io::{self, ErrorKind};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
// Version of the llvm-sys crate that we (through inkwell) depend on.
|
||||
const LLVM_SYS_MAJOR_VERSION: &str = "80";
|
||||
const LLVM_SYS_MINOR_VERSION: &str = "0";
|
||||
|
||||
// Environment variables that can guide compilation
|
||||
//
|
||||
// When adding new ones, they should also be added to main() to force a
|
||||
// rebuild if they are changed.
|
||||
lazy_static! {
|
||||
|
||||
/// A single path to search for LLVM in (containing bin/llvm-config)
|
||||
static ref ENV_LLVM_PREFIX: String =
|
||||
format!("LLVM_SYS_{}_PREFIX", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If exactly "YES", ignore the version blacklist
|
||||
static ref ENV_IGNORE_BLACKLIST: String =
|
||||
format!("LLVM_SYS_{}_IGNORE_BLACKLIST", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, enforce precise correspondence between crate and binary versions.
|
||||
static ref ENV_STRICT_VERSIONING: String =
|
||||
format!("LLVM_SYS_{}_STRICT_VERSIONING", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, do not attempt to strip irrelevant options for llvm-config --cflags
|
||||
static ref ENV_NO_CLEAN_CXXFLAGS: String =
|
||||
format!("LLVM_SYS_{}_NO_CLEAN_CXXFLAGS", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set and targeting MSVC, force the debug runtime library
|
||||
static ref ENV_USE_DEBUG_MSVCRT: String =
|
||||
format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, always link against libffi
|
||||
static ref ENV_FORCE_FFI: String =
|
||||
format!("LLVM_SYS_{}_FFI_WORKAROUND", LLVM_SYS_MAJOR_VERSION);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Version::new(LLVM_SYS_MAJOR_VERSION.parse::<u64>().unwrap() / 10,
|
||||
LLVM_SYS_MINOR_VERSION.parse::<u64>().unwrap() % 10,
|
||||
0)
|
||||
};
|
||||
|
||||
static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
|
||||
vec![
|
||||
"llvm-config".into(),
|
||||
// format!("llvm-config-{}", CRATE_VERSION.major),
|
||||
// format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
format!("llvm-config-{}", CRATE_VERSION.major),
|
||||
format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
]
|
||||
};
|
||||
|
||||
@ -41,21 +72,7 @@ lazy_static! {
|
||||
|
||||
// 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 {
|
||||
if let Some(path) = env::var_os(&*ENV_LLVM_PREFIX) {
|
||||
for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() {
|
||||
let mut pb: PathBuf = path.clone().into();
|
||||
pb.push("bin");
|
||||
@ -67,7 +84,7 @@ lazy_static! {
|
||||
return pb;
|
||||
} else {
|
||||
println!("LLVM binaries specified by {} are the wrong version.
|
||||
(Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION);
|
||||
(Found {}, need {}.)", *ENV_LLVM_PREFIX, ver, *CRATE_VERSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +96,7 @@ lazy_static! {
|
||||
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);
|
||||
llvmenv: https://crates.io/crates/llvmenv", *ENV_LLVM_PREFIX);
|
||||
panic!("Could not find a compatible version of LLVM");
|
||||
};
|
||||
}
|
||||
@ -115,15 +132,55 @@ fn locate_system_llvm_config() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Check whether the given version of LLVM is blacklisted,
|
||||
/// returning `Some(reason)` if it is.
|
||||
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
|
||||
static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];
|
||||
|
||||
if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
|
||||
if &x == "YES" {
|
||||
println!(
|
||||
"cargo:warning=Ignoring blacklist entry for LLVM {}",
|
||||
llvm_version
|
||||
);
|
||||
return None;
|
||||
} else {
|
||||
println!(
|
||||
"cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
|
||||
*ENV_IGNORE_BLACKLIST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for &(major, minor, patch, reason) in BLACKLIST.iter() {
|
||||
let bad_version = Version {
|
||||
major: major,
|
||||
minor: minor,
|
||||
patch: patch,
|
||||
pre: vec![],
|
||||
build: vec![],
|
||||
};
|
||||
|
||||
if &bad_version == llvm_version {
|
||||
return Some(reason);
|
||||
}
|
||||
}
|
||||
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 let Some(reason) = is_blacklisted_llvm(llvm_version) {
|
||||
println!(
|
||||
"Found LLVM {}, which is blacklisted: {}",
|
||||
llvm_version, reason
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let strict =
|
||||
env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
|
||||
if strict {
|
||||
llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
|
||||
} else {
|
||||
@ -184,11 +241,7 @@ fn get_llvm_cxxflags() -> String {
|
||||
// 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();
|
||||
let no_clean = env::var_os(&*ENV_NO_CLEAN_CXXFLAGS).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
|
||||
@ -204,20 +257,43 @@ fn get_llvm_cxxflags() -> String {
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
fn is_llvm_debug() -> bool {
|
||||
// Has to be either Debug or Release
|
||||
llvm_config("--build-mode").contains("Debug")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=static=llvm-backend");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CXXFLAGS);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);
|
||||
|
||||
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");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
|
||||
|
||||
// Enable "nightly" cfg if the current compiler is nightly.
|
||||
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
|
||||
let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
|
||||
if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
|
||||
println!("cargo:rustc-link-lib={}", "msvcrtd");
|
||||
}
|
||||
|
||||
// Link libffi if the user requested this workaround.
|
||||
// See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
|
||||
let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
|
||||
if force_ffi {
|
||||
println!("cargo:rustc-link-lib=dylib={}", "ffi");
|
||||
}
|
||||
}
|
||||
|
@ -94,30 +94,40 @@ public:
|
||||
}
|
||||
|
||||
/* Turn on the `reserveAllocationSpace` callback. */
|
||||
virtual bool needsToReserveAllocationSpace() override {
|
||||
return true;
|
||||
}
|
||||
virtual bool needsToReserveAllocationSpace() override { return true; }
|
||||
|
||||
virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override {
|
||||
virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
|
||||
size_t size) override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
eh_frame_ptr = addr;
|
||||
eh_frame_size = size;
|
||||
eh_frames_registered = true;
|
||||
callbacks.visit_fde(addr, size, __register_frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void deregisterEHFrames() override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
if (eh_frames_registered) {
|
||||
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
auto read_result = callbacks.protect_memory(
|
||||
read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
|
||||
if (read_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
@ -127,14 +137,18 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {}
|
||||
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) {
|
||||
uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size,
|
||||
size_t align) {
|
||||
auto aligner = [](uintptr_t &ptr, size_t align) {
|
||||
ptr = (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
@ -168,24 +182,19 @@ struct SymbolLookup : llvm::JITSymbolResolver {
|
||||
public:
|
||||
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
|
||||
virtual llvm::Expected<LookupResult> lookup(const LookupSet& symbols) override {
|
||||
void lookup(const LookupSet &symbols, OnResolvedFunction OnResolved) {
|
||||
LookupResult result;
|
||||
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol));
|
||||
}
|
||||
|
||||
return result;
|
||||
OnResolved(result);
|
||||
}
|
||||
|
||||
virtual llvm::Expected<LookupFlagsResult> lookupFlags(const LookupSet& symbols) override {
|
||||
LookupFlagsResult result;
|
||||
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol).getFlags());
|
||||
}
|
||||
|
||||
return result;
|
||||
llvm::Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
|
||||
const std::set<llvm::StringRef> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -198,20 +207,19 @@ private:
|
||||
callbacks_t callbacks;
|
||||
};
|
||||
|
||||
WasmModule::WasmModule(
|
||||
const uint8_t *object_start,
|
||||
size_t object_size,
|
||||
callbacks_t callbacks
|
||||
) : memory_manager(std::unique_ptr<MemoryManager>(new MemoryManager(callbacks)))
|
||||
{
|
||||
WasmModule::WasmModule(const uint8_t *object_start, size_t object_size,
|
||||
callbacks_t callbacks)
|
||||
: memory_manager(
|
||||
std::unique_ptr<MemoryManager>(new MemoryManager(callbacks))) {
|
||||
|
||||
|
||||
if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
|
||||
llvm::StringRef((const char *)object_start, object_size), "object"
|
||||
))) {
|
||||
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<llvm::RuntimeDyld>(new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
|
||||
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(
|
||||
new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
|
||||
|
||||
runtime_dyld->setProcessAllSections(true);
|
||||
|
||||
|
@ -1,20 +1,19 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
|
||||
typedef enum {
|
||||
PROTECT_NONE,
|
||||
PROTECT_READ,
|
||||
PROTECT_READ_WRITE,
|
||||
PROTECT_READ_EXECUTE,
|
||||
} mem_protect_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
RESULT_OK,
|
||||
RESULT_ALLOCATE_FAILURE,
|
||||
RESULT_PROTECT_FAILURE,
|
||||
@ -22,17 +21,19 @@ typedef enum
|
||||
RESULT_OBJECT_LOAD_FAILURE,
|
||||
} result_t;
|
||||
|
||||
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t **ptr_out, size_t *size_out);
|
||||
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size, mem_protect_t protect);
|
||||
typedef result_t (*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 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
|
||||
{
|
||||
typedef struct {
|
||||
/* Memory management. */
|
||||
alloc_memory_t alloc_memory;
|
||||
protect_memory_t protect_memory;
|
||||
@ -43,33 +44,27 @@ typedef struct
|
||||
visit_fde_t visit_fde;
|
||||
} callbacks_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
size_t data, vtable;
|
||||
} box_any_t;
|
||||
|
||||
struct WasmException
|
||||
{
|
||||
struct WasmException {
|
||||
public:
|
||||
virtual std::string description() const noexcept = 0;
|
||||
};
|
||||
|
||||
struct UncatchableException : WasmException
|
||||
{
|
||||
struct UncatchableException : WasmException {
|
||||
public:
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
virtual std::string description() const noexcept override {
|
||||
return "Uncatchable exception";
|
||||
}
|
||||
};
|
||||
|
||||
struct UserException : UncatchableException
|
||||
{
|
||||
struct UserException : UncatchableException {
|
||||
public:
|
||||
UserException(size_t data, size_t vtable) : error_data({data, vtable}) {}
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
virtual std::string description() const noexcept override {
|
||||
return "user exception";
|
||||
}
|
||||
|
||||
@ -77,11 +72,41 @@ struct UserException : UncatchableException
|
||||
box_any_t error_data;
|
||||
};
|
||||
|
||||
struct WasmTrap : UncatchableException
|
||||
struct BreakpointException : UncatchableException {
|
||||
public:
|
||||
BreakpointException(uintptr_t callback) : callback(callback) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
return "breakpoint exception";
|
||||
}
|
||||
|
||||
uintptr_t callback;
|
||||
};
|
||||
|
||||
struct WasmModule
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
WasmModule(
|
||||
const uint8_t *object_start,
|
||||
size_t object_size,
|
||||
callbacks_t callbacks);
|
||||
|
||||
void *get_func(llvm::StringRef name) const;
|
||||
uint8_t *get_stack_map_ptr() const;
|
||||
size_t get_stack_map_size() const;
|
||||
uint8_t *get_code_ptr() const;
|
||||
size_t get_code_size() const;
|
||||
|
||||
bool _init_failed = false;
|
||||
private:
|
||||
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
|
||||
std::unique_ptr<llvm::object::ObjectFile> object_file;
|
||||
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
|
||||
};
|
||||
|
||||
struct WasmTrap : UncatchableException {
|
||||
public:
|
||||
enum Type {
|
||||
Unreachable = 0,
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
MemoryOutOfBounds = 2,
|
||||
@ -92,12 +117,9 @@ struct WasmTrap : UncatchableException
|
||||
|
||||
WasmTrap(Type type) : type(type) {}
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
virtual std::string description() const noexcept override {
|
||||
std::ostringstream ss;
|
||||
ss
|
||||
<< "WebAssembly trap:" << '\n'
|
||||
<< " - type: " << type << '\n';
|
||||
ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n';
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
@ -105,10 +127,8 @@ struct WasmTrap : UncatchableException
|
||||
Type type;
|
||||
|
||||
private:
|
||||
friend std::ostream &operator<<(std::ostream &out, const Type &ty)
|
||||
{
|
||||
switch (ty)
|
||||
{
|
||||
friend std::ostream &operator<<(std::ostream &out, const Type &ty) {
|
||||
switch (ty) {
|
||||
case Type::Unreachable:
|
||||
out << "unreachable";
|
||||
break;
|
||||
@ -133,13 +153,12 @@ struct WasmTrap : UncatchableException
|
||||
}
|
||||
};
|
||||
|
||||
struct CatchableException : WasmException
|
||||
{
|
||||
struct CatchableException : WasmException {
|
||||
public:
|
||||
CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {}
|
||||
CatchableException(uint32_t type_id, uint32_t value_num)
|
||||
: type_id(type_id), value_num(value_num) {}
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
virtual std::string description() const noexcept override {
|
||||
return "catchable exception";
|
||||
}
|
||||
|
||||
@ -147,31 +166,11 @@ struct CatchableException : WasmException
|
||||
uint64_t values[1];
|
||||
};
|
||||
|
||||
struct WasmModule
|
||||
{
|
||||
public:
|
||||
WasmModule(
|
||||
const uint8_t *object_start,
|
||||
size_t object_size,
|
||||
callbacks_t callbacks);
|
||||
extern "C" {
|
||||
void callback_trampoline(void *, void *);
|
||||
|
||||
void *get_func(llvm::StringRef name) const;
|
||||
uint8_t *get_stack_map_ptr() const;
|
||||
size_t get_stack_map_size() const;
|
||||
uint8_t *get_code_ptr() const;
|
||||
size_t get_code_size() const;
|
||||
|
||||
bool _init_failed = false;
|
||||
private:
|
||||
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
|
||||
std::unique_ptr<llvm::object::ObjectFile> object_file;
|
||||
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
result_t module_load(const uint8_t *mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule **module_out)
|
||||
{
|
||||
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) {
|
||||
@ -181,14 +180,9 @@ extern "C"
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_trap(WasmTrap::Type ty) {
|
||||
throw WasmTrap(ty);
|
||||
}
|
||||
[[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); }
|
||||
|
||||
void module_delete(WasmModule *module)
|
||||
{
|
||||
delete module;
|
||||
}
|
||||
void module_delete(WasmModule *module) { delete module; }
|
||||
|
||||
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
|
||||
// side.
|
||||
@ -196,45 +190,37 @@ extern "C"
|
||||
throw UserException(data, vtable);
|
||||
}
|
||||
|
||||
bool invoke_trampoline(
|
||||
trampoline_t trampoline,
|
||||
void *ctx,
|
||||
void *func,
|
||||
void *params,
|
||||
void *results,
|
||||
WasmTrap::Type *trap_out,
|
||||
box_any_t *user_error,
|
||||
void *invoke_env) throw()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Throw a pointer that's assumed to be codegen::BreakpointHandler on the
|
||||
// rust side.
|
||||
[[noreturn]] void throw_breakpoint(uintptr_t callback) {
|
||||
throw BreakpointException(callback);
|
||||
}
|
||||
|
||||
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
void *params, void *results, WasmTrap::Type *trap_out,
|
||||
box_any_t *user_error, void *invoke_env) noexcept {
|
||||
try {
|
||||
trampoline(ctx, func, params, results);
|
||||
return true;
|
||||
}
|
||||
catch (const WasmTrap &e)
|
||||
{
|
||||
} catch (const WasmTrap &e) {
|
||||
*trap_out = e.type;
|
||||
return false;
|
||||
}
|
||||
catch (const UserException &e)
|
||||
{
|
||||
} catch (const UserException &e) {
|
||||
*user_error = e.error_data;
|
||||
return false;
|
||||
}
|
||||
catch (const WasmException &e)
|
||||
{
|
||||
} catch (const BreakpointException &e) {
|
||||
callback_trampoline(user_error, (void *)e.callback);
|
||||
return false;
|
||||
} catch (const WasmException &e) {
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
} catch (...) {
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void *get_func_symbol(WasmModule *module, const char *name)
|
||||
{
|
||||
void *get_func_symbol(WasmModule *module, const char *name) {
|
||||
return module->get_func(llvm::StringRef(name));
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
use super::stackmap::{self, StackmapRegistry, StkMapRecord, StkSizeRecord};
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect};
|
||||
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 libc::c_char;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::BTreeMap,
|
||||
ffi::{c_void, CString},
|
||||
fs::File,
|
||||
io::Write,
|
||||
mem,
|
||||
ops::Deref,
|
||||
ptr::{self, NonNull},
|
||||
@ -34,42 +34,6 @@ use wasmer_runtime_core::{
|
||||
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,
|
||||
@ -84,7 +48,8 @@ extern "C" {
|
||||
fn llvm_backend_get_code_ptr(module: *const LLVMModule) -> *const u8;
|
||||
fn llvm_backend_get_code_size(module: *const LLVMModule) -> usize;
|
||||
|
||||
fn throw_trap(ty: i32);
|
||||
fn throw_trap(ty: i32) -> !;
|
||||
fn throw_breakpoint(ty: i64) -> !;
|
||||
|
||||
/// This should be the same as spliting up the fat pointer into two arguments,
|
||||
/// but this is cleaner, I think?
|
||||
@ -106,69 +71,21 @@ extern "C" {
|
||||
}
|
||||
|
||||
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
|
||||
unsafe { crate::platform::alloc_memory(size, protect, ptr_out, size_out) }
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
unsafe { crate::platform::protect_memory(ptr, size, protect) }
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
unsafe { crate::platform::dealloc_memory(ptr, size) }
|
||||
}
|
||||
|
||||
extern "C" fn lookup_vm_symbol(name_ptr: *const c_char, length: usize) -> *const vm::Func {
|
||||
@ -195,7 +112,13 @@ fn get_callbacks() -> Callbacks {
|
||||
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.memory.grow.dynamic.import") => vmcalls::imported_dynamic_memory_grow as _,
|
||||
fn_name!("vm.memory.size.dynamic.import") => vmcalls::imported_dynamic_memory_size as _,
|
||||
fn_name!("vm.memory.grow.static.import") => vmcalls::imported_static_memory_grow as _,
|
||||
fn_name!("vm.memory.size.static.import") => vmcalls::imported_static_memory_size as _,
|
||||
|
||||
fn_name!("vm.exception.trap") => throw_trap as _,
|
||||
fn_name!("vm.breakpoint") => throw_breakpoint as _,
|
||||
|
||||
_ => ptr::null(),
|
||||
}
|
||||
@ -275,6 +198,14 @@ impl LLVMBackend {
|
||||
.unwrap();
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
|
||||
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.obj_file } {
|
||||
let mut file = File::create(path).unwrap();
|
||||
let mut pos = 0;
|
||||
while pos < mem_buf_slice.len() {
|
||||
pos += file.write(&mem_buf_slice[pos..]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let callbacks = get_callbacks();
|
||||
let mut module: *mut LLVMModule = ptr::null_mut();
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,15 @@
|
||||
use hashbrown::HashMap;
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
module::Module,
|
||||
types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType},
|
||||
types::{
|
||||
BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType,
|
||||
},
|
||||
values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use wasmer_runtime_core::{
|
||||
memory::MemoryType,
|
||||
@ -16,7 +19,7 @@ use wasmer_runtime_core::{
|
||||
GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
vm::Ctx,
|
||||
vm::{Ctx, INTERNALS_SIZE},
|
||||
};
|
||||
|
||||
fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
|
||||
@ -25,6 +28,7 @@ fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
|
||||
Type::I64 => intrinsics.i64_ptr_ty,
|
||||
Type::F32 => intrinsics.f32_ptr_ty,
|
||||
Type::F64 => intrinsics.f64_ptr_ty,
|
||||
Type::V128 => intrinsics.i128_ptr_ty,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,12 +44,18 @@ pub struct Intrinsics {
|
||||
|
||||
pub sqrt_f32: FunctionValue,
|
||||
pub sqrt_f64: FunctionValue,
|
||||
pub sqrt_f32x4: FunctionValue,
|
||||
pub sqrt_f64x2: FunctionValue,
|
||||
|
||||
pub minimum_f32: FunctionValue,
|
||||
pub minimum_f64: FunctionValue,
|
||||
pub minimum_f32x4: FunctionValue,
|
||||
pub minimum_f64x2: FunctionValue,
|
||||
|
||||
pub maximum_f32: FunctionValue,
|
||||
pub maximum_f64: FunctionValue,
|
||||
pub maximum_f32x4: FunctionValue,
|
||||
pub maximum_f64x2: FunctionValue,
|
||||
|
||||
pub ceil_f32: FunctionValue,
|
||||
pub ceil_f64: FunctionValue,
|
||||
@ -61,10 +71,22 @@ pub struct Intrinsics {
|
||||
|
||||
pub fabs_f32: FunctionValue,
|
||||
pub fabs_f64: FunctionValue,
|
||||
pub fabs_f32x4: FunctionValue,
|
||||
pub fabs_f64x2: FunctionValue,
|
||||
|
||||
pub copysign_f32: FunctionValue,
|
||||
pub copysign_f64: FunctionValue,
|
||||
|
||||
pub sadd_sat_i8x16: FunctionValue,
|
||||
pub sadd_sat_i16x8: FunctionValue,
|
||||
pub uadd_sat_i8x16: FunctionValue,
|
||||
pub uadd_sat_i16x8: FunctionValue,
|
||||
|
||||
pub ssub_sat_i8x16: FunctionValue,
|
||||
pub ssub_sat_i16x8: FunctionValue,
|
||||
pub usub_sat_i8x16: FunctionValue,
|
||||
pub usub_sat_i16x8: FunctionValue,
|
||||
|
||||
pub expect_i1: FunctionValue,
|
||||
pub trap: FunctionValue,
|
||||
|
||||
@ -74,21 +96,33 @@ pub struct Intrinsics {
|
||||
pub i16_ty: IntType,
|
||||
pub i32_ty: IntType,
|
||||
pub i64_ty: IntType,
|
||||
pub i128_ty: IntType,
|
||||
pub f32_ty: FloatType,
|
||||
pub f64_ty: FloatType,
|
||||
|
||||
pub i1x128_ty: VectorType,
|
||||
pub i8x16_ty: VectorType,
|
||||
pub i16x8_ty: VectorType,
|
||||
pub i32x4_ty: VectorType,
|
||||
pub i64x2_ty: VectorType,
|
||||
pub f32x4_ty: VectorType,
|
||||
pub f64x2_ty: VectorType,
|
||||
|
||||
pub i8_ptr_ty: PointerType,
|
||||
pub i16_ptr_ty: PointerType,
|
||||
pub i32_ptr_ty: PointerType,
|
||||
pub i64_ptr_ty: PointerType,
|
||||
pub i128_ptr_ty: PointerType,
|
||||
pub f32_ptr_ty: PointerType,
|
||||
pub f64_ptr_ty: PointerType,
|
||||
|
||||
pub anyfunc_ty: StructType,
|
||||
|
||||
pub i1_zero: IntValue,
|
||||
pub i8_zero: IntValue,
|
||||
pub i32_zero: IntValue,
|
||||
pub i64_zero: IntValue,
|
||||
pub i128_zero: IntValue,
|
||||
pub f32_zero: FloatValue,
|
||||
pub f64_zero: FloatValue,
|
||||
|
||||
@ -114,6 +148,7 @@ pub struct Intrinsics {
|
||||
pub memory_size_shared_import: FunctionValue,
|
||||
|
||||
pub throw_trap: FunctionValue,
|
||||
pub throw_breakpoint: FunctionValue,
|
||||
|
||||
pub experimental_stackmap: FunctionValue,
|
||||
|
||||
@ -128,19 +163,31 @@ impl Intrinsics {
|
||||
let i16_ty = context.i16_type();
|
||||
let i32_ty = context.i32_type();
|
||||
let i64_ty = context.i64_type();
|
||||
let i128_ty = context.i128_type();
|
||||
let f32_ty = context.f32_type();
|
||||
let f64_ty = context.f64_type();
|
||||
|
||||
let i1x128_ty = i1_ty.vec_type(128);
|
||||
let i8x16_ty = i8_ty.vec_type(16);
|
||||
let i16x8_ty = i16_ty.vec_type(8);
|
||||
let i32x4_ty = i32_ty.vec_type(4);
|
||||
let i64x2_ty = i64_ty.vec_type(2);
|
||||
let f32x4_ty = f32_ty.vec_type(4);
|
||||
let f64x2_ty = f64_ty.vec_type(2);
|
||||
|
||||
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 i128_ptr_ty = i128_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 i8_zero = i8_ty.const_int(0, false);
|
||||
let i32_zero = i32_ty.const_int(0, false);
|
||||
let i64_zero = i64_ty.const_int(0, false);
|
||||
let i128_zero = i128_ty.const_int(0, false);
|
||||
let f32_zero = f32_ty.const_float(0.0);
|
||||
let f64_zero = f64_ty.const_float(0.0);
|
||||
|
||||
@ -149,6 +196,10 @@ impl Intrinsics {
|
||||
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 i8x16_ty_basic = i8x16_ty.as_basic_type_enum();
|
||||
let i16x8_ty_basic = i16x8_ty.as_basic_type_enum();
|
||||
let f32x4_ty_basic = f32x4_ty.as_basic_type_enum();
|
||||
let f64x2_ty_basic = f64x2_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");
|
||||
@ -235,6 +286,9 @@ impl Intrinsics {
|
||||
false,
|
||||
);
|
||||
|
||||
let ret_i8x16_take_i8x16_i8x16 = i8x16_ty.fn_type(&[i8x16_ty_basic, i8x16_ty_basic], false);
|
||||
let ret_i16x8_take_i16x8_i16x8 = i16x8_ty.fn_type(&[i16x8_ty_basic, i16x8_ty_basic], 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);
|
||||
|
||||
@ -243,9 +297,13 @@ impl Intrinsics {
|
||||
|
||||
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_f32x4_take_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic], false);
|
||||
let ret_f64x2_take_f64x2 = f64x2_ty.fn_type(&[f64x2_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_f32x4_take_f32x4_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic, f32x4_ty_basic], false);
|
||||
let ret_f64x2_take_f64x2_f64x2 = f64x2_ty.fn_type(&[f64x2_ty_basic, f64x2_ty_basic], false);
|
||||
|
||||
let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type(
|
||||
&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic],
|
||||
@ -255,8 +313,7 @@ impl Intrinsics {
|
||||
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 {
|
||||
let intrinsics = 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),
|
||||
|
||||
@ -268,12 +325,34 @@ impl Intrinsics {
|
||||
|
||||
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),
|
||||
sqrt_f32x4: module.add_function("llvm.sqrt.v4f32", ret_f32x4_take_f32x4, None),
|
||||
sqrt_f64x2: module.add_function("llvm.sqrt.v2f64", ret_f64x2_take_f64x2, None),
|
||||
|
||||
minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None),
|
||||
minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None),
|
||||
minimum_f32x4: module.add_function(
|
||||
"llvm.minnum.v4f32",
|
||||
ret_f32x4_take_f32x4_f32x4,
|
||||
None,
|
||||
),
|
||||
minimum_f64x2: module.add_function(
|
||||
"llvm.minnum.v2f64",
|
||||
ret_f64x2_take_f64x2_f64x2,
|
||||
None,
|
||||
),
|
||||
|
||||
maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None),
|
||||
maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None),
|
||||
maximum_f32x4: module.add_function(
|
||||
"llvm.maxnum.v4f32",
|
||||
ret_f32x4_take_f32x4_f32x4,
|
||||
None,
|
||||
),
|
||||
maximum_f64x2: module.add_function(
|
||||
"llvm.maxnum.v2f64",
|
||||
ret_f64x2_take_f64x2_f64x2,
|
||||
None,
|
||||
),
|
||||
|
||||
ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None),
|
||||
ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None),
|
||||
@ -289,10 +368,54 @@ impl Intrinsics {
|
||||
|
||||
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),
|
||||
fabs_f32x4: module.add_function("llvm.fabs.v4f32", ret_f32x4_take_f32x4, None),
|
||||
fabs_f64x2: module.add_function("llvm.fabs.v2f64", ret_f64x2_take_f64x2, 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),
|
||||
|
||||
sadd_sat_i8x16: module.add_function(
|
||||
"llvm.sadd.sat.v16i8",
|
||||
ret_i8x16_take_i8x16_i8x16,
|
||||
None,
|
||||
),
|
||||
sadd_sat_i16x8: module.add_function(
|
||||
"llvm.sadd.sat.v8i16",
|
||||
ret_i16x8_take_i16x8_i16x8,
|
||||
None,
|
||||
),
|
||||
uadd_sat_i8x16: module.add_function(
|
||||
"llvm.uadd.sat.v16i8",
|
||||
ret_i8x16_take_i8x16_i8x16,
|
||||
None,
|
||||
),
|
||||
uadd_sat_i16x8: module.add_function(
|
||||
"llvm.uadd.sat.v8i16",
|
||||
ret_i16x8_take_i16x8_i16x8,
|
||||
None,
|
||||
),
|
||||
|
||||
ssub_sat_i8x16: module.add_function(
|
||||
"llvm.ssub.sat.v16i8",
|
||||
ret_i8x16_take_i8x16_i8x16,
|
||||
None,
|
||||
),
|
||||
ssub_sat_i16x8: module.add_function(
|
||||
"llvm.ssub.sat.v8i16",
|
||||
ret_i16x8_take_i16x8_i16x8,
|
||||
None,
|
||||
),
|
||||
usub_sat_i8x16: module.add_function(
|
||||
"llvm.usub.sat.v16i8",
|
||||
ret_i8x16_take_i8x16_i8x16,
|
||||
None,
|
||||
),
|
||||
usub_sat_i16x8: module.add_function(
|
||||
"llvm.usub.sat.v8i16",
|
||||
ret_i16x8_take_i16x8_i16x8,
|
||||
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),
|
||||
|
||||
@ -302,21 +425,33 @@ impl Intrinsics {
|
||||
i16_ty,
|
||||
i32_ty,
|
||||
i64_ty,
|
||||
i128_ty,
|
||||
f32_ty,
|
||||
f64_ty,
|
||||
|
||||
i1x128_ty,
|
||||
i8x16_ty,
|
||||
i16x8_ty,
|
||||
i32x4_ty,
|
||||
i64x2_ty,
|
||||
f32x4_ty,
|
||||
f64x2_ty,
|
||||
|
||||
i8_ptr_ty,
|
||||
i16_ptr_ty,
|
||||
i32_ptr_ty,
|
||||
i64_ptr_ty,
|
||||
i128_ptr_ty,
|
||||
f32_ptr_ty,
|
||||
f64_ptr_ty,
|
||||
|
||||
anyfunc_ty,
|
||||
|
||||
i1_zero,
|
||||
i8_zero,
|
||||
i32_zero,
|
||||
i64_zero,
|
||||
i128_zero,
|
||||
f32_zero,
|
||||
f64_zero,
|
||||
|
||||
@ -404,8 +539,45 @@ impl Intrinsics {
|
||||
),
|
||||
None,
|
||||
),
|
||||
throw_breakpoint: module.add_function(
|
||||
"vm.breakpoint",
|
||||
void_ty.fn_type(&[i64_ty_basic], false),
|
||||
None,
|
||||
),
|
||||
ctx_ptr_ty,
|
||||
}
|
||||
};
|
||||
|
||||
let readonly =
|
||||
context.create_enum_attribute(Attribute::get_named_enum_kind_id("readonly"), 0);
|
||||
intrinsics
|
||||
.memory_size_dynamic_local
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
intrinsics
|
||||
.memory_size_static_local
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
intrinsics
|
||||
.memory_size_shared_local
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
intrinsics
|
||||
.memory_size_dynamic_import
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
intrinsics
|
||||
.memory_size_static_import
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
intrinsics
|
||||
.memory_size_shared_import
|
||||
.add_attribute(AttributeLoc::Function, readonly);
|
||||
|
||||
let noreturn =
|
||||
context.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0);
|
||||
intrinsics
|
||||
.throw_trap
|
||||
.add_attribute(AttributeLoc::Function, noreturn);
|
||||
intrinsics
|
||||
.throw_breakpoint
|
||||
.add_attribute(AttributeLoc::Function, noreturn);
|
||||
|
||||
intrinsics
|
||||
}
|
||||
}
|
||||
|
||||
@ -855,4 +1027,31 @@ impl<'a> CtxType<'a> {
|
||||
|
||||
(imported_func_cache.func_ptr, imported_func_cache.ctx_ptr)
|
||||
}
|
||||
|
||||
pub fn internal_field(
|
||||
&mut self,
|
||||
index: usize,
|
||||
intrinsics: &Intrinsics,
|
||||
builder: &Builder,
|
||||
) -> PointerValue {
|
||||
assert!(index < INTERNALS_SIZE);
|
||||
|
||||
let local_internals_ptr_ptr = unsafe {
|
||||
builder.build_struct_gep(
|
||||
self.ctx_ptr_value,
|
||||
offset_to_index(Ctx::offset_internals()),
|
||||
"local_internals_ptr_ptr",
|
||||
)
|
||||
};
|
||||
let local_internals_ptr = builder
|
||||
.build_load(local_internals_ptr_ptr, "local_internals_ptr")
|
||||
.into_pointer_value();
|
||||
unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
local_internals_ptr,
|
||||
&[intrinsics.i32_ty.const_int(index as u64, false)],
|
||||
"local_internal_field_ptr",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
mod backend;
|
||||
@ -8,8 +14,14 @@ mod platform;
|
||||
mod read_info;
|
||||
mod stackmap;
|
||||
mod state;
|
||||
mod structs;
|
||||
mod trampolines;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator;
|
||||
pub use code::LLVMModuleCodeGenerator as ModuleCodeGenerator;
|
||||
|
||||
use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen;
|
||||
|
||||
pub type LLVMCompiler = SimpleStreamingCompilerGen<
|
||||
@ -18,3 +30,22 @@ pub type LLVMCompiler = SimpleStreamingCompilerGen<
|
||||
backend::LLVMBackend,
|
||||
code::CodegenError,
|
||||
>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// LLVM backend flags.
|
||||
pub struct LLVMOptions {
|
||||
/// Emit LLVM IR before optimization pipeline.
|
||||
pub pre_opt_ir: Option<PathBuf>,
|
||||
|
||||
/// Emit LLVM IR after optimization pipeline.
|
||||
pub post_opt_ir: Option<PathBuf>,
|
||||
|
||||
/// Emit LLVM generated native code object file.
|
||||
pub obj_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub static mut GLOBAL_OPTIONS: LLVMOptions = LLVMOptions {
|
||||
pre_opt_ir: None,
|
||||
post_opt_ir: None,
|
||||
obj_file: None,
|
||||
};
|
||||
|
3
lib/llvm-backend/src/platform/common.rs
Normal file
3
lib/llvm-backend/src/platform/common.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn round_up_to_page_size(size: usize) -> usize {
|
||||
(size + (4096 - 1)) & !(4096 - 1)
|
||||
}
|
@ -1,7 +1,14 @@
|
||||
mod common;
|
||||
|
||||
#[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");
|
||||
mod win;
|
||||
#[cfg(target_family = "windows")]
|
||||
pub use self::win::*;
|
||||
|
||||
#[cfg(not(any(unix, target_family = "windows")))]
|
||||
compile_error!("Your system is not yet supported for the llvm-based compiler backend");
|
||||
|
@ -1,5 +1,11 @@
|
||||
use libc::{c_void, siginfo_t};
|
||||
use super::common::round_up_to_page_size;
|
||||
use crate::structs::{LLVMResult, MemProtect};
|
||||
use libc::{
|
||||
c_void, mmap, mprotect, munmap, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE,
|
||||
PROT_READ, PROT_WRITE,
|
||||
};
|
||||
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV};
|
||||
use std::ptr;
|
||||
|
||||
/// `__register_frame` and `__deregister_frame` on macos take a single fde as an
|
||||
/// argument, so we need to parse the fde table here.
|
||||
@ -68,3 +74,60 @@ extern "C" fn signal_trap_handler(
|
||||
throw_trap(2);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe 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 = 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
|
||||
}
|
||||
|
||||
pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
|
||||
let res = 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
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
|
||||
let res = munmap(ptr as _, round_up_to_page_size(size));
|
||||
|
||||
if res == 0 {
|
||||
LLVMResult::OK
|
||||
} else {
|
||||
LLVMResult::DEALLOC_FAILURE
|
||||
}
|
||||
}
|
||||
|
80
lib/llvm-backend/src/platform/win.rs
Normal file
80
lib/llvm-backend/src/platform/win.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use super::common::round_up_to_page_size;
|
||||
use crate::structs::{LLVMResult, MemProtect};
|
||||
use std::ptr;
|
||||
|
||||
use winapi::um::memoryapi::{VirtualAlloc, VirtualFree};
|
||||
use winapi::um::winnt::{
|
||||
MEM_COMMIT, MEM_DECOMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_NOACCESS, PAGE_READONLY,
|
||||
PAGE_READWRITE,
|
||||
};
|
||||
|
||||
pub unsafe fn visit_fde(_addr: *mut u8, _size: usize, _visitor: extern "C" fn(*mut u8)) {
|
||||
// Do nothing on Windows
|
||||
}
|
||||
|
||||
pub unsafe fn install_signal_handler() {
|
||||
// Do nothing on Windows
|
||||
}
|
||||
|
||||
pub unsafe 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 flags = if protect == MemProtect::NONE {
|
||||
MEM_RESERVE
|
||||
} else {
|
||||
MEM_RESERVE | MEM_COMMIT
|
||||
};
|
||||
let ptr = VirtualAlloc(
|
||||
ptr::null_mut(),
|
||||
size,
|
||||
flags,
|
||||
memprotect_to_protect_const(protect),
|
||||
);
|
||||
|
||||
if ptr.is_null() {
|
||||
return LLVMResult::ALLOCATE_FAILURE;
|
||||
}
|
||||
|
||||
*ptr_out = ptr as _;
|
||||
*size_out = size;
|
||||
LLVMResult::OK
|
||||
}
|
||||
|
||||
pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
|
||||
let size = round_up_to_page_size(size);
|
||||
let ptr = VirtualAlloc(
|
||||
ptr as _,
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
memprotect_to_protect_const(protect),
|
||||
);
|
||||
|
||||
if ptr.is_null() {
|
||||
LLVMResult::PROTECT_FAILURE
|
||||
} else {
|
||||
LLVMResult::OK
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
|
||||
let success = VirtualFree(ptr as _, size, MEM_DECOMMIT);
|
||||
// If the function succeeds, the return value is nonzero.
|
||||
if success == 1 {
|
||||
LLVMResult::OK
|
||||
} else {
|
||||
LLVMResult::DEALLOC_FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
fn memprotect_to_protect_const(protect: MemProtect) -> u32 {
|
||||
match protect {
|
||||
MemProtect::NONE => PAGE_NOACCESS,
|
||||
MemProtect::READ => PAGE_READONLY,
|
||||
MemProtect::READ_WRITE => PAGE_READWRITE,
|
||||
MemProtect::READ_EXECUTE => PAGE_EXECUTE_READ,
|
||||
}
|
||||
}
|
@ -7,12 +7,7 @@ pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
WpType::V128 => Type::V128,
|
||||
_ => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "that type is not supported as a wasmer type",
|
||||
|
@ -97,12 +97,6 @@ impl StackmapEntry {
|
||||
end: Option<(&StackmapEntry, &StkMapRecord)>,
|
||||
msm: &mut ModuleStateMap,
|
||||
) {
|
||||
#[derive(Clone, Debug)]
|
||||
enum RuntimeOrConstant {
|
||||
Runtime(MachineValue),
|
||||
Constant(u64),
|
||||
}
|
||||
|
||||
let func_base_addr = (size_record.function_address as usize)
|
||||
.checked_sub(code_addr)
|
||||
.unwrap();
|
||||
|
39
lib/llvm-backend/src/structs.rs
Normal file
39
lib/llvm-backend/src/structs.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use libc::c_char;
|
||||
|
||||
use wasmer_runtime_core::vm;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LLVMModule {
|
||||
_private: [u8; 0],
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub enum MemProtect {
|
||||
NONE,
|
||||
READ,
|
||||
READ_WRITE,
|
||||
READ_EXECUTE,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub enum LLVMResult {
|
||||
OK,
|
||||
ALLOCATE_FAILURE,
|
||||
PROTECT_FAILURE,
|
||||
DEALLOC_FAILURE,
|
||||
OBJECT_LOAD_FAILURE,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Callbacks {
|
||||
pub alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult,
|
||||
pub protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult,
|
||||
pub dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult,
|
||||
|
||||
pub lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func,
|
||||
pub visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)),
|
||||
}
|
@ -72,12 +72,14 @@ fn generate_trampoline(
|
||||
Type::I64 => intrinsics.i64_ptr_ty,
|
||||
Type::F32 => intrinsics.f32_ptr_ty,
|
||||
Type::F64 => intrinsics.f64_ptr_ty,
|
||||
Type::V128 => intrinsics.i128_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 mut i = 0;
|
||||
for param_ty in func_sig.params().iter() {
|
||||
let index = intrinsics.i32_ty.const_int(i as _, false);
|
||||
let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") };
|
||||
|
||||
@ -88,6 +90,10 @@ fn generate_trampoline(
|
||||
|
||||
let arg = builder.build_load(typed_item_pointer, "arg");
|
||||
args_vec.push(arg);
|
||||
i = i + 1;
|
||||
if *param_ty == Type::V128 {
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let call_site = builder.build_call(func_ptr, &args_vec, "call");
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-middleware-common"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "Wasmer runtime common middlewares"
|
||||
license = "MIT"
|
||||
@ -9,12 +9,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core" }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.5.6" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.5.6", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.5.6", optional = true }
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" }
|
||||
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true }
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.7.4"
|
||||
wabt = "0.9.1"
|
||||
criterion = "0.2"
|
||||
|
||||
[features]
|
||||
|
@ -135,10 +135,9 @@ static WAT_GAS: &'static str = r#"
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
|
||||
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
|
||||
use wasmer_llvm_backend::ModuleCodeGenerator;
|
||||
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
|
||||
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
|
||||
StreamingCompiler::new(move || {
|
||||
let c: StreamingCompiler<ModuleCodeGenerator, _, _, _, _> = StreamingCompiler::new(move || {
|
||||
let mut chain = MiddlewareChain::new();
|
||||
if metering {
|
||||
chain.push(Metering::new(limit));
|
||||
@ -164,15 +163,11 @@ fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
|
||||
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
|
||||
panic!("compiler not specified, activate a compiler via features");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
compile_error!("compiler not specified, activate a compiler via features");
|
||||
|
||||
#[cfg(feature = "clif")]
|
||||
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
|
||||
panic!("cranelift does not implement metering");
|
||||
compile_error!("cranelift does not implement metering");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
pub mod call_trace;
|
||||
pub mod metering;
|
||||
|
@ -129,19 +129,18 @@ pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) {
|
||||
ctx.set_internal(&INTERNAL_FIELD, value);
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "singlepass"))]
|
||||
#[cfg(all(test, any(feature = "singlepass", feature = "llvm")))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wabt::wat2wasm;
|
||||
|
||||
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
|
||||
use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func};
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
fn get_compiler(limit: u64) -> impl Compiler {
|
||||
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
|
||||
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
|
||||
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
|
||||
StreamingCompiler::new(move || {
|
||||
use wasmer_llvm_backend::ModuleCodeGenerator as LLVMMCG;
|
||||
let c: StreamingCompiler<LLVMMCG, _, _, _, _> = StreamingCompiler::new(move || {
|
||||
let mut chain = MiddlewareChain::new();
|
||||
chain.push(Metering::new(limit));
|
||||
chain
|
||||
@ -151,7 +150,6 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "singlepass")]
|
||||
fn get_compiler(limit: u64) -> impl Compiler {
|
||||
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
|
||||
use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
|
||||
let c: StreamingCompiler<SinglePassMCG, _, _, _, _> = StreamingCompiler::new(move || {
|
||||
let mut chain = MiddlewareChain::new();
|
||||
@ -162,15 +160,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
|
||||
fn get_compiler(_limit: u64) -> impl Compiler {
|
||||
panic!("compiler not specified, activate a compiler via features");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
compile_error!("compiler not specified, activate a compiler via features");
|
||||
|
||||
#[cfg(feature = "clif")]
|
||||
fn get_compiler(_limit: u64) -> impl Compiler {
|
||||
panic!("cranelift does not implement metering");
|
||||
compile_error!("cranelift does not implement metering");
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
@ -253,7 +247,7 @@ mod tests {
|
||||
// verify it returns the correct value
|
||||
assert_eq!(value, 7);
|
||||
|
||||
// verify is uses the correct number of points
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 74);
|
||||
}
|
||||
|
||||
@ -282,7 +276,7 @@ mod tests {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// verify is uses the correct number of points
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-abi"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,12 +8,11 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.50"
|
||||
libc = "0.2.60"
|
||||
wasmer-runtime-core = { path = "../runtime-core" }
|
||||
hashbrown = "0.1"
|
||||
failure = "0.1"
|
||||
tar = "0.4"
|
||||
wasmparser = "0.32.1"
|
||||
wasmparser = "0.35.1"
|
||||
zstd = "0.4"
|
||||
|
||||
# [target.'cfg(unix)'.dependencies.zbox]
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(dead_code, unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
|
@ -1,7 +1,7 @@
|
||||
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::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-c-api"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer C API library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -12,19 +12,19 @@ readme = "README.md"
|
||||
crate-type = ["cdylib", "rlib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libc = "0.2.60"
|
||||
|
||||
[dependencies.wasmer-runtime]
|
||||
path = "../runtime"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
path = "../runtime-core"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime/debug"]
|
||||
llvm = ["wasmer-runtime/llvm"]
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.8"
|
||||
cbindgen = "0.9.0"
|
||||
|
@ -8,17 +8,17 @@ use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
thread_local! {
|
||||
static LAST_ERROR: RefCell<Option<Box<Error>>> = RefCell::new(None);
|
||||
static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
pub(crate) fn update_last_error<E: Error + 'static>(err: E) {
|
||||
pub fn update_last_error<E: Error + 'static>(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<Box<Error>> {
|
||||
pub(crate) fn take_last_error() -> Option<Box<dyn Error>> {
|
||||
LAST_ERROR.with(|prev| prev.borrow_mut().take())
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CApiError {
|
||||
pub(crate) msg: String,
|
||||
pub struct CApiError {
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl Display for CApiError {
|
||||
|
@ -402,6 +402,7 @@ pub unsafe extern "C" fn wasmer_export_func_call(
|
||||
tag: wasmer_value_tag::WASM_F64,
|
||||
value: wasmer_value { F64: x },
|
||||
},
|
||||
Value::V128(_) => unimplemented!("returning V128 type"),
|
||||
};
|
||||
results[0] = ret;
|
||||
}
|
||||
|
@ -10,9 +10,10 @@ use crate::{
|
||||
};
|
||||
use libc::c_uint;
|
||||
use std::{ffi::c_void, ptr, slice, sync::Arc};
|
||||
use wasmer_runtime::Module;
|
||||
use wasmer_runtime::{Global, Memory, Module, Table};
|
||||
use wasmer_runtime_core::{
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::ImportObject,
|
||||
module::ImportName,
|
||||
types::{FuncSig, Type},
|
||||
};
|
||||
@ -25,6 +26,9 @@ pub struct wasmer_import_t {
|
||||
pub value: wasmer_import_export_value,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_import_object_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_func_t;
|
||||
@ -37,6 +41,83 @@ pub struct wasmer_import_descriptor_t;
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_descriptors_t;
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object_t {
|
||||
let import_object = Box::new(ImportObject::new());
|
||||
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_extend(
|
||||
import_object: *mut wasmer_import_object_t,
|
||||
imports: *mut wasmer_import_t,
|
||||
imports_len: c_uint,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
||||
|
||||
let mut extensions: Vec<(String, String, Export)> = Vec::new();
|
||||
|
||||
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
|
||||
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 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())
|
||||
}
|
||||
};
|
||||
|
||||
let extension = (module_name.to_string(), import_name.to_string(), export);
|
||||
extensions.push(extension)
|
||||
}
|
||||
|
||||
import_object.extend(extensions);
|
||||
|
||||
return wasmer_result_t::WASMER_OK;
|
||||
}
|
||||
|
||||
/// Gets import descriptors for the given module
|
||||
///
|
||||
/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it.
|
||||
@ -352,6 +433,14 @@ pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees memory of the given ImportObject
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_import_object_destroy(import_object: *mut wasmer_import_object_t) {
|
||||
if !import_object.is_null() {
|
||||
unsafe { Box::from_raw(import_object as *mut ImportObject) };
|
||||
}
|
||||
}
|
||||
|
||||
struct NamedImportDescriptor {
|
||||
module: String,
|
||||
name: String,
|
||||
|
@ -3,15 +3,19 @@
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports},
|
||||
import::wasmer_import_t,
|
||||
import::{wasmer_import_object_t, wasmer_import_t},
|
||||
memory::wasmer_memory_t,
|
||||
module::wasmer_module_t,
|
||||
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
|
||||
wasmer_result_t,
|
||||
};
|
||||
use libc::{c_char, c_int, c_void};
|
||||
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};
|
||||
use wasmer_runtime::{Ctx, Global, Instance, Memory, Module, Table, Value};
|
||||
use wasmer_runtime_core::{
|
||||
export::Export,
|
||||
import::{ImportObject, Namespace},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_instance_t;
|
||||
@ -108,6 +112,45 @@ pub unsafe extern "C" fn wasmer_instantiate(
|
||||
wasmer_result_t::WASMER_OK
|
||||
}
|
||||
|
||||
/// Given:
|
||||
/// * A prepared `wasmer` import-object
|
||||
/// * A compiled wasmer module
|
||||
///
|
||||
/// Instantiates a wasmer instance
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_module_import_instantiate(
|
||||
instance: *mut *mut wasmer_instance_t,
|
||||
module: *const wasmer_module_t,
|
||||
import_object: *const wasmer_import_object_t,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &ImportObject = &*(import_object as *const ImportObject);
|
||||
let module: &Module = &*(module as *const Module);
|
||||
|
||||
let new_instance: Instance = match module.instantiate(import_object) {
|
||||
Ok(instance) => instance,
|
||||
Err(error) => {
|
||||
update_last_error(error);
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
};
|
||||
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
|
||||
|
||||
return wasmer_result_t::WASMER_OK;
|
||||
}
|
||||
|
||||
/// Extracts the instance's context and returns it.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_instance_context_get(
|
||||
instance: *mut wasmer_instance_t,
|
||||
) -> *const wasmer_instance_context_t {
|
||||
let instance_ref = &*(instance as *const Instance);
|
||||
|
||||
let ctx: *const Ctx = instance_ref.context() as *const _;
|
||||
|
||||
ctx as *const wasmer_instance_context_t
|
||||
}
|
||||
|
||||
/// Calls an instances exported function by `name` with the provided parameters.
|
||||
/// Results are set using the provided `results` pointer.
|
||||
///
|
||||
@ -173,6 +216,7 @@ pub unsafe extern "C" fn wasmer_instance_call(
|
||||
tag: wasmer_value_tag::WASM_F64,
|
||||
value: wasmer_value { F64: x },
|
||||
},
|
||||
Value::V128(_) => unimplemented!("calling function with V128 parameter"),
|
||||
};
|
||||
results[0] = ret;
|
||||
}
|
||||
|
@ -80,8 +80,13 @@
|
||||
//!
|
||||
//! [wasmer_h]: ./wasmer.h
|
||||
//! [wasmer_hh]: ./wasmer.hh
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
extern crate wasmer_runtime;
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
@ -118,6 +123,6 @@ pub struct wasmer_limit_option_t {
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_byte_array {
|
||||
bytes: *const u8,
|
||||
bytes_len: u32,
|
||||
pub bytes: *const u8,
|
||||
pub bytes_len: u32,
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ impl From<wasmer_value_t> for Value {
|
||||
tag: wasmer_value_tag::WASM_F64,
|
||||
value: wasmer_value { F64 },
|
||||
} => Value::F64(F64),
|
||||
_ => panic!("not implemented"),
|
||||
_ => unreachable!("unknown WASM type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,7 @@ impl From<Value> for wasmer_value_t {
|
||||
tag: wasmer_value_tag::WASM_F64,
|
||||
value: wasmer_value { F64: x },
|
||||
},
|
||||
Value::V128(_) => unimplemented!("V128 not supported in C API"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,7 +89,7 @@ impl From<Type> for wasmer_value_tag {
|
||||
Type::I64 => wasmer_value_tag::WASM_I64,
|
||||
Type::F32 => wasmer_value_tag::WASM_F32,
|
||||
Type::F64 => wasmer_value_tag::WASM_F64,
|
||||
_ => panic!("not implemented"),
|
||||
Type::V128 => unreachable!("V128 not supported in C API"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +102,7 @@ impl From<wasmer_value_tag> for Type {
|
||||
wasmer_value_tag::WASM_I64 => Type::I64,
|
||||
wasmer_value_tag::WASM_F32 => Type::F32,
|
||||
wasmer_value_tag::WASM_F64 => Type::F64,
|
||||
_ => panic!("not implemented"),
|
||||
_ => unreachable!("unknown WASM type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,6 +114,7 @@ impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag {
|
||||
Type::I64 => wasmer_value_tag::WASM_I64,
|
||||
Type::F32 => wasmer_value_tag::WASM_F32,
|
||||
Type::F64 => wasmer_value_tag::WASM_F64,
|
||||
Type::V128 => unimplemented!("V128 not supported in C API"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
lib/runtime-c-api/tests/.gitignore
vendored
2
lib/runtime-c-api/tests/.gitignore
vendored
@ -23,3 +23,5 @@ test-module-imports
|
||||
test-module-serialize
|
||||
test-tables
|
||||
test-validate
|
||||
test-context
|
||||
test-module-import-instantiate
|
||||
|
@ -14,6 +14,8 @@ 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)
|
||||
add_executable(test-context test-context.c)
|
||||
add_executable(test-module-import-instantiate test-module-import-instantiate.c)
|
||||
|
||||
find_library(
|
||||
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll
|
||||
@ -87,3 +89,11 @@ 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)
|
||||
|
||||
target_link_libraries(test-context general ${WASMER_LIB})
|
||||
target_compile_options(test-context PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-context test-context)
|
||||
|
||||
target_link_libraries(test-module-import-instantiate general ${WASMER_LIB})
|
||||
target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-module-import-instantiate test-module-import-instantiate)
|
||||
|
BIN
lib/runtime-c-api/tests/a.out
Executable file
BIN
lib/runtime-c-api/tests/a.out
Executable file
Binary file not shown.
BIN
lib/runtime-c-api/tests/assets/inc.wasm
Normal file
BIN
lib/runtime-c-api/tests/assets/inc.wasm
Normal file
Binary file not shown.
12
lib/runtime-c-api/tests/assets/inc.wast
Normal file
12
lib/runtime-c-api/tests/assets/inc.wast
Normal file
@ -0,0 +1,12 @@
|
||||
(module
|
||||
(func $inc (import "env" "inc"))
|
||||
(func $mul (import "env" "mul"))
|
||||
(func $get (import "env" "get") (result i32))
|
||||
|
||||
(func (export "inc_and_get") (result i32)
|
||||
call $inc
|
||||
call $get)
|
||||
|
||||
(func (export "mul_and_get") (result i32)
|
||||
call $mul
|
||||
call $get))
|
@ -4,19 +4,17 @@ use std::process::Command;
|
||||
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, Some("-Wdev -Werror=dev"));
|
||||
run_command("make", project_tests_dir, Some("test"));
|
||||
run_command("cmake", project_tests_dir, vec!["."]);
|
||||
run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]);
|
||||
run_command("make", project_tests_dir, vec!["test", "ARGS=\"-V\""]);
|
||||
}
|
||||
|
||||
fn run_command(command_str: &str, dir: &str, arg: Option<&str>) {
|
||||
println!("Running command: `{}` arg: {:?}", command_str, arg);
|
||||
fn run_command(command_str: &str, dir: &str, args: Vec<&str>) {
|
||||
println!("Running command: `{}` args: {:?}", command_str, args);
|
||||
|
||||
let mut command = Command::new(command_str);
|
||||
|
||||
if let Some(a) = arg {
|
||||
command.arg(a);
|
||||
}
|
||||
command.args(&args);
|
||||
|
||||
command.current_dir(dir);
|
||||
|
||||
|
137
lib/runtime-c-api/tests/test-context.c
Normal file
137
lib/runtime-c-api/tests/test-context.c
Normal file
@ -0,0 +1,137 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int32_t amount;
|
||||
int32_t value;
|
||||
} counter_data;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* bytes;
|
||||
long bytes_len;
|
||||
} wasm_file_t;
|
||||
|
||||
wasm_file_t read_wasm_file(const char* file_name) {
|
||||
wasm_file_t wasm_file;
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
fseek(file, 0, SEEK_END);
|
||||
wasm_file.bytes_len = ftell(file);
|
||||
|
||||
wasm_file.bytes = malloc(wasm_file.bytes_len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
|
||||
fclose(file);
|
||||
|
||||
return wasm_file;
|
||||
}
|
||||
|
||||
void inc_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value + data->amount;
|
||||
}
|
||||
|
||||
void mul_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value * data->amount;
|
||||
}
|
||||
|
||||
int32_t get_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
return data->value;
|
||||
}
|
||||
|
||||
counter_data *init_counter(int32_t value, int32_t amount) {
|
||||
counter_data* counter = malloc(sizeof(counter_data));
|
||||
counter->value = value;
|
||||
counter->amount = amount;
|
||||
return counter;
|
||||
}
|
||||
|
||||
void assert_counter(wasmer_instance_t *instance, int32_t expected) {
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t results[] = {result_one};
|
||||
|
||||
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call1_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
assert(results[0].value.I32 == expected);
|
||||
assert(call1_result == WASMER_OK);
|
||||
|
||||
const wasmer_instance_context_t *ctx = wasmer_instance_context_get(instance);
|
||||
counter_data *cd = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
assert(cd->value == expected);
|
||||
}
|
||||
|
||||
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
|
||||
wasmer_import_t import;
|
||||
wasmer_byte_array module_name_bytes;
|
||||
wasmer_byte_array import_name_bytes;
|
||||
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
|
||||
import.module_name = module_name_bytes;
|
||||
import.import_name = import_name_bytes;
|
||||
|
||||
import.tag = WASM_FUNCTION;
|
||||
import.value.func = func;
|
||||
|
||||
return import;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Prepare Imports
|
||||
wasmer_value_tag inc_params_sig[] = {};
|
||||
wasmer_value_tag inc_returns_sig[] = {};
|
||||
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
|
||||
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
|
||||
|
||||
wasmer_value_tag mul_params_sig[] = {};
|
||||
wasmer_value_tag mul_returns_sig[] = {};
|
||||
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
|
||||
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
|
||||
|
||||
wasmer_value_tag get_params_sig[] = {};
|
||||
wasmer_value_tag get_returns_sig[] = {WASM_I32};
|
||||
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
|
||||
wasmer_import_t get_import = create_import("env", "get", get_func);
|
||||
|
||||
wasmer_import_t imports[] = {inc_import, mul_import, get_import};
|
||||
|
||||
// Read the wasm file
|
||||
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
|
||||
|
||||
// Instantiate instance
|
||||
printf("Instantiating\n");
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_res = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 3);
|
||||
printf("Compile result: %d\n", instantiate_res);
|
||||
assert(instantiate_res == WASMER_OK);
|
||||
|
||||
// Init counter
|
||||
counter_data *counter = init_counter(2, 5);
|
||||
wasmer_instance_context_data_set(instance, counter);
|
||||
|
||||
// Run `instance.inc_and_get` and assert
|
||||
assert_counter(instance, 7);
|
||||
assert_counter(instance, 12);
|
||||
assert_counter(instance, 17);
|
||||
|
||||
// Clear resources
|
||||
wasmer_import_func_destroy(inc_func);
|
||||
wasmer_import_func_destroy(get_func);
|
||||
wasmer_instance_destroy(instance);
|
||||
free(counter);
|
||||
free(wasm_file.bytes);
|
||||
|
||||
return 0;
|
||||
}
|
@ -42,8 +42,9 @@ int main()
|
||||
assert(export_length == 5);
|
||||
|
||||
// Get the `memory` export.
|
||||
wasmer_export_t *export = wasmer_exports_get(exports, 1);
|
||||
wasmer_export_t *export = wasmer_exports_get(exports, 0);
|
||||
wasmer_import_export_kind kind = wasmer_export_kind(export);
|
||||
printf("Wasmer import export kind: %d (Memory is %d)\n", kind, WASM_MEMORY);
|
||||
assert(kind == WASM_MEMORY);
|
||||
|
||||
wasmer_byte_array export_name = wasmer_export_name(export);
|
||||
|
149
lib/runtime-c-api/tests/test-module-import-instantiate.c
Normal file
149
lib/runtime-c-api/tests/test-module-import-instantiate.c
Normal file
@ -0,0 +1,149 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int32_t amount;
|
||||
int32_t value;
|
||||
} counter_data;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* bytes;
|
||||
long bytes_len;
|
||||
} wasm_file_t;
|
||||
|
||||
wasm_file_t read_wasm_file(const char* file_name) {
|
||||
wasm_file_t wasm_file;
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
fseek(file, 0, SEEK_END);
|
||||
wasm_file.bytes_len = ftell(file);
|
||||
|
||||
wasm_file.bytes = malloc(wasm_file.bytes_len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
|
||||
fclose(file);
|
||||
|
||||
return wasm_file;
|
||||
}
|
||||
|
||||
void inc_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value + data->amount;
|
||||
}
|
||||
|
||||
void mul_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value * data->amount;
|
||||
}
|
||||
|
||||
int32_t get_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
return data->value;
|
||||
}
|
||||
|
||||
counter_data *init_counter(int32_t value, int32_t amount) {
|
||||
counter_data* counter = malloc(sizeof(counter_data));
|
||||
counter->value = value;
|
||||
counter->amount = amount;
|
||||
return counter;
|
||||
}
|
||||
|
||||
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
|
||||
wasmer_import_t import;
|
||||
wasmer_byte_array module_name_bytes;
|
||||
wasmer_byte_array import_name_bytes;
|
||||
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
|
||||
import.module_name = module_name_bytes;
|
||||
import.import_name = import_name_bytes;
|
||||
|
||||
import.tag = WASM_FUNCTION;
|
||||
import.value.func = func;
|
||||
|
||||
return import;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Prepare Imports
|
||||
wasmer_value_tag inc_params_sig[] = {};
|
||||
wasmer_value_tag inc_returns_sig[] = {};
|
||||
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
|
||||
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
|
||||
|
||||
wasmer_value_tag mul_params_sig[] = {};
|
||||
wasmer_value_tag mul_returns_sig[] = {};
|
||||
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
|
||||
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
|
||||
|
||||
wasmer_value_tag get_params_sig[] = {};
|
||||
wasmer_value_tag get_returns_sig[] = {WASM_I32};
|
||||
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
|
||||
wasmer_import_t get_import = create_import("env", "get", get_func);
|
||||
|
||||
// Read the wasm file
|
||||
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
|
||||
|
||||
// Compile module
|
||||
wasmer_module_t *module = NULL;
|
||||
wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len);
|
||||
assert(compile_res == WASMER_OK);
|
||||
|
||||
// Prepare Import Object
|
||||
wasmer_import_object_t *import_object = wasmer_import_object_new();
|
||||
|
||||
// First, we import `inc_counter` and `mul_counter`
|
||||
wasmer_import_t imports[] = {inc_import, mul_import};
|
||||
wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2);
|
||||
assert(extend_res == WASMER_OK);
|
||||
|
||||
// Now, we'll import `inc_counter` and `mul_counter`
|
||||
wasmer_import_t more_imports[] = {get_import};
|
||||
wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1);
|
||||
assert(extend_res2 == WASMER_OK);
|
||||
|
||||
// Same `wasmer_import_object_extend` as the first, doesn't affect anything
|
||||
wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2);
|
||||
assert(extend_res3 == WASMER_OK);
|
||||
|
||||
// Instantiate instance
|
||||
printf("Instantiating\n");
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object);
|
||||
printf("Compile result: %d\n", instantiate_res);
|
||||
assert(instantiate_res == WASMER_OK);
|
||||
|
||||
// Init counter
|
||||
counter_data *counter = init_counter(2, 5);
|
||||
wasmer_instance_context_data_set(instance, counter);
|
||||
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t results[] = {result_one};
|
||||
|
||||
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call1_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
|
||||
wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call2_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
|
||||
// Clear resources
|
||||
wasmer_import_func_destroy(inc_func);
|
||||
wasmer_import_func_destroy(mul_func);
|
||||
wasmer_import_func_destroy(get_func);
|
||||
wasmer_instance_destroy(instance);
|
||||
free(counter);
|
||||
free(wasm_file.bytes);
|
||||
|
||||
return 0;
|
||||
}
|
@ -95,11 +95,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_context_t;
|
||||
} wasmer_import_object_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -119,6 +115,14 @@ typedef struct {
|
||||
wasmer_import_export_value value;
|
||||
} wasmer_import_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_context_t;
|
||||
|
||||
typedef struct {
|
||||
bool has_some;
|
||||
uint32_t some;
|
||||
@ -392,6 +396,24 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
|
||||
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
|
||||
uint32_t *result);
|
||||
|
||||
/**
|
||||
* Frees memory of the given ImportObject
|
||||
*/
|
||||
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Extends an existing import object with new imports
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/**
|
||||
* Creates a new empty import object.
|
||||
* See also `wasmer_import_object_append`
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_import_object_new(void);
|
||||
|
||||
/**
|
||||
* Calls an instances exported function by `name` with the provided parameters.
|
||||
* Results are set using the provided `results` pointer.
|
||||
@ -417,6 +439,11 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
|
||||
*/
|
||||
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
|
||||
|
||||
/**
|
||||
* Extracts the instance's context and returns it.
|
||||
*/
|
||||
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
|
||||
|
||||
/**
|
||||
* Gets the memory within the context at the index `memory_idx`.
|
||||
* The index is always 0 until multiple memories are supported.
|
||||
@ -526,6 +553,16 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
|
||||
*/
|
||||
void wasmer_module_destroy(wasmer_module_t *module);
|
||||
|
||||
/**
|
||||
* Given:
|
||||
* A prepared `wasmer` import-object
|
||||
* A compiled wasmer module
|
||||
* Instantiates a wasmer instance
|
||||
*/
|
||||
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
|
||||
const wasmer_module_t *module,
|
||||
const wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Creates a new Instance from the given module and imports.
|
||||
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
|
@ -91,11 +91,7 @@ struct wasmer_import_func_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_context_t {
|
||||
struct wasmer_import_object_t {
|
||||
|
||||
};
|
||||
|
||||
@ -117,6 +113,14 @@ struct wasmer_import_t {
|
||||
wasmer_import_export_value value;
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_context_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_limit_option_t {
|
||||
bool has_some;
|
||||
uint32_t some;
|
||||
@ -318,6 +322,18 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
|
||||
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
|
||||
uint32_t *result);
|
||||
|
||||
/// Frees memory of the given ImportObject
|
||||
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
wasmer_import_object_t *wasmer_import_object_new();
|
||||
|
||||
/// 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.
|
||||
@ -337,6 +353,9 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
|
||||
/// passed to all imported function for instance.
|
||||
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
|
||||
|
||||
/// Extracts the instance's context and returns it.
|
||||
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
|
||||
|
||||
/// 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(const wasmer_instance_context_t *ctx,
|
||||
@ -418,6 +437,14 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
|
||||
/// Frees memory for the given Module
|
||||
void wasmer_module_destroy(wasmer_module_t *module);
|
||||
|
||||
/// Given:
|
||||
/// A prepared `wasmer` import-object
|
||||
/// A compiled wasmer module
|
||||
/// Instantiates a wasmer instance
|
||||
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
|
||||
const wasmer_module_t *module,
|
||||
const wasmer_import_object_t *import_object);
|
||||
|
||||
/// 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`
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.5.6"
|
||||
version = "0.6.0"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,46 +8,46 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
nix = "0.14.0"
|
||||
nix = "0.14.1"
|
||||
page_size = "0.4.1"
|
||||
wasmparser = "0.32.1"
|
||||
parking_lot = "0.7.1"
|
||||
lazy_static = "1.2.0"
|
||||
indexmap = "1.0.2"
|
||||
wasmparser = "0.35.1"
|
||||
parking_lot = "0.9.0"
|
||||
lazy_static = "1.3.0"
|
||||
errno = "0.2.4"
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
hex = "0.3.2"
|
||||
smallvec = "0.6.9"
|
||||
smallvec = "0.6.10"
|
||||
bincode = "1.1"
|
||||
colored = "1.8"
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "1.0.2"
|
||||
features = ["serde-1"]
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc).
|
||||
features = ["rc"]
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
[dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
version = "0.11.1"
|
||||
[dependencies.serde-bench]
|
||||
version = "0.0.7"
|
||||
[dependencies.blake2b_simd]
|
||||
version = "0.4.1"
|
||||
version = "0.5.6"
|
||||
[dependencies.digest]
|
||||
version = "0.8.0"
|
||||
[dependencies.hashbrown]
|
||||
version = "0.1"
|
||||
features = ["serde"]
|
||||
version = "0.8.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["memoryapi"] }
|
||||
winapi = { version = "0.3.7", features = ["memoryapi"] }
|
||||
|
||||
[dev-dependencies]
|
||||
field-offset = "0.1.1"
|
||||
|
||||
[build-dependencies]
|
||||
blake2b_simd = "0.4.1"
|
||||
blake2b_simd = "0.5.6"
|
||||
rustc_version = "0.2.3"
|
||||
cc = "1.0"
|
||||
|
||||
@ -55,6 +55,7 @@ cc = "1.0"
|
||||
debug = []
|
||||
trace = ["debug"]
|
||||
# backend flags used in conditional compilation of Backend::variants
|
||||
"backend-cranelift" = []
|
||||
"backend-singlepass" = []
|
||||
"backend-llvm" = []
|
||||
managed = []
|
@ -15,7 +15,7 @@ use crate::{
|
||||
};
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod sys {
|
||||
pub use crate::sys::*;
|
||||
@ -32,6 +32,7 @@ pub enum Backend {
|
||||
impl Backend {
|
||||
pub fn variants() -> &'static [&'static str] {
|
||||
&[
|
||||
#[cfg(feature = "backend-cranelift")]
|
||||
"cranelift",
|
||||
#[cfg(feature = "backend-singlepass")]
|
||||
"singlepass",
|
||||
@ -110,14 +111,20 @@ impl Default for MemoryBoundCheckMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Features {
|
||||
pub simd: bool,
|
||||
}
|
||||
|
||||
/// Configuration data for the compiler
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CompilerConfig {
|
||||
/// Symbol information generated from emscripten; used for more detailed debug messages
|
||||
pub symbol_map: Option<HashMap<u32, String>>,
|
||||
pub memory_bound_check_mode: MemoryBoundCheckMode,
|
||||
pub enforce_stack_check: bool,
|
||||
pub track_state: bool,
|
||||
pub features: Features,
|
||||
}
|
||||
|
||||
pub trait Compiler {
|
||||
|
@ -55,11 +55,19 @@ pub struct LocalBacking {
|
||||
}
|
||||
|
||||
impl LocalBacking {
|
||||
pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
|
||||
pub(crate) fn new(
|
||||
module: &ModuleInner,
|
||||
imports: &ImportBacking,
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> LinkResult<Self> {
|
||||
let mut memories = Self::generate_memories(module);
|
||||
let mut tables = Self::generate_tables(module);
|
||||
let mut globals = Self::generate_globals(module, imports);
|
||||
|
||||
// Ensure all initializers are valid before running finalizers
|
||||
Self::validate_memories(module, imports)?;
|
||||
Self::validate_tables(module, imports, &mut tables)?;
|
||||
|
||||
let vm_memories = Self::finalize_memories(module, imports, &mut memories);
|
||||
let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx);
|
||||
let vm_globals = Self::finalize_globals(&mut globals);
|
||||
@ -67,7 +75,7 @@ impl LocalBacking {
|
||||
let dynamic_sigindices = Self::generate_sigindices(&module.info);
|
||||
let local_functions = Self::generate_local_functions(module);
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
memories,
|
||||
tables,
|
||||
globals,
|
||||
@ -80,7 +88,7 @@ impl LocalBacking {
|
||||
local_functions,
|
||||
|
||||
internals: Internals([0; INTERNALS_SIZE]),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_local_functions(module: &ModuleInner) -> BoxedMap<LocalFuncIndex, *const vm::Func> {
|
||||
@ -117,6 +125,51 @@ impl LocalBacking {
|
||||
memories.into_boxed_map()
|
||||
}
|
||||
|
||||
/// Validate each locally-defined memory in the Module.
|
||||
///
|
||||
/// This involves copying in the data initializers.
|
||||
fn validate_memories(module: &ModuleInner, imports: &ImportBacking) -> LinkResult<()> {
|
||||
// Validate data size fits
|
||||
for init in module.info.data_initializers.iter() {
|
||||
let init_base = match init.base {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
if let Value::I32(x) = imports.globals[import_global_index].get() {
|
||||
x as u32
|
||||
} else {
|
||||
panic!("unsupported global type for initializer")
|
||||
}
|
||||
}
|
||||
} as usize;
|
||||
|
||||
// Validate data size fits
|
||||
match init.memory_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_memory_index) => {
|
||||
let memory_desc = module.info.memories[local_memory_index];
|
||||
let data_top = init_base + init.data.len();
|
||||
if memory_desc.minimum.bytes().0 < data_top || data_top < init_base {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "data segment does not fit".to_string(),
|
||||
}]);
|
||||
}
|
||||
}
|
||||
LocalOrImport::Import(imported_memory_index) => {
|
||||
// Write the initialization data to the memory that
|
||||
// we think the imported memory is.
|
||||
let local_memory = unsafe { &*imports.vm_memories[imported_memory_index] };
|
||||
let data_top = init_base + init.data.len();
|
||||
if local_memory.bound < data_top || data_top < init_base {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "data segment does not fit".to_string(),
|
||||
}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize each locally-defined memory in the Module.
|
||||
///
|
||||
/// This involves copying in the data initializers.
|
||||
@ -126,12 +179,8 @@ impl LocalBacking {
|
||||
memories: &mut SliceMap<LocalMemoryIndex, Memory>,
|
||||
) -> BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory> {
|
||||
// For each init that has some data...
|
||||
for init in module
|
||||
.info
|
||||
.data_initializers
|
||||
.iter()
|
||||
.filter(|init| init.data.len() > 0)
|
||||
{
|
||||
// Initialize data
|
||||
for init in module.info.data_initializers.iter() {
|
||||
let init_base = match init.base {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
|
||||
@ -146,10 +195,6 @@ impl LocalBacking {
|
||||
|
||||
match init.memory_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_memory_index) => {
|
||||
let memory_desc = module.info.memories[local_memory_index];
|
||||
let data_top = init_base + init.data.len();
|
||||
assert!(memory_desc.minimum.bytes().0 >= data_top);
|
||||
|
||||
let mem = &memories[local_memory_index];
|
||||
for (mem_byte, data_byte) in mem.view()[init_base..init_base + init.data.len()]
|
||||
.iter()
|
||||
@ -161,18 +206,16 @@ impl LocalBacking {
|
||||
LocalOrImport::Import(imported_memory_index) => {
|
||||
// Write the initialization data to the memory that
|
||||
// we think the imported memory is.
|
||||
unsafe {
|
||||
let memory_slice = unsafe {
|
||||
let local_memory = &*imports.vm_memories[imported_memory_index];
|
||||
let memory_slice =
|
||||
slice::from_raw_parts_mut(local_memory.base, local_memory.bound);
|
||||
slice::from_raw_parts_mut(local_memory.base, local_memory.bound)
|
||||
};
|
||||
|
||||
let mem_init_view =
|
||||
&mut memory_slice[init_base..init_base + init.data.len()];
|
||||
let mem_init_view = &mut memory_slice[init_base..init_base + init.data.len()];
|
||||
mem_init_view.copy_from_slice(&init.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memories
|
||||
.iter_mut()
|
||||
@ -192,6 +235,50 @@ impl LocalBacking {
|
||||
tables.into_boxed_map()
|
||||
}
|
||||
|
||||
/// This validates all of the locally-defined tables in the Module.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
fn validate_tables(
|
||||
module: &ModuleInner,
|
||||
imports: &ImportBacking,
|
||||
tables: &mut SliceMap<LocalTableIndex, Table>,
|
||||
) -> LinkResult<()> {
|
||||
for init in &module.info.elem_initializers {
|
||||
let init_base = match init.base {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
if let Value::I32(x) = imports.globals[import_global_index].get() {
|
||||
x as u32
|
||||
} else {
|
||||
panic!("unsupported global type for initializer")
|
||||
}
|
||||
}
|
||||
} as usize;
|
||||
|
||||
match init.table_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_table_index) => {
|
||||
let table = &tables[local_table_index];
|
||||
|
||||
if (table.size() as usize) < init_base + init.elements.len() {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "elements segment does not fit".to_string(),
|
||||
}]);
|
||||
}
|
||||
}
|
||||
LocalOrImport::Import(import_table_index) => {
|
||||
let table = &imports.tables[import_table_index];
|
||||
|
||||
if (table.size() as usize) < init_base + init.elements.len() {
|
||||
return Err(vec![LinkError::Generic {
|
||||
message: "elements segment does not fit".to_string(),
|
||||
}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This initializes all of the locally-defined tables in the Module, e.g.
|
||||
/// putting all the table elements (function pointers)
|
||||
/// in the right places.
|
||||
@ -218,13 +305,6 @@ impl LocalBacking {
|
||||
match init.table_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_table_index) => {
|
||||
let table = &tables[local_table_index];
|
||||
|
||||
if (table.size() as usize) < init_base + init.elements.len() {
|
||||
let delta = (init_base + init.elements.len()) - table.size() as usize;
|
||||
// Grow the table if it's too small.
|
||||
table.grow(delta as u32).expect("couldn't grow table");
|
||||
}
|
||||
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
let sig_index = module.info.func_assoc[func_index];
|
||||
@ -258,12 +338,6 @@ impl LocalBacking {
|
||||
LocalOrImport::Import(import_table_index) => {
|
||||
let table = &imports.tables[import_table_index];
|
||||
|
||||
if (table.size() as usize) < init_base + init.elements.len() {
|
||||
let delta = (init_base + init.elements.len()) - table.size() as usize;
|
||||
// Grow the table if it's too small.
|
||||
table.grow(delta as u32).expect("couldn't grow table");
|
||||
}
|
||||
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
let sig_index = module.info.func_assoc[func_index];
|
||||
|
@ -20,6 +20,7 @@ pub enum Error {
|
||||
Unknown(String),
|
||||
InvalidFile(InvalidFileType),
|
||||
InvalidatedCache,
|
||||
UnsupportedBackend(Backend),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
backend::RunnableModule,
|
||||
backend::{Backend, CacheGen, Compiler, CompilerConfig, Token},
|
||||
backend::{Backend, CacheGen, Compiler, CompilerConfig, Features, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::{CompileError, CompileResult},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
@ -18,7 +18,7 @@ use wasmparser::{self, WasmDecoder};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
pub type BreakpointHandler =
|
||||
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
|
||||
Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
|
||||
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -137,8 +137,22 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(bytes: &[u8]) -> CompileResult<()> {
|
||||
let mut parser = wasmparser::ValidatingParser::new(bytes, None);
|
||||
pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingParserConfig {
|
||||
wasmparser::ValidatingParserConfig {
|
||||
operator_config: wasmparser::OperatorValidatorConfig {
|
||||
enable_threads: false,
|
||||
enable_reference_types: false,
|
||||
enable_simd: features.simd,
|
||||
enable_bulk_memory: false,
|
||||
enable_multi_value: false,
|
||||
},
|
||||
mutable_global_imports: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_with_features(bytes: &[u8], features: &Features) -> CompileResult<()> {
|
||||
let mut parser =
|
||||
wasmparser::ValidatingParser::new(bytes, Some(validating_parser_config(features)));
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
@ -166,7 +180,7 @@ impl<
|
||||
_: Token,
|
||||
) -> CompileResult<ModuleInner> {
|
||||
if requires_pre_validation(MCG::backend_id()) {
|
||||
validate(wasm)?;
|
||||
validate_with_features(wasm, &compiler_config.features)?;
|
||||
}
|
||||
|
||||
let mut mcg = MCG::new();
|
||||
@ -218,7 +232,7 @@ impl<'a, 'b> EventSink<'a, 'b> {
|
||||
}
|
||||
|
||||
pub struct MiddlewareChain {
|
||||
chain: Vec<Box<GenericFunctionMiddleware>>,
|
||||
chain: Vec<Box<dyn GenericFunctionMiddleware>>,
|
||||
}
|
||||
|
||||
impl MiddlewareChain {
|
||||
|
@ -80,6 +80,9 @@ pub enum LinkError {
|
||||
expected: GlobalDescriptor,
|
||||
found: GlobalDescriptor,
|
||||
},
|
||||
Generic {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl PartialEq for LinkError {
|
||||
@ -107,6 +110,9 @@ impl std::fmt::Display for LinkError {
|
||||
LinkError::IncorrectTableDescriptor{namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect table descriptor, namespace: {}, name: {}, expected table descriptor: {:?}, found table descriptor: {:?}", namespace, name, expected, found)
|
||||
},
|
||||
LinkError::Generic { message } => {
|
||||
write!(f, "{}", message)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,7 +140,7 @@ impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occured during runtime: {}", msg)
|
||||
write!(f, "WebAssembly trap occurred during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
|
||||
module::ModuleInner, table::Table, types::FuncSig, vm,
|
||||
};
|
||||
use hashbrown::hash_map;
|
||||
use indexmap::map::Iter as IndexMapIter;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -41,7 +41,7 @@ impl FuncPointer {
|
||||
|
||||
pub struct ExportIter<'a> {
|
||||
inner: &'a InstanceInner,
|
||||
iter: hash_map::Iter<'a, String, ExportIndex>,
|
||||
iter: IndexMapIter<'a, String, ExportIndex>,
|
||||
module: &'a ModuleInner,
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
|
||||
struct UnwindInfo {
|
||||
jmpbuf: SetJmpBuffer, // in
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
payload: Option<Box<Any>>, // out
|
||||
payload: Option<Box<dyn Any>>, // out
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
@ -125,7 +125,7 @@ pub unsafe fn clear_wasm_interrupt() {
|
||||
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
|
||||
f: F,
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
) -> Result<R, Box<Any>> {
|
||||
) -> Result<R, Box<dyn Any>> {
|
||||
let unwind = UNWIND.with(|x| x.get());
|
||||
let old = (*unwind).take();
|
||||
*unwind = Some(UnwindInfo {
|
||||
@ -147,7 +147,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn begin_unsafe_unwind(e: Box<Any>) -> ! {
|
||||
pub unsafe fn begin_unsafe_unwind(e: Box<dyn Any>) -> ! {
|
||||
let unwind = UNWIND.with(|x| x.get());
|
||||
let inner = (*unwind)
|
||||
.as_mut()
|
||||
|
@ -46,10 +46,11 @@ impl Global {
|
||||
|
||||
let local_global = vm::LocalGlobal {
|
||||
data: match value {
|
||||
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(),
|
||||
Value::I32(x) => x as u128,
|
||||
Value::I64(x) => x as u128,
|
||||
Value::F32(x) => x.to_bits() as u128,
|
||||
Value::F64(x) => x.to_bits() as u128,
|
||||
Value::V128(x) => x,
|
||||
},
|
||||
};
|
||||
|
||||
@ -75,10 +76,11 @@ impl Global {
|
||||
if self.desc.ty == value.ty() {
|
||||
let local_global = vm::LocalGlobal {
|
||||
data: match value {
|
||||
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(),
|
||||
Value::I32(x) => x as u128,
|
||||
Value::I64(x) => x as u128,
|
||||
Value::F32(x) => x.to_bits() as u128,
|
||||
Value::F64(x) => x.to_bits() as u128,
|
||||
Value::V128(x) => x,
|
||||
},
|
||||
};
|
||||
*self.storage.borrow_mut() = local_global;
|
||||
@ -98,7 +100,8 @@ impl Global {
|
||||
Type::I32 => Value::I32(data as i32),
|
||||
Type::I64 => Value::I64(data as i64),
|
||||
Type::F32 => Value::F32(f32::from_bits(data as u32)),
|
||||
Type::F64 => Value::F64(f64::from_bits(data)),
|
||||
Type::F64 => Value::F64(f64::from_bits(data as u64)),
|
||||
Type::V128 => Value::V128(data),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::export::Export;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
ffi::c_void,
|
||||
@ -46,7 +46,7 @@ impl IsExport for Export {
|
||||
/// ```
|
||||
pub struct ImportObject {
|
||||
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
|
||||
pub(crate) state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
|
||||
pub(crate) state_creator: Option<Rc<dyn Fn() -> (*mut c_void, fn(*mut c_void))>>,
|
||||
pub allow_missing_functions: bool,
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
vm::{self, InternalField},
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{mem, ptr::NonNull, sync::Arc};
|
||||
use std::{mem, pin::Pin, ptr::NonNull, sync::Arc};
|
||||
|
||||
pub(crate) struct InstanceInner {
|
||||
#[allow(dead_code)]
|
||||
@ -41,7 +41,7 @@ impl Drop for InstanceInner {
|
||||
/// [`ImportObject`]: struct.ImportObject.html
|
||||
pub struct Instance {
|
||||
pub module: Arc<ModuleInner>,
|
||||
inner: Box<InstanceInner>,
|
||||
inner: Pin<Box<InstanceInner>>,
|
||||
#[allow(dead_code)]
|
||||
import_object: ImportObject,
|
||||
}
|
||||
@ -51,32 +51,32 @@ impl Instance {
|
||||
// We need the backing and import_backing to create a vm::Ctx, but we need
|
||||
// a vm::Ctx to create a backing and an import_backing. The solution is to create an
|
||||
// uninitialized vm::Ctx and then initialize it in-place.
|
||||
let mut vmctx = unsafe { Box::new(mem::uninitialized()) };
|
||||
let mut vmctx: Box<mem::MaybeUninit<vm::Ctx>> =
|
||||
Box::new(mem::MaybeUninit::<vm::Ctx>::zeroed());
|
||||
|
||||
let import_backing = ImportBacking::new(&module, &imports, &mut *vmctx)?;
|
||||
let backing = LocalBacking::new(&module, &import_backing, &mut *vmctx);
|
||||
let import_backing = ImportBacking::new(&module, &imports, vmctx.as_mut_ptr())?;
|
||||
let backing = LocalBacking::new(&module, &import_backing, vmctx.as_mut_ptr())?;
|
||||
|
||||
// When Pin is stablized, this will use `Box::pinned` instead of `Box::new`.
|
||||
let mut inner = Box::new(InstanceInner {
|
||||
let mut inner = Box::pin(InstanceInner {
|
||||
backing,
|
||||
import_backing,
|
||||
vmctx: Box::leak(vmctx),
|
||||
vmctx: vmctx.as_mut_ptr(),
|
||||
});
|
||||
|
||||
// Initialize the vm::Ctx in-place after the backing
|
||||
// has been boxed.
|
||||
unsafe {
|
||||
*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 backing = &mut *(&mut inner.backing as *mut _);
|
||||
let import_backing = &mut *(&mut inner.import_backing as *mut _);
|
||||
let real_ctx = match imports.call_state_creator() {
|
||||
Some((data, dtor)) => {
|
||||
vm::Ctx::new_with_data(backing, import_backing, &module, data, dtor)
|
||||
}
|
||||
None => vm::Ctx::new(backing, import_backing, &module),
|
||||
};
|
||||
vmctx.as_mut_ptr().write(real_ctx);
|
||||
};
|
||||
Box::leak(vmctx);
|
||||
|
||||
let instance = Instance {
|
||||
module,
|
||||
@ -500,6 +500,23 @@ impl LikeNamespace for Instance {
|
||||
}
|
||||
}
|
||||
|
||||
use std::rc::Rc;
|
||||
impl LikeNamespace for Rc<Instance> {
|
||||
fn get_export(&self, name: &str) -> Option<Export> {
|
||||
let export_index = self.module.info.exports.get(name)?;
|
||||
|
||||
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,
|
||||
@ -519,6 +536,12 @@ fn call_func_with_index(
|
||||
|
||||
let signature = &info.signatures[sig_index];
|
||||
let num_results = signature.returns().len();
|
||||
let num_results = num_results
|
||||
+ signature
|
||||
.returns()
|
||||
.iter()
|
||||
.filter(|&&ty| ty == Type::V128)
|
||||
.count();
|
||||
rets.reserve(num_results);
|
||||
|
||||
if !signature.check_param_value_types(args) {
|
||||
@ -544,15 +567,32 @@ fn call_func_with_index(
|
||||
}
|
||||
};
|
||||
|
||||
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 mut raw_args: SmallVec<[u64; 8]> = SmallVec::new();
|
||||
for v in args {
|
||||
match v {
|
||||
Value::I32(i) => {
|
||||
raw_args.push(*i as u64);
|
||||
}
|
||||
Value::I64(i) => {
|
||||
raw_args.push(*i as u64);
|
||||
}
|
||||
Value::F32(f) => {
|
||||
raw_args.push(f.to_bits() as u64);
|
||||
}
|
||||
Value::F64(f) => {
|
||||
raw_args.push(f.to_bits() as u64);
|
||||
}
|
||||
Value::V128(v) => {
|
||||
let bytes = v.to_le_bytes();
|
||||
let mut lo = [0u8; 8];
|
||||
lo.clone_from_slice(&bytes[0..8]);
|
||||
raw_args.push(u64::from_le_bytes(lo));
|
||||
let mut hi = [0u8; 8];
|
||||
hi.clone_from_slice(&bytes[8..16]);
|
||||
raw_args.push(u64::from_le_bytes(hi));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Wasm {
|
||||
trampoline,
|
||||
@ -595,6 +635,7 @@ fn call_func_with_index(
|
||||
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)),
|
||||
Type::V128 => unreachable!("V128 does not map to any single value"),
|
||||
};
|
||||
|
||||
match signature.returns() {
|
||||
@ -602,6 +643,21 @@ fn call_func_with_index(
|
||||
run_wasm(0 as *mut u64)?;
|
||||
Ok(())
|
||||
}
|
||||
&[Type::V128] => {
|
||||
let mut result = [0u64; 2];
|
||||
|
||||
run_wasm(result.as_mut_ptr())?;
|
||||
|
||||
let mut bytes = [0u8; 16];
|
||||
let lo = result[0].to_le_bytes();
|
||||
let hi = result[1].to_le_bytes();
|
||||
for i in 0..8 {
|
||||
bytes[i] = lo[i];
|
||||
bytes[i + 8] = hi[i];
|
||||
}
|
||||
rets.push(Value::V128(u128::from_le_bytes(bytes)));
|
||||
Ok(())
|
||||
}
|
||||
&[ty] => {
|
||||
let mut result = 0u64;
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
@ -120,8 +126,26 @@ pub fn validate(wasm: &[u8]) -> bool {
|
||||
|
||||
/// The same as `validate` but with an Error message on failure
|
||||
pub fn validate_and_report_errors(wasm: &[u8]) -> ::std::result::Result<(), String> {
|
||||
validate_and_report_errors_with_features(wasm, Default::default())
|
||||
}
|
||||
|
||||
/// The same as `validate_and_report_errors` but with a Features.
|
||||
pub fn validate_and_report_errors_with_features(
|
||||
wasm: &[u8],
|
||||
features: backend::Features,
|
||||
) -> ::std::result::Result<(), String> {
|
||||
use wasmparser::WasmDecoder;
|
||||
let mut parser = wasmparser::ValidatingParser::new(wasm, None);
|
||||
let config = wasmparser::ValidatingParserConfig {
|
||||
operator_config: wasmparser::OperatorValidatorConfig {
|
||||
enable_simd: features.simd,
|
||||
enable_bulk_memory: false,
|
||||
enable_multi_value: false,
|
||||
enable_reference_types: false,
|
||||
enable_threads: false,
|
||||
},
|
||||
mutable_global_imports: true,
|
||||
};
|
||||
let mut parser = wasmparser::ValidatingParser::new(wasm, Some(config));
|
||||
loop {
|
||||
let state = parser.read();
|
||||
match *state {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{backend::RunnableModule, module::ModuleInfo, types::Value, vm::Ctx};
|
||||
use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx};
|
||||
#[cfg(unix)]
|
||||
use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
|
||||
use std::{
|
||||
@ -20,7 +20,7 @@ pub trait Loader {
|
||||
|
||||
pub trait Instance {
|
||||
type Error: Debug;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error>;
|
||||
fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
@ -61,35 +61,49 @@ pub struct LocalInstance {
|
||||
|
||||
impl Instance for LocalInstance {
|
||||
type Error = String;
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error> {
|
||||
fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error> {
|
||||
let mut args_u64: Vec<u64> = Vec::new();
|
||||
for arg in args {
|
||||
if arg.ty() == Type::V128 {
|
||||
let bytes = arg.to_u128().to_le_bytes();
|
||||
let mut lo = [0u8; 8];
|
||||
lo.clone_from_slice(&bytes[0..8]);
|
||||
args_u64.push(u64::from_le_bytes(lo));
|
||||
let mut hi = [0u8; 8];
|
||||
hi.clone_from_slice(&bytes[8..16]);
|
||||
args_u64.push(u64::from_le_bytes(hi));
|
||||
} else {
|
||||
args_u64.push(arg.to_u128() as u64);
|
||||
}
|
||||
}
|
||||
let offset = self.offsets[id];
|
||||
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
|
||||
use std::mem::transmute;
|
||||
Ok(unsafe {
|
||||
match args.len() {
|
||||
0 => (transmute::<_, extern "C" fn() -> u64>(addr))(),
|
||||
1 => (transmute::<_, extern "C" fn(u64) -> u64>(addr))(args[0].to_u64()),
|
||||
2 => (transmute::<_, extern "C" fn(u64, u64) -> u64>(addr))(
|
||||
args[0].to_u64(),
|
||||
args[1].to_u64(),
|
||||
match args_u64.len() {
|
||||
0 => (transmute::<_, extern "C" fn() -> u128>(addr))(),
|
||||
1 => (transmute::<_, extern "C" fn(u64) -> u128>(addr))(args_u64[0]),
|
||||
2 => (transmute::<_, extern "C" fn(u64, u64) -> u128>(addr))(
|
||||
args_u64[0],
|
||||
args_u64[1],
|
||||
),
|
||||
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(addr))(
|
||||
args[0].to_u64(),
|
||||
args[1].to_u64(),
|
||||
args[2].to_u64(),
|
||||
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u128>(addr))(
|
||||
args_u64[0],
|
||||
args_u64[1],
|
||||
args_u64[2],
|
||||
),
|
||||
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(addr))(
|
||||
args[0].to_u64(),
|
||||
args[1].to_u64(),
|
||||
args[2].to_u64(),
|
||||
args[3].to_u64(),
|
||||
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u128>(addr))(
|
||||
args_u64[0],
|
||||
args_u64[1],
|
||||
args_u64[2],
|
||||
args_u64[3],
|
||||
),
|
||||
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(addr))(
|
||||
args[0].to_u64(),
|
||||
args[1].to_u64(),
|
||||
args[2].to_u64(),
|
||||
args[3].to_u64(),
|
||||
args[4].to_u64(),
|
||||
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u128>(addr))(
|
||||
args_u64[0],
|
||||
args_u64[1],
|
||||
args_u64[2],
|
||||
args_u64[3],
|
||||
args_u64[4],
|
||||
),
|
||||
_ => return Err("too many arguments".into()),
|
||||
}
|
||||
|
@ -73,6 +73,12 @@ impl Memory {
|
||||
}
|
||||
}
|
||||
|
||||
if desc.shared && desc.maximum.is_none() {
|
||||
return Err(CreationError::InvalidDescriptor(
|
||||
"Max number of pages is required for shared memory".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let variant = if !desc.shared {
|
||||
MemoryVariant::Unshared(UnsharedMemory::new(desc)?)
|
||||
} else {
|
||||
@ -325,4 +331,17 @@ mod memory_tests {
|
||||
assert_eq!(unshared_memory.size(), Pages(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_descriptor_returns_error() {
|
||||
let result = Memory::new(MemoryDescriptor {
|
||||
minimum: Pages(10),
|
||||
maximum: None,
|
||||
shared: true,
|
||||
});
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Max number of pages is required for shared memory"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -126,6 +126,15 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
||||
[index as usize..slice_full_len];
|
||||
Some(cell_ptrs)
|
||||
}
|
||||
|
||||
pub fn get_utf8_string<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> {
|
||||
if self.offset as usize + str_len as usize > memory.size().bytes().0 {
|
||||
return None;
|
||||
}
|
||||
let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
|
||||
let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, str_len as usize) };
|
||||
std::str::from_utf8(slice).ok()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
|
||||
|
@ -14,8 +14,8 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::backend::CacheGen;
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This is used to instantiate a new WebAssembly module.
|
||||
@ -40,7 +40,7 @@ pub struct ModuleInfo {
|
||||
pub imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
|
||||
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
|
||||
|
||||
pub exports: HashMap<String, ExportIndex>,
|
||||
pub exports: IndexMap<String, ExportIndex>,
|
||||
|
||||
pub data_initializers: Vec<DataInitializer>,
|
||||
pub elem_initializers: Vec<TableInitializer>,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user