Merge branch 'master' into feature/dynasm-backend

This commit is contained in:
Brandon Fish
2019-03-12 20:58:22 -05:00
358 changed files with 13992 additions and 1805 deletions

View File

@ -1,11 +1,12 @@
branches:
except:
- master
version: "{build} ~ {branch}" version: "{build} ~ {branch}"
os: Visual Studio 2017 os: Visual Studio 2017
branches:
only:
- staging
- trying
environment: environment:
matrix: matrix:
- CHANNEL: stable - CHANNEL: stable
@ -13,20 +14,60 @@ environment:
ABI: msvc ABI: msvc
TARGET: x86_64-pc-windows-msvc TARGET: x86_64-pc-windows-msvc
cache:
- 'C:\Users\appveyor\.cargo'
- target
install: install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe # # Install LLVM
- rustup-init.exe -yv --default-host %target% # - mkdir C:\projects\deps
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin # - cd C:\projects\deps
# - appveyor DownloadFile http://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe -FileName llvm.exe
# - 7z x llvm.exe -oC:\projects\deps\llvm
# # - set "PATH=%PATH%;C:\projects\deps\llvm\bin"
# - set "LLD_LINK=C:\projects\deps\llvm\bin\lld-link.exe"
# - set "LLVM_SYS_70_PREFIX=C:\projects\deps\llvm"
# - cd "%APPVEYOR_BUILD_FOLDER%"
# Install Rust
# uncomment these lines if the cache is cleared, or if we must re-install rust for some reason
# - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
# - rustup-init.exe -yv --default-host %target%
- set PATH=%PATH%;C:\\Libraries\\llvm-5.0.0\\bin;%USERPROFILE%\.cargo\bin
- rustup default stable-%target%
- rustup update
- rustc -vV - rustc -vV
- cargo -vV - cargo -vV
artifacts: # Install InnoSetup
- path: target\debug\wasmer.exe - appveyor-retry appveyor DownloadFile https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-08-22-is.exe
name: wasmer.exe - 2017-08-22-is.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-
- set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH%
# uncomment to RDP to appveyor
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script: build_script:
- cargo build --verbose - cargo build --release --verbose
test_script: test_script:
- set RUST_BACKTRACE=1 - cargo test --manifest-path lib/spectests/Cargo.toml --features clif
- cargo test --verbose
before_deploy:
- cd ./src/installer
- iscc wasmer.iss
- copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- appveyor PushArtifact ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- cd ..\..\
matrix:
fast_finish: true
deploy:
description: 'WasmerInstaller'
artifact: /.*\.exe/
auth_token:
secure: CaKtncy7S1PWxzDUQ0p2264pe3HwxzDn5VIyRizDaa72/SVfskNcoMjwwRh0ut22
provider: GitHub
on:
branch: master
appveyor_repo_tag: true

View File

@ -13,6 +13,8 @@ jobs:
name: Install dependencies name: Install dependencies
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run: - run:
name: Install lint deps name: Install lint deps
command: | command: |
@ -20,7 +22,9 @@ jobs:
rustup component add clippy rustup component add clippy
- run: - run:
name: Execute lints name: Execute lints
command: make lint command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make lint
- save_cache: - save_cache:
paths: paths:
- /usr/local/cargo/registry - /usr/local/cargo/registry
@ -41,8 +45,23 @@ jobs:
name: Install dependencies name: Install dependencies
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
- run: make test curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run: make integration-tests tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run:
name: Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Emscripten Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten
- run:
name: Integration Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make integration-tests
- save_cache: - save_cache:
paths: paths:
- /usr/local/cargo/registry - /usr/local/cargo/registry
@ -66,6 +85,9 @@ jobs:
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz 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" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# Installing LLVM outside of brew
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
- run: - run:
name: Install Rust name: Install Rust
command: | command: |
@ -73,19 +95,31 @@ jobs:
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
cargo --version cargo --version
- run: - run:
name: Execute tests name: Tests
command: | command: |
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac # We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000 ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test make test
- run: - run:
name: Execute integration tests name: Emscripten Tests
command: | command: |
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten
- run:
name: Integration Tests
command: |
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
make integration-tests make integration-tests
- save_cache: - save_cache:
paths: paths:
@ -110,12 +144,22 @@ jobs:
name: Install dependencies name: Install dependencies
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run: - run:
name: Execute tests name: Tests
command: make test
- run:
name: Make release build
command: | command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Emscripten Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten
- run:
name: Release Build
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make release make release
mkdir -p artifacts mkdir -p artifacts
VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
@ -153,6 +197,9 @@ jobs:
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz 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" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# Installing LLVM outside of brew
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
- run: - run:
name: Install Rust name: Install Rust
command: | command: |
@ -160,19 +207,31 @@ jobs:
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
cargo --version cargo --version
- run: - run:
name: Execute tests name: Tests
command: | command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac # We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000 ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test make test
- run: - run:
name: Make release build name: Emscripten Tests
command: | command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten
- run:
name: Release Build
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
make release make release
mkdir -p artifacts mkdir -p artifacts
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
@ -205,8 +264,12 @@ jobs:
name: Install dependencies name: Install dependencies
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run: rustup default nightly - run: rustup default nightly
- run: make test - run: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- save_cache: - save_cache:
paths: paths:
- /usr/local/cargo/registry - /usr/local/cargo/registry
@ -249,23 +312,31 @@ workflows:
- test: - test:
filters: filters:
branches: branches:
ignore: master only:
- trying
- staging
- test-macos: - test-macos:
filters: filters:
branches: branches:
ignore: master only:
- trying
- staging
- test-and-build: - test-and-build:
filters: filters:
branches: branches:
only: master only:
- master
- test-and-build-macos: - test-and-build-macos:
filters: filters:
branches: branches:
only: master only:
- master
- test-rust-nightly: - test-rust-nightly:
filters: filters:
branches: branches:
only: master only:
- trying
- staging
- publish-github-release: - publish-github-release:
requires: requires:
- lint - lint

43
.github/ISSUE_TEMPLATE/---bug-report.md vendored Normal file
View File

@ -0,0 +1,43 @@
---
name: "\U0001F41E Bug report"
about: Create a report to help us improve
title: ''
labels: "\U0001F41E bug"
assignees: ''
---
Thanks for the bug report!
### Describe the bug
A clear and concise description of what the bug is.
Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment.
```bash
echo "`wasmer -V` | `rustc -V` | `uname -m`"
```
### Steps to reproduce
1. Go to '…'
2. Compile with '…'
3. Run '…'
4. See error
If applicable, add a link to a test case (as a zip file or link to a repository we can clone).
### Expected behavior
A clear and concise description of what you expected to happen.
### Actual behavior
A clear and concise description of what actually happened.
If applicable, add screenshots to help explain your problem.
### Additional context
Add any other context about the problem here.

View File

@ -0,0 +1,26 @@
---
name: "\U0001F389 Feature request"
about: Suggest an idea for this project
title: ''
labels: "\U0001F389 enhancement"
assignees: ''
---
Thanks for proposing a new feature!
### Motivation
A clear and concise description of what the motivation for the new feature is, and what problem it is solving.
### Proposed solution
A clear and concise description of the feature you would like to add, and how it solves the motivating problem.
### Alternatives
A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better.
### Additional context
Add any other context or screenshots about the feature request here.

16
.github/ISSUE_TEMPLATE/--question.md vendored Normal file
View File

@ -0,0 +1,16 @@
---
name: "❓ Question"
about: Ask a question about this project
title: ''
labels: "❓ question"
assignees: ''
---
### Summary
A clear and concise summary of your question.
### Additional details
Provide any additional details here.

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/artifacts /artifacts
.DS_Store .DS_Store
.idea .idea
**/.vscode

View File

@ -2,19 +2,19 @@
Wasmer uses the following components: Wasmer uses the following components:
- [Cranelift](https://github.com/cranestation/cranelift): for compiling WASM function binaries into Machine IR - [Cranelift](https://github.com/cranestation/cranelift): for compiling Wasm binaries to machine code
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and also to run WebAssembly spectests - [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and running WebAssembly spec tests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly Modules - [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly modules
## How Wasmer works? ## How Wasmer works
The first time you run `wasmer run myfile.wasm`, wasmer will: The first time you run `wasmer run myfile.wasm`, Wasmer will:
- Check if is a `.wast` file. If so, transform it to `.wasm` - Check if is a `.wast` file, and if so, transform it to `.wasm`
- Check that the provided binary is a valid WebAssembly one. That means, that its binary format starts with `\0asm`. - Check that the provided binary is a valid WebAssembly one, i.e. its binary format starts with `\0asm`.
- If it looks like a WebAssembly file, try to parse it with `wasmparser` and generate a `Module` from it - Parse it with `wasmparser` and generate a `Module` from it
- Once a `Module` is generated, an `Instance` is created with the proper `import_object` (that means, if is detected as an emscripten file, it will add the emscripten expected imports) - Generate an `Instance` with the proper `import_object` (that means, if is detected to be an Emscripten file, it will add the Emscripten expected imports)
- Try to call the WebAssembly start function, or if unexistent try to search for the one that is exported as `main`. - Try to call the WebAssembly `start` function, or if it does not exist, try to search for the function that is exported as `main`
Find a more detailed explanation of the process below: Find a more detailed explanation of the process below:
@ -22,7 +22,7 @@ Find a more detailed explanation of the process below:
As the WebAssembly file is being parsed, it will read the sections in the WebAssembly file (memory, table, function, global and element definitions) using the `Module` (or `ModuleEnvironment`) as the structure to hold this information. As the WebAssembly file is being parsed, it will read the sections in the WebAssembly file (memory, table, function, global and element definitions) using the `Module` (or `ModuleEnvironment`) as the structure to hold this information.
However, the real IR initialization happens while a function body is being parsed/created. That means, when the parser reads the section `(func ...)`. However, the real IR initialization happens while a function body is being parsed/created, i.e. when the parser reads the section `(func ...)`.
While the function body is being parsed the corresponding `FuncEnvironment` methods will be called. While the function body is being parsed the corresponding `FuncEnvironment` methods will be called.
So for example, if the function is using a table, the `make_table` method within that `FuncEnvironment` will be called. So for example, if the function is using a table, the `make_table` method within that `FuncEnvironment` will be called.
@ -41,15 +41,14 @@ Once we have the compiled values, we will push them to memory and mark them as e
#### Relocations #### Relocations
Sometimes the functions that we generated will need to call other functions. Sometimes the functions that we generate will need to call other functions, but the generated code has no idea how to link these functions together.
However the generated code have no idea how to link this functions together.
For example, if a function `A` is calling function `B` (that means is having a `(call b)` on it's body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory). For example, if a function `A` is calling function `B` (that means is having a `(call b)` on its body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory).
For that reason, we will start collecting all the calls that function `A` will need to do under the hood, and save it's offsets. For that reason, we will start collecting all the calls that function `A` will need to do under the hood, and save it's offsets.
We do that, so we can patch the function calls after compilation, to point to the correct memory address. We do that, so we can patch the function calls after compilation, to point to the correct memory address.
Note: Sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values. Note: sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values.
#### Traps #### Traps
@ -66,5 +65,5 @@ Once that's finished, we will have a `Instance` function that will be ready to e
## Emscripten ## Emscripten
The Wasmer Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs. Wasmer's Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the emscripten functions, while instantiating the WebAssembly Instance. We provide this integration by filling the `import_object` with the Emscripten functions, while instantiating the WebAssembly Instance.

View File

@ -2,7 +2,7 @@
Wasmer is a community effort. Wasmer is a community effort.
In order to build the best WebAssembly runtime it's our duty to see how other runtimes are approaching the same space In order to build the best WebAssembly runtime it's our duty to see how other runtimes are approaching the same space
and get inspired from them on the things that they got right, so wasmer and its community can benefit from a solid and get inspired from them on the things that they got right, so Wasmer and its community can benefit from a solid
foundation. foundation.
These are the different project that we used as inspiration: These are the different project that we used as inspiration:
@ -10,9 +10,9 @@ These are the different project that we used as inspiration:
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime - [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework - [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework
- [greenwasm](https://github.com/Kimundi/greenwasm): for their [spectests framework](https://github.com/Kimundi/greenwasm/tree/master/greenwasm-spectest) - [greenwasm](https://github.com/Kimundi/greenwasm): for their [spectests framework](https://github.com/Kimundi/greenwasm/tree/master/greenwasm-spectest)
- [wasmtime](/wasmtime): on their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs). - [wasmtime](https://github.com/CraneStation/wasmtime): for their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs)
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys. - [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility. - [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility
We would love to hear from you if you think we can take inspiration from other projects that we haven't covered here. We would love to hear from you if you think we can take inspiration from other projects that we haven't covered here.
😊 😊
@ -21,7 +21,7 @@ We would love to hear from you if you think we can take inspiration from other p
### Nebulet ### Nebulet
``` ```text
MIT License MIT License
Copyright (c) 2018 Copyright (c) 2018
@ -47,7 +47,7 @@ SOFTWARE.
### WAVM ### WAVM
``` ```text
Copyright (c) 2018, Andrew Scheidecker Copyright (c) 2018, Andrew Scheidecker
All rights reserved. All rights reserved.
@ -69,7 +69,7 @@ The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/L
### Greenwasm ### Greenwasm
``` ```text
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
@ -275,7 +275,7 @@ limitations under the License.
### Wasmtime ### Wasmtime
``` ```text
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
@ -497,7 +497,7 @@ Software.
``` ```
### Emscripten ### Emscripten
``` ```text
Emscripten is available under 2 licenses, the MIT license and the Emscripten is available under 2 licenses, the MIT license and the
University of Illinois/NCSA Open Source License. University of Illinois/NCSA Open Source License.

1011
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer" name = "wasmer"
version = "0.1.4" version = "0.2.1"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
repository = "https://github.com/wasmerio/wasmer" repository = "https://github.com/wasmerio/wasmer"
@ -26,8 +26,11 @@ wasmer-runtime = { path = "lib/runtime" }
wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-runtime-core = { path = "lib/runtime-core" }
wasmer-emscripten = { path = "lib/emscripten" } wasmer-emscripten = { path = "lib/emscripten" }
[target.'cfg(not(windows))'.dependencies]
wasmer-llvm-backend = { path = "lib/llvm-backend" }
[workspace] [workspace]
members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests"] members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"]
[build-dependencies] [build-dependencies]
wabt = "0.7.2" wabt = "0.7.2"
@ -36,6 +39,6 @@ glob = "0.2.11"
[features] [features]
default = ["fast-tests"] default = ["fast-tests"]
debug = [] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster # This feature will allow cargo test to run much faster
fast-tests = [] fast-tests = []

View File

@ -1,6 +1,4 @@
The MIT License (MIT) Copyright (c) 2019 Syrus Akbary
Copyright (c) 2018-Present Syrus Akbary
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -16,7 +16,7 @@ emtests:
# rm -rf artifacts # rm -rf artifacts
build: build:
cargo build cargo build --features debug
install: install:
cargo install --path . cargo install --path .
@ -34,15 +34,25 @@ precommit: lint test
test: test:
# We use one thread so the emscripten stdouts doesn't collide # We use one thread so the emscripten stdouts doesn't collide
cargo test --all -- --test-threads=1 $(runargs) cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests -- $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
# cargo test -p wasmer-spectests -- --test-threads=1 $(runargs) cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture
test-emscripten:
cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)
release: release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows # If you are in OS-X, you will need mingw-w64 for cross compiling to windows
# brew install mingw-w64 # brew install mingw-w64
cargo build --release cargo build --release
debug-release:
cargo build --release --features debug
debug-release: debug-release:
cargo build --release --features "debug" cargo build --release --features "debug"

View File

@ -1,16 +1,24 @@
<p align="center"><a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"><img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"></a></p> <p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a>
</p>
<p align="center"> <p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"><img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"></a> <a href="https://circleci.com/gh/wasmerio/wasmer/">
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"><img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"></a> <img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License">
</a>
<a href="https://spectrum.chat/wasmer"> <a href="https://spectrum.chat/wasmer">
<img alt="Join the Wasmer Community" src="https://withspectrum.github.io/badge/badge.svg" /> <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a> </a>
</p> </p>
## Introduction ## Introduction
[Wasmer](https://wasmer.io/) is a Standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go. [Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go.
Install Wasmer with: Install Wasmer with:
@ -18,20 +26,20 @@ Install Wasmer with:
curl https://get.wasmer.io -sSfL | sh curl https://get.wasmer.io -sSfL | sh
``` ```
_**NEW ✨**: Now you can also embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how to do it!_ _**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_
### Usage ### Usage
`wasmer` can execute both the standard binary format (`.wasm`) and the text Wasmer can execute both the standard binary format (`.wasm`) and the text
format defined by the WebAssembly reference interpreter (`.wat`). format defined by the WebAssembly reference interpreter (`.wat`).
Once installed, you will be able to run any WebAssembly files (_including Nginx, and Lua!_): Once installed, you will be able to run any WebAssembly files (_including nginx and Lua!_):
```sh ```sh
# Run Lua # Run Lua
wasmer run examples/lua.wasm wasmer run examples/lua.wasm
# Run Nginx # Run nginx
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
``` ```
@ -39,18 +47,18 @@ wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
Wasmer is structured into different directories: Wasmer is structured into different directories:
- [`src`](./src): code related to the wasmer excutable binary itself - [`src`](./src): code related to the Wasmer executable itself
- [`lib`](./lib): modularized libraries that Wasmer uses under the hood - [`lib`](./lib): modularized libraries that Wasmer uses under the hood
- [`examples`](./examples): some useful examples to getting started with wasmer - [`examples`](./examples): some useful examples to getting started with Wasmer
## Dependencies ## Dependencies
Building wasmer requires [rustup](https://rustup.rs/). Building Wasmer requires [rustup](https://rustup.rs/).
To install on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/) To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
then follow the onscreen instructions. then follow the onscreen instructions.
To install on other systems, run: To build on other systems, run:
```sh ```sh
curl https://sh.rustup.rs -sSf | sh curl https://sh.rustup.rs -sSf | sh
@ -66,13 +74,13 @@ Please select your operating system:
#### macOS #### macOS
If you have [homebrew](https://brew.sh/) installed: If you have [Homebrew](https://brew.sh/) installed:
```sh ```sh
brew install cmake brew install cmake
``` ```
Or, in case you have [ports](https://www.macports.org/install.php): Or, in case you have [MacPorts](https://www.macports.org/install.php):
```sh ```sh
sudo port install cmake sudo port install cmake
@ -86,15 +94,23 @@ sudo apt install cmake
#### Windows (MSVC) #### Windows (MSVC)
Right now Windows support is _highly experimental_. Windows support is _highly experimental_. Only simple Wasm programs may be run, and no syscalls are allowed. This means
We are working on this so Wasmer can soon be released for Windows. nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmerio/wasmer/issues/176) regarding Emscripten syscall polyfills for Windows.
1. Install Python for Windows (https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine. 1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15)
You should change the installation to install the "Add python.exe to Path" feature.
2. Install Git for Windows (https://git-scm.com/download/win). DO allow it to add git.exe to the PATH (default 2. Install [Rust for Windows](https://win.rustup.rs)
3. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine.
Make sure to enable "Add python.exe to Path" during installation.
4. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default
settings for the installer are fine). settings for the installer are fine).
5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH.
6. Install [LLVM 7.0](https://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe)
## Building ## Building
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager. Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
@ -111,7 +127,7 @@ cargo install --path .
## Testing ## Testing
Thanks to [spectests](https://github.com/wasmerio/wasmer/tree/master/lib/runtime-core/spectests) we can assure 100% compatibility with the WebAssembly spec test suite. Thanks to [spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests) we can ensure 100% compatibility with the WebAssembly spec test suite.
Tests can be run with: Tests can be run with:
@ -119,7 +135,7 @@ Tests can be run with:
make test make test
``` ```
If you need to re-generate the Rust tests from the spectests If you need to regenerate the Rust tests from the spec tests
you can run: you can run:
```sh ```sh
@ -132,24 +148,32 @@ You can also run integration tests with:
make integration-tests make integration-tests
``` ```
## Benchmarking
Benchmarks can be run with:
```sh
cargo bench --all
```
## Roadmap ## Roadmap
Wasmer is an open project guided by strong principles, aiming to be modular, flexible and fast. It is open to the community to help set its direction. Wasmer is an open project guided by strong principles, aiming to be modular, flexible and fast. It is open to the community to help set its direction.
Below are some of the goals (written with order) of this project: Below are some of the goals of this project (in order of priority):
- [x] It should be 100% compatible with the [WebAssembly Spectest](https://github.com/wasmerio/wasmer/tree/master/spectests) - [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] It should be fast _(partially achieved)_
- [ ] Support Emscripten calls _(in the works)_ - [ ] Support Emscripten calls _(in the works)_
- [ ] Support Rust ABI calls - [ ] Support Rust ABI calls
- [ ] Support GO ABI calls - [ ] Support Go ABI calls
## Architecture ## Architecture
If you would like to know how Wasmer works under the hood, please visit our [ARCHITECTURE](https://github.com/wasmerio/wasmer/blob/master/ARCHITECTURE.md) document. If you would like to know how Wasmer works under the hood, please see [ARCHITECTURE.md](./ARCHITECTURE.md).
## License ## License
MIT/Apache-2.0 Wasmer is primarily distributed under the terms of the [MIT license](http://opensource.org/licenses/MIT) ([LICENSE](./LICENSE)).
<small>[Attributions](./ATTRIBUTIONS.md)</small>. [ATTRIBUTIONS](./ATTRIBUTIONS.md)

9
bors.toml Normal file
View File

@ -0,0 +1,9 @@
status = [
"ci/circleci: lint",
"ci/circleci: test",
"ci/circleci: test-macos",
"continuous-integration/appveyor/branch"
]
required_approvals = 1
timeout_sec = 900
delete_merged_branches = true

26
examples/nginx/LICENSE Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2002-2019 Igor Sysoev
* Copyright (C) 2011-2019 Nginx, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

View File

@ -130,10 +130,10 @@ wasmer_link() {
printf "$cyan> Adding to bash profile...$reset\n" printf "$cyan> Adding to bash profile...$reset\n"
WASMER_PROFILE="$(wasmer_detect_profile)" WASMER_PROFILE="$(wasmer_detect_profile)"
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n"
# We create the wasmer.sh file # We create the wasmer.sh file
echo "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh"
if [ -z "${WASMER_PROFILE-}" ] ; then if [ -z "${WASMER_PROFILE-}" ] ; then
printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n" printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n"

View File

@ -1,8 +1,8 @@
# `lua` integration test # `lua` integration test
This starts wasmer with the lua wasm file. The test asserts on This starts Wasmer with the Lua Wasm file. The test makes assertions on
the output of wasmer. Run test with: the output of Wasmer. Run test with:
``` ```
> ./integration_tests/lua/test.sh > ./integration_tests/lua/test.sh

View File

@ -1,7 +1,6 @@
#! /bin/bash #! /bin/bash
nohup ./target/release/wasmer run examples/lua.wasm & nohup ./target/release/wasmer run examples/lua.wasm --disable-cache -- -v
sleep 3s
if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out
then then

View File

@ -1,7 +1,7 @@
# `nginx` integration test # `nginx` integration test
This starts wasmer with the nginx wasm file and serves an html This starts Wasmer with the nginx Wasm file and serves an HTML
file with some simple text to assert on. The test script does file with some simple text to assert on. The test script does
the assertion. the assertion.

View File

@ -1,22 +1,14 @@
#! /bin/bash #! /bin/bash
nohup ./target/release/wasmer run examples/nginx/nginx.wasm -- -p integration_tests/nginx/ -c nginx.conf & nohup ./target/release/wasmer run examples/nginx/nginx.wasm --disable-cache -- -v
sleep 3s
curl localhost:8080 > ./nginx.out if grep "nginx version: nginx/1.15.3" ./nohup.out
if grep "wasmer" ./nginx.out
then then
echo "nginx integration test succeeded" echo "nginx integration test succeeded"
rm ./nohup.out rm ./nohup.out
rm ./nginx.out
rm -rf ./integration_tests/nginx/*_temp
exit 0 exit 0
else else
echo "nginx integration test failed" echo "nginx integration test failed"
rm ./nohup.out rm ./nohup.out
rm ./nginx.out
rm -rf ./integration_tests/nginx/*_temp
exit -1 exit -1
fi fi

View File

@ -2,35 +2,36 @@
Wasmer is modularized into different libraries, separated into three main sections: Wasmer is modularized into different libraries, separated into three main sections:
- [Runtime](#Runtime) - [Runtime](#runtime)
- [Integrations](#Integrations) - [Integrations](#integrations)
- [Backends](#Backends) - [Backends](#backends)
## Runtime ## Runtime
The core of Wasmer is the runtime, which provides the necessary The core of Wasmer is the runtime, which provides the necessary
abstractions to create a good user-experience when embedding. abstractions to create a good user experience when embedding.
The runtime is divided into two main libraries: The runtime is divided into two main libraries:
- [runtime-core](./runtime-core/): The main implementation of the runtime. - [runtime-core](./runtime-core/): The main implementation of the runtime.
- [runtime](./runtime/): Easy-to-use api on top of runtime-core. - [runtime](./runtime/): Easy-to-use API on top of `runtime-core`.
## Integrations ## Integrations
The intergration run on-top of the Wasmer runtime and allow us to run WebAssembly files compiled for different environments. The integration builds on the Wasmer runtime and allow us to run WebAssembly files compiled for different environments.
Wasmer intends to support different integrations: Wasmer intends to support different integrations:
- [emscripten](./emscripten): run emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [Nginx](../examples/nginx/nginx.wasm). - [emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
- Go ABI: _we will work on this soon! Want to give us a hand? ✋_ - Go ABI: _we will work on this soon! Want to give us a hand? ✋_
- Blazor: _researching period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_ - Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
## Backends ## Backends
The Wasmer [runtime](./runtime) is designed to support multiple compiler backends, allowing the user The Wasmer [runtime](./runtime) is designed to support multiple compiler backends, allowing the user
to tune the codegen properties (compile speed, performance, etc) to fit your usecase best. to tune the codegen properties (compile speed, performance, etc) to best fit their use case.
Currently, we support a Cranelift compiler backend: Currently, we support multiple backends for compiling WebAssembly to machine code:
- [clif-backend](./clif-backend/): The integration of Wasmer with Cranelift - [clif-backend](./clif-backend/): Cranelift backend
- [llvm-backend](./llvm-backend/): LLVM backend

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-clif-backend" name = "wasmer-clif-backend"
version = "0.1.2" version = "0.2.0"
description = "Wasmer runtime Cranelift compiler backend" description = "Wasmer runtime Cranelift compiler backend"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" }
cranelift-native = "0.26.0" cranelift-native = "0.26.0"
cranelift-codegen = "0.26.0" cranelift-codegen = "0.26.0"
cranelift-entity = "0.26.0" cranelift-entity = "0.26.0"
@ -18,25 +18,22 @@ target-lexicon = "0.2.0"
wasmparser = "0.23.0" wasmparser = "0.23.0"
byteorder = "1" byteorder = "1"
nix = "0.13.0" nix = "0.13.0"
libc = "0.2.48" libc = "0.2.49"
rayon = "1.0"
# Dependencies for caching. # Dependencies for caching.
[dependencies.serde] [dependencies.serde]
version = "1.0" version = "1.0"
optional = true
[dependencies.serde_derive] [dependencies.serde_derive]
version = "1.0" version = "1.0"
optional = true
[dependencies.serde_bytes] [dependencies.serde_bytes]
version = "0.10" version = "0.10"
optional = true
# [dependencies.bincode]
# version = "1.0.1"
# optional = true
[dependencies.serde-bench] [dependencies.serde-bench]
version = "0.0.7" version = "0.0.7"
optional = true
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.2.0" }
[features] [features]
cache = ["serde", "serde_derive", "serde_bytes", "serde-bench", "wasmer-runtime-core/cache"]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]

View File

@ -1,16 +1,50 @@
use crate::relocation::{ExternalRelocation, TrapSink}; use crate::relocation::{ExternalRelocation, TrapSink};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::sync::Arc;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::Memory, backend::{sys::Memory, CacheGen},
cache::{Cache, Error}, cache::{Artifact, Error},
module::ModuleInfo, module::{ModuleInfo, ModuleInner},
structures::Map, structures::Map,
types::{LocalFuncIndex, SigIndex}, types::{LocalFuncIndex, SigIndex},
}; };
use serde_bench::{deserialize, serialize}; use serde_bench::{deserialize, serialize};
pub struct CacheGenerator {
backend_cache: BackendCache,
memory: Arc<Memory>,
}
impl CacheGenerator {
pub fn new(backend_cache: BackendCache, memory: Arc<Memory>) -> Self {
Self {
backend_cache,
memory,
}
}
}
impl CacheGen for CacheGenerator {
fn generate_cache(
&self,
module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), Error> {
let info = Box::new(module.info.clone());
// Clone the memory to a new location. This could take a long time,
// depending on the throughput of your memcpy implementation.
let compiled_code = (*self.memory).clone();
Ok((
info,
self.backend_cache.into_backend_data()?.into_boxed_slice(),
compiled_code,
))
}
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct TrampolineCache { pub struct TrampolineCache {
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
@ -22,24 +56,24 @@ pub struct TrampolineCache {
pub struct BackendCache { pub struct BackendCache {
pub external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>, pub external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
pub offsets: Map<LocalFuncIndex, usize>, pub offsets: Map<LocalFuncIndex, usize>,
pub trap_sink: TrapSink, pub trap_sink: Arc<TrapSink>,
pub trampolines: TrampolineCache, pub trampolines: TrampolineCache,
} }
impl BackendCache { impl BackendCache {
pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> { pub fn from_cache(cache: Artifact) -> Result<(ModuleInfo, Memory, Self), Error> {
let (info, backend_data, compiled_code) = cache.consume(); let (info, backend_data, compiled_code) = cache.consume();
let backend_cache = deserialize(backend_data.as_slice()) let backend_cache =
.map_err(|e| Error::DeserializeError(e.to_string()))?; deserialize(&backend_data).map_err(|e| Error::DeserializeError(e.to_string()))?;
Ok((info, compiled_code, backend_cache)) Ok((info, compiled_code, backend_cache))
} }
pub fn into_backend_data(self) -> Result<Vec<u8>, Error> { pub fn into_backend_data(&self) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?; serialize(&mut buffer, self).map_err(|e| Error::SerializeError(e.to_string()))?;
Ok(buffer) Ok(buffer)
} }

View File

@ -32,10 +32,10 @@ impl<'env, 'module, 'isa> FuncEnv<'env, 'module, 'isa> {
let mut signature = self.env.signatures[Converter(clif_sig_index).into()].clone(); let mut signature = self.env.signatures[Converter(clif_sig_index).into()].clone();
// Add the vmctx parameter type to it // Add the vmctx parameter type to it
signature.params.push(ir::AbiParam::special( signature.params.insert(
self.pointer_type(), 0,
ir::ArgumentPurpose::VMContext, ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
)); );
// Return signature // Return signature
signature signature
@ -75,7 +75,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type(); let ptr_type = self.pointer_type();
let local_global_addr = match global_index.local_or_import(self.env.module) { let local_global_addr = match global_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_global_index) => { LocalOrImport::Local(local_global_index) => {
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx, base: vmctx,
@ -145,7 +145,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type(); let ptr_type = self.pointer_type();
let (local_memory_ptr_ptr, description) = match mem_index.local_or_import(self.env.module) { let (local_memory_ptr_ptr, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => { LocalOrImport::Local(local_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx, base: vmctx,
@ -253,7 +254,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type(); let ptr_type = self.pointer_type();
let (table_struct_ptr_ptr, description) = match table_index.local_or_import(self.env.module) let (table_struct_ptr_ptr, description) = match table_index
.local_or_import(&self.env.module.info)
{ {
LocalOrImport::Local(local_table_index) => { LocalOrImport::Local(local_table_index) => {
let tables_base = func.create_global_value(ir::GlobalValueData::Load { let tables_base = func.create_global_value(ir::GlobalValueData::Load {
@ -443,6 +445,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
pos.ins().symbol_value(ir::types::I64, sig_index_global) pos.ins().symbol_value(ir::types::I64, sig_index_global)
// let dynamic_sigindices_array_ptr = pos.ins().load(
// ptr_type,
// mflags,
// )
// let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64); // let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
// self.env.deduplicated[clif_sig_index] // self.env.deduplicated[clif_sig_index]
@ -459,8 +467,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
// Build a value list for the indirect call instruction containing the call_args // Build a value list for the indirect call instruction containing the call_args
// and the vmctx parameter. // and the vmctx parameter.
let mut args = Vec::with_capacity(call_args.len() + 1); let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx_ptr); args.push(vmctx_ptr);
args.extend(call_args.iter().cloned());
Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args)) Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args))
} }
@ -475,9 +483,10 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
call_args: &[ir::Value], call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> { ) -> cranelift_wasm::WasmResult<ir::Inst> {
let callee_index: FuncIndex = Converter(clif_callee_index).into(); let callee_index: FuncIndex = Converter(clif_callee_index).into();
let ptr_type = self.pointer_type();
match callee_index.local_or_import(self.env.module) { match callee_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(_) => { LocalOrImport::Local(local_function_index) => {
// this is an internal function // this is an internal function
let vmctx = pos let vmctx = pos
.func .func
@ -485,13 +494,31 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
.expect("missing vmctx parameter"); .expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1); let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx); args.push(vmctx);
args.extend(call_args.iter().cloned());
Ok(pos.ins().call(callee, &args)) let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let function_ptr = {
let mflags = ir::MemFlags::trusted();
let function_array_ptr = pos.ins().load(
ptr_type,
mflags,
vmctx,
vm::Ctx::offset_local_functions() as i32,
);
pos.ins().load(
ptr_type,
mflags,
function_array_ptr,
(local_function_index.index() as i32) * 8,
)
};
Ok(pos.ins().call_indirect(sig_ref, function_ptr, &args))
} }
LocalOrImport::Import(imported_func_index) => { LocalOrImport::Import(imported_func_index) => {
let ptr_type = self.pointer_type();
// this is an imported function // this is an imported function
let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext); let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext);
@ -532,8 +559,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let sig_ref = pos.func.dfg.ext_funcs[callee].signature; let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let mut args = Vec::with_capacity(call_args.len() + 1); let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(imported_vmctx_addr); args.push(imported_vmctx_addr);
args.extend(call_args.iter().cloned());
Ok(pos Ok(pos
.ins() .ins()
@ -559,16 +586,17 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext), ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
], ],
returns: vec![ir::AbiParam::new(ir::types::I32)], returns: vec![ir::AbiParam::new(ir::types::I32)],
}); });
let mem_index: MemoryIndex = Converter(clif_mem_index).into(); let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { let (namespace, mem_index, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => ( LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE, call_names::LOCAL_NAMESPACE,
local_mem_index.index(), local_mem_index.index(),
@ -604,7 +632,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let call_inst = pos let call_inst = pos
.ins() .ins()
.call(mem_grow_func, &[const_mem_index, by_value, vmctx]); .call(mem_grow_func, &[vmctx, const_mem_index, by_value]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
} }
@ -623,15 +651,16 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext), ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
], ],
returns: vec![ir::AbiParam::new(ir::types::I32)], returns: vec![ir::AbiParam::new(ir::types::I32)],
}); });
let mem_index: MemoryIndex = Converter(clif_mem_index).into(); let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { let (namespace, mem_index, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => ( LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE, call_names::LOCAL_NAMESPACE,
local_mem_index.index(), local_mem_index.index(),
@ -664,7 +693,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
.special_param(ir::ArgumentPurpose::VMContext) .special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter"); .expect("missing vmctx parameter");
let call_inst = pos.ins().call(mem_grow_func, &[const_mem_index, vmctx]); let call_inst = pos.ins().call(mem_grow_func, &[vmctx, const_mem_index]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
} }

View File

@ -1,4 +1,3 @@
#[cfg(feature = "cache")]
mod cache; mod cache;
mod func_env; mod func_env;
mod libcalls; mod libcalls;
@ -14,21 +13,18 @@ use cranelift_codegen::{
settings::{self, Configurable}, settings::{self, Configurable},
}; };
use target_lexicon::Triple; use target_lexicon::Triple;
#[cfg(feature = "cache")]
use wasmer_runtime_core::{ use wasmer_runtime_core::cache::{Artifact, Error as CacheError};
backend::sys::Memory,
cache::{Cache, Error as CacheError},
module::ModuleInfo,
};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::{Compiler, Token}, backend::{Compiler, Token},
error::{CompileError, CompileResult}, error::{CompileError, CompileResult},
module::ModuleInner, module::ModuleInner,
}; };
#[cfg(feature = "cache")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[cfg(feature = "cache")]
extern crate rayon;
extern crate serde; extern crate serde;
use wasmparser::{self, WasmDecoder}; use wasmparser::{self, WasmDecoder};
@ -48,7 +44,7 @@ impl Compiler for CraneliftCompiler {
let isa = get_isa(); let isa = get_isa();
let mut module = module::Module::empty(); let mut module = module::Module::new();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa); let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?; let func_bodies = module_env.translate(wasm)?;
@ -57,41 +53,41 @@ impl Compiler for CraneliftCompiler {
} }
/// Create a wasmer Module from an already-compiled cache. /// Create a wasmer Module from an already-compiled cache.
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> { unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
module::Module::from_cache(cache) module::Module::from_cache(cache)
} }
#[cfg(feature = "cache")] //
fn compile_to_backend_cache_data( // fn compile_to_backend_cache_data(
&self, // &self,
wasm: &[u8], // wasm: &[u8],
_: Token, // _: Token,
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> { // ) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> {
validate(wasm)?; // validate(wasm)?;
let isa = get_isa(); // let isa = get_isa();
let mut module = module::Module::empty(); // let mut module = module::Module::new(wasm);
let module_env = module_env::ModuleEnv::new(&mut module, &*isa); // let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?; // let func_bodies = module_env.translate(wasm)?;
let (info, backend_cache, compiled_code) = module // let (info, backend_cache, compiled_code) = module
.compile_to_backend_cache(&*isa, func_bodies) // .compile_to_backend_cache(&*isa, func_bodies)
.map_err(|e| CompileError::InternalError { // .map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e), // msg: format!("{:?}", e),
})?; // })?;
let buffer = // let buffer =
backend_cache // backend_cache
.into_backend_data() // .into_backend_data()
.map_err(|e| CompileError::InternalError { // .map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e), // msg: format!("{:?}", e),
})?; // })?;
Ok((Box::new(info), buffer, compiled_code)) // Ok((Box::new(info), buffer, compiled_code))
} // }
} }
fn get_isa() -> Box<isa::TargetIsa> { fn get_isa() -> Box<isa::TargetIsa> {

View File

@ -1,75 +1,32 @@
#[cfg(feature = "cache")] use crate::cache::{BackendCache, CacheGenerator};
use crate::cache::BackendCache;
use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines}; use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines};
use cranelift_codegen::{ir, isa}; use cranelift_codegen::{ir, isa};
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm; use cranelift_wasm;
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{ use std::sync::Arc;
ops::{Deref, DerefMut},
ptr::NonNull, use wasmer_runtime_core::cache::{Artifact, Error as CacheError};
};
#[cfg(feature = "cache")]
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::Memory, backend::Backend,
cache::{Cache, Error as CacheError}, error::CompileResult,
};
use wasmer_runtime_core::{
backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper},
error::{CompileResult, RuntimeResult},
module::{ModuleInfo, ModuleInner, StringTable}, module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex}, structures::{Map, TypedIndex},
types::{ types::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type, FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
Value,
}, },
vm::{self, ImportBacking},
}; };
struct Placeholder;
impl FuncResolver for Placeholder {
fn get(
&self,
_module: &ModuleInner,
_local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
None
}
}
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_import_backing: &ImportBacking,
_vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
Ok(vec![])
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
}
/// This contains all of the items in a `ModuleInner` except the `func_resolver`. /// This contains all of the items in a `ModuleInner` except the `func_resolver`.
pub struct Module { pub struct Module {
pub module: ModuleInner, pub info: ModuleInfo,
} }
impl Module { impl Module {
pub fn empty() -> Self { pub fn new() -> Self {
Self { Self {
module: ModuleInner {
// this is a placeholder
func_resolver: Box::new(Placeholder),
protected_caller: Box::new(Placeholder),
info: ModuleInfo { info: ModuleInfo {
memories: Map::new(), memories: Map::new(),
globals: Map::new(), globals: Map::new(),
@ -94,85 +51,72 @@ impl Module {
namespace_table: StringTable::new(), namespace_table: StringTable::new(),
name_table: StringTable::new(), name_table: StringTable::new(),
}, },
},
} }
} }
pub fn compile( pub fn compile(
mut self, self,
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>, functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<ModuleInner> { ) -> CompileResult<ModuleInner> {
let (func_resolver_builder, handler_data) = let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, &self.module.info)?; FuncResolverBuilder::new(isa, functions, &self.info)?;
self.module.func_resolver = let trampolines = Arc::new(Trampolines::new(isa, &self.info));
Box::new(func_resolver_builder.finalize(&self.module.info.signatures)?);
let trampolines = Trampolines::new(isa, &self.module.info); let (func_resolver, backend_cache) = func_resolver_builder.finalize(
&self.info.signatures,
Arc::clone(&trampolines),
handler_data.clone(),
)?;
self.module.protected_caller = let protected_caller = Caller::new(&self.info, handler_data, trampolines);
Box::new(Caller::new(&self.module.info, handler_data, trampolines));
Ok(self.module) let cache_gen = Box::new(CacheGenerator::new(
backend_cache,
Arc::clone(&func_resolver.memory),
));
Ok(ModuleInner {
func_resolver: Box::new(func_resolver),
protected_caller: Box::new(protected_caller),
cache_gen,
info: self.info,
})
} }
#[cfg(feature = "cache")] pub fn from_cache(cache: Artifact) -> Result<ModuleInner, CacheError> {
pub fn compile_to_backend_cache(
self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<(ModuleInfo, BackendCache, Memory)> {
let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
let trampolines = Trampolines::new(isa, &self.module.info);
let trampoline_cache = trampolines.to_trampoline_cache();
let (backend_cache, compiled_code) =
func_resolver_builder.to_backend_cache(trampoline_cache, handler_data);
Ok((self.module.info, backend_cache, compiled_code))
}
#[cfg(feature = "cache")]
pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> {
let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?; let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?;
let (func_resolver_builder, trampolines, handler_data) = let (func_resolver_builder, trampolines, handler_data) =
FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?; FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?;
let func_resolver = Box::new( let (func_resolver, backend_cache) = func_resolver_builder
func_resolver_builder .finalize(
.finalize(&info.signatures) &info.signatures,
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?, Arc::clone(&trampolines),
); handler_data.clone(),
)
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?;
let protected_caller = Box::new(Caller::new(&info, handler_data, trampolines)); let protected_caller = Caller::new(&info, handler_data, trampolines);
let cache_gen = Box::new(CacheGenerator::new(
backend_cache,
Arc::clone(&func_resolver.memory),
));
Ok(ModuleInner { Ok(ModuleInner {
func_resolver, func_resolver: Box::new(func_resolver),
protected_caller, protected_caller: Box::new(protected_caller),
cache_gen,
info, info,
}) })
} }
} }
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&self.module
}
}
impl DerefMut for Module {
fn deref_mut(&mut self) -> &mut ModuleInner {
&mut self.module
}
}
pub struct Converter<T>(pub T); pub struct Converter<T>(pub T);
macro_rules! convert_clif_to_runtime_index { macro_rules! convert_clif_to_runtime_index {

View File

@ -4,7 +4,6 @@ use crate::{
}; };
use cranelift_codegen::{ir, isa}; use cranelift_codegen::{ir, isa};
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment}; use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
use std::sync::Arc;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
error::{CompileError, CompileResult}, error::{CompileError, CompileResult},
module::{ module::{
@ -62,10 +61,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a function signature to the environment. /// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: &ir::Signature) { fn declare_signature(&mut self, sig: &ir::Signature) {
self.signatures.push(sig.clone()); self.signatures.push(sig.clone());
self.module self.module.info.signatures.push(Converter(sig).into());
.info
.signatures
.push(Arc::new(Converter(sig).into()));
} }
/// Return the signature with the given index. /// Return the signature with the given index.
@ -139,7 +135,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
// assert!(!desc.mutable); // assert!(!desc.mutable);
let global_index: GlobalIndex = Converter(global_index).into(); let global_index: GlobalIndex = Converter(global_index).into();
let imported_global_index = global_index let imported_global_index = global_index
.local_or_import(self.module) .local_or_import(&self.module.info)
.import() .import()
.expect("invalid global initializer when declaring an imported global"); .expect("invalid global initializer when declaring an imported global");
Initializer::GetGlobal(imported_global_index) Initializer::GetGlobal(imported_global_index)
@ -249,7 +245,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let base = match base { let base = match base {
Some(global_index) => { Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into(); let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) { Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) {
LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => { LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global") panic!("invalid global initializer when declaring an imported global")
@ -319,7 +315,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let base = match base { let base = match base {
Some(global_index) => { Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into(); let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) { Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) {
LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => { LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global") panic!("invalid global initializer when declaring an imported global")
@ -389,7 +385,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let name = ir::ExternalName::user(0, func_index.index() as u32); let name = ir::ExternalName::user(0, func_index.index() as u32);
let sig = func_env.generate_signature( let sig = func_env.generate_signature(
self.get_func_type(Converter(func_index.convert_up(self.module)).into()), self.get_func_type(Converter(func_index.convert_up(&self.module.info)).into()),
); );
let mut func = ir::Function::with_name_signature(name, sig); let mut func = ir::Function::with_name_signature(name, sig);
@ -419,8 +415,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
], ],
returns: vec![], returns: vec![],
}); });
@ -457,8 +453,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
], ],
returns: vec![], returns: vec![],
}); });
@ -476,8 +472,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::I64),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I64),
], ],
returns: vec![], returns: vec![],
}); });
@ -495,8 +491,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::F32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F32),
], ],
returns: vec![], returns: vec![],
}); });
@ -514,8 +510,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature { let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv, call_conv: self.target_config().default_call_conv,
params: vec![ params: vec![
ir::AbiParam::new(ir::types::F64),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F64),
], ],
returns: vec![], returns: vec![],
}); });
@ -536,14 +532,14 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64); let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64);
pos.ins().call(start_debug, &[func_index, vmctx]); pos.ins().call(start_debug, &[vmctx, func_index]);
for param in new_ebb_params.iter().cloned() { for param in new_ebb_params.iter().cloned() {
match pos.func.dfg.value_type(param) { match pos.func.dfg.value_type(param) {
ir::types::I32 => pos.ins().call(i32_print, &[param, vmctx]), ir::types::I32 => pos.ins().call(i32_print, &[vmctx, param]),
ir::types::I64 => pos.ins().call(i64_print, &[param, vmctx]), ir::types::I64 => pos.ins().call(i64_print, &[vmctx, param]),
ir::types::F32 => pos.ins().call(f32_print, &[param, vmctx]), ir::types::F32 => pos.ins().call(f32_print, &[vmctx, param]),
ir::types::F64 => pos.ins().call(f64_print, &[param, vmctx]), ir::types::F64 => pos.ins().call(f64_print, &[vmctx, param]),
_ => unimplemented!(), _ => unimplemented!(),
}; };
} }

View File

@ -22,16 +22,14 @@ pub mod call_names {
pub const DYNAMIC_MEM_SIZE: u32 = 5; pub const DYNAMIC_MEM_SIZE: u32 = 5;
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Reloc { pub enum Reloc {
Abs8, Abs8,
X86PCRel4, X86PCRel4,
X86CallPCRel4, X86CallPCRel4,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub enum LibCall { pub enum LibCall {
Probestack, Probestack,
CeilF32, CeilF32,
@ -44,8 +42,7 @@ pub enum LibCall {
NearestF64, NearestF64,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Debug, Clone)]
pub struct ExternalRelocation { pub struct ExternalRelocation {
/// The relocation code. /// The relocation code.
pub reloc: Reloc, pub reloc: Reloc,
@ -66,8 +63,7 @@ pub struct LocalRelocation {
pub target: FuncIndex, pub target: FuncIndex,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum VmCallKind { pub enum VmCallKind {
StaticMemoryGrow, StaticMemoryGrow,
StaticMemorySize, StaticMemorySize,
@ -79,16 +75,14 @@ pub enum VmCallKind {
DynamicMemorySize, DynamicMemorySize,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum VmCall { pub enum VmCall {
Local(VmCallKind), Local(VmCallKind),
Import(VmCallKind), Import(VmCallKind),
} }
/// Specify the type of relocation /// Specify the type of relocation
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Debug, Clone)]
pub enum RelocationType { pub enum RelocationType {
Intrinsic(String), Intrinsic(String),
LibCall(LibCall), LibCall(LibCall),
@ -218,8 +212,7 @@ impl binemit::RelocSink for RelocSink {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum TrapCode { pub enum TrapCode {
StackOverflow, StackOverflow,
HeapOutOfBounds, HeapOutOfBounds,
@ -244,8 +237,7 @@ impl RelocSink {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub struct TrapData { pub struct TrapData {
pub trapcode: TrapCode, pub trapcode: TrapCode,
pub srcloc: u32, pub srcloc: u32,
@ -253,7 +245,7 @@ pub struct TrapData {
/// Simple implementation of a TrapSink /// Simple implementation of a TrapSink
/// that saves the info for later. /// that saves the info for later.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
pub struct TrapSink { pub struct TrapSink {
trap_datas: Vec<(usize, TrapData)>, trap_datas: Vec<(usize, TrapData)>,
} }

View File

@ -1,8 +1,4 @@
#[cfg(feature = "cache")] use crate::{cache::BackendCache, trampoline::Trampolines};
use crate::{
cache::{BackendCache, TrampolineCache},
trampoline::Trampolines,
};
use crate::{ use crate::{
libcalls, libcalls,
relocation::{ relocation::{
@ -11,6 +7,7 @@ use crate::{
}, },
signal::HandlerData, signal::HandlerData,
}; };
use rayon::prelude::*;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context}; use cranelift_codegen::{ir, isa, Context};
@ -19,7 +16,7 @@ use std::{
ptr::{write_unaligned, NonNull}, ptr::{write_unaligned, NonNull},
sync::Arc, sync::Arc,
}; };
#[cfg(feature = "cache")]
use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
self, self,
@ -42,21 +39,32 @@ extern "C" {
pub fn __chkstk(); pub fn __chkstk();
} }
fn lookup_func(
map: &SliceMap<LocalFuncIndex, usize>,
memory: &Memory,
local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
let offset = *map.get(local_func_index)?;
let ptr = unsafe { memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
#[allow(dead_code)] #[allow(dead_code)]
pub struct FuncResolverBuilder { pub struct FuncResolverBuilder {
resolver: FuncResolver, map: Map<LocalFuncIndex, usize>,
memory: Memory,
local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>, local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>,
external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>, external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
import_len: usize, import_len: usize,
} }
impl FuncResolverBuilder { impl FuncResolverBuilder {
#[cfg(feature = "cache")]
pub fn new_from_backend_cache( pub fn new_from_backend_cache(
backend_cache: BackendCache, backend_cache: BackendCache,
mut code: Memory, mut code: Memory,
info: &ModuleInfo, info: &ModuleInfo,
) -> Result<(Self, Trampolines, HandlerData), CacheError> { ) -> Result<(Self, Arc<Trampolines>, HandlerData), CacheError> {
unsafe { unsafe {
code.protect(.., Protect::ReadWrite) code.protect(.., Protect::ReadWrite)
.map_err(|e| CacheError::Unknown(e.to_string()))?; .map_err(|e| CacheError::Unknown(e.to_string()))?;
@ -67,61 +75,62 @@ impl FuncResolverBuilder {
Ok(( Ok((
Self { Self {
resolver: FuncResolver {
map: backend_cache.offsets, map: backend_cache.offsets,
memory: code, memory: code,
},
local_relocs: Map::new(), local_relocs: Map::new(),
external_relocs: backend_cache.external_relocs, external_relocs: backend_cache.external_relocs,
import_len: info.imported_functions.len(), import_len: info.imported_functions.len(),
}, },
Trampolines::from_trampoline_cache(backend_cache.trampolines), Arc::new(Trampolines::from_trampoline_cache(
backend_cache.trampolines,
)),
handler_data, handler_data,
)) ))
} }
#[cfg(feature = "cache")]
pub fn to_backend_cache(
mut self,
trampolines: TrampolineCache,
handler_data: HandlerData,
) -> (BackendCache, Memory) {
self.relocate_locals();
(
BackendCache {
external_relocs: self.external_relocs,
offsets: self.resolver.map,
trap_sink: handler_data.trap_data,
trampolines,
},
self.resolver.memory,
)
}
pub fn new( pub fn new(
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>, function_bodies: Map<LocalFuncIndex, ir::Function>,
info: &ModuleInfo, info: &ModuleInfo,
) -> CompileResult<(Self, HandlerData)> { ) -> CompileResult<(Self, HandlerData)> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len()); let num_func_bodies = function_bodies.len();
let mut local_relocs = Map::with_capacity(function_bodies.len()); let mut local_relocs = Map::with_capacity(num_func_bodies);
let mut external_relocs = Map::new(); let mut external_relocs = Map::with_capacity(num_func_bodies);
let mut trap_sink = TrapSink::new(); let mut trap_sink = TrapSink::new();
let compiled_functions: Result<Vec<(Vec<u8>, (RelocSink, LocalTrapSink))>, CompileError> =
function_bodies
.into_vec()
.par_iter()
.map_init(
|| Context::new(),
|ctx, func| {
let mut code_buf = Vec::new();
ctx.func = func.to_owned();
let mut reloc_sink = RelocSink::new();
let mut local_trap_sink = LocalTrapSink::new(); let mut local_trap_sink = LocalTrapSink::new();
let mut ctx = Context::new(); ctx.compile_and_emit(
let mut total_size = 0; isa,
&mut code_buf,
for (_, func) in function_bodies { &mut reloc_sink,
ctx.func = func; &mut local_trap_sink,
let mut code_buf = Vec::new(); )
let mut reloc_sink = RelocSink::new();
ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut local_trap_sink)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?; .map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
ctx.clear(); ctx.clear();
Ok((code_buf, (reloc_sink, local_trap_sink)))
},
)
.collect();
let compiled_functions = compiled_functions?;
let mut total_size = 0;
// We separate into two iterators, one iterable and one into iterable
let (code_bufs, sinks): (Vec<Vec<u8>>, Vec<(RelocSink, LocalTrapSink)>) =
compiled_functions.into_iter().unzip();
for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter())
{
// Clear the local trap sink and consolidate all trap info // Clear the local trap sink and consolidate all trap info
// into a single location. // into a single location.
trap_sink.drain_local(total_size, &mut local_trap_sink); trap_sink.drain_local(total_size, &mut local_trap_sink);
@ -129,7 +138,6 @@ impl FuncResolverBuilder {
// Round up each function's size to pointer alignment. // Round up each function's size to pointer alignment.
total_size += round_up(code_buf.len(), mem::size_of::<usize>()); total_size += round_up(code_buf.len(), mem::size_of::<usize>());
compiled_functions.push(code_buf);
local_relocs.push(reloc_sink.local_relocs.into_boxed_slice()); local_relocs.push(reloc_sink.local_relocs.into_boxed_slice());
external_relocs.push(reloc_sink.external_relocs.into_boxed_slice()); external_relocs.push(reloc_sink.external_relocs.into_boxed_slice());
} }
@ -156,10 +164,10 @@ impl FuncResolverBuilder {
*i = 0xCC; *i = 0xCC;
} }
let mut map = Map::with_capacity(compiled_functions.len()); let mut map = Map::with_capacity(num_func_bodies);
let mut previous_end = 0; let mut previous_end = 0;
for compiled in compiled_functions.iter() { for compiled in code_bufs.iter() {
let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>()); let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>());
unsafe { unsafe {
memory.as_slice_mut()[previous_end..previous_end + compiled.len()] memory.as_slice_mut()[previous_end..previous_end + compiled.len()]
@ -169,10 +177,12 @@ impl FuncResolverBuilder {
previous_end = new_end; previous_end = new_end;
} }
let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size()); let handler_data =
HandlerData::new(Arc::new(trap_sink), memory.as_ptr() as _, memory.size());
let mut func_resolver_builder = Self { let mut func_resolver_builder = Self {
resolver: FuncResolver { map, memory }, map,
memory,
local_relocs, local_relocs,
external_relocs, external_relocs,
import_len: info.imported_functions.len(), import_len: info.imported_functions.len(),
@ -187,12 +197,15 @@ impl FuncResolverBuilder {
for (index, relocs) in self.local_relocs.iter() { for (index, relocs) in self.local_relocs.iter() {
for ref reloc in relocs.iter() { for ref reloc in relocs.iter() {
let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len); let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len);
let target_func_address = let target_func_address = lookup_func(&self.map, &self.memory, local_func_index)
self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize; .unwrap()
.as_ptr() as usize;
// We need the address of the current function // We need the address of the current function
// because these calls are relative. // because these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize; let func_addr = lookup_func(&self.map, &self.memory, index)
.unwrap()
.as_ptr() as usize;
unsafe { unsafe {
let reloc_address = func_addr + reloc.offset as usize; let reloc_address = func_addr + reloc.offset as usize;
@ -208,8 +221,10 @@ impl FuncResolverBuilder {
pub fn finalize( pub fn finalize(
mut self, mut self,
signatures: &SliceMap<SigIndex, Arc<FuncSig>>, signatures: &SliceMap<SigIndex, FuncSig>,
) -> CompileResult<FuncResolver> { trampolines: Arc<Trampolines>,
handler_data: HandlerData,
) -> CompileResult<(FuncResolver, BackendCache)> {
for (index, relocs) in self.external_relocs.iter() { for (index, relocs) in self.external_relocs.iter() {
for ref reloc in relocs.iter() { for ref reloc in relocs.iter() {
let target_func_address: isize = match reloc.target { let target_func_address: isize = match reloc.target {
@ -223,9 +238,9 @@ impl FuncResolverBuilder {
LibCall::TruncF64 => libcalls::truncf64 as isize, LibCall::TruncF64 => libcalls::truncf64 as isize,
LibCall::NearestF64 => libcalls::nearbyintf64 as isize, LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
#[cfg(all(target_pointer_width = "64", target_os = "windows"))] #[cfg(all(target_pointer_width = "64", target_os = "windows"))]
Probestack => __chkstk as isize, LibCall::Probestack => __chkstk as isize,
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
Probestack => __rust_probestack as isize, LibCall::Probestack => __rust_probestack as isize,
}, },
RelocationType::Intrinsic(ref name) => match name.as_str() { RelocationType::Intrinsic(ref name) => match name.as_str() {
"i32print" => i32_print as isize, "i32print" => i32_print as isize,
@ -273,15 +288,17 @@ impl FuncResolverBuilder {
}, },
}, },
RelocationType::Signature(sig_index) => { RelocationType::Signature(sig_index) => {
let sig_index = let signature = SigRegistry.lookup_signature_ref(&signatures[sig_index]);
SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index])); let sig_index = SigRegistry.lookup_sig_index(signature);
sig_index.index() as _ sig_index.index() as _
} }
}; };
// We need the address of the current function // We need the address of the current function
// because some of these calls are relative. // because some of these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr(); let func_addr = lookup_func(&self.map, &self.memory, index)
.unwrap()
.as_ptr() as usize;
// Determine relocation type and apply relocation. // Determine relocation type and apply relocation.
match reloc.reloc { match reloc.reloc {
@ -289,9 +306,9 @@ impl FuncResolverBuilder {
let ptr_to_write = (target_func_address as u64) let ptr_to_write = (target_func_address as u64)
.checked_add(reloc.addend as u64) .checked_add(reloc.addend as u64)
.unwrap(); .unwrap();
let empty_space_offset = self.resolver.map[index] + reloc.offset as usize; let empty_space_offset = self.map[index] + reloc.offset as usize;
let ptr_slice = unsafe { let ptr_slice = unsafe {
&mut self.resolver.memory.as_slice_mut() &mut self.memory.as_slice_mut()
[empty_space_offset..empty_space_offset + 8] [empty_space_offset..empty_space_offset + 8]
}; };
LittleEndian::write_u64(ptr_slice, ptr_to_write); LittleEndian::write_u64(ptr_slice, ptr_to_write);
@ -309,29 +326,35 @@ impl FuncResolverBuilder {
} }
unsafe { unsafe {
self.resolver self.memory
.memory
.protect(.., Protect::ReadExec) .protect(.., Protect::ReadExec)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?; .map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
} }
Ok(self.resolver) let backend_cache = BackendCache {
external_relocs: self.external_relocs.clone(),
offsets: self.map.clone(),
trap_sink: handler_data.trap_data,
trampolines: trampolines.to_trampoline_cache(),
};
Ok((
FuncResolver {
map: self.map,
memory: Arc::new(self.memory),
},
backend_cache,
))
} }
} }
unsafe impl Sync for FuncResolver {}
unsafe impl Send for FuncResolver {}
/// Resolves a function index to a function address. /// Resolves a function index to a function address.
pub struct FuncResolver { pub struct FuncResolver {
map: Map<LocalFuncIndex, usize>, map: Map<LocalFuncIndex, usize>,
memory: Memory, pub(crate) memory: Arc<Memory>,
}
impl FuncResolver {
fn lookup(&self, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self.map.get(local_func_index)?;
let ptr = unsafe { self.memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
} }
// Implements FuncResolver trait. // Implements FuncResolver trait.
@ -341,7 +364,7 @@ impl backend::FuncResolver for FuncResolver {
_module: &wasmer_runtime_core::module::ModuleInner, _module: &wasmer_runtime_core::module::ModuleInner,
index: LocalFuncIndex, index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> { ) -> Option<NonNull<vm::Func>> {
self.lookup(index) lookup_func(&self.map, &self.memory, index)
} }
} }
@ -350,21 +373,21 @@ fn round_up(n: usize, multiple: usize) -> usize {
(n + multiple - 1) & !(multiple - 1) (n + multiple - 1) & !(multiple - 1)
} }
extern "C" fn i32_print(n: i32) { extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) {
print!(" i32: {},", n); print!(" i32: {},", n);
} }
extern "C" fn i64_print(n: i64) { extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) {
print!(" i64: {},", n); print!(" i64: {},", n);
} }
extern "C" fn f32_print(n: f32) { extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) {
print!(" f32: {},", n); print!(" f32: {},", n);
} }
extern "C" fn f64_print(n: f64) { extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) {
print!(" f64: {},", n); print!(" f64: {},", n);
} }
extern "C" fn start_debug(func_index: u32) { extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) {
print!("func ({}), args: [", func_index); print!("func ({}), args: [", func_index);
} }
extern "C" fn end_debug() { extern "C" fn end_debug(_ctx: &mut vm::Ctx) {
println!(" ]"); println!(" ]");
} }

View File

@ -2,7 +2,7 @@ use crate::relocation::{TrapData, TrapSink};
use crate::trampoline::Trampolines; use crate::trampoline::Trampolines;
use hashbrown::HashSet; use hashbrown::HashSet;
use libc::c_void; use libc::c_void;
use std::{cell::Cell, sync::Arc}; use std::{any::Any, cell::Cell, sync::Arc};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::{ProtectedCaller, Token, UserTrapper}, backend::{ProtectedCaller, Token, UserTrapper},
error::RuntimeResult, error::RuntimeResult,
@ -25,14 +25,14 @@ pub use self::unix::*;
pub use self::windows::*; pub use self::windows::*;
thread_local! { thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<String>> = Cell::new(None); pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
} }
pub struct Trapper; pub struct Trapper;
impl UserTrapper for Trapper { impl UserTrapper for Trapper {
unsafe fn do_early_trap(&self, msg: String) -> ! { unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg))); TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
trigger_trap() trigger_trap()
} }
} }
@ -40,11 +40,15 @@ impl UserTrapper for Trapper {
pub struct Caller { pub struct Caller {
func_export_set: HashSet<FuncIndex>, func_export_set: HashSet<FuncIndex>,
handler_data: HandlerData, handler_data: HandlerData,
trampolines: Trampolines, trampolines: Arc<Trampolines>,
} }
impl Caller { impl Caller {
pub fn new(module: &ModuleInfo, handler_data: HandlerData, trampolines: Trampolines) -> Self { pub fn new(
module: &ModuleInfo,
handler_data: HandlerData,
trampolines: Arc<Trampolines>,
) -> Self {
let mut func_export_set = HashSet::new(); let mut func_export_set = HashSet::new();
for export_index in module.exports.values() { for export_index in module.exports.values() {
if let ExportIndex::Func(func_index) = export_index { if let ExportIndex::Func(func_index) = export_index {
@ -110,6 +114,7 @@ impl ProtectedCaller for Caller {
.lookup(sig_index) .lookup(sig_index)
.expect("that trampoline doesn't exist"); .expect("that trampoline doesn't exist");
#[cfg(not(target_os = "windows"))]
call_protected(&self.handler_data, || unsafe { call_protected(&self.handler_data, || unsafe {
// Leap of faith. // Leap of faith.
trampoline( trampoline(
@ -120,6 +125,17 @@ impl ProtectedCaller for Caller {
); );
})?; })?;
// the trampoline is called from C on windows
#[cfg(target_os = "windows")]
call_protected(
&self.handler_data,
trampoline,
vmctx_ptr,
func_ptr,
param_vec.as_ptr(),
return_vec.as_mut_ptr(),
)?;
Ok(return_vec Ok(return_vec
.iter() .iter()
.zip(signature.returns().iter()) .zip(signature.returns().iter())
@ -137,18 +153,18 @@ impl ProtectedCaller for Caller {
} }
} }
fn get_func_from_index( fn get_func_from_index<'a>(
module: &ModuleInner, module: &'a ModuleInner,
import_backing: &ImportBacking, import_backing: &ImportBacking,
func_index: FuncIndex, func_index: FuncIndex,
) -> (*const vm::Func, Context, Arc<FuncSig>, SigIndex) { ) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) {
let sig_index = *module let sig_index = *module
.info .info
.func_assoc .func_assoc
.get(func_index) .get(func_index)
.expect("broken invariant, incorrect func index"); .expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(module) { let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
LocalOrImport::Local(local_func_index) => ( LocalOrImport::Local(local_func_index) => (
module module
.func_resolver .func_resolver
@ -167,7 +183,7 @@ fn get_func_from_index(
} }
}; };
let signature = Arc::clone(&module.info.signatures[sig_index]); let signature = &module.info.signatures[sig_index];
(func_ptr, ctx, signature, sig_index) (func_ptr, ctx, signature, sig_index)
} }
@ -175,15 +191,16 @@ fn get_func_from_index(
unsafe impl Send for HandlerData {} unsafe impl Send for HandlerData {}
unsafe impl Sync for HandlerData {} unsafe impl Sync for HandlerData {}
#[derive(Clone)]
pub struct HandlerData { pub struct HandlerData {
pub trap_data: TrapSink, pub trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void, exec_buffer_ptr: *const c_void,
exec_buffer_size: usize, exec_buffer_size: usize,
} }
impl HandlerData { impl HandlerData {
pub fn new( pub fn new(
trap_data: TrapSink, trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void, exec_buffer_ptr: *const c_void,
exec_buffer_size: usize, exec_buffer_size: usize,
) -> Self { ) -> Self {

View File

@ -18,11 +18,7 @@ use nix::sys::signal::{
use std::cell::{Cell, UnsafeCell}; use std::cell::{Cell, UnsafeCell};
use std::ptr; use std::ptr;
use std::sync::Once; use std::sync::Once;
use wasmer_runtime_core::{ use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
error::{RuntimeError, RuntimeResult},
structures::TypedIndex,
types::{MemoryIndex, TableIndex},
};
extern "C" fn signal_trap_handler( extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int, signum: ::nix::libc::c_int,
@ -79,8 +75,8 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
if signum != 0 { if signum != 0 {
*jmp_buf = prev_jmp_buf; *jmp_buf = prev_jmp_buf;
if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(RuntimeError::User { msg }) Err(RuntimeError::Panic { data })
} else { } else {
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
@ -91,28 +87,28 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
{ {
Err(match Signal::from_c_int(signum) { Err(match Signal::from_c_int(signum) {
Ok(SIGILL) => match trapcode { Ok(SIGILL) => match trapcode {
TrapCode::BadSignature => RuntimeError::IndirectCallSignature { TrapCode::BadSignature => RuntimeError::Trap {
table: TableIndex::new(0), msg: "incorrect call_indirect signature".into(),
}, },
TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { TrapCode::IndirectCallToNull => RuntimeError::Trap {
table: TableIndex::new(0), msg: "indirect call to null".into(),
}, },
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { TrapCode::HeapOutOfBounds => RuntimeError::Trap {
memory: MemoryIndex::new(0), msg: "memory out-of-bounds access".into(),
addr: None,
}, },
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { TrapCode::TableOutOfBounds => RuntimeError::Trap {
table: TableIndex::new(0), msg: "table out-of-bounds access".into(),
}, },
_ => RuntimeError::Unknown { _ => RuntimeError::Trap {
msg: "unknown trap".to_string(), msg: "unknown trap".into(),
}, },
}, },
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap {
memory: MemoryIndex::new(0), msg: "memory out-of-bounds access".into(),
addr: None, },
Ok(SIGFPE) => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
}, },
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
_ => unimplemented!(), _ => unimplemented!(),
} }
.into()) .into())
@ -126,8 +122,8 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
_ => "unkown trapped signal", _ => "unkown trapped signal",
}; };
// When the trap-handler is fully implemented, this will return more information. // When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown { Err(RuntimeError::Trap {
msg: format!("trap at {:p} - {}", faulting_addr, signal), msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
} }
.into()) .into())
} }

View File

@ -1,10 +1,115 @@
use crate::relocation::{TrapCode, TrapData};
use crate::signal::HandlerData; use crate::signal::HandlerData;
use wasmer_runtime_core::error::RuntimeResult; use crate::trampoline::Trampoline;
use std::cell::Cell;
use std::ffi::c_void;
use std::ptr;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::vm::Func;
use wasmer_runtime_core::{
error::{RuntimeError, RuntimeResult},
structures::TypedIndex,
types::{MemoryIndex, TableIndex},
};
use wasmer_win_exception_handler::CallProtectedData;
pub use wasmer_win_exception_handler::_call_protected;
use winapi::shared::minwindef::DWORD;
use winapi::um::minwinbase::{
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW,
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW,
};
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> { thread_local! {
unimplemented!("TODO"); pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
}
pub fn call_protected(
handler_data: &HandlerData,
trampoline: Trampoline,
ctx: *mut Ctx,
func: *const Func,
param_vec: *const u64,
return_vec: *mut u64,
) -> RuntimeResult<()> {
// TODO: trap early
// user code error
// if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
// return Err(RuntimeError::User { msg });
// }
let result = _call_protected(trampoline, ctx, func, param_vec, return_vec);
if let Ok(_) = result {
return Ok(());
}
let CallProtectedData {
code: signum,
exceptionAddress: exception_address,
instructionPointer: instruction_pointer,
} = result.unwrap_err();
if let Some(TrapData {
trapcode,
srcloc: _,
}) = handler_data.lookup(instruction_pointer as _)
{
Err(match signum as DWORD {
EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
TrapCode::BadSignature => RuntimeError::Trap {
msg: "incorrect call_indirect signature".into(),
},
TrapCode::IndirectCallToNull => RuntimeError::Trap {
msg: "indirect call to null".into(),
},
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
TrapCode::TableOutOfBounds => RuntimeError::Trap {
msg: "table out-of-bounds access".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
},
EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap {
msg: "stack overflow trap".into(),
},
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
}
.into())
} else {
let signal = match signum as DWORD {
EXCEPTION_FLT_DENORMAL_OPERAND
| EXCEPTION_FLT_DIVIDE_BY_ZERO
| EXCEPTION_FLT_INEXACT_RESULT
| EXCEPTION_FLT_INVALID_OPERATION
| EXCEPTION_FLT_OVERFLOW
| EXCEPTION_FLT_STACK_CHECK
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
_ => "unkown trapped signal",
};
Err(RuntimeError::Trap {
msg: format!("unknown trap at {} - {}", exception_address, signal).into(),
}
.into())
}
} }
pub unsafe fn trigger_trap() -> ! { pub unsafe fn trigger_trap() -> ! {
unimplemented!("TODO"); // TODO
unimplemented!();
} }

View File

@ -1,4 +1,3 @@
#[cfg(feature = "cache")]
use crate::cache::TrampolineCache; use crate::cache::TrampolineCache;
use cranelift_codegen::{ use cranelift_codegen::{
binemit::{NullTrapSink, Reloc, RelocSink}, binemit::{NullTrapSink, Reloc, RelocSink},
@ -7,6 +6,7 @@ use cranelift_codegen::{
isa, Context, isa, Context,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use std::ffi::c_void;
use std::{iter, mem}; use std::{iter, mem};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::{Memory, Protect}, backend::sys::{Memory, Protect},
@ -23,13 +23,15 @@ impl RelocSink for NullRelocSink {
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {} fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
} }
pub type Trampoline =
unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void;
pub struct Trampolines { pub struct Trampolines {
memory: Memory, memory: Memory,
offsets: HashMap<SigIndex, usize>, offsets: HashMap<SigIndex, usize>,
} }
impl Trampolines { impl Trampolines {
#[cfg(feature = "cache")]
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self { pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
// pub struct TrampolineCache { // pub struct TrampolineCache {
// #[serde(with = "serde_bytes")] // #[serde(with = "serde_bytes")]
@ -53,8 +55,7 @@ impl Trampolines {
} }
} }
#[cfg(feature = "cache")] pub fn to_trampoline_cache(&self) -> TrampolineCache {
pub fn to_trampoline_cache(self) -> TrampolineCache {
let mut code = vec![0; self.memory.size()]; let mut code = vec![0; self.memory.size()];
unsafe { unsafe {
@ -63,7 +64,7 @@ impl Trampolines {
TrampolineCache { TrampolineCache {
code, code,
offsets: self.offsets, offsets: self.offsets.clone(),
} }
} }
@ -138,10 +139,7 @@ impl Trampolines {
} }
} }
pub fn lookup( pub fn lookup(&self, sig_index: SigIndex) -> Option<Trampoline> {
&self,
sig_index: SigIndex,
) -> Option<unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64)> {
let offset = *self.offsets.get(&sig_index)?; let offset = *self.offsets.get(&sig_index)?;
let ptr = unsafe { self.memory.as_ptr().add(offset) }; let ptr = unsafe { self.memory.as_ptr().add(offset) };
@ -169,6 +167,7 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function {
let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(entry_ebb); let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(entry_ebb);
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1); let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
args_vec.push(vmctx_ptr);
for (index, wasm_ty) in func_sig.params().iter().enumerate() { for (index, wasm_ty) in func_sig.params().iter().enumerate() {
let mem_flags = ir::MemFlags::trusted(); let mem_flags = ir::MemFlags::trusted();
@ -180,7 +179,6 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function {
); );
args_vec.push(val); args_vec.push(val);
} }
args_vec.push(vmctx_ptr);
let call_inst = pos.ins().call_indirect(export_sig_ref, func_ptr, &args_vec); let call_inst = pos.ins().call_indirect(export_sig_ref, func_ptr, &args_vec);
@ -212,7 +210,9 @@ fn wasm_ty_to_clif(ty: Type) -> ir::types::Type {
} }
fn generate_trampoline_signature() -> ir::Signature { fn generate_trampoline_signature() -> ir::Signature {
let mut sig = ir::Signature::new(isa::CallConv::SystemV); let isa = super::get_isa();
let call_convention = isa.default_call_conv();
let mut sig = ir::Signature::new(call_convention);
let ptr_param = ir::AbiParam { let ptr_param = ir::AbiParam {
value_type: ir::types::I64, value_type: ir::types::I64,
@ -227,23 +227,24 @@ fn generate_trampoline_signature() -> ir::Signature {
} }
fn generate_export_signature(func_sig: &FuncSig) -> ir::Signature { fn generate_export_signature(func_sig: &FuncSig) -> ir::Signature {
let mut export_clif_sig = ir::Signature::new(isa::CallConv::SystemV); let isa = super::get_isa();
let call_convention = isa.default_call_conv();
let mut export_clif_sig = ir::Signature::new(call_convention);
export_clif_sig.params = func_sig let func_sig_iter = func_sig.params().iter().map(|wasm_ty| ir::AbiParam {
.params()
.iter()
.map(|wasm_ty| ir::AbiParam {
value_type: wasm_ty_to_clif(*wasm_ty), value_type: wasm_ty_to_clif(*wasm_ty),
purpose: ir::ArgumentPurpose::Normal, purpose: ir::ArgumentPurpose::Normal,
extension: ir::ArgumentExtension::None, extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned, location: ir::ArgumentLoc::Unassigned,
}) });
.chain(iter::once(ir::AbiParam {
export_clif_sig.params = iter::once(ir::AbiParam {
value_type: ir::types::I64, value_type: ir::types::I64,
purpose: ir::ArgumentPurpose::VMContext, purpose: ir::ArgumentPurpose::VMContext,
extension: ir::ArgumentExtension::None, extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned, location: ir::ArgumentLoc::Unassigned,
})) })
.chain(func_sig_iter)
.collect(); .collect();
export_clif_sig.returns = func_sig export_clif_sig.returns = func_sig

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-emscripten" name = "wasmer-emscripten"
version = "0.1.0" version = "0.2.1"
description = "Wasmer runtime emscripten implementation library" description = "Wasmer runtime emscripten implementation library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,14 +9,25 @@ edition = "2018"
build = "build/mod.rs" build = "build/mod.rs"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.0" } wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" }
libc = { git = "https://github.com/rust-lang/libc" } lazy_static = "1.2.0"
libc = "0.2.49"
byteorder = "1" byteorder = "1"
time = "0.1.41" time = "0.1.41"
[target.'cfg(windows)'.dependencies]
rand = "0.6"
[dev-dependencies] [dev-dependencies]
wasmer-clif-backend = { path = "../clif-backend", version = "0.1.0" } wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" }
wabt = "0.7.2" wabt = "0.7.2"
[target.'cfg(not(windows))'.dev-dependencies]
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0" }
[build-dependencies] [build-dependencies]
glob = "0.2.11" glob = "0.2.11"
[features]
clif = []
llvm = []

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

17
lib/emscripten/emtests/test_execvp.c vendored Normal file
View File

@ -0,0 +1,17 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "touch";
char arg1[] = "foo.txt";
char* argv[3];
argv[0] = command;
argv[1] = arg1;
argv[2] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

View File

@ -0,0 +1 @@
_execvp

BIN
lib/emscripten/emtests/test_execvp.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "C:\\Windows\\System32\\cmd.exe";
char arg1[] = "echo";
char arg2[] = "foo";
char* argv[4];
argv[0] = command;
argv[1] = arg1;
argv[2] = arg2;
argv[3] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More