diff --git a/.azure/install-llvm.yml b/.azure/install-llvm.yml
index 0a2a674b1..5b01ee7b9 100644
--- a/.azure/install-llvm.yml
+++ b/.azure/install-llvm.yml
@@ -31,20 +31,21 @@ steps:
- bash: |
set -ex
- if [ -x "`command -v llvm-config`" ]; then
- echo `command -v cmake` `llvm-config --version` installed
- else
- curl -OL https://github.com/wasmerio/windows-llvm-build/releases/download/v8.0.0/llvm-8.0.0-install.zip
- 7z x llvm-8.0.0-install.zip
- LLVM_PATH=`pwd`/llvm-8.0.0-install
- LLVM_PATH_WIN=$SYSTEM_DEFAULTWORKINGDIRECTORY\\llvm-8.0.0-install
- echo "##vso[task.prependpath]$LLVM_PATH/bin"
- echo "##vso[task.setvariable variable=LLVM_SYS_80_PREFIX]$LLVM_PATH_WIN"
- # chocolatey install cmake --installargs 'ADD_CMAKE_TO_PATH=System'
- fi
+ curl -OL https://github.com/wasmerio/windows-llvm-build/releases/download/v8.0.0/llvm-8.0.0-install.zip
+ 7z x llvm-8.0.0-install.zip
+ llvm=`pwd`/llvm-8.0.0-install
+ echo "##vso[task.prependpath]$llvm/bin"
+ echo "##vso[task.setvariable variable=LLVM_SYS_80_PREFIX;]$llvm"
displayName: "Install LLVM (Windows)"
condition: eq(variables['Agent.OS'], 'Windows_NT')
+ # Just to make sure the paths and vars are set properly
+ - powershell: |
+ Write-Host "##vso[task.prependpath]$pwd/llvm-8.0.0-install/bin"
+ Write-Host "##vso[task.setvariable variable=LLVM_SYS_80_PREFIX;]$pwd/llvm-8.0.0-install/"
+ displayName: Install LLVM (Windows)
+ condition: eq(variables['Agent.OS'], 'Windows_NT')
+
- bash: |
set -ex
env
diff --git a/.azure/install-rust.yml b/.azure/install-rust.yml
index 4a7a340c0..243cb7bf9 100644
--- a/.azure/install-rust.yml
+++ b/.azure/install-rust.yml
@@ -6,6 +6,15 @@
# Wasm-bindgen template: https://github.com/rustwasm/wasm-bindgen/blob/master/ci/azure-install-rust.yml
steps:
+ # - bash: |
+ # set -ex
+ # brew install openssl@1.1 curl
+ # brew link openssl@1.1 --force
+ # echo "##vso[task.prependpath]/usr/local/opt/openssl/bin"
+ # echo "##vso[task.setvariable variable=LDFLAGS;]-L/usr/local/opt/openssl/lib"
+ # echo "##vso[task.setvariable variable=CPPFLAGS;]-I/usr/local/opt/openssl/include"
+ # displayName: "Fix Cargo SSL (macOS)"
+ # condition: eq(variables['Agent.OS'], 'Darwin')
- bash: |
set -ex
if [ -x "`command -v rustup`" ]; then
@@ -15,24 +24,24 @@ steps:
echo "##vso[task.prependpath]$HOME/.cargo/bin"
fi
displayName: "Install Rust (Linux, macOS)"
- condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
+ condition: ne(variables['Agent.OS'], 'Windows_NT')
+
+ # - bash: |
+ # set -ex
+ # if [ -x "`command -v rustup`" ]; then
+ # echo `command -v rustup` `rustup -V` installed
+ # else
+ # choco install rust -y
+ # # curl -sSf -o rustup-init.exe https://win.rustup.rs
+ # # ./rustup-init.exe -y --default-toolchain $RUST_TOOLCHAIN
+ # # echo "##vso[task.prependpath]$USERPROFILE/.cargo/bin"
+ # fi
+ # displayName: "Install Rust (Windows)"
+ # condition: eq(variables['Agent.OS'], 'Windows_NT')
- bash: |
set -ex
- if [ -x "`command -v rustup`" ]; then
- echo `command -v rustup` `rustup -V` installed
- else
- choco install rust -y
- # curl -sSf -o rustup-init.exe https://win.rustup.rs
- # ./rustup-init.exe -y --default-toolchain $RUST_TOOLCHAIN
- # echo "##vso[task.prependpath]$USERPROFILE/.cargo/bin"
- fi
- displayName: "Install Rust (Windows)"
- condition: eq(variables['Agent.OS'], 'Windows_NT')
-
- - bash: |
- set -ex
- rustup update $RUST_TOOLCHAIN
+ rustup update --no-self-update $RUST_TOOLCHAIN
rustup default $RUST_TOOLCHAIN
rustc -Vv
diff --git a/.azure/install-sccache.yml b/.azure/install-sccache.yml
index dffbb13d3..fa46bf6b0 100644
--- a/.azure/install-sccache.yml
+++ b/.azure/install-sccache.yml
@@ -28,6 +28,7 @@ steps:
- bash: |
set -ex
env
+ mkdir -p $SCCACHE_DIR
SCCACHE_ERROR_LOG=`pwd`/sccache.log RUST_LOG=debug $RUSTC_WRAPPER --start-server
$RUSTC_WRAPPER -s
cat sccache.log
@@ -35,3 +36,9 @@ steps:
env:
SCCACHE_AZURE_CONNECTION_STRING: $(SCCACHE_AZURE_CONNECTION_STRING)
SCCACHE_AZURE_BLOB_CONTAINER: $(SCCACHE_AZURE_BLOB_CONTAINER)
+ SCCACHE_DIR: $(Pipeline.Workspace)/.sccache
+ - task: CacheBeta@0
+ inputs:
+ key: sccache | $(Agent.OS) | Cargo.lock
+ path: $(Pipeline.Workspace)/.sccache
+ displayName: Cache Cargo Target
diff --git a/Cargo.lock b/Cargo.lock
index ebcfbc882..9746f9922 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,7 +13,7 @@ name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -40,7 +40,7 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -173,16 +173,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cbindgen"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -491,7 +491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -719,7 +719,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -764,7 +764,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -773,7 +773,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -873,7 +873,7 @@ dependencies = [
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -946,7 +946,7 @@ dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1009,7 +1009,7 @@ dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1095,7 +1095,7 @@ name = "remove_dir_all"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1325,7 +1325,7 @@ dependencies = [
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1359,7 +1359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1481,7 +1481,7 @@ version = "2.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1538,7 +1538,7 @@ dependencies = [
"wasmer-runtime-core 0.6.0",
"wasmer-win-exception-handler 0.6.0",
"wasmparser 0.35.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1610,6 +1610,7 @@ dependencies = [
name = "wasmer-llvm-backend"
version = "0.6.0"
dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"capstone 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1624,7 +1625,7 @@ dependencies = [
"wabt 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.6.0",
"wasmparser 0.35.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1666,7 +1667,7 @@ dependencies = [
name = "wasmer-runtime-c-api"
version = "0.6.0"
dependencies = [
- "cbindgen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime 0.6.0",
"wasmer-runtime-core 0.6.0",
@@ -1697,7 +1698,7 @@ dependencies = [
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.35.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1742,7 +1743,7 @@ dependencies = [
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.6.0",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1768,7 +1769,7 @@ dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.6.0",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1792,7 +1793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
-version = "0.3.7"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1814,7 +1815,7 @@ name = "winapi-util"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1827,7 +1828,7 @@ name = "wincolor"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1839,7 +1840,7 @@ dependencies = [
"cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
@@ -1863,7 +1864,7 @@ dependencies = [
"checksum capstone-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fae25eddcb80e24f98c35952c37a91ff7f8d0f60dbbdafb9763e8d5cc566b8d7"
"checksum cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "097f5ce64ba566a83d9d914fd005de1e5937fdd57d8c5d99a7593040955d75a9"
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
-"checksum cbindgen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7e19db9a3892c88c74cbbdcd218196068a928f1b60e736c448b13a1e81f277"
+"checksum cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd"
"checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7"
"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
@@ -2024,7 +2025,7 @@ dependencies = [
"checksum wasmparser 0.35.3 (registry+https://github.com/rust-lang/crates.io-index)" = "099aaf77635ffad3d9ab57253c29b16a46e93755c3e54cfe33fb8cf4b54f760b"
"checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
+"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
diff --git a/Cargo.toml b/Cargo.toml
index 03e224abb..bbd596087 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -99,6 +99,7 @@ backend-singlepass = [
"wasmer-wasi-tests/singlepass"
]
wasi = ["wasmer-wasi"]
+managed = ["backend-singlepass", "wasmer-runtime-core/managed"]
# vfs = ["wasmer-runtime-abi"]
[[example]]
diff --git a/Makefile b/Makefile
index c5a357e6f..007e89853 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: spectests emtests clean build install lint precommit
+.PHONY: spectests emtests clean build install lint precommit docs
# Generate files
generate-spectests:
@@ -97,9 +97,11 @@ llvm: spectests-llvm emtests-llvm wasitests-llvm
capi:
cargo build --release
cargo build -p wasmer-runtime-c-api --release
+
+test-capi: capi
cargo test -p wasmer-runtime-c-api --release
-test-rest: capi
+test-rest:
cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests
circleci-clean:
@@ -182,3 +184,6 @@ publish-release:
# must install graphviz for `dot`
dep-graph:
cargo deps --optional-deps --filter wasmer-wasi wasmer-wasi-tests wasmer-kernel-loader wasmer-dev-utils wasmer-llvm-backend wasmer-emscripten wasmer-emscripten-tests wasmer-runtime-core wasmer-runtime wasmer-middleware-common wasmer-middleware-common-tests wasmer-singlepass-backend wasmer-clif-backend wasmer --manifest-path Cargo.toml | dot -Tpng > wasmer_depgraph.png
+
+docs:
+ cargo doc --features=backend-singlepass,backend-llvm,wasi,managed
diff --git a/README.md b/README.md
index a023b1a2c..e4fa07e05 100644
--- a/README.md
+++ b/README.md
@@ -35,19 +35,19 @@ curl https://get.wasmer.io -sSfL | sh
Wasmer runtime can be used as a library embedded in different languages, so you can use WebAssembly anywhere:
-| | Language | Author(s) | Maintenance | Release |
-|-|-|-|-|-|
-|  | [**Rust**](https://github.com/wasmerio/wasmer-rust-example) | Wasmer | actively developed |   |
-|  | [**C/C++**](https://github.com/wasmerio/wasmer-c-api) | Wasmer | actively developed |   |
-|  | [**Python**](https://github.com/wasmerio/python-ext-wasm) | Wasmer | actively developed |   |
-|  | [**Go**](https://github.com/wasmerio/go-ext-wasm) | Wasmer | actively developed |   |
-|  | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed |   |
-|  | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed |   |
-|  | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed |   |
-|  | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed |   |
-|  | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed |  |
-|  | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintened |  |
-| ❓ | [your language is missing?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) | | |
+| | Language | Author(s) | Maintenance | Release | Stars |
+|-|-|-|-|-|-|
+|  | [**Rust**](https://github.com/wasmerio/wasmer-rust-example) | Wasmer | actively developed |  |  |
+|  | [**C/C++**](https://github.com/wasmerio/wasmer-c-api) | Wasmer | actively developed |  |  |
+|  | [**Python**](https://github.com/wasmerio/python-ext-wasm) | Wasmer | actively developed |  |  |
+|  | [**Go**](https://github.com/wasmerio/go-ext-wasm) | Wasmer | actively developed |  |  |
+|  | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | Wasmer | actively developed |  |  |
+|  | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | Wasmer | actively developed |  |  |
+|  | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | Wasmer | actively developed |  |  |
+|  | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Miguel de Icaza](https://github.com/migueldeicaza) | actively developed |  |  |
+|  | [**R**](https://github.com/dirkschumacher/wasmr) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | |  |
+|  | [**Swift**](https://github.com/markmals/swift-ext-wasm) | [Mark Malström](https://github.com/markmals/) | passively maintened | |  |
+| ❓ | [your language is missing?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) | | | |
### Usage
@@ -177,7 +177,7 @@ nginx and Lua do not work on Windows - you can track the progress on [this issue
## Building
-[](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html)
+[](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html)
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9ae585715..aad922544 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -29,10 +29,14 @@ jobs:
matrix:
linux:
imageName: "ubuntu-16.04"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
mac:
imageName: "macos-10.14"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
+ # By default schannel checks revocation of certificates unlike some other SSL
+ # backends, but we've historically had problems on CI where a revocation
+ # server goes down presumably. See #43333 for more info
+ CARGO_HTTP_CHECK_REVOKE: false
windows:
imageName: "vs2017-win2016"
rust_toolchain: stable
@@ -43,9 +47,9 @@ jobs:
- checkout: self
submodules: true
- template: .azure/install-rust.yml
+ - template: .azure/install-llvm.yml
- template: .azure/install-sccache.yml
- template: .azure/install-cmake.yml
- - template: .azure/install-llvm.yml
- bash: make test
displayName: Tests (*nix)
condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT')))
@@ -55,17 +59,17 @@ jobs:
- job: Check
pool:
- vmImage: "macos-10.14"
+ vmImage: "ubuntu-16.04"
variables:
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
condition: in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/staging', 'refs/heads/trying')
steps:
- checkout: self
submodules: true
- template: .azure/install-rust.yml
+ - template: .azure/install-llvm.yml
- template: .azure/install-sccache.yml
- template: .azure/install-cmake.yml
- - template: .azure/install-llvm.yml
- bash: make check
displayName: Check with Flags
condition: and(succeeded(), not(eq(variables['Agent.OS'], 'Windows_NT')))
@@ -75,10 +79,10 @@ jobs:
matrix:
linux:
imageName: "ubuntu-16.04"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
mac:
imageName: "macos-10.14"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
MACOSX_DEPLOYMENT_TARGET: 10.10
windows:
imageName: "vs2017-win2016"
@@ -91,10 +95,10 @@ jobs:
- checkout: self
submodules: true
- template: .azure/install-rust.yml
+ - template: .azure/install-llvm.yml
- template: .azure/install-sccache.yml
- template: .azure/install-cmake.yml
- template: .azure/install-innosetup.yml
- - template: .azure/install-llvm.yml
- bash: |
mkdir -p artifacts
displayName: Create Artifacts Dir
@@ -116,6 +120,7 @@ jobs:
condition: |
and(
succeeded(),
+ eq(variables['Build.SourceBranch'], 'refs/heads/master'),
not(eq(variables['Agent.OS'], 'Windows_NT'))
)
- bash: |
@@ -137,10 +142,10 @@ jobs:
matrix:
linux:
imageName: "ubuntu-16.04"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
mac:
imageName: "macos-10.14"
- rust_toolchain: nightly-2019-06-10
+ rust_toolchain: nightly-2019-08-15
MACOSX_DEPLOYMENT_TARGET: 10.10
windows:
imageName: "vs2017-win2016"
@@ -153,26 +158,29 @@ jobs:
- checkout: self
submodules: true
- template: .azure/install-rust.yml
+ # - template: .azure/install-llvm.yml
- template: .azure/install-sccache.yml
- template: .azure/install-cmake.yml
- # - template: .azure/install-llvm.yml
- bash: |
mkdir -p artifacts
displayName: Create Artifacts Dir
- bash: |
make capi
+ make test-capi
cp target/release/libwasmer_runtime_c_api.so ./artifacts
displayName: Build c-api (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- bash: |
make capi
+ make test-capi
install_name_tool -id "@rpath/libwasmer_runtime_c_api.dylib" target/release/libwasmer_runtime_c_api.dylib
cp target/release/libwasmer_runtime_c_api.dylib ./artifacts
displayName: Build c-api (Darwin)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
- bash: |
- cargo build --release
- cargo build -p wasmer-runtime-c-api --release
+ make capi
+ # Tests are failing on Windows, comment for now
+ # make test-capi
cp target/release/wasmer_runtime_c_api.dll ./artifacts
displayName: Build c-api (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
@@ -203,6 +211,22 @@ jobs:
tag: dev
assets: $(Build.ArtifactStagingDirectory)
+ - job: Docs
+ pool:
+ vmImage: "ubuntu-16.04"
+ variables:
+ rust_toolchain: nightly-2019-08-15
+ steps:
+ - checkout: self
+ submodules: true
+ - template: .azure/install-rust.yml
+ - template: .azure/install-llvm.yml
+ - template: .azure/install-sccache.yml
+ - template: .azure/install-cmake.yml
+ - bash: |
+ make docs
+ displayName: Build documentation
+
# We only run the pipelines on PRs to Master
pr:
- master
diff --git a/docs/debugging.md b/docs/debugging.md
new file mode 100644
index 000000000..8d49d5217
--- /dev/null
+++ b/docs/debugging.md
@@ -0,0 +1,50 @@
+# Debugging Wasmer
+
+## When is this document useful?
+
+If you're developing wasmer or running into issues, this document will explain to useful techniques and common errors.
+
+## Tracing syscalls
+
+To trace syscalls, compile with the `debug` feature (`cargo build --features "debug"`). For even more verbose messages, use the `trace` flag.
+
+## Tracing calls
+
+TODO: did we disable tracing calls? if not talk about how to enable it
+TODO: someone with more context on the backends mention which backends this works for
+
+If you'd like to see all calls and you're using emscripten, you can use a symbol map to get better error output with the `em-symbol-map` flag.
+
+## Common things that can go wrong
+
+### Missing imports
+
+If, when attempting to run a wasm module, you get an error about missing imports there are a number of things that could be going wrong.
+
+The most likely is that we haven't implemented those imports for your ABI. If you're targeting emscripten, this is probably the issue.
+
+However if that's not the case, then there's a chance that you're using an unsupported ABI (let us know!) or that the wasm is invalid for the detected ABI. (TODO: link to wasm contracts or something)
+
+### Hitting `undefined`
+
+If this happens it's because wasmer does not have full support for whatever feature you tried to use. Running with tracing on can help clarify the issue if it's not clear from the message.
+
+To fix this, file an issue letting us know that wasmer is missing a feature that's important to you. If you'd like, you can try to implement it yourself and send us a PR.
+
+### No output
+
+If you're seeing no output from running the wasm module then it may be that:
+- this is the intended behavior of the wasm module
+- or it's very slow to compile (try compiling with a faster backend like cranelift (the default) or singlepass (requires nightly))
+
+### Segfault
+
+If you're seeing a segfault while developing wasmer, chances are that it's a cache issue. We reset the cache on every version bump, but if you're running it from source then the cache may become invalid, which can lead to segfaults.
+
+To fix this delete the cache with `wasmer cache clean` or run the command with the `disable-cache` flag (`wasmer run some.wasm --disable-cache`)
+
+If you're seeing a segfault with a released version of wasmer, please file an issue so we can ship an updated version as soon as possible.
+
+### Something else
+
+If none of this has helped with your issue, let us know and we'll do our best to help.
diff --git a/examples/iterative_hash/src/main.rs b/examples/iterative_hash/src/main.rs
index f25672047..043bc02a1 100644
--- a/examples/iterative_hash/src/main.rs
+++ b/examples/iterative_hash/src/main.rs
@@ -1,7 +1,13 @@
use blake2::{Blake2b, Digest};
+use std::time::{Duration, SystemTime};
fn main() {
let mut data: Vec = b"test".to_vec();
+ let now = SystemTime::now();
+
+ let mut last_millis: u128 = 0;
+ let mut round_count: usize = 0;
+ let mut record_count: usize = 0;
for i in 0.. {
let mut hasher = Blake2b::new();
@@ -9,8 +15,15 @@ fn main() {
let out = hasher.result();
data = out.to_vec();
- if i % 1000000 == 0 {
- println!("Round {}: {:?}", i, data);
+ if i != 0 && i % 1000 == 0 {
+ let millis = now.elapsed().unwrap().as_millis();
+ let diff = millis - last_millis;
+ if diff >= 100 {
+ record_count += 1;
+ println!("{}", (i - round_count) as f64 / diff as f64);
+ last_millis = millis;
+ round_count = i;
+ }
}
}
}
diff --git a/examples/many_params.wat b/examples/many_params.wat
new file mode 100644
index 000000000..db5e4bb9b
--- /dev/null
+++ b/examples/many_params.wat
@@ -0,0 +1,57 @@
+;; Test case for correctness of reading state with the presence of parameters passed on (machine) stack.
+;; Usage: Run with a backend with support for OSR. Interrupt execution randomly.
+;; Should see the stack frame for `$foo` to have locals `[0] = 1, [1] = 2, [2] = 3, [3] = 4, [4] = 5, [5] = 6, [6] = 7, [7] = 8` with high probability.
+;; If the logic for reading stack parameters is broken, it's likely to see `[0] = 1, [1] = 2, [2] = 3, [3] = 4, [4] = 5, [5] = ?, [6] = ?, [7] = ?`.
+
+(module
+ (import "wasi_unstable" "proc_exit" (func $__wasi_proc_exit (param i32)))
+ (func $long_running
+ (local $count i32)
+ (loop
+ (if (i32.eq (get_local $count) (i32.const 1000000)) (then (return)))
+ (set_local $count (i32.add (i32.const 1) (get_local $count)))
+ (br 0)
+ )
+ (unreachable)
+ )
+
+ (func $foo (param i32) (param i64) (param i32) (param i32) (param i32) (param i64) (param i64) (param i64) (result i32)
+ (set_local 2 (i32.const 3))
+ (call $long_running)
+ (i32.add
+ (i32.mul (i32.const 2) (get_local 0))
+ (i32.add
+ (i32.mul (i32.const 3) (i32.wrap/i64 (get_local 1)))
+ (i32.add
+ (i32.mul (i32.const 5) (get_local 2))
+ (i32.add
+ (i32.mul (i32.const 7) (get_local 3))
+ (i32.add
+ (i32.mul (i32.const 11) (get_local 4))
+ (i32.add
+ (i32.mul (i32.const 13) (i32.wrap/i64 (get_local 5)))
+ (i32.add
+ (i32.mul (i32.const 17) (i32.wrap/i64 (get_local 6)))
+ (i32.mul (i32.const 19) (i32.wrap/i64 (get_local 7)))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (func $_start (export "_start")
+ (local $count i32)
+ (loop
+ (if (i32.eq (get_local $count) (i32.const 10000)) (then (return)))
+ (set_local $count (i32.add (i32.const 1) (get_local $count)))
+ (call $foo (i32.const 1) (i64.const 2) (i32.const 30) (i32.const 4) (i32.const 5) (i64.const 6) (i64.const 7) (i64.const 8))
+ (if (i32.ne (i32.const 455))
+ (then unreachable)
+ )
+ (br 0)
+ )
+ (unreachable)
+ )
+)
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 5c37cb47e..229a36e7f 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -10,6 +10,8 @@ cargo-fuzz = true
[dependencies]
wasmer-runtime = { path = "../lib/runtime" }
+wasmer-runtime-core = { path = "../lib/runtime-core" }
+wasmer = { path = "../" }
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" }
# Prevent this from interfering with workspaces
@@ -19,3 +21,7 @@ members = ["."]
[[bin]]
name = "simple_instantiate"
path = "fuzz_targets/simple_instantiate.rs"
+
+[[bin]]
+name = "validate_wasm"
+path = "fuzz_targets/validate_wasm.rs"
diff --git a/fuzz/README.md b/fuzz/README.md
index cac0a320a..dda80ce7a 100644
--- a/fuzz/README.md
+++ b/fuzz/README.md
@@ -10,12 +10,16 @@ $ cargo install cargo-fuzz
`cargo-fuzz` is documented in the [Rust Fuzz Book](https://rust-fuzz.github.io/book/cargo-fuzz.html).
-## Running a fuzzer
+## Running a fuzzer (simple_instantiate, validate_wasm)
Once `cargo-fuzz` is installed, you can run the `simple_instantiate` fuzzer with
```sh
cargo fuzz run simple_instantiate
```
+or the `validate_wasm` fuzzer
+```sh
+cargo fuzz run validate_wasm
+```
You should see output that looks something like this:
diff --git a/fuzz/fuzz_targets/simple_instantiate.rs b/fuzz/fuzz_targets/simple_instantiate.rs
index 831bbb1a5..e4912546d 100644
--- a/fuzz/fuzz_targets/simple_instantiate.rs
+++ b/fuzz/fuzz_targets/simple_instantiate.rs
@@ -1,11 +1,9 @@
#![no_main]
-#[macro_use] extern crate libfuzzer_sys;
+#[macro_use]
+extern crate libfuzzer_sys;
extern crate wasmer_runtime;
-use wasmer_runtime::{
- instantiate,
- imports,
-};
+use wasmer_runtime::{imports, instantiate};
fuzz_target!(|data: &[u8]| {
let import_object = imports! {};
diff --git a/fuzz/fuzz_targets/validate_wasm.rs b/fuzz/fuzz_targets/validate_wasm.rs
new file mode 100644
index 000000000..f386105ec
--- /dev/null
+++ b/fuzz/fuzz_targets/validate_wasm.rs
@@ -0,0 +1,20 @@
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+
+extern crate wasmer;
+extern crate wasmer_runtime_core;
+
+use wasmer_runtime_core::backend::Features;
+
+fuzz_target!(|data: &[u8]| {
+ let _ = wasmer::utils::is_wasm_binary(data);
+ let _ = wasmer_runtime_core::validate_and_report_errors_with_features(
+ &data,
+ Features {
+ // Modify these values to explore additional parts of wasmer.
+ simd: false,
+ threads: false,
+ },
+ );
+});
diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml
index d0bf64d7b..a07c86899 100644
--- a/lib/clif-backend/Cargo.toml
+++ b/lib/clif-backend/Cargo.toml
@@ -34,7 +34,7 @@ version = "0.11.2"
version = "0.0.7"
[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3.7", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
+winapi = { version = "0.3.8", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.6.0" }
[features]
diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs
index eed22d39e..60f19a877 100644
--- a/lib/clif-backend/src/lib.rs
+++ b/lib/clif-backend/src/lib.rs
@@ -7,6 +7,9 @@
unused_unsafe,
unreachable_patterns
)]
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
mod cache;
mod code;
mod libcalls;
diff --git a/lib/dev-utils/src/lib.rs b/lib/dev-utils/src/lib.rs
index 76ae6804f..2a470bba4 100644
--- a/lib/dev-utils/src/lib.rs
+++ b/lib/dev-utils/src/lib.rs
@@ -1,2 +1,5 @@
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
pub mod file_descriptor;
pub mod stdio;
diff --git a/lib/emscripten-tests/emtests/ignores.txt b/lib/emscripten-tests/emtests/ignores.txt
index 16ff1a2a7..af8f21b00 100644
--- a/lib/emscripten-tests/emtests/ignores.txt
+++ b/lib/emscripten-tests/emtests/ignores.txt
@@ -29,6 +29,7 @@ test_i16_emcc_intrinsic
test_i64
test_i64_7z
test_i64_varargs
+test_indirectbr_many
test_llvm_intrinsics
test_longjmp_exc
test_lower_intrinsics
diff --git a/lib/emscripten-tests/tests/emtests/test_indirectbr_many.rs b/lib/emscripten-tests/tests/emtests/test_indirectbr_many.rs
index fba25e6b2..a96fe2138 100644
--- a/lib/emscripten-tests/tests/emtests/test_indirectbr_many.rs
+++ b/lib/emscripten-tests/tests/emtests/test_indirectbr_many.rs
@@ -1,4 +1,5 @@
#[test]
+#[ignore]
fn test_test_indirectbr_many() {
assert_emscripten_output!(
"../../emtests/test_indirectbr_many.wasm",
diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs
index c09f9b772..ba25b4be8 100644
--- a/lib/emscripten/src/lib.rs
+++ b/lib/emscripten/src/lib.rs
@@ -7,6 +7,9 @@
unused_unsafe,
unreachable_patterns
)]
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
#[macro_use]
extern crate wasmer_runtime_core;
diff --git a/lib/kernel-loader/src/lib.rs b/lib/kernel-loader/src/lib.rs
index 51ca6c7da..37f51a424 100644
--- a/lib/kernel-loader/src/lib.rs
+++ b/lib/kernel-loader/src/lib.rs
@@ -1,3 +1,6 @@
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
pub mod service;
use service::{ImportInfo, LoadProfile, RunProfile, ServiceContext, TableEntryRequest};
diff --git a/lib/kernel-net/src/lib.rs b/lib/kernel-net/src/lib.rs
index 4686867a7..cc6f91820 100644
--- a/lib/kernel-net/src/lib.rs
+++ b/lib/kernel-net/src/lib.rs
@@ -1,5 +1,7 @@
#![cfg(all(target_arch = "wasm32", target_os = "wasi"))]
#![feature(wasi_ext)]
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
use std::cell::RefCell;
use std::fs::File;
diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml
index 886df698b..56adae97b 100644
--- a/lib/llvm-backend/Cargo.toml
+++ b/lib/llvm-backend/Cargo.toml
@@ -12,6 +12,7 @@ smallvec = "0.6.10"
goblin = "0.0.24"
libc = "0.2.60"
capstone = { version = "0.6.0", optional = true }
+byteorder = "1"
[dependencies.inkwell]
git = "https://github.com/wasmerio/inkwell"
@@ -23,7 +24,7 @@ features = ["llvm8-0", "target-x86"]
nix = "0.15.0"
[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3.7", features = ["memoryapi"] }
+winapi = { version = "0.3.8", features = ["memoryapi"] }
[build-dependencies]
cc = "1.0"
diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp
index bf034a308..c4fc6d936 100644
--- a/lib/llvm-backend/cpp/object_loader.cpp
+++ b/lib/llvm-backend/cpp/object_loader.cpp
@@ -1,161 +1,196 @@
#include "object_loader.hh"
#include
#include
+#include
extern "C" void __register_frame(uint8_t *);
extern "C" void __deregister_frame(uint8_t *);
-struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
-public:
- MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
+MemoryManager::~MemoryManager() {
+ deregisterEHFrames();
+ // Deallocate all of the allocated memory.
+ callbacks.dealloc_memory(code_section.base, code_section.size);
+ callbacks.dealloc_memory(read_section.base, read_section.size);
+ callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
+}
+void unwinding_setjmp(jmp_buf stack_out, void (*func)(void *), void *userdata) {
+ if (setjmp(stack_out)) {
- virtual ~MemoryManager() override {
- deregisterEHFrames();
- // Deallocate all of the allocated memory.
- callbacks.dealloc_memory(code_section.base, code_section.size);
- callbacks.dealloc_memory(read_section.base, read_section.size);
- callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
+ } else {
+ func(userdata);
}
+}
- virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment,
- unsigned section_id,
- llvm::StringRef section_name) override {
- return allocate_bump(code_section, code_bump_ptr, size, alignment);
+[[noreturn]] void unwinding_longjmp(jmp_buf stack_in) { longjmp(stack_in, 42); }
+
+struct UnwindPoint {
+ UnwindPoint *prev;
+ jmp_buf stack;
+ std::function *f;
+ std::unique_ptr exception;
+};
+
+static thread_local UnwindPoint *unwind_state = nullptr;
+
+static void unwind_payload(void *_point) {
+ UnwindPoint *point = (UnwindPoint *)_point;
+ (*point->f)();
+}
+
+void catch_unwind(std::function &&f) {
+ UnwindPoint current;
+ current.prev = unwind_state;
+ current.f = &f;
+ unwind_state = ¤t;
+
+ unwinding_setjmp(current.stack, unwind_payload, (void *)¤t);
+
+ unwind_state = current.prev;
+ if (current.exception) {
+ throw std::move(current.exception);
}
+}
- virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment,
- unsigned section_id,
- llvm::StringRef section_name,
- bool read_only) override {
- // Allocate from the read-only section or the read-write section, depending
- // on if this allocation should be read-only or not.
- if (read_only) {
- return allocate_bump(read_section, read_bump_ptr, size, alignment);
- } else {
- return allocate_bump(readwrite_section, readwrite_bump_ptr, size,
- alignment);
+void unsafe_unwind(WasmException *exception) {
+ UnwindPoint *state = unwind_state;
+ if (state) {
+ state->exception.reset(exception);
+ unwinding_longjmp(state->stack);
+ } else {
+ abort();
+ }
+}
+
+uint8_t *MemoryManager::allocateCodeSection(uintptr_t size, unsigned alignment,
+ unsigned section_id,
+ llvm::StringRef section_name) {
+ return allocate_bump(code_section, code_bump_ptr, size, alignment);
+}
+
+uint8_t *MemoryManager::allocateDataSection(uintptr_t size, unsigned alignment,
+ unsigned section_id,
+ llvm::StringRef section_name,
+ bool read_only) {
+ // Allocate from the read-only section or the read-write section, depending
+ // on if this allocation should be read-only or not.
+ uint8_t *ret;
+ if (read_only) {
+ ret = allocate_bump(read_section, read_bump_ptr, size, alignment);
+ } else {
+ ret = allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment);
+ }
+ if (section_name.equals(llvm::StringRef("__llvm_stackmaps")) ||
+ section_name.equals(llvm::StringRef(".llvm_stackmaps"))) {
+ stack_map_ptr = ret;
+ stack_map_size = size;
+ }
+ return ret;
+}
+
+void MemoryManager::reserveAllocationSpace(uintptr_t code_size,
+ uint32_t code_align,
+ uintptr_t read_data_size,
+ uint32_t read_data_align,
+ uintptr_t read_write_data_size,
+ uint32_t read_write_data_align) {
+ auto aligner = [](uintptr_t ptr, size_t align) {
+ if (ptr == 0) {
+ return align;
}
- }
+ return (ptr + align - 1) & ~(align - 1);
+ };
+ uint8_t *code_ptr_out = nullptr;
+ size_t code_size_out = 0;
+ auto code_result =
+ callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE,
+ &code_ptr_out, &code_size_out);
+ assert(code_result == RESULT_OK);
+ code_section = Section{code_ptr_out, code_size_out};
+ code_bump_ptr = (uintptr_t)code_ptr_out;
+ code_start_ptr = (uintptr_t)code_ptr_out;
+ this->code_size = code_size;
- virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align,
- uintptr_t read_data_size,
- uint32_t read_data_align,
- uintptr_t read_write_data_size,
- uint32_t read_write_data_align) override {
- auto aligner = [](uintptr_t ptr, size_t align) {
- if (ptr == 0) {
- return align;
- }
- return (ptr + align - 1) & ~(align - 1);
- };
+ uint8_t *read_ptr_out = nullptr;
+ size_t read_size_out = 0;
+ auto read_result =
+ callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE,
+ &read_ptr_out, &read_size_out);
+ assert(read_result == RESULT_OK);
+ read_section = Section{read_ptr_out, read_size_out};
+ read_bump_ptr = (uintptr_t)read_ptr_out;
- uint8_t *code_ptr_out = nullptr;
- size_t code_size_out = 0;
- auto code_result =
- callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE,
- &code_ptr_out, &code_size_out);
- assert(code_result == RESULT_OK);
- code_section = Section{code_ptr_out, code_size_out};
- code_bump_ptr = (uintptr_t)code_ptr_out;
+ uint8_t *readwrite_ptr_out = nullptr;
+ size_t readwrite_size_out = 0;
+ auto readwrite_result = callbacks.alloc_memory(
+ aligner(read_write_data_size, 4096), PROTECT_READ_WRITE,
+ &readwrite_ptr_out, &readwrite_size_out);
+ assert(readwrite_result == RESULT_OK);
+ readwrite_section = Section{readwrite_ptr_out, readwrite_size_out};
+ readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
+}
- uint8_t *read_ptr_out = nullptr;
- size_t read_size_out = 0;
- auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096),
- PROTECT_READ_WRITE, &read_ptr_out,
- &read_size_out);
- assert(read_result == RESULT_OK);
- read_section = Section{read_ptr_out, read_size_out};
- read_bump_ptr = (uintptr_t)read_ptr_out;
+bool MemoryManager::needsToReserveAllocationSpace() { return true; }
- uint8_t *readwrite_ptr_out = nullptr;
- size_t readwrite_size_out = 0;
- auto readwrite_result = callbacks.alloc_memory(
- aligner(read_write_data_size, 4096), PROTECT_READ_WRITE,
- &readwrite_ptr_out, &readwrite_size_out);
- assert(readwrite_result == RESULT_OK);
- readwrite_section = Section{readwrite_ptr_out, readwrite_size_out};
- readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
- }
-
- /* Turn on the `reserveAllocationSpace` callback. */
- virtual bool needsToReserveAllocationSpace() override { return true; }
-
- virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
- size_t size) override {
+void MemoryManager::registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
+ size_t size) {
// We don't know yet how to do this on Windows, so we hide this on compilation
// so we can compile and pass spectests on unix systems
#ifndef _WIN32
- eh_frame_ptr = addr;
- eh_frame_size = size;
- eh_frames_registered = true;
- callbacks.visit_fde(addr, size, __register_frame);
+ eh_frame_ptr = addr;
+ eh_frame_size = size;
+ eh_frames_registered = true;
+ callbacks.visit_fde(addr, size, __register_frame);
#endif
- }
+}
- virtual void deregisterEHFrames() override {
+void MemoryManager::deregisterEHFrames() {
// We don't know yet how to do this on Windows, so we hide this on compilation
// so we can compile and pass spectests on unix systems
#ifndef _WIN32
- if (eh_frames_registered) {
- callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
- }
-#endif
+ if (eh_frames_registered) {
+ callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
}
+#endif
+}
- virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override {
- auto code_result =
- callbacks.protect_memory(code_section.base, code_section.size,
- mem_protect_t::PROTECT_READ_EXECUTE);
- if (code_result != RESULT_OK) {
- return false;
- }
-
- auto read_result = callbacks.protect_memory(
- read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
- if (read_result != RESULT_OK) {
- return false;
- }
-
- // The readwrite section is already mapped as read-write.
-
+bool MemoryManager::finalizeMemory(std::string *ErrMsg) {
+ auto code_result =
+ callbacks.protect_memory(code_section.base, code_section.size,
+ mem_protect_t::PROTECT_READ_EXECUTE);
+ if (code_result != RESULT_OK) {
return false;
}
- virtual void
- notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
- const llvm::object::ObjectFile &Obj) override {}
-
-private:
- struct Section {
- uint8_t *base;
- size_t size;
- };
-
- uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size,
- size_t align) {
- auto aligner = [](uintptr_t &ptr, size_t align) {
- ptr = (ptr + align - 1) & ~(align - 1);
- };
-
- // Align the bump pointer to the requires alignment.
- aligner(bump_ptr, align);
-
- auto ret_ptr = bump_ptr;
- bump_ptr += size;
-
- assert(bump_ptr <= (uintptr_t)section.base + section.size);
-
- return (uint8_t *)ret_ptr;
+ auto read_result = callbacks.protect_memory(
+ read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
+ if (read_result != RESULT_OK) {
+ return false;
}
- Section code_section, read_section, readwrite_section;
- uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
- uint8_t *eh_frame_ptr;
- size_t eh_frame_size;
- bool eh_frames_registered = false;
+ // The readwrite section is already mapped as read-write.
- callbacks_t callbacks;
-};
+ return false;
+}
+
+void MemoryManager::notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
+ const llvm::object::ObjectFile &Obj) {}
+
+uint8_t *MemoryManager::allocate_bump(Section §ion, uintptr_t &bump_ptr,
+ size_t size, size_t align) {
+ auto aligner = [](uintptr_t &ptr, size_t align) {
+ ptr = (ptr + align - 1) & ~(align - 1);
+ };
+
+ // Align the bump pointer to the requires alignment.
+ aligner(bump_ptr, align);
+
+ auto ret_ptr = bump_ptr;
+ bump_ptr += size;
+
+ assert(bump_ptr <= (uintptr_t)section.base + section.size);
+
+ return (uint8_t *)ret_ptr;
+}
struct SymbolLookup : llvm::JITSymbolResolver {
public:
@@ -218,3 +253,19 @@ void *WasmModule::get_func(llvm::StringRef name) const {
auto symbol = runtime_dyld->getSymbol(name);
return (void *)symbol.getAddress();
}
+
+uint8_t *WasmModule::get_stack_map_ptr() const {
+ return memory_manager->get_stack_map_ptr();
+}
+
+size_t WasmModule::get_stack_map_size() const {
+ return memory_manager->get_stack_map_size();
+}
+
+uint8_t *WasmModule::get_code_ptr() const {
+ return memory_manager->get_code_ptr();
+}
+
+size_t WasmModule::get_code_size() const {
+ return memory_manager->get_code_size();
+}
diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh
index 7a410b2dc..bc9b9ab67 100644
--- a/lib/llvm-backend/cpp/object_loader.hh
+++ b/lib/llvm-backend/cpp/object_loader.hh
@@ -1,7 +1,12 @@
+#pragma once
+
#include
#include
#include
+#include
#include
+#include
+#include
#include
#include
@@ -48,11 +53,92 @@ typedef struct {
size_t data, vtable;
} box_any_t;
-struct WasmException {
-public:
- virtual std::string description() const noexcept = 0;
+enum WasmTrapType {
+ Unreachable = 0,
+ IncorrectCallIndirectSignature = 1,
+ MemoryOutOfBounds = 2,
+ CallIndirectOOB = 3,
+ IllegalArithmetic = 4,
+ Unknown,
};
+extern "C" void callback_trampoline(void *, void *);
+
+struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
+public:
+ MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
+ virtual ~MemoryManager() override;
+
+ inline uint8_t *get_stack_map_ptr() const { return stack_map_ptr; }
+ inline size_t get_stack_map_size() const { return stack_map_size; }
+ inline uint8_t *get_code_ptr() const { return (uint8_t *)code_start_ptr; }
+ inline size_t get_code_size() const { return code_size; }
+
+ virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment,
+ unsigned section_id,
+ llvm::StringRef section_name) override;
+ virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment,
+ unsigned section_id,
+ llvm::StringRef section_name,
+ bool read_only) override;
+ virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align,
+ uintptr_t read_data_size,
+ uint32_t read_data_align,
+ uintptr_t read_write_data_size,
+ uint32_t read_write_data_align) override;
+ /* Turn on the `reserveAllocationSpace` callback. */
+ virtual bool needsToReserveAllocationSpace() override;
+ virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
+ size_t size) override;
+ virtual void deregisterEHFrames() override;
+ virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override;
+ virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
+ const llvm::object::ObjectFile &Obj) override;
+
+private:
+ struct Section {
+ uint8_t *base;
+ size_t size;
+ };
+
+ uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size,
+ size_t align);
+
+ Section code_section, read_section, readwrite_section;
+ uintptr_t code_start_ptr;
+ size_t code_size;
+ uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
+ uint8_t *eh_frame_ptr;
+ size_t eh_frame_size;
+ bool eh_frames_registered = false;
+
+ callbacks_t callbacks;
+
+ uint8_t *stack_map_ptr = nullptr;
+ size_t stack_map_size = 0;
+};
+
+struct WasmErrorSink {
+ WasmTrapType *trap_out;
+ box_any_t *user_error;
+};
+
+struct WasmException : std::exception {
+public:
+ virtual std::string description() const noexcept { return "unknown"; }
+
+ virtual const char *what() const noexcept override {
+ return "wasm exception";
+ }
+
+ virtual void write_error(WasmErrorSink &out) const noexcept {
+ *out.trap_out = WasmTrapType::Unknown;
+ }
+};
+
+void catch_unwind(std::function &&f);
+[[noreturn]] void unsafe_unwind(WasmException *exception);
+
struct UncatchableException : WasmException {
public:
virtual std::string description() const noexcept override {
@@ -70,6 +156,10 @@ public:
// The parts of a `Box`.
box_any_t error_data;
+
+ virtual void write_error(WasmErrorSink &out) const noexcept override {
+ *out.user_error = error_data;
+ }
};
struct BreakpointException : UncatchableException {
@@ -81,20 +171,35 @@ public:
}
uintptr_t callback;
+
+ virtual void write_error(WasmErrorSink &out) const noexcept override {
+ puts("CB TRAMPOLINE");
+ callback_trampoline(out.user_error, (void *)callback);
+ }
+};
+
+struct WasmModule {
+public:
+ WasmModule(const uint8_t *object_start, size_t object_size,
+ callbacks_t callbacks);
+
+ void *get_func(llvm::StringRef name) const;
+ uint8_t *get_stack_map_ptr() const;
+ size_t get_stack_map_size() const;
+ uint8_t *get_code_ptr() const;
+ size_t get_code_size() const;
+
+ bool _init_failed = false;
+
+private:
+ std::unique_ptr memory_manager;
+ std::unique_ptr object_file;
+ std::unique_ptr runtime_dyld;
};
struct WasmTrap : UncatchableException {
public:
- enum Type {
- Unreachable = 0,
- IncorrectCallIndirectSignature = 1,
- MemoryOutOfBounds = 2,
- CallIndirectOOB = 3,
- IllegalArithmetic = 4,
- Unknown,
- };
-
- WasmTrap(Type type) : type(type) {}
+ WasmTrap(WasmTrapType type) : type(type) {}
virtual std::string description() const noexcept override {
std::ostringstream ss;
@@ -103,27 +208,31 @@ public:
return ss.str();
}
- Type type;
+ WasmTrapType type;
+
+ virtual void write_error(WasmErrorSink &out) const noexcept override {
+ *out.trap_out = type;
+ }
private:
- friend std::ostream &operator<<(std::ostream &out, const Type &ty) {
+ friend std::ostream &operator<<(std::ostream &out, const WasmTrapType &ty) {
switch (ty) {
- case Type::Unreachable:
+ case WasmTrapType::Unreachable:
out << "unreachable";
break;
- case Type::IncorrectCallIndirectSignature:
+ case WasmTrapType::IncorrectCallIndirectSignature:
out << "incorrect call_indirect signature";
break;
- case Type::MemoryOutOfBounds:
+ case WasmTrapType::MemoryOutOfBounds:
out << "memory access out-of-bounds";
break;
- case Type::CallIndirectOOB:
+ case WasmTrapType::CallIndirectOOB:
out << "call_indirect out-of-bounds";
break;
- case Type::IllegalArithmetic:
+ case WasmTrapType::IllegalArithmetic:
out << "illegal arithmetic operation";
break;
- case Type::Unknown:
+ case WasmTrapType::Unknown:
default:
out << "unknown";
break;
@@ -145,23 +254,7 @@ public:
uint64_t values[1];
};
-struct WasmModule {
-public:
- WasmModule(const uint8_t *object_start, size_t object_size,
- callbacks_t callbacks);
-
- void *get_func(llvm::StringRef name) const;
-
- bool _init_failed = false;
-
-private:
- std::unique_ptr memory_manager;
- std::unique_ptr object_file;
- std::unique_ptr runtime_dyld;
-};
-
extern "C" {
-void callback_trampoline(void *, void *);
result_t module_load(const uint8_t *mem_ptr, size_t mem_size,
callbacks_t callbacks, WasmModule **module_out) {
@@ -174,42 +267,40 @@ result_t module_load(const uint8_t *mem_ptr, size_t mem_size,
return RESULT_OK;
}
-[[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); }
+[[noreturn]] void throw_trap(WasmTrapType ty) {
+ unsafe_unwind(new WasmTrap(ty));
+}
void module_delete(WasmModule *module) { delete module; }
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
// side.
[[noreturn]] void throw_any(size_t data, size_t vtable) {
- throw UserException(data, vtable);
+ unsafe_unwind(new UserException(data, vtable));
}
// Throw a pointer that's assumed to be codegen::BreakpointHandler on the
// rust side.
[[noreturn]] void throw_breakpoint(uintptr_t callback) {
- throw BreakpointException(callback);
+ unsafe_unwind(new BreakpointException(callback));
}
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
- void *params, void *results, WasmTrap::Type *trap_out,
+ void *params, void *results, WasmTrapType *trap_out,
box_any_t *user_error, void *invoke_env) noexcept {
try {
- trampoline(ctx, func, params, results);
+ catch_unwind([trampoline, ctx, func, params, results]() {
+ trampoline(ctx, func, params, results);
+ });
return true;
- } catch (const WasmTrap &e) {
- *trap_out = e.type;
- return false;
- } catch (const UserException &e) {
- *user_error = e.error_data;
- return false;
- } catch (const BreakpointException &e) {
- callback_trampoline(user_error, (void *)e.callback);
- return false;
- } catch (const WasmException &e) {
- *trap_out = WasmTrap::Type::Unknown;
+ } catch (std::unique_ptr &e) {
+ WasmErrorSink sink;
+ sink.trap_out = trap_out;
+ sink.user_error = user_error;
+ e->write_error(sink);
return false;
} catch (...) {
- *trap_out = WasmTrap::Type::Unknown;
+ *trap_out = WasmTrapType::Unknown;
return false;
}
}
@@ -217,4 +308,20 @@ bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
void *get_func_symbol(WasmModule *module, const char *name) {
return module->get_func(llvm::StringRef(name));
}
+
+const uint8_t *llvm_backend_get_stack_map_ptr(const WasmModule *module) {
+ return module->get_stack_map_ptr();
+}
+
+size_t llvm_backend_get_stack_map_size(const WasmModule *module) {
+ return module->get_stack_map_size();
+}
+
+const uint8_t *llvm_backend_get_code_ptr(const WasmModule *module) {
+ return module->get_code_ptr();
+}
+
+size_t llvm_backend_get_code_size(const WasmModule *module) {
+ return module->get_code_size();
+}
}
diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs
index 92f0f06a1..a0fb80c03 100644
--- a/lib/llvm-backend/src/backend.rs
+++ b/lib/llvm-backend/src/backend.rs
@@ -1,3 +1,4 @@
+use super::stackmap::StackmapRegistry;
use crate::intrinsics::Intrinsics;
use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect};
use inkwell::{
@@ -25,6 +26,7 @@ use wasmer_runtime_core::{
},
cache::Error as CacheError,
module::ModuleInfo,
+ state::ModuleStateMap,
structures::TypedIndex,
typed_func::{Wasm, WasmTrapInfo},
types::{LocalFuncIndex, SigIndex},
@@ -40,6 +42,10 @@ extern "C" {
) -> LLVMResult;
fn module_delete(module: *mut LLVMModule);
fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func;
+ fn llvm_backend_get_stack_map_ptr(module: *const LLVMModule) -> *const u8;
+ fn llvm_backend_get_stack_map_size(module: *const LLVMModule) -> usize;
+ fn llvm_backend_get_code_ptr(module: *const LLVMModule) -> *const u8;
+ fn llvm_backend_get_code_size(module: *const LLVMModule) -> usize;
fn throw_trap(ty: i32) -> !;
fn throw_breakpoint(ty: i64) -> !;
@@ -63,6 +69,8 @@ extern "C" {
) -> bool;
}
+static SIGNAL_HANDLER_INSTALLED: Once = Once::new();
+
fn get_callbacks() -> Callbacks {
extern "C" fn alloc_memory(
size: usize,
@@ -154,10 +162,17 @@ pub struct LLVMBackend {
module: *mut LLVMModule,
#[allow(dead_code)]
buffer: Arc,
+ msm: Option,
+ local_func_id_to_offset: Vec,
}
impl LLVMBackend {
- pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMCache) {
+ pub fn new(
+ module: Module,
+ _intrinsics: Intrinsics,
+ _stackmaps: &StackmapRegistry,
+ _module_info: &ModuleInfo,
+ ) -> (Self, LLVMCache) {
Target::initialize_x86(&InitializationConfig {
asm_parser: true,
asm_printer: true,
@@ -204,22 +219,134 @@ impl LLVMBackend {
)
};
- static SIGNAL_HANDLER_INSTALLED: Once = Once::new();
-
- SIGNAL_HANDLER_INSTALLED.call_once(|| unsafe {
- crate::platform::install_signal_handler();
- });
-
if res != LLVMResult::OK {
panic!("failed to load object")
}
let buffer = Arc::new(Buffer::LlvmMemory(memory_buffer));
+ #[cfg(all(any(target_os = "linux", target_os = "macos"), target_arch = "x86_64"))]
+ {
+ use super::stackmap::{self, StkMapRecord, StkSizeRecord};
+ use std::collections::BTreeMap;
+
+ let stackmaps = _stackmaps;
+ let module_info = _module_info;
+
+ let raw_stackmap = unsafe {
+ std::slice::from_raw_parts(
+ llvm_backend_get_stack_map_ptr(module),
+ llvm_backend_get_stack_map_size(module),
+ )
+ };
+ if raw_stackmap.len() > 0 {
+ let map = stackmap::StackMap::parse(raw_stackmap).unwrap();
+
+ let (code_ptr, code_size) = unsafe {
+ (
+ llvm_backend_get_code_ptr(module),
+ llvm_backend_get_code_size(module),
+ )
+ };
+ let mut msm = ModuleStateMap {
+ local_functions: Default::default(),
+ total_size: code_size,
+ };
+
+ let num_local_functions =
+ module_info.func_assoc.len() - module_info.imported_functions.len();
+ let mut local_func_id_to_addr: Vec = Vec::with_capacity(num_local_functions);
+
+ // All local functions.
+ for index in module_info.imported_functions.len()..module_info.func_assoc.len() {
+ let name = if cfg!(target_os = "macos") {
+ format!("_fn{}", index)
+ } else {
+ format!("fn{}", index)
+ };
+
+ let c_str = CString::new(name).unwrap();
+ let ptr = unsafe { get_func_symbol(module, c_str.as_ptr()) };
+
+ assert!(!ptr.is_null());
+ local_func_id_to_addr.push(ptr as usize);
+ }
+
+ let mut addr_to_size_record: BTreeMap = BTreeMap::new();
+
+ for record in &map.stk_size_records {
+ addr_to_size_record.insert(record.function_address as usize, record);
+ }
+
+ let mut map_records: BTreeMap = BTreeMap::new();
+
+ for record in &map.stk_map_records {
+ map_records.insert(record.patchpoint_id as usize, record);
+ }
+
+ for ((start_id, start_entry), (end_id, end_entry)) in stackmaps
+ .entries
+ .iter()
+ .enumerate()
+ .step_by(2)
+ .zip(stackmaps.entries.iter().enumerate().skip(1).step_by(2))
+ {
+ if let Some(map_record) = map_records.get(&start_id) {
+ assert_eq!(start_id, map_record.patchpoint_id as usize);
+ assert!(start_entry.is_start);
+ assert!(!end_entry.is_start);
+
+ let end_record = map_records.get(&end_id);
+
+ let addr = local_func_id_to_addr[start_entry.local_function_id];
+ let size_record = *addr_to_size_record
+ .get(&addr)
+ .expect("size_record not found");
+
+ start_entry.populate_msm(
+ module_info,
+ code_ptr as usize,
+ &map,
+ size_record,
+ map_record,
+ end_record.map(|x| (end_entry, *x)),
+ &mut msm,
+ );
+ } else {
+ // The record is optimized out.
+ }
+ }
+
+ let code_ptr = unsafe { llvm_backend_get_code_ptr(module) } as usize;
+ let code_len = unsafe { llvm_backend_get_code_size(module) } as usize;
+
+ let local_func_id_to_offset: Vec = local_func_id_to_addr
+ .iter()
+ .map(|&x| {
+ assert!(x >= code_ptr && x < code_ptr + code_len);
+ x - code_ptr
+ })
+ .collect();
+
+ return (
+ Self {
+ module,
+ buffer: Arc::clone(&buffer),
+ msm: Some(msm),
+ local_func_id_to_offset,
+ },
+ LLVMCache { buffer },
+ );
+ }
+ }
+
+ // Stackmap is not supported on this platform, or this module contains no functions so no stackmaps.
(
Self {
module,
buffer: Arc::clone(&buffer),
+ msm: None,
+ local_func_id_to_offset: vec![],
},
LLVMCache { buffer },
)
@@ -237,8 +364,6 @@ impl LLVMBackend {
return Err("failed to load object".to_string());
}
- static SIGNAL_HANDLER_INSTALLED: Once = Once::new();
-
SIGNAL_HANDLER_INSTALLED.call_once(|| {
crate::platform::install_signal_handler();
});
@@ -249,6 +374,8 @@ impl LLVMBackend {
Self {
module,
buffer: Arc::clone(&buffer),
+ msm: None,
+ local_func_id_to_offset: vec![],
},
LLVMCache { buffer },
))
@@ -300,9 +427,30 @@ impl RunnableModule for LLVMBackend {
mem::transmute(symbol)
};
+ SIGNAL_HANDLER_INSTALLED.call_once(|| unsafe {
+ crate::platform::install_signal_handler();
+ });
+
Some(unsafe { Wasm::from_raw_parts(trampoline, invoke_trampoline, None) })
}
+ fn get_code(&self) -> Option<&[u8]> {
+ Some(unsafe {
+ std::slice::from_raw_parts(
+ llvm_backend_get_code_ptr(self.module),
+ llvm_backend_get_code_size(self.module),
+ )
+ })
+ }
+
+ fn get_local_function_offsets(&self) -> Option> {
+ Some(self.local_func_id_to_offset.clone())
+ }
+
+ fn get_module_state_map(&self) -> Option {
+ self.msm.clone()
+ }
+
unsafe fn do_early_trap(&self, data: Box) -> ! {
throw_any(Box::leak(data))
}
diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs
index 1f78ccc5b..206e6f37a 100644
--- a/lib/llvm-backend/src/code.rs
+++ b/lib/llvm-backend/src/code.rs
@@ -11,6 +11,8 @@ use inkwell::{
AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate,
};
use smallvec::SmallVec;
+use std::cell::RefCell;
+use std::rc::Rc;
use std::sync::{Arc, RwLock};
use wasmer_runtime_core::{
backend::{Backend, CacheGen, Token},
@@ -28,10 +30,16 @@ use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType};
use crate::backend::LLVMBackend;
use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache};
use crate::read_info::{blocktype_to_type, type_to_type};
+use crate::stackmap::{StackmapEntry, StackmapEntryKind, StackmapRegistry, ValueSemantic};
use crate::state::{ControlFrame, IfElseState, State};
use crate::trampolines::generate_trampolines;
-fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType {
+fn func_sig_to_llvm(
+ context: &Context,
+ intrinsics: &Intrinsics,
+ sig: &FuncSig,
+ type_to_llvm: fn(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum,
+) -> FunctionType {
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
let param_types: Vec<_> = std::iter::once(intrinsics.ctx_ptr_ty.as_basic_type_enum())
@@ -64,6 +72,14 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum {
}
}
+fn type_to_llvm_int_only(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum {
+ match ty {
+ Type::I32 | Type::F32 => intrinsics.i32_ty.as_basic_type_enum(),
+ Type::I64 | Type::F64 => intrinsics.i64_ty.as_basic_type_enum(),
+ Type::V128 => intrinsics.i128_ty.as_basic_type_enum(),
+ }
+}
+
// Create a vector where each lane contains the same value.
fn splat_vector(
builder: &Builder,
@@ -491,6 +507,90 @@ fn resolve_memory_ptr(
Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name()))
}
+fn emit_stack_map(
+ _module_info: &ModuleInfo,
+ intrinsics: &Intrinsics,
+ builder: &Builder,
+ local_function_id: usize,
+ target: &mut StackmapRegistry,
+ kind: StackmapEntryKind,
+ locals: &[PointerValue],
+ state: &State,
+ _ctx: &mut CtxType,
+ opcode_offset: usize,
+) {
+ let stackmap_id = target.entries.len();
+
+ let mut params = Vec::with_capacity(2 + locals.len() + state.stack.len());
+
+ params.push(
+ intrinsics
+ .i64_ty
+ .const_int(stackmap_id as u64, false)
+ .as_basic_value_enum(),
+ );
+ params.push(intrinsics.i32_ty.const_int(0, false).as_basic_value_enum());
+
+ let locals: Vec<_> = locals.iter().map(|x| x.as_basic_value_enum()).collect();
+ let mut value_semantics: Vec =
+ Vec::with_capacity(locals.len() + state.stack.len());
+
+ params.extend_from_slice(&locals);
+ value_semantics.extend((0..locals.len()).map(ValueSemantic::WasmLocal));
+
+ params.extend_from_slice(&state.stack);
+ value_semantics.extend((0..state.stack.len()).map(ValueSemantic::WasmStack));
+
+ // FIXME: Information needed for Abstract -> Runtime state transform is not fully preserved
+ // to accelerate compilation and reduce memory usage. Check this again when we try to support
+ // "full" LLVM OSR.
+
+ assert_eq!(params.len(), value_semantics.len() + 2);
+
+ builder.build_call(intrinsics.experimental_stackmap, ¶ms, &state.var_name());
+
+ target.entries.push(StackmapEntry {
+ kind,
+ local_function_id,
+ local_count: locals.len(),
+ stack_count: state.stack.len(),
+ opcode_offset,
+ value_semantics,
+ is_start: true,
+ });
+}
+
+fn finalize_opcode_stack_map(
+ intrinsics: &Intrinsics,
+ builder: &Builder,
+ local_function_id: usize,
+ target: &mut StackmapRegistry,
+ kind: StackmapEntryKind,
+ opcode_offset: usize,
+) {
+ let stackmap_id = target.entries.len();
+ builder.build_call(
+ intrinsics.experimental_stackmap,
+ &[
+ intrinsics
+ .i64_ty
+ .const_int(stackmap_id as u64, false)
+ .as_basic_value_enum(),
+ intrinsics.i32_ty.const_int(0, false).as_basic_value_enum(),
+ ],
+ "opcode_stack_map_end",
+ );
+ target.entries.push(StackmapEntry {
+ kind,
+ local_function_id,
+ local_count: 0,
+ stack_count: 0,
+ opcode_offset,
+ value_semantics: vec![],
+ is_start: false,
+ });
+}
+
fn trap_if_misaligned(
builder: &Builder,
intrinsics: &Intrinsics,
@@ -575,6 +675,7 @@ pub struct LLVMModuleCodeGenerator {
func_import_count: usize,
personality_func: FunctionValue,
module: Module,
+ stackmaps: Rc>,
}
pub struct LLVMFunctionCodeGenerator {
@@ -589,6 +690,9 @@ pub struct LLVMFunctionCodeGenerator {
num_params: usize,
ctx: Option>,
unreachable_depth: usize,
+ stackmaps: Rc>,
+ index: usize,
+ opcode_offset: usize,
}
impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
@@ -658,6 +762,35 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
let ctx = CtxType::new(module_info, function, cache_builder);
self.ctx = Some(ctx);
+
+ {
+ let state = &mut self.state;
+ let builder = self.builder.as_ref().unwrap();
+ let intrinsics = self.intrinsics.as_ref().unwrap();
+
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ emit_stack_map(
+ &module_info,
+ &intrinsics,
+ &builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::FunctionHeader,
+ &self.locals,
+ &state,
+ self.ctx.as_mut().unwrap(),
+ ::std::usize::MAX,
+ );
+ finalize_opcode_stack_map(
+ &intrinsics,
+ &builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::FunctionHeader,
+ ::std::usize::MAX,
+ );
+ }
+
Ok(())
}
@@ -672,9 +805,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
let signatures = &self.signatures;
let mut ctx = self.ctx.as_mut().unwrap();
+ let mut opcode_offset: Option = None;
let op = match event {
- Event::Wasm(x) => x,
- Event::WasmOwned(ref x) => x,
+ Event::Wasm(x) => {
+ opcode_offset = Some(self.opcode_offset);
+ self.opcode_offset += 1;
+ x
+ }
Event::Internal(x) => {
match x {
InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {
@@ -709,6 +846,7 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
}
return Ok(());
}
+ Event::WasmOwned(ref x) => x,
};
if !state.reachable {
@@ -779,6 +917,35 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
};
builder.position_at_end(&loop_body);
+
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ emit_stack_map(
+ &info,
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Loop,
+ &self.locals,
+ state,
+ ctx,
+ offset,
+ );
+ let signal_mem = ctx.signal_mem();
+ let iv = builder
+ .build_store(signal_mem, context.i8_type().const_int(0 as u64, false));
+ iv.set_volatile(true);
+ finalize_opcode_stack_map(
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Loop,
+ offset,
+ );
+ }
+
state.push_loop(loop_body, loop_next, phis);
}
Operator::Br { relative_depth } => {
@@ -1040,6 +1207,33 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
// If llvm cannot prove that this is never touched,
// it will emit a `ud2` instruction on x86_64 arches.
+ // Comment out this `if` block to allow spectests to pass.
+ // TODO: fix this
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ emit_stack_map(
+ &info,
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Trappable,
+ &self.locals,
+ state,
+ ctx,
+ offset,
+ );
+ builder.build_call(intrinsics.trap, &[], "trap");
+ finalize_opcode_stack_map(
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Trappable,
+ offset,
+ );
+ }
+
builder.build_call(
intrinsics.throw_trap,
&[intrinsics.trap_unreachable],
@@ -1228,26 +1422,59 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
let llvm_sig = signatures[sigindex];
let func_sig = &info.signatures[sigindex];
- let call_site = match func_index.local_or_import(info) {
+ let (params, func_ptr) = match func_index.local_or_import(info) {
LocalOrImport::Local(local_func_index) => {
- let params: Vec<_> = [ctx.basic()]
- .iter()
- .chain(state.peekn(func_sig.params().len())?.iter())
- .map(|v| *v)
+ let params: Vec<_> = std::iter::once(ctx.basic())
+ .chain(
+ state
+ .peekn(func_sig.params().len())?
+ .iter()
+ .enumerate()
+ .map(|(i, &v)| match func_sig.params()[i] {
+ Type::F32 => builder.build_bitcast(
+ v,
+ intrinsics.i32_ty,
+ &state.var_name(),
+ ),
+ Type::F64 => builder.build_bitcast(
+ v,
+ intrinsics.i64_ty,
+ &state.var_name(),
+ ),
+ _ => v,
+ }),
+ )
.collect();
let func_ptr =
ctx.local_func(local_func_index, llvm_sig, intrinsics, builder);
- builder.build_call(func_ptr, ¶ms, &state.var_name())
+ (params, func_ptr)
}
LocalOrImport::Import(import_func_index) => {
let (func_ptr_untyped, ctx_ptr) =
ctx.imported_func(import_func_index, intrinsics);
- let params: Vec<_> = [ctx_ptr.as_basic_value_enum()]
- .iter()
- .chain(state.peekn(func_sig.params().len())?.iter())
- .map(|v| *v)
+
+ let params: Vec<_> = std::iter::once(ctx_ptr.as_basic_value_enum())
+ .chain(
+ state
+ .peekn(func_sig.params().len())?
+ .iter()
+ .enumerate()
+ .map(|(i, &v)| match func_sig.params()[i] {
+ Type::F32 => builder.build_bitcast(
+ v,
+ intrinsics.i32_ty,
+ &state.var_name(),
+ ),
+ Type::F64 => builder.build_bitcast(
+ v,
+ intrinsics.i64_ty,
+ &state.var_name(),
+ ),
+ _ => v,
+ }),
+ )
.collect();
let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic);
@@ -1258,15 +1485,50 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
"typed_func_ptr",
);
- builder.build_call(func_ptr, ¶ms, &state.var_name())
+ (params, func_ptr)
}
};
state.popn(func_sig.params().len())?;
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ emit_stack_map(
+ &info,
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Call,
+ &self.locals,
+ state,
+ ctx,
+ offset,
+ )
+ }
+ let call_site = builder.build_call(func_ptr, ¶ms, &state.var_name());
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ finalize_opcode_stack_map(
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Call,
+ offset,
+ )
+ }
if let Some(basic_value) = call_site.try_as_basic_value().left() {
match func_sig.returns().len() {
- 1 => state.push1(basic_value),
+ 1 => state.push1(match func_sig.returns()[0] {
+ Type::F32 => {
+ builder.build_bitcast(basic_value, intrinsics.f32_ty, "ret_cast")
+ }
+ Type::F64 => {
+ builder.build_bitcast(basic_value, intrinsics.f64_ty, "ret_cast")
+ }
+ _ => basic_value,
+ }),
count @ _ => {
// This is a multi-value return.
let struct_value = basic_value.into_struct_value();
@@ -1418,7 +1680,17 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?;
let args: Vec<_> = std::iter::once(ctx_ptr)
- .chain(pushed_args.into_iter())
+ .chain(pushed_args.into_iter().enumerate().map(|(i, v)| {
+ match wasmer_fn_sig.params()[i] {
+ Type::F32 => {
+ builder.build_bitcast(v, intrinsics.i32_ty, &state.var_name())
+ }
+ Type::F64 => {
+ builder.build_bitcast(v, intrinsics.i64_ty, &state.var_name())
+ }
+ _ => v,
+ }
+ }))
.collect();
let typed_func_ptr = builder.build_pointer_cast(
@@ -1427,13 +1699,47 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
"typed_func_ptr",
);
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ emit_stack_map(
+ &info,
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Call,
+ &self.locals,
+ state,
+ ctx,
+ offset,
+ )
+ }
let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call");
+ if let Some(offset) = opcode_offset {
+ let mut stackmaps = self.stackmaps.borrow_mut();
+ finalize_opcode_stack_map(
+ intrinsics,
+ builder,
+ self.index,
+ &mut *stackmaps,
+ StackmapEntryKind::Call,
+ offset,
+ )
+ }
match wasmer_fn_sig.returns() {
[] => {}
[_] => {
let value = call_site.try_as_basic_value().left().unwrap();
- state.push1(value);
+ state.push1(match wasmer_fn_sig.returns()[0] {
+ Type::F32 => {
+ builder.build_bitcast(value, intrinsics.f32_ty, "ret_cast")
+ }
+ Type::F64 => {
+ builder.build_bitcast(value, intrinsics.f64_ty, "ret_cast")
+ }
+ _ => value,
+ });
}
_ => unimplemented!("multi-value returns"),
}
@@ -6538,7 +6844,13 @@ impl FunctionCodeGenerator for LLVMFunctionCodeGenerator {
self.builder.as_ref().unwrap().build_return(None);
}
[one_value] => {
- self.builder.as_ref().unwrap().build_return(Some(one_value));
+ let builder = self.builder.as_ref().unwrap();
+ let intrinsics = self.intrinsics.as_ref().unwrap();
+ builder.build_return(Some(&builder.build_bitcast(
+ one_value.as_basic_value_enum(),
+ type_to_llvm_int_only(intrinsics, self.func_sig.returns()[0]),
+ "return",
+ )));
}
_ => unimplemented!("multi-value returns not yet implemented"),
}
@@ -6583,6 +6895,7 @@ impl ModuleCodeGenerator
function_signatures: None,
func_import_count: 0,
personality_func,
+ stackmaps: Rc::new(RefCell::new(StackmapRegistry::default())),
}
}
@@ -6646,15 +6959,27 @@ impl ModuleCodeGenerator
.skip(1)
.enumerate()
.map(|(index, param)| {
- let ty = param.get_type();
+ //let ty = param.get_type();
+ let real_ty = func_sig.params()[index];
+ let real_ty_llvm = type_to_llvm(&intrinsics, real_ty);
- let alloca = builder.build_alloca(ty, &format!("local{}", index));
- builder.build_store(alloca, param);
+ let alloca = builder.build_alloca(real_ty_llvm, &format!("local{}", index));
+
+ //if real_ty_llvm != ty {
+ builder.build_store(
+ alloca,
+ builder.build_bitcast(param, real_ty_llvm, &state.var_name()),
+ );
+ /*} else {
+ builder.build_store(alloca, param);
+ }*/
alloca
}),
);
let num_params = locals.len();
+ let local_func_index = self.functions.len();
+
let code = LLVMFunctionCodeGenerator {
state,
context: Some(context),
@@ -6667,6 +6992,9 @@ impl ModuleCodeGenerator
num_params,
ctx: None,
unreachable_depth: 0,
+ stackmaps: self.stackmaps.clone(),
+ index: local_func_index,
+ opcode_offset: 0,
};
self.functions.push(code);
Ok(self.functions.last_mut().unwrap())
@@ -6728,7 +7056,14 @@ impl ModuleCodeGenerator
self.module.print_to_file(path).unwrap();
}
- let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap());
+ let stackmaps = self.stackmaps.borrow();
+
+ let (backend, cache_gen) = LLVMBackend::new(
+ self.module,
+ self.intrinsics.take().unwrap(),
+ &*stackmaps,
+ module_info,
+ );
Ok((backend, Box::new(cache_gen)))
}
@@ -6740,6 +7075,7 @@ impl ModuleCodeGenerator
self.context.as_ref().unwrap(),
self.intrinsics.as_ref().unwrap(),
sig,
+ type_to_llvm_int_only,
)
})
.collect();
diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs
index 22f1b3cfd..278e087bb 100644
--- a/lib/llvm-backend/src/intrinsics.rs
+++ b/lib/llvm-backend/src/intrinsics.rs
@@ -151,6 +151,8 @@ pub struct Intrinsics {
pub throw_trap: FunctionValue,
pub throw_breakpoint: FunctionValue,
+ pub experimental_stackmap: FunctionValue,
+
pub ctx_ptr_ty: PointerType,
}
@@ -528,6 +530,17 @@ impl Intrinsics {
void_ty.fn_type(&[i32_ty_basic], false),
None,
),
+ experimental_stackmap: module.add_function(
+ "llvm.experimental.stackmap",
+ void_ty.fn_type(
+ &[
+ i64_ty_basic, /* id */
+ i32_ty_basic, /* numShadowBytes */
+ ],
+ true,
+ ),
+ None,
+ ),
throw_breakpoint: module.add_function(
"vm.breakpoint",
void_ty.fn_type(&[i64_ty_basic], false),
@@ -606,6 +619,8 @@ pub struct CtxType<'a> {
info: &'a ModuleInfo,
cache_builder: Builder,
+ cached_signal_mem: Option,
+
cached_memories: HashMap,
cached_tables: HashMap,
cached_sigindices: HashMap,
@@ -631,6 +646,8 @@ impl<'a> CtxType<'a> {
info,
cache_builder,
+ cached_signal_mem: None,
+
cached_memories: HashMap::new(),
cached_tables: HashMap::new(),
cached_sigindices: HashMap::new(),
@@ -645,6 +662,27 @@ impl<'a> CtxType<'a> {
self.ctx_ptr_value.as_basic_value_enum()
}
+ pub fn signal_mem(&mut self) -> PointerValue {
+ if let Some(x) = self.cached_signal_mem {
+ return x;
+ }
+
+ let (ctx_ptr_value, cache_builder) = (self.ctx_ptr_value, &self.cache_builder);
+
+ let ptr_ptr = unsafe {
+ cache_builder.build_struct_gep(
+ ctx_ptr_value,
+ offset_to_index(Ctx::offset_interrupt_signal_mem()),
+ "interrupt_signal_mem_ptr",
+ )
+ };
+ let ptr = cache_builder
+ .build_load(ptr_ptr, "interrupt_signal_mem")
+ .into_pointer_value();
+ self.cached_signal_mem = Some(ptr);
+ ptr
+ }
+
pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache {
let (cached_memories, info, ctx_ptr_value, cache_builder) = (
&mut self.cached_memories,
@@ -718,12 +756,11 @@ impl<'a> CtxType<'a> {
})
}
- pub fn table(
+ pub fn table_prepare(
&mut self,
index: TableIndex,
intrinsics: &Intrinsics,
- builder: &Builder,
- ) -> (PointerValue, IntValue) {
+ ) -> (PointerValue, PointerValue) {
let (cached_tables, info, ctx_ptr_value, cache_builder) = (
&mut self.cached_tables,
self.info,
@@ -782,6 +819,16 @@ impl<'a> CtxType<'a> {
}
});
+ (ptr_to_base_ptr, ptr_to_bounds)
+ }
+
+ pub fn table(
+ &mut self,
+ index: TableIndex,
+ intrinsics: &Intrinsics,
+ builder: &Builder,
+ ) -> (PointerValue, IntValue) {
+ let (ptr_to_base_ptr, ptr_to_bounds) = self.table_prepare(index, intrinsics);
(
builder
.build_load(ptr_to_base_ptr, "base_ptr")
diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs
index 454fad100..8194f797a 100644
--- a/lib/llvm-backend/src/lib.rs
+++ b/lib/llvm-backend/src/lib.rs
@@ -1,5 +1,4 @@
#![deny(
- dead_code,
nonstandard_style,
unused_imports,
unused_mut,
@@ -7,13 +6,17 @@
unused_unsafe,
unreachable_patterns
)]
+#![cfg_attr(not(target_os = "windows"), deny(dead_code))]
#![cfg_attr(nightly, feature(unwind_attributes))]
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
mod backend;
mod code;
mod intrinsics;
mod platform;
mod read_info;
+mod stackmap;
mod state;
mod structs;
mod trampolines;
diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs
index a07afa130..88ceb6854 100644
--- a/lib/llvm-backend/src/platform/unix.rs
+++ b/lib/llvm-backend/src/platform/unix.rs
@@ -4,7 +4,9 @@ use libc::{
c_void, mmap, mprotect, munmap, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE,
PROT_READ, PROT_WRITE,
};
-use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV};
+use nix::sys::signal::{
+ sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGILL, SIGSEGV,
+};
use std::ptr;
/// `__register_frame` and `__deregister_frame` on macos take a single fde as an
@@ -57,6 +59,7 @@ pub unsafe fn install_signal_handler() {
);
sigaction(SIGSEGV, &sa).unwrap();
sigaction(SIGBUS, &sa).unwrap();
+ sigaction(SIGILL, &sa).unwrap();
}
#[cfg_attr(nightly, unwind(allowed))]
@@ -66,6 +69,9 @@ extern "C" fn signal_trap_handler(
_ucontext: *mut c_void,
) {
unsafe {
+ if SigSet::all().thread_unblock().is_err() {
+ std::process::abort();
+ }
// Apparently, we can unwind from arbitary instructions, as long
// as we don't need to catch the exception inside the function that
// was interrupted.
diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs
new file mode 100644
index 000000000..a56c3c6a3
--- /dev/null
+++ b/lib/llvm-backend/src/stackmap.rs
@@ -0,0 +1,568 @@
+// https://llvm.org/docs/StackMaps.html#stackmap-section
+
+use byteorder::{LittleEndian, ReadBytesExt};
+use std::io::{self, Cursor};
+use wasmer_runtime_core::vm::Ctx;
+use wasmer_runtime_core::{
+ module::ModuleInfo,
+ structures::TypedIndex,
+ types::{GlobalIndex, LocalOrImport, TableIndex},
+};
+
+#[derive(Default, Debug, Clone)]
+pub struct StackmapRegistry {
+ pub entries: Vec,
+}
+
+#[derive(Debug, Clone)]
+pub struct StackmapEntry {
+ pub kind: StackmapEntryKind,
+ pub local_function_id: usize,
+ pub opcode_offset: usize,
+ pub value_semantics: Vec,
+ pub local_count: usize,
+ pub stack_count: usize,
+ pub is_start: bool,
+}
+
+#[derive(Debug, Clone)]
+pub enum ValueSemantic {
+ WasmLocal(usize),
+ WasmStack(usize),
+ Ctx,
+ SignalMem,
+ PointerToMemoryBase,
+ PointerToMemoryBound, // 64-bit
+ MemoryBase,
+ MemoryBound, // 64-bit
+ PointerToGlobal(usize),
+ Global(usize),
+ PointerToTableBase,
+ PointerToTableBound,
+ ImportedFuncPointer(usize),
+ ImportedFuncCtx(usize),
+ DynamicSigindice(usize),
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum StackmapEntryKind {
+ FunctionHeader,
+ Loop,
+ Call,
+ Trappable,
+}
+
+impl StackmapEntry {
+ #[cfg(all(any(target_os = "linux", target_os = "macos"), target_arch = "x86_64"))]
+ pub fn populate_msm(
+ &self,
+ module_info: &ModuleInfo,
+ code_addr: usize,
+ llvm_map: &StackMap,
+ size_record: &StkSizeRecord,
+ map_record: &StkMapRecord,
+ end: Option<(&StackmapEntry, &StkMapRecord)>,
+ msm: &mut wasmer_runtime_core::state::ModuleStateMap,
+ ) {
+ use std::collections::{BTreeMap, HashMap};
+ use wasmer_runtime_core::state::{
+ x64::{new_machine_state, X64Register, GPR},
+ FunctionStateMap, MachineStateDiff, MachineValue, OffsetInfo, RegisterIndex,
+ SuspendOffset, WasmAbstractValue,
+ };
+ use wasmer_runtime_core::vm;
+
+ let func_base_addr = (size_record.function_address as usize)
+ .checked_sub(code_addr)
+ .unwrap();
+ let target_offset = func_base_addr + map_record.instruction_offset as usize;
+ assert!(self.is_start);
+
+ if msm.local_functions.len() == self.local_function_id {
+ assert_eq!(self.kind, StackmapEntryKind::FunctionHeader);
+ msm.local_functions.insert(
+ target_offset,
+ FunctionStateMap::new(new_machine_state(), self.local_function_id, 0, vec![]),
+ );
+ } else if msm.local_functions.len() == self.local_function_id + 1 {
+ } else {
+ panic!("unordered local functions");
+ }
+
+ let (_, fsm) = msm.local_functions.iter_mut().last().unwrap();
+
+ assert_eq!(self.value_semantics.len(), map_record.locations.len());
+
+ // System V requires 16-byte alignment before each call instruction.
+ // Considering the saved rbp we need to ensure the stack size % 16 always equals to 8.
+ assert!(size_record.stack_size % 16 == 8);
+
+ // Layout begins just below saved rbp. (push rbp; mov rbp, rsp)
+ let mut machine_stack_half_layout: Vec =
+ vec![MachineValue::Undefined; (size_record.stack_size - 8) as usize / 4];
+ let mut regs: Vec<(RegisterIndex, MachineValue)> = vec![];
+ let mut stack_constants: HashMap = HashMap::new();
+
+ let mut prev_frame_diff: BTreeMap> = BTreeMap::new();
+
+ let mut wasm_locals: Vec = vec![];
+ let mut wasm_stack: Vec = vec![];
+
+ for (i, loc) in map_record.locations.iter().enumerate() {
+ let mv = match self.value_semantics[i] {
+ ValueSemantic::WasmLocal(x) => {
+ if x != wasm_locals.len() {
+ panic!("unordered local values");
+ }
+ wasm_locals.push(WasmAbstractValue::Runtime);
+ MachineValue::WasmLocal(x)
+ }
+ ValueSemantic::WasmStack(x) => {
+ if x != wasm_stack.len() {
+ panic!("unordered stack values");
+ }
+ wasm_stack.push(WasmAbstractValue::Runtime);
+ MachineValue::WasmStack(x)
+ }
+ ValueSemantic::Ctx => MachineValue::Vmctx,
+ ValueSemantic::SignalMem => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_interrupt_signal_mem() as usize, 0])
+ }
+ ValueSemantic::PointerToMemoryBase => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_memory_base() as usize])
+ }
+ ValueSemantic::PointerToMemoryBound => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_memory_bound() as usize])
+ }
+ ValueSemantic::MemoryBase => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_memory_base() as usize, 0])
+ }
+ ValueSemantic::MemoryBound => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_memory_bound() as usize, 0])
+ }
+ ValueSemantic::PointerToGlobal(idx) => {
+ MachineValue::VmctxDeref(deref_global(module_info, idx, false))
+ }
+ ValueSemantic::Global(idx) => {
+ MachineValue::VmctxDeref(deref_global(module_info, idx, true))
+ }
+ ValueSemantic::PointerToTableBase => {
+ MachineValue::VmctxDeref(deref_table_base(module_info, 0, false))
+ }
+ ValueSemantic::PointerToTableBound => {
+ MachineValue::VmctxDeref(deref_table_bound(module_info, 0, false))
+ }
+ ValueSemantic::ImportedFuncPointer(idx) => MachineValue::VmctxDeref(vec![
+ Ctx::offset_imported_funcs() as usize,
+ vm::ImportedFunc::size() as usize * idx
+ + vm::ImportedFunc::offset_func() as usize,
+ 0,
+ ]),
+ ValueSemantic::ImportedFuncCtx(idx) => MachineValue::VmctxDeref(vec![
+ Ctx::offset_imported_funcs() as usize,
+ vm::ImportedFunc::size() as usize * idx
+ + vm::ImportedFunc::offset_vmctx() as usize,
+ 0,
+ ]),
+ ValueSemantic::DynamicSigindice(idx) => {
+ MachineValue::VmctxDeref(vec![Ctx::offset_signatures() as usize, idx * 4, 0])
+ }
+ };
+ match loc.ty {
+ LocationType::Register => {
+ let index = X64Register::from_dwarf_regnum(loc.dwarf_regnum)
+ .expect("invalid regnum")
+ .to_index();
+ regs.push((index, mv));
+ }
+ LocationType::Constant => {
+ let v = loc.offset_or_small_constant as u32 as u64;
+ match mv {
+ MachineValue::WasmStack(x) => {
+ stack_constants.insert(x, v);
+ *wasm_stack.last_mut().unwrap() = WasmAbstractValue::Const(v);
+ }
+ _ => {} // TODO
+ }
+ }
+ LocationType::ConstantIndex => {
+ let v =
+ llvm_map.constants[loc.offset_or_small_constant as usize].large_constant;
+ match mv {
+ MachineValue::WasmStack(x) => {
+ stack_constants.insert(x, v);
+ *wasm_stack.last_mut().unwrap() = WasmAbstractValue::Const(v);
+ }
+ _ => {} // TODO
+ }
+ }
+ LocationType::Direct => match mv {
+ MachineValue::WasmLocal(_) => {
+ assert_eq!(loc.location_size, 8); // the pointer itself
+ assert!(
+ X64Register::from_dwarf_regnum(loc.dwarf_regnum).unwrap()
+ == X64Register::GPR(GPR::RBP)
+ );
+ if loc.offset_or_small_constant >= 0 {
+ assert!(loc.offset_or_small_constant >= 16); // (saved_rbp, return_address)
+ assert!(loc.offset_or_small_constant % 8 == 0);
+ prev_frame_diff
+ .insert((loc.offset_or_small_constant as usize - 16) / 8, Some(mv));
+ } else {
+ let stack_offset = ((-loc.offset_or_small_constant) / 4) as usize;
+ assert!(
+ stack_offset > 0 && stack_offset <= machine_stack_half_layout.len()
+ );
+ machine_stack_half_layout[stack_offset - 1] = mv;
+ }
+ }
+ _ => unreachable!(
+ "Direct location type is not expected for values other than local"
+ ),
+ },
+ LocationType::Indirect => {
+ assert!(loc.offset_or_small_constant < 0);
+ assert!(
+ X64Register::from_dwarf_regnum(loc.dwarf_regnum).unwrap()
+ == X64Register::GPR(GPR::RBP)
+ );
+ let stack_offset = ((-loc.offset_or_small_constant) / 4) as usize;
+ assert!(stack_offset > 0 && stack_offset <= machine_stack_half_layout.len());
+ machine_stack_half_layout[stack_offset - 1] = mv;
+ }
+ }
+ }
+
+ assert_eq!(wasm_stack.len(), self.stack_count);
+ assert_eq!(wasm_locals.len(), self.local_count);
+
+ let mut machine_stack_layout: Vec =
+ Vec::with_capacity(machine_stack_half_layout.len() / 2);
+
+ for i in 0..machine_stack_half_layout.len() / 2 {
+ let major = &machine_stack_half_layout[i * 2 + 1]; // mod 8 == 0
+ let minor = &machine_stack_half_layout[i * 2]; // mod 8 == 4
+ let only_major = match *minor {
+ MachineValue::Undefined => true,
+ _ => false,
+ };
+ if only_major {
+ machine_stack_layout.push(major.clone());
+ } else {
+ machine_stack_layout.push(MachineValue::TwoHalves(Box::new((
+ major.clone(),
+ minor.clone(),
+ ))));
+ }
+ }
+
+ let diff = MachineStateDiff {
+ last: None,
+ stack_push: machine_stack_layout,
+ stack_pop: 0,
+ prev_frame_diff,
+ reg_diff: regs,
+ wasm_stack_push: wasm_stack,
+ wasm_stack_pop: 0,
+ wasm_stack_private_depth: 0,
+ wasm_inst_offset: self.opcode_offset,
+ };
+ let diff_id = fsm.diffs.len();
+ fsm.diffs.push(diff);
+
+ match self.kind {
+ StackmapEntryKind::FunctionHeader => {
+ fsm.locals = wasm_locals;
+ }
+ _ => {
+ assert_eq!(fsm.locals, wasm_locals);
+ }
+ }
+
+ let end_offset = {
+ if let Some(end) = end {
+ let (end_entry, end_record) = end;
+ assert_eq!(end_entry.is_start, false);
+ assert_eq!(self.opcode_offset, end_entry.opcode_offset);
+ let end_offset = func_base_addr + end_record.instruction_offset as usize;
+ assert!(end_offset >= target_offset);
+ end_offset
+ } else {
+ target_offset + 1
+ }
+ };
+
+ match self.kind {
+ StackmapEntryKind::Loop => {
+ fsm.wasm_offset_to_target_offset
+ .insert(self.opcode_offset, SuspendOffset::Loop(target_offset));
+ fsm.loop_offsets.insert(
+ target_offset,
+ OffsetInfo {
+ end_offset,
+ diff_id,
+ activate_offset: target_offset,
+ },
+ );
+ }
+ StackmapEntryKind::Call => {
+ fsm.wasm_offset_to_target_offset
+ .insert(self.opcode_offset, SuspendOffset::Call(target_offset));
+ fsm.call_offsets.insert(
+ target_offset,
+ OffsetInfo {
+ end_offset: end_offset + 1, // The return address is just after 'call' instruction. Offset by one here.
+ diff_id,
+ activate_offset: target_offset,
+ },
+ );
+ }
+ StackmapEntryKind::Trappable => {
+ fsm.wasm_offset_to_target_offset
+ .insert(self.opcode_offset, SuspendOffset::Trappable(target_offset));
+ fsm.trappable_offsets.insert(
+ target_offset,
+ OffsetInfo {
+ end_offset,
+ diff_id,
+ activate_offset: target_offset,
+ },
+ );
+ }
+ StackmapEntryKind::FunctionHeader => {
+ fsm.wasm_function_header_target_offset = Some(SuspendOffset::Loop(target_offset));
+ fsm.loop_offsets.insert(
+ target_offset,
+ OffsetInfo {
+ end_offset,
+ diff_id,
+ activate_offset: target_offset,
+ },
+ );
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct StackMap {
+ pub version: u8,
+ pub stk_size_records: Vec,
+ pub constants: Vec,
+ pub stk_map_records: Vec,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct StkSizeRecord {
+ pub function_address: u64,
+ pub stack_size: u64,
+ pub record_count: u64,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct Constant {
+ pub large_constant: u64,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct StkMapRecord {
+ pub patchpoint_id: u64,
+ pub instruction_offset: u32,
+ pub locations: Vec,
+ pub live_outs: Vec,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Location {
+ pub ty: LocationType,
+ pub location_size: u16,
+ pub dwarf_regnum: u16,
+ pub offset_or_small_constant: i32,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct LiveOut {
+ pub dwarf_regnum: u16,
+ pub size_in_bytes: u8,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LocationType {
+ Register,
+ Direct,
+ Indirect,
+ Constant,
+ ConstantIndex,
+}
+
+impl StackMap {
+ pub fn parse(raw: &[u8]) -> io::Result {
+ let mut reader = Cursor::new(raw);
+ let mut map = StackMap::default();
+
+ let version = reader.read_u8()?;
+ if version != 3 {
+ return Err(io::Error::new(io::ErrorKind::Other, "version is not 3"));
+ }
+ map.version = version;
+ if reader.read_u8()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (1)",
+ ));
+ }
+ if reader.read_u16::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (2)",
+ ));
+ }
+ let num_functions = reader.read_u32::()?;
+ let num_constants = reader.read_u32::()?;
+ let num_records = reader.read_u32::()?;
+ for _ in 0..num_functions {
+ let mut record = StkSizeRecord::default();
+ record.function_address = reader.read_u64::()?;
+ record.stack_size = reader.read_u64::()?;
+ record.record_count = reader.read_u64::()?;
+ map.stk_size_records.push(record);
+ }
+ for _ in 0..num_constants {
+ map.constants.push(Constant {
+ large_constant: reader.read_u64::()?,
+ });
+ }
+ for _ in 0..num_records {
+ let mut record = StkMapRecord::default();
+
+ record.patchpoint_id = reader.read_u64::()?;
+ record.instruction_offset = reader.read_u32::()?;
+ if reader.read_u16::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (3)",
+ ));
+ }
+ let num_locations = reader.read_u16::()?;
+ for _ in 0..num_locations {
+ let ty = reader.read_u8()?;
+
+ let mut location = Location {
+ ty: match ty {
+ 1 => LocationType::Register,
+ 2 => LocationType::Direct,
+ 3 => LocationType::Indirect,
+ 4 => LocationType::Constant,
+ 5 => LocationType::ConstantIndex,
+ _ => {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "unknown location type",
+ ))
+ }
+ },
+ location_size: 0,
+ dwarf_regnum: 0,
+ offset_or_small_constant: 0,
+ };
+
+ if reader.read_u8()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (4)",
+ ));
+ }
+ location.location_size = reader.read_u16::()?;
+ location.dwarf_regnum = reader.read_u16::()?;
+ if reader.read_u16::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (5)",
+ ));
+ }
+ location.offset_or_small_constant = reader.read_i32::()?;
+
+ record.locations.push(location);
+ }
+ if reader.position() % 8 != 0 {
+ if reader.read_u32::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (6)",
+ ));
+ }
+ }
+ if reader.read_u16::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (7)",
+ ));
+ }
+ let num_live_outs = reader.read_u16::()?;
+ for _ in 0..num_live_outs {
+ let mut liveout = LiveOut::default();
+
+ liveout.dwarf_regnum = reader.read_u16::()?;
+ if reader.read_u8()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (8)",
+ ));
+ }
+ liveout.size_in_bytes = reader.read_u8()?;
+
+ record.live_outs.push(liveout);
+ }
+ if reader.position() % 8 != 0 {
+ if reader.read_u32::()? != 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "reserved field is not zero (9)",
+ ));
+ }
+ }
+
+ map.stk_map_records.push(record);
+ }
+ Ok(map)
+ }
+}
+
+fn deref_global(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec {
+ let mut x: Vec = match GlobalIndex::new(idx).local_or_import(info) {
+ LocalOrImport::Local(idx) => vec![Ctx::offset_globals() as usize, idx.index() * 8, 0],
+ LocalOrImport::Import(idx) => {
+ vec![Ctx::offset_imported_globals() as usize, idx.index() * 8, 0]
+ }
+ };
+ if deref_into_value {
+ x.push(0);
+ }
+ x
+}
+
+fn deref_table_base(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec {
+ let mut x: Vec = match TableIndex::new(idx).local_or_import(info) {
+ LocalOrImport::Local(idx) => vec![Ctx::offset_tables() as usize, idx.index() * 8, 0],
+ LocalOrImport::Import(idx) => {
+ vec![Ctx::offset_imported_tables() as usize, idx.index() * 8, 0]
+ }
+ };
+ if deref_into_value {
+ x.push(0);
+ }
+ x
+}
+
+fn deref_table_bound(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec {
+ let mut x: Vec = match TableIndex::new(idx).local_or_import(info) {
+ LocalOrImport::Local(idx) => vec![Ctx::offset_tables() as usize, idx.index() * 8, 8],
+ LocalOrImport::Import(idx) => {
+ vec![Ctx::offset_imported_tables() as usize, idx.index() * 8, 8]
+ }
+ };
+ if deref_into_value {
+ x.push(0);
+ }
+ x
+}
diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs
index 47da54a6d..20f915ebc 100644
--- a/lib/llvm-backend/src/state.rs
+++ b/lib/llvm-backend/src/state.rs
@@ -69,7 +69,7 @@ impl ControlFrame {
#[derive(Debug)]
pub struct State {
- stack: Vec,
+ pub stack: Vec,
control_stack: Vec,
value_counter: Cell,
diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs
index 95fd676b9..cebfa9d46 100644
--- a/lib/llvm-backend/src/trampolines.rs
+++ b/lib/llvm-backend/src/trampolines.rs
@@ -68,10 +68,8 @@ fn generate_trampoline(
};
let cast_ptr_ty = |wasmer_ty| match wasmer_ty {
- Type::I32 => intrinsics.i32_ptr_ty,
- Type::I64 => intrinsics.i64_ptr_ty,
- Type::F32 => intrinsics.f32_ptr_ty,
- Type::F64 => intrinsics.f64_ptr_ty,
+ Type::I32 | Type::F32 => intrinsics.i32_ptr_ty,
+ Type::I64 | Type::F64 => intrinsics.i64_ptr_ty,
Type::V128 => intrinsics.i128_ptr_ty,
};
diff --git a/lib/middleware-common-tests/src/lib.rs b/lib/middleware-common-tests/src/lib.rs
index 31b3e1065..0b6754c5f 100644
--- a/lib/middleware-common-tests/src/lib.rs
+++ b/lib/middleware-common-tests/src/lib.rs
@@ -148,5 +148,4 @@ mod tests {
// verify it used the correct number of points
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
}
-
}
diff --git a/lib/middleware-common/src/lib.rs b/lib/middleware-common/src/lib.rs
index 929558f25..c7900c83a 100644
--- a/lib/middleware-common/src/lib.rs
+++ b/lib/middleware-common/src/lib.rs
@@ -7,5 +7,8 @@
unused_unsafe,
unreachable_patterns
)]
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
pub mod call_trace;
pub mod metering;
diff --git a/lib/runtime-abi/src/lib.rs b/lib/runtime-abi/src/lib.rs
index 237e351b1..84923b437 100644
--- a/lib/runtime-abi/src/lib.rs
+++ b/lib/runtime-abi/src/lib.rs
@@ -1,4 +1,8 @@
#![deny(dead_code, unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
+
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
#[cfg(not(target_os = "windows"))]
#[macro_use]
extern crate failure;
diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml
index c95442c62..774411853 100644
--- a/lib/runtime-c-api/Cargo.toml
+++ b/lib/runtime-c-api/Cargo.toml
@@ -32,4 +32,4 @@ llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"]
singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backend-singlepass"]
[build-dependencies]
-cbindgen = "0.9.0"
+cbindgen = "0.9.1"
diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs
index 0fde6d330..4dae18cd5 100644
--- a/lib/runtime-c-api/src/lib.rs
+++ b/lib/runtime-c-api/src/lib.rs
@@ -1,3 +1,6 @@
+#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
+#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
+
//! # Wasmer Runtime C API
//!
//! Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h
index 297428ee2..816dbfeac 100644
--- a/lib/runtime-c-api/wasmer.h
+++ b/lib/runtime-c-api/wasmer.h
@@ -6,6 +6,9 @@
#include
#include
+/**
+ * List of export/import kinds.
+ */
enum wasmer_import_export_kind {
WASM_FUNCTION,
WASM_GLOBAL,
@@ -31,6 +34,9 @@ typedef struct {
} wasmer_module_t;
+/**
+ * Opaque pointer to `NamedExportDescriptor`.
+ */
typedef struct {
} wasmer_export_descriptor_t;
@@ -40,10 +46,16 @@ typedef struct {
uint32_t bytes_len;
} wasmer_byte_array;
+/**
+ * Opaque pointer to `NamedExportDescriptors`.
+ */
typedef struct {
} wasmer_export_descriptors_t;
+/**
+ * Opaque pointer to `wasmer_export_t`.
+ */
typedef struct {
} wasmer_export_func_t;
@@ -60,6 +72,9 @@ typedef struct {
wasmer_value value;
} wasmer_value_t;
+/**
+ * Opaque pointer to `NamedExport`.
+ */
typedef struct {
} wasmer_export_t;
@@ -68,6 +83,9 @@ typedef struct {
} wasmer_memory_t;
+/**
+ * Opaque pointer to `NamedExports`.
+ */
typedef struct {
} wasmer_exports_t;
@@ -101,6 +119,9 @@ typedef struct {
} wasmer_table_t;
+/**
+ * Union of import/export value.
+ */
typedef union {
const wasmer_import_func_t *func;
const wasmer_table_t *table;
diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh
index cf7a1c7b3..afe825364 100644
--- a/lib/runtime-c-api/wasmer.hh
+++ b/lib/runtime-c-api/wasmer.hh
@@ -6,6 +6,7 @@
#include
#include
+/// List of export/import kinds.
enum class wasmer_import_export_kind : uint32_t {
WASM_FUNCTION,
WASM_GLOBAL,
@@ -29,6 +30,7 @@ struct wasmer_module_t {
};
+/// Opaque pointer to `NamedExportDescriptor`.
struct wasmer_export_descriptor_t {
};
@@ -38,10 +40,12 @@ struct wasmer_byte_array {
uint32_t bytes_len;
};
+/// Opaque pointer to `NamedExportDescriptors`.
struct wasmer_export_descriptors_t {
};
+/// Opaque pointer to `wasmer_export_t`.
struct wasmer_export_func_t {
};
@@ -58,6 +62,7 @@ struct wasmer_value_t {
wasmer_value value;
};
+/// Opaque pointer to `NamedExport`.
struct wasmer_export_t {
};
@@ -66,6 +71,7 @@ struct wasmer_memory_t {
};
+/// Opaque pointer to `NamedExports`.
struct wasmer_exports_t {
};
@@ -99,6 +105,7 @@ struct wasmer_table_t {
};
+/// Union of import/export value.
union wasmer_import_export_value {
const wasmer_import_func_t *func;
const wasmer_table_t *table;
diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml
index 5f1370623..6c31cf5f5 100644
--- a/lib/runtime-core/Cargo.toml
+++ b/lib/runtime-core/Cargo.toml
@@ -41,7 +41,7 @@ version = "0.5.6"
version = "0.8.1"
[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3.7", features = ["memoryapi"] }
+winapi = { version = "0.3.8", features = ["memoryapi"] }
[dev-dependencies]
field-offset = "0.1.1"
@@ -58,3 +58,4 @@ trace = ["debug"]
"backend-cranelift" = []
"backend-singlepass" = []
"backend-llvm" = []
+managed = []
\ No newline at end of file
diff --git a/lib/runtime-core/build.rs b/lib/runtime-core/build.rs
index 35aafc835..81884f0e1 100644
--- a/lib/runtime-core/build.rs
+++ b/lib/runtime-core/build.rs
@@ -38,6 +38,5 @@ fn main() {
.file("image-loading-macos-x86-64.s")
.compile("image-loading");
} else {
-
}
}
diff --git a/lib/runtime-core/image-loading-linux-x86-64.s b/lib/runtime-core/image-loading-linux-x86-64.s
index 37ed0f986..1d86bab08 100644
--- a/lib/runtime-core/image-loading-linux-x86-64.s
+++ b/lib/runtime-core/image-loading-linux-x86-64.s
@@ -67,3 +67,37 @@ popq %r13
popq %r14
popq %r15
retq
+
+# For switching into a backend without information about where registers are preserved.
+.globl register_preservation_trampoline
+register_preservation_trampoline:
+subq $8, %rsp
+pushq %rax
+pushq %rcx
+pushq %rdx
+pushq %rdi
+pushq %rsi
+pushq %r8
+pushq %r9
+pushq %r10
+
+callq get_boundary_register_preservation
+
+# Keep this consistent with BoundaryRegisterPreservation
+movq %r15, 0(%rax)
+movq %r14, 8(%rax)
+movq %r13, 16(%rax)
+movq %r12, 24(%rax)
+movq %rbx, 32(%rax)
+
+popq %r10
+popq %r9
+popq %r8
+popq %rsi
+popq %rdi
+popq %rdx
+popq %rcx
+popq %rax
+addq $8, %rsp
+
+jmpq *%rax
diff --git a/lib/runtime-core/image-loading-macos-x86-64.s b/lib/runtime-core/image-loading-macos-x86-64.s
index a6a307f1f..ef6f94510 100644
--- a/lib/runtime-core/image-loading-macos-x86-64.s
+++ b/lib/runtime-core/image-loading-macos-x86-64.s
@@ -67,3 +67,37 @@ popq %r13
popq %r14
popq %r15
retq
+
+# For switching into a backend without information about where registers are preserved.
+.globl _register_preservation_trampoline
+_register_preservation_trampoline:
+subq $8, %rsp
+pushq %rax
+pushq %rcx
+pushq %rdx
+pushq %rdi
+pushq %rsi
+pushq %r8
+pushq %r9
+pushq %r10
+
+callq _get_boundary_register_preservation
+
+# Keep this consistent with BoundaryRegisterPreservation
+movq %r15, 0(%rax)
+movq %r14, 8(%rax)
+movq %r13, 16(%rax)
+movq %r12, 24(%rax)
+movq %rbx, 32(%rax)
+
+popq %r10
+popq %r9
+popq %r8
+popq %rsi
+popq %rdi
+popq %rdx
+popq %rcx
+popq %rax
+addq $8, %rsp
+
+jmpq *%rax
diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs
index 2bb230318..0a062ba5f 100644
--- a/lib/runtime-core/src/backend.rs
+++ b/lib/runtime-core/src/backend.rs
@@ -159,6 +159,10 @@ pub trait RunnableModule: Send + Sync {
None
}
+ unsafe fn patch_local_function(&self, _idx: usize, _target_address: usize) -> bool {
+ false
+ }
+
/// A wasm trampoline contains the necessary data to dynamically call an exported wasm function.
/// Given a particular signature index, we are returned a trampoline that is matched with that
/// signature and an invoke function that can call the trampoline.
@@ -175,6 +179,11 @@ pub trait RunnableModule: Send + Sync {
fn get_offsets(&self) -> Option> {
None
}
+
+ /// Returns the beginning offsets of all local functions.
+ fn get_local_function_offsets(&self) -> Option> {
+ None
+ }
}
pub trait CacheGen: Send + Sync {
diff --git a/lib/runtime-core/src/fault.rs b/lib/runtime-core/src/fault.rs
index b44978b1b..2d46d8ca2 100644
--- a/lib/runtime-core/src/fault.rs
+++ b/lib/runtime-core/src/fault.rs
@@ -1,8 +1,9 @@
-mod raw {
+pub mod raw {
use std::ffi::c_void;
extern "C" {
pub fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64;
+ pub fn register_preservation_trampoline(); // NOT safe to call directly
pub fn setjmp(env: *mut c_void) -> i32;
pub fn longjmp(env: *mut c_void, val: i32) -> !;
}
@@ -10,6 +11,7 @@ mod raw {
use crate::codegen::{BreakpointInfo, BreakpointMap};
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR, XMM};
+use crate::state::CodeVersion;
use crate::vm;
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use nix::sys::signal::{
@@ -17,7 +19,7 @@ use nix::sys::signal::{
SIGSEGV, SIGTRAP,
};
use std::any::Any;
-use std::cell::UnsafeCell;
+use std::cell::{Cell, RefCell, UnsafeCell};
use std::ffi::c_void;
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -38,8 +40,27 @@ struct UnwindInfo {
payload: Option>, // out
}
+#[repr(packed)]
+#[derive(Default, Copy, Clone)]
+pub struct BoundaryRegisterPreservation {
+ pub r15: u64,
+ pub r14: u64,
+ pub r13: u64,
+ pub r12: u64,
+ pub rbx: u64,
+}
+
thread_local! {
static UNWIND: UnsafeCell