mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-17 01:28:44 +00:00
Merge branch 'master' into feature/add-table-example
This commit is contained in:
6
.github/workflows/benchmark.yaml
vendored
6
.github/workflows/benchmark.yaml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
# that are needed during the build process. Additionally, this works
|
||||
# around a bug in the 'cache' action that causes directories outside of
|
||||
# the workspace dir to be saved/restored incorrectly.
|
||||
run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home"
|
||||
run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
|
||||
- name: Cache
|
||||
uses: actions/cache@master
|
||||
with:
|
||||
@@ -56,8 +56,8 @@ jobs:
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz
|
||||
mkdir -p /opt/llvm-10
|
||||
tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10
|
||||
echo ::add-path::/opt/llvm-10/bin
|
||||
echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10
|
||||
echo '/opt/llvm-10/bin' >> $GITHUB_PATH
|
||||
echo 'name=LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
|
||||
4
.github/workflows/coverage.yaml
vendored
4
.github/workflows/coverage.yaml
vendored
@@ -29,8 +29,8 @@ jobs:
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -L -o llvm.tar.xz
|
||||
mkdir -p /opt/llvm-10
|
||||
tar xf llvm.tar.xz --strip-components=1 -C /opt/llvm-10
|
||||
echo ::add-path::/opt/llvm-10/bin
|
||||
echo ::set-env name=LLVM_SYS_100_PREFIX::/opt/llvm-10
|
||||
echo '/opt/llvm-10/bin' >> $GITHUB_PATH
|
||||
echo 'LLVM_SYS_100_PREFIX=/opt/llvm-10' >> $GITHUB_ENV
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
cargo install cargo-tarpaulin
|
||||
|
||||
30
.github/workflows/main.yaml
vendored
30
.github/workflows/main.yaml
vendored
@@ -41,9 +41,9 @@ jobs:
|
||||
build: [linux, macos, windows, linux-aarch64]
|
||||
include:
|
||||
- build: linux
|
||||
os: ubuntu-latest
|
||||
os: ubuntu-18.04
|
||||
rust: 1.46.0
|
||||
llvm_url: 'https://github.com/wasmerio/llvm-build/releases/download/10.x/Ubuntu1910_Release.tar.xz'
|
||||
llvm_url: 'https://github.com/wasmerio/llvm-build/releases/download/10.x/Ubuntu1604_Release.tar.xz'
|
||||
# llvm_url: 'https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz'
|
||||
artifact_name: 'wasmer-linux-amd64'
|
||||
run_integration_tests: true
|
||||
@@ -70,6 +70,12 @@ jobs:
|
||||
SCCACHE_AZURE_CONNECTION_STRING: ${{ secrets.SCCACHE_AZURE_CONNECTION_STRING }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up libstdc++ on Linux
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y --allow-downgrades libstdc++6=8.4.0-1ubuntu1~18.04
|
||||
sudo apt-get install --reinstall g++-8
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -81,7 +87,7 @@ jobs:
|
||||
# that are needed during the build process. Additionally, this works
|
||||
# around a bug in the 'cache' action that causes directories outside of
|
||||
# the workspace dir to be saved/restored incorrectly.
|
||||
run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home"
|
||||
run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
|
||||
- name: Cache
|
||||
uses: actions/cache@master
|
||||
with:
|
||||
@@ -103,13 +109,13 @@ jobs:
|
||||
# key: cargo-sccache-bin-${{ env.CARGO_SCCACHE_VERSION }}
|
||||
# - name: Install sccache
|
||||
# run: |
|
||||
# echo "::add-path::${{ runner.tool_cache }}/cargo-sccache/bin"
|
||||
# echo "${{ runner.tool_cache }}/cargo-sccache/bin" >> $GITHUB_PATH
|
||||
# cargo install sccache --version ${{ env.CARGO_SCCACHE_VERSION }} --root ${{ runner.tool_cache }}/cargo-sccache
|
||||
# - name: Start sccache
|
||||
# run: |
|
||||
# ${{ runner.tool_cache }}/cargo-sccache/bin/sccache --start-server
|
||||
# ${{ runner.tool_cache }}/cargo-sccache/bin/sscache -s
|
||||
# echo "::set-env name=RUSTC_WRAPPER::${{ runner.tool_cache }}/cargo-sccache/bin/sccache"
|
||||
# echo "RUSTC_WRAPPER=${{ runner.tool_cache }}/cargo-sccache/bin/sccache" >> $GITHUB_ENV
|
||||
- name: Install LLVM (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: cmd
|
||||
@@ -118,17 +124,17 @@ jobs:
|
||||
# run: |
|
||||
# curl --proto '=https' --tlsv1.2 -sSf https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe -L -o llvm-installer.exe
|
||||
# 7z x llvm-installer.exe -oC:/llvm-10
|
||||
# echo ::add-path::C:/llvm-10/bin
|
||||
# echo ::set-env name=LLVM_SYS_100_PREFIX::C:/llvm-10
|
||||
# echo ::set-env name=LIBCLANG_PATH::C:/llvm-10/bin/libclang.dll
|
||||
# echo C:/llvm-10/bin >> $GITHUB_PATH
|
||||
# echo "LLVM_SYS_100_PREFIX=C:/llvm-10" >> $GITHUB_ENV
|
||||
# echo "LIBCLANG_PATH=C:/llvm-10/bin/libclang.dll" >> $GITHUB_ENV
|
||||
- name: Install LLVM (Unix)
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf ${{ matrix.llvm_url }} -L -o llvm.tar.xz
|
||||
mkdir -p ${{ env.LLVM_DIR }}
|
||||
tar xf llvm.tar.xz --strip-components=1 -C ${{ env.LLVM_DIR }}
|
||||
echo "::add-path::${{ env.LLVM_DIR }}/bin"
|
||||
echo "::set-env name=LLVM_SYS_100_PREFIX::${{ env.LLVM_DIR }}"
|
||||
echo "${{ env.LLVM_DIR }}/bin" >> $GITHUB_PATH
|
||||
echo "LLVM_SYS_100_PREFIX=${{ env.LLVM_DIR }}" >> $GITHUB_ENV
|
||||
env:
|
||||
LLVM_DIR: ${{ github.workspace }}/llvm-10
|
||||
- name: Set up dependencies for Mac OS
|
||||
@@ -138,7 +144,7 @@ jobs:
|
||||
- name: Build and Test C API
|
||||
run: |
|
||||
make build-capi
|
||||
make test-capi-cranelift
|
||||
make test-capi
|
||||
if: matrix.os != 'windows-latest'
|
||||
- name: Build C API on Windows
|
||||
run: make build-capi
|
||||
@@ -248,7 +254,7 @@ jobs:
|
||||
path: ${{ runner.tool_cache }}/cargo-audit
|
||||
key: cargo-audit-bin-${{ env.CARGO_AUDIT_VERSION }}
|
||||
- run: |
|
||||
echo "::add-path::${{ runner.tool_cache }}/cargo-audit/bin"
|
||||
echo "${{ runner.tool_cache }}/cargo-audit/bin" >> $GITHUB_PATH
|
||||
- run: |
|
||||
cargo install cargo-audit --version ${{ env.CARGO_AUDIT_VERSION }} --root ${{ runner.tool_cache }}/cargo-audit
|
||||
cargo audit
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -7,13 +7,28 @@
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#1710](https://github.com/wasmerio/wasmer/pull/1710) Memory for function call trampolines is now owned by the Artifact.
|
||||
### Added
|
||||
|
||||
|
||||
- [#1687](https://github.com/wasmerio/wasmer/pull/1687) Add basic usage examples; fix ownership of local memory and local table metadata in the VM.
|
||||
- [#1687](https://github.com/wasmerio/wasmer/pull/1687) Add basic table example; fix ownership of local memory and local table metadata in the VM.
|
||||
- [#1751](https://github.com/wasmerio/wasmer/pull/1751) Implement `wasm_trap_t` inside a function declared with `wasm_func_new_with_env` in the Wasm C API.
|
||||
- [#1741](https://github.com/wasmerio/wasmer/pull/1741) Implement `wasm_memory_type` in the Wasm C API.
|
||||
- [#1736](https://github.com/wasmerio/wasmer/pull/1736) Implement `wasm_global_type` in the Wasm C API.
|
||||
- [#1699](https://github.com/wasmerio/wasmer/pull/1699) Update `wasm.h` to its latest version.
|
||||
- [#1685](https://github.com/wasmerio/wasmer/pull/1685) Implement `wasm_exporttype_delete` in the Wasm C API.
|
||||
- [#1725](https://github.com/wasmerio/wasmer/pull/1725) Implement `wasm_func_type` in the Wasm C API.
|
||||
- [#1715](https://github.com/wasmerio/wasmer/pull/1715) Register errors from `wasm_module_serialize` in the Wasm C API.
|
||||
- [#1709](https://github.com/wasmerio/wasmer/pull/1709) Implement `wasm_module_name` and `wasm_module_set_name` in the Wasm(er) C API.
|
||||
- [#1700](https://github.com/wasmerio/wasmer/pull/1700) Implement `wasm_externtype_copy` in the Wasm C API.
|
||||
|
||||
### Changed
|
||||
|
||||
- [#1762](https://github.com/wasmerio/wasmer/pull/1762) Allow the `=` sign in a WASI environment variable value.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#1718](https://github.com/wasmerio/wasmer/pull/1718) Fix panic in the API in some situations when the memory's min bound was greater than the memory's max bound.
|
||||
|
||||
## 1.0.0-alpha4 - 2020-10-08
|
||||
|
||||
### Added
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -131,9 +131,11 @@ test-llvm = [
|
||||
|
||||
test-native = [
|
||||
"native",
|
||||
"test-generator/test-native",
|
||||
]
|
||||
test-jit = [
|
||||
"jit",
|
||||
"test-generator/test-jit",
|
||||
]
|
||||
|
||||
# Specifies that we're running in coverage testing mode. This disables tests
|
||||
@@ -189,6 +191,26 @@ name = "exported-function"
|
||||
path = "examples/exports_function.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "exported-global"
|
||||
path = "examples/exports_global.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "exported-memory"
|
||||
path = "examples/exports_memory.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "imported-function"
|
||||
path = "examples/imports_function.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "imported-global"
|
||||
path = "examples/imports_global.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "wasi"
|
||||
path = "examples/wasi.rs"
|
||||
|
||||
87
Makefile
87
Makefile
@@ -8,15 +8,13 @@ else
|
||||
UNAME_S :=
|
||||
endif
|
||||
|
||||
compilers :=
|
||||
# Which compilers we build. These have dependencies that may not be on the system.
|
||||
compilers := cranelift
|
||||
|
||||
# In the form "$(compiler)-$(engine)" which compiler+engine combinations to test
|
||||
# in `make test`.
|
||||
test_compilers_engines :=
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
# In X64, Cranelift is enabled
|
||||
compilers += cranelift
|
||||
# LLVM could be enabled if not in Windows
|
||||
ifneq ($(OS), Windows_NT)
|
||||
# Singlepass doesn't work yet on Windows
|
||||
compilers += singlepass
|
||||
# Autodetect LLVM from llvm-config
|
||||
ifneq (, $(shell which llvm-config))
|
||||
LLVM_VERSION := $(shell llvm-config --version)
|
||||
@@ -29,10 +27,32 @@ ifeq ($(ARCH), x86_64)
|
||||
compilers += llvm
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
test_compilers_engines += cranelift-jit
|
||||
# LLVM could be enabled if not in Windows
|
||||
ifneq ($(OS), Windows_NT)
|
||||
# Native engine doesn't work on Windows yet.
|
||||
test_compilers_engines += cranelift-native
|
||||
# Singlepass doesn't work yet on Windows.
|
||||
compilers += singlepass
|
||||
# Singlepass doesn't work with the native engine.
|
||||
test_compilers_engines += singlepass-jit
|
||||
ifneq (, $(findstring llvm,$(compilers)))
|
||||
test_compilers_engines += llvm-jit llvm-native
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
test_compilers_engines += cranelift-jit
|
||||
ifneq (, $(findstring llvm,$(compilers)))
|
||||
test_compilers_engines += llvm-native
|
||||
endif
|
||||
endif
|
||||
|
||||
compilers := $(filter-out ,$(compilers))
|
||||
test_compilers_engines := $(filter-out ,$(test_compilers_engines))
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
bold := $(shell tput bold)
|
||||
@@ -72,33 +92,40 @@ build-docs-capi:
|
||||
cd lib/c-api/ && doxygen doxyfile
|
||||
|
||||
# We use cranelift as the default backend for the capi for now
|
||||
build-capi: build-capi-cranelift
|
||||
build-capi: build-capi-cranelift-jit
|
||||
|
||||
build-capi-singlepass:
|
||||
build-capi-singlepass-jit:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,object-file,singlepass,wasi
|
||||
|
||||
build-capi-cranelift:
|
||||
build-capi-cranelift-jit:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,object-file,cranelift,wasi
|
||||
|
||||
build-capi-cranelift-native:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,native,object-file,cranelift,wasi
|
||||
|
||||
build-capi-cranelift-system-libffi:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,object-file,cranelift,wasi,system-libffi
|
||||
--no-default-features --features wat,jit,native,object-file,cranelift,wasi,system-libffi
|
||||
|
||||
build-capi-llvm:
|
||||
build-capi-llvm-jit:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,object-file,llvm,wasi
|
||||
|
||||
build-capi-llvm-native:
|
||||
cargo build --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,native,object-file,llvm,wasi
|
||||
|
||||
###########
|
||||
# Testing #
|
||||
###########
|
||||
|
||||
test: $(foreach compiler,$(compilers),test-$(compiler)-jit) test-packages test-examples test-deprecated
|
||||
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples test-deprecated
|
||||
|
||||
# Singlepass and native engine don't work together, this rule does nothing.
|
||||
test-singlepass-native:
|
||||
@:
|
||||
cargo test --release $(compiler_features) --features "test-singlepass test-native"
|
||||
|
||||
test-singlepass-jit:
|
||||
cargo test --release $(compiler_features) --features "test-singlepass test-jit"
|
||||
@@ -115,6 +142,12 @@ test-llvm-native:
|
||||
test-llvm-jit:
|
||||
cargo test --release $(compiler_features) --features "test-llvm test-jit"
|
||||
|
||||
test-singlepass: $(foreach singlepass_engine,$(filter singlepass-%,$(test_compilers_engines)),test-$(singlepass_engine))
|
||||
|
||||
test-cranelift: $(foreach cranelift_engine,$(filter cranelift-%,$(test_compilers_engines)),test-$(cranelift_engine))
|
||||
|
||||
test-llvm: $(foreach llvm_engine,$(filter llvm-%,$(test_compilers_engines)),test-$(llvm_engine))
|
||||
|
||||
test-packages:
|
||||
cargo test -p wasmer --release
|
||||
cargo test -p wasmer-vm --release
|
||||
@@ -124,23 +157,35 @@ test-packages:
|
||||
cargo test -p wasmer-engine-native --release --no-default-features
|
||||
cargo test -p wasmer-cli --release
|
||||
|
||||
test-capi-singlepass: build-capi-singlepass
|
||||
# The test-capi rules depend on the build-capi rules to build the .a files to
|
||||
# link the tests against. cargo test doesn't know that the tests will be running
|
||||
# cmake + make to build programs whose dependencies cargo isn't aware of.
|
||||
|
||||
test-capi-singlepass-jit: build-capi-singlepass-jit
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,singlepass,wasi -- --nocapture
|
||||
|
||||
test-capi-cranelift: build-capi-cranelift
|
||||
test-capi-cranelift-jit: build-capi-cranelift-jit
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,cranelift,wasi -- --nocapture
|
||||
|
||||
test-capi-cranelift-system-libffi: build-capi-cranelift-system-libffi
|
||||
test-capi-cranelift-native: build-capi-cranelift-native
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,native,cranelift,wasi -- --nocapture
|
||||
|
||||
test-capi-cranelift-jit-system-libffi:
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,cranelift,wasi,system-libffi -- --nocapture
|
||||
|
||||
test-capi-llvm: build-capi-llvm
|
||||
test-capi-llvm-jit:
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,jit,llvm,wasi -- --nocapture
|
||||
|
||||
test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm
|
||||
test-capi-llvm-native:
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
--no-default-features --features wat,native,llvm,wasi -- --nocapture
|
||||
|
||||
test-capi: $(foreach compiler_engine,$(test_compilers_engines),test-capi-$(compiler_engine)) $(if $(findstring cranelift-jit,$(test_compilers_engines)),test-capi-cranelift-jit-system-libffi)
|
||||
|
||||
test-wasi-unit:
|
||||
cargo test --manifest-path lib/wasi/Cargo.toml --release
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
* **Pluggable**. Wasmer supports different compilation frameworks to best suit your needs (LLVM, Cranelift...).
|
||||
|
||||
* **Universal**. You can run Wasmer in almost any *platform* (macOS, Linux and Windows) and *chipset*.
|
||||
* **Universal**. You can run Wasmer in any *platform* (macOS, Linux and Windows) and *chipset*.
|
||||
|
||||
* **Standards compliant**. The runtime passes [official WebAssembly test
|
||||
suite](https://github.com/WebAssembly/testsuite) supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/).
|
||||
|
||||
@@ -149,9 +149,24 @@ example.
|
||||
|
||||
</details>
|
||||
|
||||
### Externals
|
||||
### Exports
|
||||
|
||||
8. [**Exported function**][exported-function], explains how to get and
|
||||
8. [**Exported global**][exported-global], explains how to work with
|
||||
exported globals: get/set their value, have information about their
|
||||
type.
|
||||
|
||||
_Keywords_: export, global.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example exported-globals --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
9. [**Exported function**][exported-function], explains how to get and
|
||||
how to call an exported function. They come in 2 flavors: dynamic,
|
||||
and “static”/native. The pros and cons are discussed briefly.
|
||||
|
||||
@@ -166,7 +181,55 @@ example.
|
||||
|
||||
</details>
|
||||
|
||||
9. [**Table**][table], explains how to use Wasm Tables from the Wasmer API.
|
||||
|
||||
10. [**Exported memory**][exported-memory], explains how to read from
|
||||
and write to exported memory.
|
||||
|
||||
_Keywords_: export, memory.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example exported-memory --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Imports
|
||||
|
||||
11. [**Imported global**][imported-global], explains how to work with
|
||||
imported globals: create globals, import them, get/set their value.
|
||||
|
||||
_Keywords_: import, global.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example imported-globals --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
12. [**Imported function**][imported-function], explains how to define
|
||||
an imported function. They come in 2 flavors: dynamic,
|
||||
and “static”/native.
|
||||
|
||||
_Keywords_: import, function, dynamic, static, native.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example imported-function --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Externs
|
||||
|
||||
13. [**Table**][table], explains how to use Wasm Tables from the Wasmer API.
|
||||
|
||||
_Keywords_: basic, table, call_indirect
|
||||
|
||||
@@ -179,7 +242,7 @@ example.
|
||||
|
||||
</details>
|
||||
|
||||
10. [**Memory**][memory], explains how to use Wasm Memories from
|
||||
14. [**Memory**][memory], explains how to use Wasm Memories from
|
||||
the Wasmer API. Memory example is a work in progress.
|
||||
|
||||
_Keywords_: basic, memory
|
||||
@@ -193,10 +256,9 @@ example.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Integrations
|
||||
|
||||
11. [**WASI**][wasi], explains how to use the [WebAssembly System
|
||||
15. [**WASI**][wasi], explains how to use the [WebAssembly System
|
||||
Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate.
|
||||
|
||||
_Keywords_: wasi, system, interface
|
||||
@@ -217,7 +279,11 @@ example.
|
||||
[compiler-cranelift]: ./compiler_cranelift.rs
|
||||
[compiler-llvm]: ./compiler_llvm.rs
|
||||
[cross-compilation]: ./engine_cross_compilation.rs
|
||||
[exported-global]: ./exports_global.rs
|
||||
[exported-function]: ./exports_function.rs
|
||||
[exported-memory]: ./exports_memory.rs
|
||||
[imported-global]: ./imports_global.rs
|
||||
[imported-function]: ./imports_function.rs
|
||||
[wasi]: ./wasi.rs
|
||||
[table]: ./table.rs
|
||||
[memory]: ./memory.rs
|
||||
|
||||
@@ -147,7 +147,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(any(windows, target_arch = "aarch64")))]
|
||||
fn test_engine_headless() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
fn test_engine_native() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Much nicer, isn't it?
|
||||
//
|
||||
// Those two API exist because they addres different needs. The
|
||||
// Those two API exist because they address different needs. The
|
||||
// former has a more dynamic approach, while the second has a more
|
||||
// static approach.
|
||||
|
||||
|
||||
143
examples/exports_global.rs
Normal file
143
examples/exports_global.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
//! A Wasm module can export entities, like functions, memories,
|
||||
//! globals and tables.
|
||||
//!
|
||||
//! This example illustrates how to use exported globals. They come
|
||||
//! in 2 flavors:
|
||||
//!
|
||||
//! 1. Immutable globals (const),
|
||||
//! 2. Mutable globals.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```shell
|
||||
//! cargo run --example exported-global --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Instance, Module, Mutability, Store, Type, Value};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
br#"
|
||||
(module
|
||||
(global $one (export "one") f32 (f32.const 1))
|
||||
(global $some (export "some") (mut f32) (f32.const 0))
|
||||
|
||||
(func (export "get_one") (result f32) (global.get $one))
|
||||
(func (export "get_some") (result f32) (global.get $some))
|
||||
|
||||
(func (export "set_some") (param f32) (global.set $some (local.get 0))))
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a Store.
|
||||
// Note that we don't need to specify the engine/compiler if we want to use
|
||||
// the default provided by Wasmer.
|
||||
// You can use `Store::default()` for that.
|
||||
let store = Store::new(&JIT::new(&Cranelift::default()).engine());
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Create an empty import object.
|
||||
let import_object = imports! {};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
// Here we go.
|
||||
//
|
||||
// The Wasm module exports some globals. Let's get them.
|
||||
// Note that
|
||||
//
|
||||
// ```
|
||||
// get_global(name)
|
||||
// ```
|
||||
//
|
||||
// is just an alias to
|
||||
//
|
||||
// ```
|
||||
// get::<Global>(name)`.
|
||||
// ```
|
||||
let one = instance.exports.get_global("one")?;
|
||||
let some = instance.exports.get_global("some")?;
|
||||
|
||||
println!("Getting global type informations...");
|
||||
// Let's get the globals types. The results are `GlobalType`s.
|
||||
let one_type = one.ty();
|
||||
let some_type = some.ty();
|
||||
|
||||
println!("one type: {:?} {:?}", one_type.mutability, one_type.ty);
|
||||
assert_eq!(one_type.mutability, Mutability::Const);
|
||||
assert_eq!(one_type.ty, Type::F32);
|
||||
|
||||
println!("some type: {:?} {:?}", some_type.mutability, some_type.ty);
|
||||
assert_eq!(some_type.mutability, Mutability::Var);
|
||||
assert_eq!(some_type.ty, Type::F32);
|
||||
|
||||
println!("Getting global values...");
|
||||
// Getting the values of globals can be done in two ways:
|
||||
// 1. Through an exported function,
|
||||
// 2. Using the Global API directly.
|
||||
//
|
||||
// We will use an exported function for the `one` global
|
||||
// and the Global API for `some`.
|
||||
let get_one = instance
|
||||
.exports
|
||||
.get_function("get_one")?
|
||||
.native::<(), f32>()?;
|
||||
|
||||
let one_result = get_one.call()?;
|
||||
let some_result = some.get();
|
||||
|
||||
println!("one value: {:?}", one_result);
|
||||
assert_eq!(one_result, 1.0);
|
||||
|
||||
println!("some value: {:?}", some_result);
|
||||
assert_eq!(some_result, Value::F32(0.0));
|
||||
|
||||
println!("Setting global values...");
|
||||
// Trying to set the value of a immutable global (`const`)
|
||||
// will result in a `RuntimeError`.
|
||||
let result = one.set(Value::F32(42.0));
|
||||
assert_eq!(
|
||||
result.expect_err("Expected an error").message(),
|
||||
"Attempted to set an immutable global"
|
||||
);
|
||||
|
||||
let one_result = one.get();
|
||||
println!("one value after `set`: {:?}", one_result);
|
||||
assert_eq!(one_result, Value::F32(1.0));
|
||||
|
||||
// Setting the values of globals can be done in two ways:
|
||||
// 1. Through an exported function,
|
||||
// 2. Using the Global API directly.
|
||||
//
|
||||
// We will use both for the `some` global.
|
||||
let set_some = instance
|
||||
.exports
|
||||
.get_function("set_some")?
|
||||
.native::<f32, ()>()?;
|
||||
set_some.call(21.0)?;
|
||||
let some_result = some.get();
|
||||
println!("some value after `set_some`: {:?}", some_result);
|
||||
assert_eq!(some_result, Value::F32(21.0));
|
||||
|
||||
some.set(Value::F32(42.0))?;
|
||||
let some_result = some.get();
|
||||
println!("some value after `set`: {:?}", some_result);
|
||||
assert_eq!(some_result, Value::F32(42.0));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exported_global() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
120
examples/exports_memory.rs
Normal file
120
examples/exports_memory.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
//! A Wasm module can export entities, like functions, memories,
|
||||
//! globals and tables.
|
||||
//!
|
||||
//! This example illustrates how to use exported memories
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```shell
|
||||
//! cargo run --example exported-memory --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Array, Instance, Module, Store, WasmPtr};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
br#"
|
||||
(module
|
||||
(memory (export "mem") 1)
|
||||
|
||||
(global $offset i32 (i32.const 42))
|
||||
(global $length (mut i32) (i32.const 13))
|
||||
|
||||
(func (export "load") (result i32 i32)
|
||||
global.get $offset
|
||||
global.get $length)
|
||||
|
||||
(data (global.get $offset) "Hello, World!"))
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a Store.
|
||||
// Note that we don't need to specify the engine/compiler if we want to use
|
||||
// the default provided by Wasmer.
|
||||
// You can use `Store::default()` for that.
|
||||
let store = Store::new(&JIT::new(&Cranelift::default()).engine());
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Create an empty import object.
|
||||
let import_object = imports! {};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
let load = instance
|
||||
.exports
|
||||
.get_native_function::<(), (WasmPtr<u8, Array>, i32)>("load")?;
|
||||
|
||||
// Here we go.
|
||||
//
|
||||
// The Wasm module exports a memory under "mem". Let's get it.
|
||||
let memory = instance.exports.get_memory("mem")?;
|
||||
|
||||
// Now that we have the exported memory, let's get some
|
||||
// information about it.
|
||||
//
|
||||
// The first thing we might be intersted in is the size of the memory.
|
||||
// Let's get it!
|
||||
println!("Memory size (pages) {:?}", memory.size());
|
||||
println!("Memory size (bytes) {:?}", memory.data_size());
|
||||
|
||||
// Next, we'll want to read the contents of the memory.
|
||||
//
|
||||
// To do so, we have to get a `View` of the memory.
|
||||
//let view = memory.view::<u8>();
|
||||
|
||||
// Oh! Wait, before reading the contents, we need to know
|
||||
// where to find what we are looking for.
|
||||
//
|
||||
// Fortunately, the Wasm module exports a `load` function
|
||||
// which will tell us the offset and length of the string.
|
||||
let (ptr, length) = load.call()?;
|
||||
println!("String offset: {:?}", ptr.offset());
|
||||
println!("String length: {:?}", length);
|
||||
|
||||
// We now know where to fin our string, let's read it.
|
||||
//
|
||||
// We will get bytes out of the memory so we need to
|
||||
// decode them into a string.
|
||||
let str = ptr.get_utf8_string(memory, length as u32).unwrap();
|
||||
println!("Memory contents: {:?}", str);
|
||||
|
||||
// What about changing the contents of the memory with a more
|
||||
// appropriate string?
|
||||
//
|
||||
// To do that, we'll dereference our pointer and change the content
|
||||
// of each `Cell`
|
||||
let new_str = b"Hello, Wasmer!";
|
||||
let values = ptr.deref(memory, 0, new_str.len() as u32).unwrap();
|
||||
for i in 0..new_str.len() {
|
||||
values[i].set(new_str[i]);
|
||||
}
|
||||
|
||||
// And now, let's see the result.
|
||||
//
|
||||
// Since the new strings is bigger than the older one, we
|
||||
// query the length again. The offset remains the same as
|
||||
// before.
|
||||
println!("New string length: {:?}", new_str.len());
|
||||
|
||||
let str = ptr.get_utf8_string(memory, new_str.len() as u32).unwrap();
|
||||
println!("New memory contents: {:?}", str);
|
||||
|
||||
// Much better, don't you think?
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exported_memory() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
107
examples/imports_function.rs
Normal file
107
examples/imports_function.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
//! A Wasm module can import entities, like functions, memories,
|
||||
//! globals and tables.
|
||||
//!
|
||||
//! This example illustrates how to use imported functions. They come
|
||||
//! in 2 flavors:
|
||||
//!
|
||||
//! 1. Dynamic functions, where parameters and results are of a
|
||||
//! slice of `Value`,
|
||||
//! 2. Native function, where parameters and results are statically
|
||||
//! typed Rust values.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```shell
|
||||
//! cargo run --example imported-function --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Function, FunctionType, Instance, Module, Store, Type, Value};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
br#"
|
||||
(module
|
||||
(func $multiply_dynamic (import "env" "multiply_dynamic") (param i32) (result i32))
|
||||
(func $multiply_native (import "env" "multiply_native") (param i32) (result i32))
|
||||
|
||||
(type $sum_t (func (param i32) (param i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
(call $multiply_dynamic (local.get $x))
|
||||
(call $multiply_native (local.get $y))
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a Store.
|
||||
// Note that we don't need to specify the engine/compiler if we want to use
|
||||
// the default provided by Wasmer.
|
||||
// You can use `Store::default()` for that.
|
||||
let store = Store::new(&JIT::new(&Cranelift::default()).engine());
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Create the functions
|
||||
let multiply_dynamic_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
|
||||
let multiply_dynamic = Function::new(&store, &multiply_dynamic_signature, |args| {
|
||||
println!("Calling `multiply_dynamic`...");
|
||||
|
||||
let result = args[0].unwrap_i32() * 2;
|
||||
|
||||
println!("Result of `multiply_dynamic`: {:?}", result);
|
||||
|
||||
Ok(vec![Value::I32(result)])
|
||||
});
|
||||
|
||||
fn multiply(a: i32) -> i32 {
|
||||
println!("Calling `multiply_native`...");
|
||||
let result = a * 3;
|
||||
|
||||
println!("Result of `multiply_native`: {:?}", result);
|
||||
|
||||
result
|
||||
}
|
||||
let multiply_native = Function::new_native(&store, multiply);
|
||||
|
||||
// Create an empty import object.
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"multiply_dynamic" => multiply_dynamic,
|
||||
"multiply_native" => multiply_native,
|
||||
}
|
||||
};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
// Here we go.
|
||||
//
|
||||
// The Wasm module exports a function called `sum`. Let's get it.
|
||||
let sum = instance
|
||||
.exports
|
||||
.get_function("sum")?
|
||||
.native::<(i32, i32), i32>()?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// Let's call the `sum` exported function. It will call each
|
||||
// of the imported functions.
|
||||
let result = sum.call(1, 2)?;
|
||||
|
||||
println!("Results of `sum`: {:?}", result);
|
||||
assert_eq!(result, 8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exported_function() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
120
examples/imports_global.rs
Normal file
120
examples/imports_global.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
//! A Wasm module can import entities, like functions, memories,
|
||||
//! globals and tables.
|
||||
//!
|
||||
//! This example illustrates how to use imported globals. They come
|
||||
//! in 2 flavors:
|
||||
//!
|
||||
//! 1. Immutable globals (const),
|
||||
//! 2. Mutable globals.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```shell
|
||||
//! cargo run --example imported-global --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Global, Instance, Module, Store, Value};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
br#"
|
||||
(module
|
||||
(global $some (import "env" "some") f32)
|
||||
(global $other (import "env" "other") (mut f32))
|
||||
|
||||
(func (export "get_some") (result f32) (global.get $some))
|
||||
(func (export "get_other") (result f32) (global.get $other))
|
||||
|
||||
(func (export "set_other") (param f32) (global.set $other (local.get 0))))
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a Store.
|
||||
// Note that we don't need to specify the engine/compiler if we want to use
|
||||
// the default provided by Wasmer.
|
||||
// You can use `Store::default()` for that.
|
||||
let store = Store::new(&JIT::new(&Cranelift::default()).engine());
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Create the globals
|
||||
let some = Global::new(&store, Value::F32(1.0));
|
||||
let other = Global::new_mut(&store, Value::F32(2.0));
|
||||
|
||||
// Create an import object.
|
||||
// We add the two required globals in the `env` namespace.
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"some" => some.clone(),
|
||||
"other" => other.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
// Here we go.
|
||||
//
|
||||
// The Wasm module only imports some globals. We'll have to interact
|
||||
// with them either using the Global API or exported functions.
|
||||
let get_some = instance
|
||||
.exports
|
||||
.get_function("get_some")?
|
||||
.native::<(), f32>()?;
|
||||
let get_other = instance
|
||||
.exports
|
||||
.get_function("get_other")?
|
||||
.native::<(), f32>()?;
|
||||
|
||||
let some_result = get_some.call()?;
|
||||
let other_result = get_other.call()?;
|
||||
|
||||
println!("some value (via `get_some`): {:?}", some_result);
|
||||
println!("some value (via Global API): {:?}", some.get());
|
||||
println!("other value (via `get_other`): {:?}", other_result);
|
||||
println!("other value (via Global API): {:?}", other.get());
|
||||
|
||||
assert_eq!(some_result, some.get().f32().unwrap());
|
||||
assert_eq!(other_result, other.get().f32().unwrap());
|
||||
|
||||
println!("Setting global values...");
|
||||
// Trying to set the value of a immutable global (`const`)
|
||||
// will result in a `RuntimeError`.
|
||||
let result = some.set(Value::F32(42.0));
|
||||
assert_eq!(
|
||||
result.expect_err("Expected an error").message(),
|
||||
"Attempted to set an immutable global"
|
||||
);
|
||||
|
||||
other.set(Value::F32(21.0))?;
|
||||
let other_result = other.get();
|
||||
println!("other value after `set`: {:?}", other_result);
|
||||
assert_eq!(other_result, Value::F32(21.0));
|
||||
|
||||
println!("Altering global values through exported functions...");
|
||||
// Changes made to global through exported functions will
|
||||
// be reflected on the host side.
|
||||
let set_other = instance
|
||||
.exports
|
||||
.get_function("set_other")?
|
||||
.native::<f32, ()>()?;
|
||||
set_other.call(42.0)?;
|
||||
|
||||
println!("other value (via Global API): {:?}", other.get());
|
||||
assert_eq!(other.get(), Value::F32(42.0));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_imported_global() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
@@ -14,16 +14,37 @@ use wasmer_vm::Export;
|
||||
///
|
||||
/// [`Instance`]: crate::Instance
|
||||
///
|
||||
/// ```ignore
|
||||
/// # let my_instance = Instance::new(...);
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Incompatible export type
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm(r#"
|
||||
/// # (module
|
||||
/// # (global $one (export "glob") f32 (f32.const 1)))
|
||||
/// # "#.as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// // This results with an error: `ExportError::IncompatibleType`.
|
||||
/// let missing_import: &Global = my_instance.exports.get("func")?;
|
||||
/// let missing_import = my_instance.exports.get_global("func")?;
|
||||
/// let export = instance.exports.get_function("glob").unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// ## Missing export
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// // This results with an error: `ExportError::Missing`.
|
||||
/// let missing_import: &Function = my_instance.exports.get("unknown")?;
|
||||
/// let missing_import = my_instance.exports.get_function("unknown")?;
|
||||
/// let export = instance.exports.get_function("unknown").unwrap();
|
||||
/// ```
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExportError {
|
||||
|
||||
179
lib/api/src/externals/function.rs
vendored
179
lib/api/src/externals/function.rs
vendored
@@ -18,7 +18,7 @@ use wasmer_vm::{
|
||||
/// A function defined in the Wasm module
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct WasmFunctionDefinition {
|
||||
// The trampoline to do the call
|
||||
// Address of the trampoline to do the call.
|
||||
pub(crate) trampoline: VMTrampoline,
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl Function {
|
||||
/// ```
|
||||
/// # use wasmer::{Function, FunctionType, Type, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
///
|
||||
/// #
|
||||
/// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]);
|
||||
///
|
||||
/// let f = Function::new(&store, &signature, |args| {
|
||||
@@ -95,6 +95,7 @@ impl Function {
|
||||
kind: VMFunctionKind::Dynamic,
|
||||
vmctx,
|
||||
signature: ty.clone(),
|
||||
call_trampoline: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -106,7 +107,7 @@ impl Function {
|
||||
/// ```
|
||||
/// # use wasmer::{Function, FunctionType, Type, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
///
|
||||
/// #
|
||||
/// struct Env {
|
||||
/// multiplier: i32,
|
||||
/// };
|
||||
@@ -144,6 +145,7 @@ impl Function {
|
||||
kind: VMFunctionKind::Dynamic,
|
||||
vmctx,
|
||||
signature: ty.clone(),
|
||||
call_trampoline: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -158,7 +160,7 @@ impl Function {
|
||||
/// ```
|
||||
/// # use wasmer::{Store, Function};
|
||||
/// # let store = Store::default();
|
||||
///
|
||||
/// #
|
||||
/// fn sum(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
@@ -185,6 +187,7 @@ impl Function {
|
||||
vmctx,
|
||||
signature,
|
||||
kind: VMFunctionKind::Static,
|
||||
call_trampoline: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -199,7 +202,7 @@ impl Function {
|
||||
/// ```
|
||||
/// # use wasmer::{Store, Function};
|
||||
/// # let store = Store::default();
|
||||
///
|
||||
/// #
|
||||
/// struct Env {
|
||||
/// multiplier: i32,
|
||||
/// };
|
||||
@@ -238,10 +241,28 @@ impl Function {
|
||||
kind: VMFunctionKind::Static,
|
||||
vmctx,
|
||||
signature,
|
||||
call_trampoline: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`FunctionType`] of the `Function`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Function, Store, Type};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// fn sum(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// let f = Function::new_native(&store, sum);
|
||||
///
|
||||
/// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]);
|
||||
/// assert_eq!(f.ty().results(), vec![Type::I32]);
|
||||
/// ```
|
||||
pub fn ty(&self) -> &FunctionType {
|
||||
&self.exported.signature
|
||||
}
|
||||
@@ -321,22 +342,74 @@ impl Function {
|
||||
}
|
||||
|
||||
/// Returns the number of parameters that this function takes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Function, Store, Type};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// fn sum(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// let f = Function::new_native(&store, sum);
|
||||
///
|
||||
/// assert_eq!(f.param_arity(), 2);
|
||||
/// ```
|
||||
pub fn param_arity(&self) -> usize {
|
||||
self.ty().params().len()
|
||||
}
|
||||
|
||||
/// Returns the number of results this function produces.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Function, Store, Type};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// fn sum(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// let f = Function::new_native(&store, sum);
|
||||
///
|
||||
/// assert_eq!(f.result_arity(), 1);
|
||||
/// ```
|
||||
pub fn result_arity(&self) -> usize {
|
||||
self.ty().results().len()
|
||||
}
|
||||
|
||||
/// Call the [`Function`] function.
|
||||
/// Call the `Function` function.
|
||||
///
|
||||
/// Depending on where the Function is defined, it will call it.
|
||||
/// 1. If the function is defined inside a WebAssembly, it will call the trampoline
|
||||
/// for the function signature.
|
||||
/// 2. If the function is defined in the host (in a native way), it will
|
||||
/// call the trampoline.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm(r#"
|
||||
/// # (module
|
||||
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
|
||||
/// # local.get $x
|
||||
/// # local.get $y
|
||||
/// # i32.add
|
||||
/// # ))
|
||||
/// # "#.as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// let sum = instance.exports.get_function("sum").unwrap();
|
||||
///
|
||||
/// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]);
|
||||
/// ```
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, RuntimeError> {
|
||||
let mut results = vec![Val::null(); self.result_arity()];
|
||||
|
||||
@@ -351,16 +424,21 @@ impl Function {
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, wasmer_export: ExportFunction) -> Self {
|
||||
let vmsignature = store.engine().register_signature(&wasmer_export.signature);
|
||||
let trampoline = store
|
||||
.engine()
|
||||
.function_call_trampoline(vmsignature)
|
||||
.expect("Can't get call trampoline for the function");
|
||||
if let Some(trampoline) = wasmer_export.call_trampoline {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }),
|
||||
exported: wasmer_export,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
definition: FunctionDefinition::Host(HostFunctionDefinition {
|
||||
has_env: !wasmer_export.vmctx.is_null(),
|
||||
}),
|
||||
exported: wasmer_export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn checked_anyfunc(&self) -> VMCallerCheckedAnyfunc {
|
||||
@@ -376,7 +454,80 @@ impl Function {
|
||||
}
|
||||
|
||||
/// Transform this WebAssembly function into a function with the
|
||||
/// native ABI. See `NativeFunc` to learn more.
|
||||
/// native ABI. See [`NativeFunc`] to learn more.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm(r#"
|
||||
/// # (module
|
||||
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
|
||||
/// # local.get $x
|
||||
/// # local.get $y
|
||||
/// # i32.add
|
||||
/// # ))
|
||||
/// # "#.as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// let sum = instance.exports.get_function("sum").unwrap();
|
||||
/// let sum_native = sum.native::<(i32, i32), i32>().unwrap();
|
||||
///
|
||||
/// assert_eq!(sum_native.call(1, 2).unwrap(), 3);
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the `Args` generic parameter does not match the exported function
|
||||
/// an error will be raised:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm(r#"
|
||||
/// # (module
|
||||
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
|
||||
/// # local.get $x
|
||||
/// # local.get $y
|
||||
/// # i32.add
|
||||
/// # ))
|
||||
/// # "#.as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// let sum = instance.exports.get_function("sum").unwrap();
|
||||
///
|
||||
/// // This results in an error: `RuntimeError`
|
||||
/// let sum_native = sum.native::<(i64, i64), i32>().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// If the `Rets` generic parameter does not match the exported function
|
||||
/// an error will be raised:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// # let wasm_bytes = wat2wasm(r#"
|
||||
/// # (module
|
||||
/// # (func (export "sum") (param $x i32) (param $y i32) (result i32)
|
||||
/// # local.get $x
|
||||
/// # local.get $y
|
||||
/// # i32.add
|
||||
/// # ))
|
||||
/// # "#.as_bytes()).unwrap();
|
||||
/// # let module = Module::new(&store, wasm_bytes).unwrap();
|
||||
/// # let import_object = imports! {};
|
||||
/// # let instance = Instance::new(&module, &import_object).unwrap();
|
||||
/// #
|
||||
/// let sum = instance.exports.get_function("sum").unwrap();
|
||||
///
|
||||
/// // This results in an error: `RuntimeError`
|
||||
/// let sum_native = sum.native::<(i32, i32), i64>().unwrap();
|
||||
/// ```
|
||||
pub fn native<'a, Args, Rets>(&self) -> Result<NativeFunc<'a, Args, Rets>, RuntimeError>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
@@ -560,9 +711,9 @@ mod inner {
|
||||
/// A trait to convert a Rust value to a `WasmNativeType` value,
|
||||
/// or to convert `WasmNativeType` value to a Rust value.
|
||||
///
|
||||
/// This trait should ideally be splitted into two traits:
|
||||
/// This trait should ideally be split into two traits:
|
||||
/// `FromNativeWasmType` and `ToNativeWasmType` but it creates a
|
||||
/// non-negligeable complexity in the `WasmTypeList`
|
||||
/// non-negligible complexity in the `WasmTypeList`
|
||||
/// implementation.
|
||||
pub unsafe trait FromToNativeWasmType: Copy
|
||||
where
|
||||
|
||||
110
lib/api/src/externals/global.rs
vendored
110
lib/api/src/externals/global.rs
vendored
@@ -23,11 +23,35 @@ pub struct Global {
|
||||
|
||||
impl Global {
|
||||
/// Create a new `Global` with the initial value [`Val`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Mutability, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// assert_eq!(g.ty().mutability, Mutability::Const);
|
||||
/// ```
|
||||
pub fn new(store: &Store, val: Val) -> Self {
|
||||
Self::from_value(store, val, Mutability::Const).unwrap()
|
||||
}
|
||||
|
||||
/// Create a mutable `Global` with the initial value [`Val`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Mutability, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new_mut(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// assert_eq!(g.ty().mutability, Mutability::Var);
|
||||
/// ```
|
||||
pub fn new_mut(store: &Store, val: Val) -> Self {
|
||||
Self::from_value(store, val, Mutability::Var).unwrap()
|
||||
}
|
||||
@@ -54,27 +78,96 @@ impl Global {
|
||||
}
|
||||
|
||||
/// Returns the [`GlobalType`] of the `Global`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let c = Global::new(&store, Value::I32(1));
|
||||
/// let v = Global::new_mut(&store, Value::I64(1));
|
||||
///
|
||||
/// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const));
|
||||
/// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var));
|
||||
/// ```
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
self.global.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Global` belongs.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.store(), &store);
|
||||
/// ```
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Retrieves the current value [`Val`] that the Global has.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// ```
|
||||
pub fn get(&self) -> Val {
|
||||
self.global.get()
|
||||
}
|
||||
|
||||
/// Sets a custom value [`Val`] to the runtime Global.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new_mut(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
///
|
||||
/// g.set(Value::I32(2));
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(2));
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will error if:
|
||||
/// * The global is not mutable
|
||||
/// * The type of the `Val` doesn't matches the Global type.
|
||||
/// Trying to mutate a immutable global will raise an error:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// g.set(Value::I32(2)).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Trying to set a value of a incompatible type will raise an error:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// // This results in an error: `RuntimeError`.
|
||||
/// g.set(Value::I64(2)).unwrap();
|
||||
/// ```
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if !val.comes_from_same_store(&self.store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
@@ -95,6 +188,17 @@ impl Global {
|
||||
}
|
||||
|
||||
/// Returns whether or not these two globals refer to the same data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert!(g.same(&g));
|
||||
/// ```
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.global, &other.global)
|
||||
}
|
||||
|
||||
82
lib/api/src/externals/memory.rs
vendored
82
lib/api/src/externals/memory.rs
vendored
@@ -34,6 +34,15 @@ impl Memory {
|
||||
/// This function will construct the `Memory` using the store [`Tunables`].
|
||||
///
|
||||
/// [`Tunables`]: crate::tunables::Tunables
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
/// ```
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Result<Self, MemoryError> {
|
||||
let tunables = store.tunables();
|
||||
let style = tunables.memory_style(&ty);
|
||||
@@ -46,11 +55,34 @@ impl Memory {
|
||||
}
|
||||
|
||||
/// Returns the [`MemoryType`] of the `Memory`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let mt = MemoryType::new(1, None, false);
|
||||
/// let m = Memory::new(&store, mt).unwrap();
|
||||
///
|
||||
/// assert_eq!(m.ty(), &mt);
|
||||
/// ```
|
||||
pub fn ty(&self) -> &MemoryType {
|
||||
self.memory.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Memory` belongs.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
///
|
||||
/// assert_eq!(m.store(), &store);
|
||||
/// ```
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
@@ -92,16 +124,51 @@ impl Memory {
|
||||
}
|
||||
|
||||
/// Returns the size (in [`Pages`]) of the `Memory`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
///
|
||||
/// assert_eq!(m.size(), Pages(1));
|
||||
/// ```
|
||||
pub fn size(&self) -> Pages {
|
||||
self.memory.size()
|
||||
}
|
||||
|
||||
/// Grow memory by the specified amount of WebAssembly [`Pages`].
|
||||
/// Grow memory by the specified amount of WebAssembly [`Pages`] and return
|
||||
/// the previous memory size.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, Some(3), false)).unwrap();
|
||||
/// let p = m.grow(2).unwrap();
|
||||
///
|
||||
/// assert_eq!(p, Pages(1));
|
||||
/// assert_eq!(m.size(), Pages(3));
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if memory can't be grown by the specified amount
|
||||
/// of pages.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, Some(1), false)).unwrap();
|
||||
///
|
||||
/// // This results in an error: `MemoryError::CouldNotGrow`.
|
||||
/// let s = m.grow(1).unwrap();
|
||||
/// ```
|
||||
pub fn grow<IntoPages>(&self, delta: IntoPages) -> Result<Pages, MemoryError>
|
||||
where
|
||||
IntoPages: Into<Pages>,
|
||||
@@ -155,7 +222,18 @@ impl Memory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not these two globals refer to the same data.
|
||||
/// Returns whether or not these two memories refer to the same data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
///
|
||||
/// assert!(m.same(&m));
|
||||
/// ```
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.memory, &other.memory)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
//! Wasmer API
|
||||
#![doc(
|
||||
html_logo_url = "https://github.com/wasmerio.png?size=200",
|
||||
html_favicon_url = "https://wasmer.io/static/icons/favicon.ico"
|
||||
)]
|
||||
#![deny(
|
||||
missing_docs,
|
||||
trivial_numeric_casts,
|
||||
|
||||
@@ -198,7 +198,7 @@ impl Module {
|
||||
self.artifact.serialize_to_file(path.as_ref())
|
||||
}
|
||||
|
||||
/// Deserializes a a serialized Module binary into a `Module`.
|
||||
/// Deserializes a serialized Module binary into a `Module`.
|
||||
/// > Note: the module has to be serialized before with the `serialize` method.
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
@@ -69,6 +69,7 @@ where
|
||||
vmctx: other.vmctx,
|
||||
signature,
|
||||
kind: other.arg_kind,
|
||||
call_trampoline: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +89,7 @@ where
|
||||
vmctx: other.vmctx,
|
||||
signature,
|
||||
kind: other.arg_kind,
|
||||
call_trampoline: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{MemoryType, Pages, TableType};
|
||||
use more_asserts::assert_ge;
|
||||
use std::cmp::min;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
@@ -71,7 +70,6 @@ impl BaseTunables for Tunables {
|
||||
// If the module doesn't declare an explicit maximum treat it as 4GiB.
|
||||
let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
|
||||
if maximum <= self.static_memory_bound {
|
||||
assert_ge!(self.static_memory_bound, memory.minimum);
|
||||
MemoryStyle::Static {
|
||||
bound: self.static_memory_bound,
|
||||
offset_guard_size: self.static_memory_offset_guard_size,
|
||||
|
||||
@@ -78,6 +78,7 @@ impl ValFuncRef for Val {
|
||||
// are converted to use the trampolines with static signatures).
|
||||
kind: wasmer_vm::VMFunctionKind::Static,
|
||||
vmctx: item.vmctx,
|
||||
call_trampoline: None,
|
||||
};
|
||||
let f = Function::from_export(store, export);
|
||||
Self::FuncRef(f)
|
||||
|
||||
@@ -73,6 +73,7 @@ fn build_wasm_c_api_headers(crate_dir: &str, out_dir: &str) {
|
||||
|
||||
let mut pre_header = format!(
|
||||
r#"// The Wasmer C/C++ header file compatible with the `wasm-c-api` standard API.
|
||||
// This file is generated by lib/c-api/build.rs.
|
||||
|
||||
#if !defined(WASMER_WASM_H_MACROS)
|
||||
|
||||
@@ -382,7 +383,6 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder {
|
||||
.exclude_item("wasi_version_t")
|
||||
.exclude_item("wasm_config_set_compiler")
|
||||
.exclude_item("wasm_config_set_engine")
|
||||
.exclude_item("wasm_instance_get_vmctx_ptr")
|
||||
.exclude_item("wasm_module_name")
|
||||
.exclude_item("wasm_module_set_name")
|
||||
.exclude_item("wasmer_compiler_t")
|
||||
|
||||
@@ -129,9 +129,22 @@ pub unsafe extern "C" fn wasmer_instantiate(
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
|
||||
if imports_len > 0 && imports.is_null() {
|
||||
update_last_error(CApiError {
|
||||
msg: "imports ptr is null".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
|
||||
let mut imported_memories = vec![];
|
||||
let mut instance_pointers_to_update = vec![];
|
||||
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
|
||||
let imports: &[wasmer_import_t] = if imports_len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(imports, imports_len as usize)
|
||||
};
|
||||
|
||||
let mut import_object = ImportObject::new();
|
||||
let mut namespaces = HashMap::new();
|
||||
for import in imports {
|
||||
|
||||
@@ -90,12 +90,6 @@ pub mod instance;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod table;
|
||||
// `not(target_family = "windows")` is simpler than `unix`. See build.rs
|
||||
// if you want to change the meaning of these `cfg`s in the header file.
|
||||
/*
|
||||
TODO: reenable `trampoline` module when the refactor gains feature parity with Wasmer master
|
||||
#[cfg(all(not(target_family = "windows"), target_arch = "x86_64"))]
|
||||
pub mod trampoline;*/
|
||||
pub mod value;
|
||||
|
||||
/// The `wasmer_result_t` enum is a type that represents either a
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
//! Trampoline emitter for transforming function calls.
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::mem;
|
||||
use wasmer_vm_core::trampoline::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_trampoline_buffer_builder_t;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_trampoline_buffer_t;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_trampoline_callable_t;
|
||||
|
||||
/// Creates a new trampoline builder.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoline_buffer_builder_t
|
||||
{
|
||||
Box::into_raw(Box::new(TrampolineBufferBuilder::new())) as *mut _
|
||||
}
|
||||
|
||||
/// Adds a context trampoline to the builder.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline(
|
||||
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||
func: *const wasmer_trampoline_callable_t,
|
||||
ctx: *const c_void,
|
||||
) -> usize {
|
||||
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
|
||||
builder.add_context_trampoline(func as *const CallTarget, ctx as *const CallContext)
|
||||
}
|
||||
|
||||
/// Adds a callinfo trampoline to the builder.
|
||||
///
|
||||
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
|
||||
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||
func: *const wasmer_trampoline_callable_t,
|
||||
ctx: *const c_void,
|
||||
num_params: u32,
|
||||
) -> usize {
|
||||
use wasmer::ValType;
|
||||
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
|
||||
builder.add_callinfo_trampoline(
|
||||
mem::transmute(func),
|
||||
ctx as *const CallContext,
|
||||
&vec![ValType::I64; num_params as usize],
|
||||
&[ValType::I64],
|
||||
)
|
||||
}
|
||||
|
||||
/// Finalizes the trampoline builder into an executable buffer.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build(
|
||||
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||
) -> *mut wasmer_trampoline_buffer_t {
|
||||
let builder = Box::from_raw(builder as *mut TrampolineBufferBuilder);
|
||||
Box::into_raw(Box::new(builder.build())) as *mut _
|
||||
}
|
||||
|
||||
/// Destroys the trampoline buffer if not null.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(buffer: *mut wasmer_trampoline_buffer_t) {
|
||||
if !buffer.is_null() {
|
||||
Box::from_raw(buffer as *mut TrampolineBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the callable pointer for the trampoline with index `idx`.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline(
|
||||
buffer: *const wasmer_trampoline_buffer_t,
|
||||
idx: usize,
|
||||
) -> *const wasmer_trampoline_callable_t {
|
||||
let buffer = &*(buffer as *const TrampolineBuffer);
|
||||
buffer.get_trampoline(idx) as _
|
||||
}
|
||||
|
||||
/// Returns the context added by `add_context_trampoline`, from within the callee function.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe extern "C" fn wasmer_trampoline_get_context() -> *mut c_void {
|
||||
get_context() as *const c_void as *mut c_void
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
//! this repository for the sake of simplicity.
|
||||
|
||||
#![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")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")]
|
||||
// temporary while in transition
|
||||
#![allow(unused_variables)]
|
||||
#![deny(
|
||||
|
||||
@@ -87,6 +87,7 @@ pub extern "C" fn wasm_config_set_engine(config: &mut wasm_config_t, engine: was
|
||||
}
|
||||
|
||||
/// cbindgen:ignore
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct wasm_engine_t {
|
||||
pub(crate) inner: Arc<dyn Engine + Send + Sync>,
|
||||
}
|
||||
@@ -170,6 +171,7 @@ pub extern "C" fn wasm_engine_new_with_config(
|
||||
// TODO: return useful error messages in failure branches
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "compiler")] {
|
||||
#[allow(unused_mut)]
|
||||
let mut compiler_config: Box<dyn CompilerConfig> = match config.compiler {
|
||||
wasmer_compiler_t::CRANELIFT => {
|
||||
cfg_if! {
|
||||
|
||||
89
lib/c-api/src/wasm_c_api/externals/function.rs
vendored
89
lib/c-api/src/wasm_c_api/externals/function.rs
vendored
@@ -1,7 +1,7 @@
|
||||
use super::super::store::wasm_store_t;
|
||||
use super::super::trap::wasm_trap_t;
|
||||
use super::super::types::{wasm_functype_t, wasm_valkind_enum};
|
||||
use super::super::value::{wasm_val_inner, wasm_val_t};
|
||||
use super::super::value::{wasm_val_inner, wasm_val_t, wasm_val_vec_t};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::c_void;
|
||||
use std::sync::Arc;
|
||||
@@ -15,14 +15,16 @@ pub struct wasm_func_t {
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type wasm_func_callback_t =
|
||||
unsafe extern "C" fn(args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t;
|
||||
pub type wasm_func_callback_t = unsafe extern "C" fn(
|
||||
args: *const wasm_val_vec_t,
|
||||
results: *mut wasm_val_vec_t,
|
||||
) -> *mut wasm_trap_t;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type wasm_func_callback_with_env_t = unsafe extern "C" fn(
|
||||
*mut c_void,
|
||||
args: *const wasm_val_t,
|
||||
results: *mut wasm_val_t,
|
||||
args: *const wasm_val_vec_t,
|
||||
results: *mut wasm_val_vec_t,
|
||||
) -> *mut wasm_trap_t;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
@@ -38,31 +40,38 @@ pub unsafe extern "C" fn wasm_func_new(
|
||||
let func_sig = ft.sig();
|
||||
let num_rets = func_sig.results().len();
|
||||
let inner_callback = move |args: &[Val]| -> Result<Vec<Val>, RuntimeError> {
|
||||
let processed_args = args
|
||||
let processed_args: wasm_val_vec_t = args
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<wasm_val_t>, _>>()
|
||||
.expect("Argument conversion failed");
|
||||
.expect("Argument conversion failed")
|
||||
.into();
|
||||
|
||||
let mut results = vec![
|
||||
let mut results: wasm_val_vec_t = vec![
|
||||
wasm_val_t {
|
||||
kind: wasm_valkind_enum::WASM_I64 as _,
|
||||
of: wasm_val_inner { int64_t: 0 },
|
||||
};
|
||||
num_rets
|
||||
];
|
||||
]
|
||||
.into();
|
||||
|
||||
let trap = callback(&processed_args, &mut results);
|
||||
|
||||
let trap = callback(processed_args.as_ptr(), results.as_mut_ptr());
|
||||
if !trap.is_null() {
|
||||
let trap: Box<wasm_trap_t> = Box::from_raw(trap);
|
||||
RuntimeError::raise(Box::new(trap.inner));
|
||||
|
||||
return Err(trap.inner);
|
||||
}
|
||||
|
||||
let processed_results = results
|
||||
.into_slice()
|
||||
.expect("Failed to convert `results` into a slice")
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<Val>, _>>()
|
||||
.expect("Result conversion failed");
|
||||
|
||||
Ok(processed_results)
|
||||
};
|
||||
let function = Function::new(&store.inner, &func_sig, inner_callback);
|
||||
@@ -86,30 +95,41 @@ pub unsafe extern "C" fn wasm_func_new_with_env(
|
||||
let num_rets = func_sig.results().len();
|
||||
let inner_callback =
|
||||
move |env: &mut *mut c_void, args: &[Val]| -> Result<Vec<Val>, RuntimeError> {
|
||||
let processed_args = args
|
||||
let processed_args: wasm_val_vec_t = args
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<wasm_val_t>, _>>()
|
||||
.expect("Argument conversion failed");
|
||||
.expect("Argument conversion failed")
|
||||
.into();
|
||||
|
||||
let mut results = vec![
|
||||
let mut results: wasm_val_vec_t = vec![
|
||||
wasm_val_t {
|
||||
kind: wasm_valkind_enum::WASM_I64 as _,
|
||||
of: wasm_val_inner { int64_t: 0 },
|
||||
};
|
||||
num_rets
|
||||
];
|
||||
]
|
||||
.into();
|
||||
|
||||
let _traps = callback(*env, processed_args.as_ptr(), results.as_mut_ptr());
|
||||
// TODO: do something with `traps`
|
||||
let trap = callback(*env, &processed_args, &mut results);
|
||||
|
||||
if !trap.is_null() {
|
||||
let trap: Box<wasm_trap_t> = Box::from_raw(trap);
|
||||
|
||||
return Err(trap.inner);
|
||||
}
|
||||
|
||||
let processed_results = results
|
||||
.into_slice()
|
||||
.expect("Failed to convert `results` into a slice")
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<Val>, _>>()
|
||||
.expect("Result conversion failed");
|
||||
|
||||
Ok(processed_results)
|
||||
};
|
||||
|
||||
let function = Function::new_with_env(&store.inner, &func_sig, env, inner_callback);
|
||||
|
||||
Some(Box::new(wasm_func_t {
|
||||
@@ -124,21 +144,29 @@ pub unsafe extern "C" fn wasm_func_delete(_func: Option<Box<wasm_func_t>>) {}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_func_call(
|
||||
func: &wasm_func_t,
|
||||
args: *const wasm_val_t,
|
||||
results: *mut wasm_val_t,
|
||||
args: &wasm_val_vec_t,
|
||||
results: &mut wasm_val_vec_t,
|
||||
) -> Option<Box<wasm_trap_t>> {
|
||||
let num_params = func.inner.ty().params().len();
|
||||
let params: Vec<Val> = (0..num_params)
|
||||
.map(|i| (&(*args.add(i))).try_into())
|
||||
.collect::<Result<_, _>>()
|
||||
.ok()?;
|
||||
let params = args
|
||||
.into_slice()
|
||||
.map(|slice| {
|
||||
slice
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<Val>, _>>()
|
||||
.expect("Argument conversion failed")
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
match func.inner.call(¶ms) {
|
||||
Ok(wasm_results) => {
|
||||
for (i, actual_result) in wasm_results.iter().enumerate() {
|
||||
let result_loc = &mut (*results.add(i));
|
||||
*result_loc = (&*actual_result).try_into().ok()?;
|
||||
}
|
||||
*results = wasm_results
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<wasm_val_t>, _>>()
|
||||
.expect("Argument conversion failed")
|
||||
.into();
|
||||
|
||||
None
|
||||
}
|
||||
Err(e) => Some(Box::new(e.into())),
|
||||
@@ -154,3 +182,8 @@ pub unsafe extern "C" fn wasm_func_param_arity(func: &wasm_func_t) -> usize {
|
||||
pub unsafe extern "C" fn wasm_func_result_arity(func: &wasm_func_t) -> usize {
|
||||
func.inner.ty().results().len()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_func_type(func: &wasm_func_t) -> Box<wasm_functype_t> {
|
||||
Box::new(wasm_functype_t::new(func.inner.ty().clone()))
|
||||
}
|
||||
|
||||
5
lib/c-api/src/wasm_c_api/externals/global.rs
vendored
5
lib/c-api/src/wasm_c_api/externals/global.rs
vendored
@@ -59,3 +59,8 @@ pub unsafe extern "C" fn wasm_global_same(
|
||||
) -> bool {
|
||||
wasm_global1.inner.same(&wasm_global2.inner)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_global_type(wasm_global: &wasm_global_t) -> Box<wasm_globaltype_t> {
|
||||
Box::new(wasm_globaltype_t::new(wasm_global.inner.ty().clone()))
|
||||
}
|
||||
|
||||
8
lib/c-api/src/wasm_c_api/externals/memory.rs
vendored
8
lib/c-api/src/wasm_c_api/externals/memory.rs
vendored
@@ -25,16 +25,16 @@ pub unsafe extern "C" fn wasm_memory_delete(_memory: Option<Box<wasm_memory_t>>)
|
||||
|
||||
// TODO: figure out if these should be deep or shallow copies
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_copy(wasm_memory: &wasm_memory_t) -> Box<wasm_memory_t> {
|
||||
pub unsafe extern "C" fn wasm_memory_copy(memory: &wasm_memory_t) -> Box<wasm_memory_t> {
|
||||
// do shallow copy
|
||||
Box::new(wasm_memory_t {
|
||||
inner: wasm_memory.inner.clone(),
|
||||
inner: memory.inner.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_type(_memory_ptr: &wasm_memory_t) -> *mut wasm_memorytype_t {
|
||||
todo!("wasm_memory_type")
|
||||
pub unsafe extern "C" fn wasm_memory_type(memory: &wasm_memory_t) -> Box<wasm_memorytype_t> {
|
||||
Box::new(wasm_memorytype_t::new(memory.inner.ty().clone()))
|
||||
}
|
||||
|
||||
// get a raw pointer into bytes
|
||||
|
||||
@@ -12,56 +12,21 @@ pub struct wasm_instance_t {
|
||||
pub(crate) inner: Arc<Instance>,
|
||||
}
|
||||
|
||||
struct CArrayIter<T: Sized + 'static> {
|
||||
cur_entry: *const *const T,
|
||||
}
|
||||
|
||||
impl<T: Sized + 'static> CArrayIter<T> {
|
||||
fn new(array: *const *const T) -> Option<Self> {
|
||||
if array.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(CArrayIter { cur_entry: array })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sized + 'static> Iterator for CArrayIter<T> {
|
||||
type Item = &'static T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_entry_candidate = unsafe { *self.cur_entry };
|
||||
if next_entry_candidate.is_null() {
|
||||
None
|
||||
} else {
|
||||
self.cur_entry = unsafe { self.cur_entry.add(1) };
|
||||
Some(unsafe { &*next_entry_candidate })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reads from null-terminated array of `wasm_extern_t`s
|
||||
unsafe fn argument_import_iter(
|
||||
imports: *const *const wasm_extern_t,
|
||||
) -> Box<dyn Iterator<Item = &'static wasm_extern_t>> {
|
||||
CArrayIter::new(imports)
|
||||
.map(|it| Box::new(it) as _)
|
||||
.unwrap_or_else(|| Box::new(std::iter::empty()) as _)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_instance_new(
|
||||
_store: &wasm_store_t,
|
||||
module: &wasm_module_t,
|
||||
imports: *const *const wasm_extern_t,
|
||||
imports: &wasm_extern_vec_t,
|
||||
// own
|
||||
_traps: *mut *mut wasm_trap_t,
|
||||
) -> Option<Box<wasm_instance_t>> {
|
||||
let wasm_module = &module.inner;
|
||||
let module_imports = wasm_module.imports();
|
||||
let module_import_count = module_imports.len();
|
||||
let imports = argument_import_iter(imports);
|
||||
let resolver: OrderedResolver = imports
|
||||
.into_slice()
|
||||
.map(|imports| imports.iter())
|
||||
.unwrap_or_else(|| [].iter())
|
||||
.map(|imp| &imp.inner)
|
||||
.take(module_import_count)
|
||||
.cloned()
|
||||
|
||||
@@ -108,7 +108,10 @@ pub unsafe extern "C" fn wasm_module_serialize(
|
||||
) {
|
||||
let byte_vec = match module.inner.serialize() {
|
||||
Ok(byte_vec) => byte_vec,
|
||||
Err(_) => return,
|
||||
Err(err) => {
|
||||
crate::error::update_last_error(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
*out_ptr = byte_vec.into();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ impl wasm_functype_t {
|
||||
unreachable!("data corruption: `wasm_functype_t` does not contain a function")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(function_type: FunctionType) -> Self {
|
||||
Self {
|
||||
extern_: wasm_externtype_t {
|
||||
inner: ExternType::Function(function_type),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasm_declare_vec!(functype);
|
||||
@@ -52,10 +60,9 @@ unsafe fn wasm_functype_new_inner(
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let extern_ = wasm_externtype_t {
|
||||
inner: ExternType::Function(FunctionType::new(params, results)),
|
||||
};
|
||||
Some(Box::new(wasm_functype_t { extern_ }))
|
||||
Some(Box::new(wasm_functype_t::new(FunctionType::new(
|
||||
params, results,
|
||||
))))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -20,6 +20,14 @@ impl wasm_globaltype_t {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(global_type: GlobalType) -> Self {
|
||||
Self {
|
||||
extern_: wasm_externtype_t {
|
||||
inner: ExternType::Global(global_type),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasm_declare_vec!(globaltype);
|
||||
@@ -42,11 +50,10 @@ unsafe fn wasm_globaltype_new_inner(
|
||||
mutability: wasm_mutability_t,
|
||||
) -> Option<Box<wasm_globaltype_t>> {
|
||||
let me: wasm_mutability_enum = mutability.try_into().ok()?;
|
||||
let gd = Box::new(wasm_globaltype_t {
|
||||
extern_: wasm_externtype_t {
|
||||
inner: ExternType::Global(GlobalType::new((*valtype).into(), me.into())),
|
||||
},
|
||||
});
|
||||
let gd = Box::new(wasm_globaltype_t::new(GlobalType::new(
|
||||
(*valtype).into(),
|
||||
me.into(),
|
||||
)));
|
||||
wasm_valtype_delete(Some(valtype));
|
||||
|
||||
Some(gd)
|
||||
|
||||
@@ -18,6 +18,14 @@ impl wasm_memorytype_t {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(memory_type: MemoryType) -> Self {
|
||||
Self {
|
||||
extern_: wasm_externtype_t {
|
||||
inner: ExternType::Memory(memory_type),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasm_declare_vec!(memorytype);
|
||||
@@ -42,11 +50,9 @@ pub unsafe extern "C" fn wasm_memorytype_new(limits: &wasm_limits_t) -> Box<wasm
|
||||
Some(Pages(limits.max as _))
|
||||
};
|
||||
|
||||
Box::new(wasm_memorytype_t {
|
||||
extern_: wasm_externtype_t {
|
||||
inner: ExternType::Memory(MemoryType::new(min_pages, max_pages, false)),
|
||||
},
|
||||
})
|
||||
Box::new(wasm_memorytype_t::new(MemoryType::new(
|
||||
min_pages, max_pages, false,
|
||||
)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct wasm_val_t {
|
||||
pub of: wasm_val_inner,
|
||||
}
|
||||
|
||||
wasm_declare_vec!(val);
|
||||
|
||||
impl Clone for wasm_val_t {
|
||||
fn clone(&self) -> Self {
|
||||
wasm_val_t {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
mod capture_files;
|
||||
|
||||
use super::{
|
||||
externals::{wasm_extern_t, wasm_func_t, wasm_memory_t},
|
||||
externals::{wasm_extern_t, wasm_extern_vec_t, wasm_func_t, wasm_memory_t},
|
||||
instance::wasm_instance_t,
|
||||
module::wasm_module_t,
|
||||
store::wasm_store_t,
|
||||
@@ -300,7 +300,7 @@ pub unsafe extern "C" fn wasi_get_imports(
|
||||
store: &wasm_store_t,
|
||||
module: &wasm_module_t,
|
||||
wasi_env: &wasi_env_t,
|
||||
imports: *mut *mut wasm_extern_t,
|
||||
imports: &mut wasm_extern_vec_t,
|
||||
) -> bool {
|
||||
wasi_get_imports_inner(store, module, wasi_env, imports).is_some()
|
||||
}
|
||||
@@ -310,7 +310,7 @@ unsafe fn wasi_get_imports_inner(
|
||||
store: &wasm_store_t,
|
||||
module: &wasm_module_t,
|
||||
wasi_env: &wasi_env_t,
|
||||
imports: *mut *mut wasm_extern_t,
|
||||
imports: &mut wasm_extern_vec_t,
|
||||
) -> Option<()> {
|
||||
let store = &store.inner;
|
||||
|
||||
@@ -322,22 +322,28 @@ unsafe fn wasi_get_imports_inner(
|
||||
|
||||
let import_object = generate_import_object_from_env(store, wasi_env.inner.clone(), version);
|
||||
|
||||
for (i, it) in module.inner.imports().enumerate() {
|
||||
*imports = module
|
||||
.inner
|
||||
.imports()
|
||||
.map(|import_type| {
|
||||
let export = c_try!(import_object
|
||||
.resolve_by_name(it.module(), it.name())
|
||||
.resolve_by_name(import_type.module(), import_type.name())
|
||||
.ok_or_else(|| CApiError {
|
||||
msg: format!(
|
||||
"Failed to resolve import \"{}\" \"{}\"",
|
||||
it.module(),
|
||||
it.name()
|
||||
import_type.module(),
|
||||
import_type.name()
|
||||
),
|
||||
}));
|
||||
let inner = Extern::from_export(store, export);
|
||||
*imports.add(i) = Box::into_raw(Box::new(wasm_extern_t {
|
||||
|
||||
Some(Box::new(wasm_extern_t {
|
||||
instance: None,
|
||||
inner,
|
||||
}));
|
||||
}
|
||||
}))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?
|
||||
.into();
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
//! Wasmer-specific extensions to the Wasm C API.
|
||||
|
||||
use super::instance::wasm_instance_t;
|
||||
use super::module::wasm_module_t;
|
||||
use super::types::wasm_name_t;
|
||||
use std::ffi::c_void;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_instance_get_vmctx_ptr(instance: &wasm_instance_t) -> *mut c_void {
|
||||
instance.inner.vmctx_ptr() as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_module_name(module: &wasm_module_t, out: &mut wasm_name_t) {
|
||||
let name = match module.inner.name() {
|
||||
|
||||
@@ -28,18 +28,6 @@ int main()
|
||||
printf("Serialized module pointer: %p\n", serialized_module_bytes.bytes);
|
||||
printf("Serialized module length: %d\n", serialized_module_bytes.bytes_len);
|
||||
assert(serialized_module_bytes.bytes != NULL);
|
||||
assert(serialized_module_bytes.bytes_len > 11);
|
||||
assert(serialized_module_bytes.bytes[0] == '\0');
|
||||
assert(serialized_module_bytes.bytes[1] == 'w');
|
||||
assert(serialized_module_bytes.bytes[2] == 'a');
|
||||
assert(serialized_module_bytes.bytes[3] == 's');
|
||||
assert(serialized_module_bytes.bytes[4] == 'm');
|
||||
assert(serialized_module_bytes.bytes[5] == 'e');
|
||||
assert(serialized_module_bytes.bytes[6] == 'r');
|
||||
assert(serialized_module_bytes.bytes[7] == '-');
|
||||
assert(serialized_module_bytes.bytes[8] == 'j');
|
||||
assert(serialized_module_bytes.bytes[9] == 'i');
|
||||
assert(serialized_module_bytes.bytes[10] == 't');
|
||||
|
||||
wasmer_module_t *module_two = NULL;
|
||||
wasmer_result_t unserialize_result = wasmer_module_deserialize(&module_two, serialized_module);
|
||||
|
||||
@@ -27,9 +27,9 @@ void print_frame(wasm_frame_t* frame) {
|
||||
|
||||
wasm_store_t *store = NULL;
|
||||
|
||||
own wasm_trap_t *early_exit(const wasm_val_t args[], wasm_val_t results[]) {
|
||||
own wasm_trap_t* early_exit(const wasm_val_vec_t* args, wasm_val_vec_t* results) {
|
||||
own wasm_message_t trap_message;
|
||||
wasm_name_new_from_string(&trap_message, "trapping from a host import");
|
||||
wasm_name_new_from_string_nt(&trap_message, "trapping from a host import");
|
||||
own wasm_trap_t *trap = wasm_trap_new(store, &trap_message);
|
||||
wasm_name_delete(&trap_message);
|
||||
return trap;
|
||||
@@ -77,9 +77,13 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
wasm_functype_delete(host_func_type);
|
||||
|
||||
const wasm_extern_t *imports[] = {wasm_func_as_extern(host_func)};
|
||||
wasm_extern_vec_t imports;
|
||||
wasm_extern_vec_new_uninitialized(&imports, 1);
|
||||
imports.data[0] = wasm_func_as_extern(host_func);
|
||||
|
||||
own wasm_instance_t *instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
print_wasmer_error();
|
||||
@@ -110,12 +114,12 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
own const wasm_val_t args[] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = 1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = 7}},
|
||||
};
|
||||
own wasm_val_t rets[1] = {};
|
||||
own wasm_trap_t *trap = wasm_func_call(run_func, args, rets);
|
||||
wasm_val_t values[2] = { WASM_I32_VAL(1), WASM_I32_VAL(7) };
|
||||
own wasm_val_vec_t args = WASM_ARRAY_VEC(values);
|
||||
wasm_val_t result = WASM_INIT_VAL;
|
||||
own wasm_val_vec_t rets = { 1, &result };
|
||||
own wasm_trap_t *trap = wasm_func_call(run_func, &args, &rets);
|
||||
|
||||
if (!trap) {
|
||||
printf("> Error calling function: expected trap!\n");
|
||||
return 1;
|
||||
@@ -151,6 +155,7 @@ int main(int argc, const char *argv[]) {
|
||||
wasm_name_delete(&message);
|
||||
|
||||
wasm_extern_vec_delete(&exports);
|
||||
wasm_extern_vec_delete(&imports);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
|
||||
@@ -62,6 +62,66 @@ int main(int argc, const char *argv[]) {
|
||||
wasm_memorytype_delete(memtype3);
|
||||
wasm_memory_delete(memory3);
|
||||
|
||||
// =====================
|
||||
wasm_limits_t limits4 = {
|
||||
.min = 0x7FFFFFFF,
|
||||
.max = 0x7FFFFFFF,
|
||||
};
|
||||
own wasm_memorytype_t* memtype4 = wasm_memorytype_new(&limits4);
|
||||
own wasm_memory_t* memory4 = wasm_memory_new(store, memtype4);
|
||||
assert(memory4 == NULL);
|
||||
error = get_wasmer_error();
|
||||
printf("Found error string: %s\n", error);
|
||||
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
|
||||
free(error);
|
||||
|
||||
wasm_memorytype_delete(memtype4);
|
||||
|
||||
// =====================
|
||||
wasm_limits_t limits5 = {
|
||||
.min = 0x7FFFFFFF,
|
||||
.max = 0x0FFFFFFF,
|
||||
};
|
||||
own wasm_memorytype_t* memtype5 = wasm_memorytype_new(&limits5);
|
||||
own wasm_memory_t* memory5 = wasm_memory_new(store, memtype5);
|
||||
assert(memory5 == NULL);
|
||||
error = get_wasmer_error();
|
||||
printf("Found error string: %s\n", error);
|
||||
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
|
||||
free(error);
|
||||
|
||||
wasm_memorytype_delete(memtype5);
|
||||
|
||||
// =====================
|
||||
wasm_limits_t limits6 = {
|
||||
.min = 15,
|
||||
.max = 10,
|
||||
};
|
||||
own wasm_memorytype_t* memtype6 = wasm_memorytype_new(&limits6);
|
||||
own wasm_memory_t* memory6 = wasm_memory_new(store, memtype6);
|
||||
assert(memory6 == NULL);
|
||||
error = get_wasmer_error();
|
||||
printf("Found error string: %s\n", error);
|
||||
assert(0 == strcmp("The memory is invalid because the maximum (10 pages) is less than the minimum (15 pages)", error));
|
||||
free(error);
|
||||
|
||||
wasm_memorytype_delete(memtype6);
|
||||
|
||||
// =====================
|
||||
wasm_limits_t limits7 = {
|
||||
.min = 0x7FFFFFFF,
|
||||
.max = 10,
|
||||
};
|
||||
own wasm_memorytype_t* memtype7 = wasm_memorytype_new(&limits7);
|
||||
own wasm_memory_t* memory7 = wasm_memory_new(store, memtype7);
|
||||
assert(memory7 == NULL);
|
||||
error = get_wasmer_error();
|
||||
printf("Found error string: %s\n", error);
|
||||
assert(0 == strcmp("The minimum requested (2147483647 pages) memory is greater than the maximum allowed memory (65536 pages)", error));
|
||||
free(error);
|
||||
|
||||
wasm_memorytype_delete(memtype7);
|
||||
|
||||
printf("Shutting down...\n");
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
|
||||
@@ -18,7 +18,6 @@ void print_wasmer_error()
|
||||
printf("Error str: `%s`\n", error_str);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
// Initialize.
|
||||
printf("Initializing...\n");
|
||||
@@ -71,18 +70,22 @@ int main(int argc, const char* argv[]) {
|
||||
printf("Instantiating module...\n");
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(module, &import_types);
|
||||
int num_imports = import_types.size;
|
||||
wasm_extern_t** imports = malloc(num_imports * sizeof(wasm_extern_t*));
|
||||
|
||||
wasm_extern_vec_t imports;
|
||||
wasm_extern_vec_new_uninitialized(&imports, import_types.size);
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports);
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, &imports);
|
||||
|
||||
if (!get_imports_result) {
|
||||
printf("> Error getting WASI imports!\n");
|
||||
print_wasmer_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, (const wasm_extern_t *const *) imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
print_wasmer_error();
|
||||
@@ -114,7 +117,11 @@ int main(int argc, const char* argv[]) {
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
printf("Evaluating \"%s\"\n", js_string);
|
||||
if (wasm_func_call(run_func, NULL, NULL)) {
|
||||
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t res = WASM_EMPTY_VEC;
|
||||
|
||||
if (wasm_func_call(run_func, &args, &res)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -131,12 +138,7 @@ int main(int argc, const char* argv[]) {
|
||||
printf("\n");
|
||||
|
||||
wasm_extern_vec_delete(&exports);
|
||||
|
||||
// NEEDS REVIEW:
|
||||
for(int i = 0; i < num_imports; ++i) {
|
||||
wasm_extern_delete(imports[i]);
|
||||
}
|
||||
free(imports);
|
||||
wasm_extern_vec_delete(&imports);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
|
||||
@@ -94,6 +94,7 @@ Currently, known implementations of this API are included in
|
||||
* V8 natively (both C and C++)
|
||||
* Wabt (only C?)
|
||||
* Wasmtime (only C?)
|
||||
* [Wasmer](https://github.com/wasmerio/wasmer/tree/master/lib/c-api) (only C, C++ coming soon)
|
||||
|
||||
|
||||
### TODO
|
||||
|
||||
@@ -35,27 +35,27 @@ void wasm_val_print(wasm_val_t val) {
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* print_callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n> ");
|
||||
wasm_val_print(args[0]);
|
||||
wasm_val_print(args->data[0]);
|
||||
printf("\n");
|
||||
|
||||
wasm_val_copy(&results[0], &args[0]);
|
||||
wasm_val_copy(&results->data[0], &args->data[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// A function closure.
|
||||
own wasm_trap_t* closure_callback(
|
||||
void* env, const wasm_val_t args[], wasm_val_t results[]
|
||||
void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
int i = *(int*)env;
|
||||
printf("Calling back closure...\n");
|
||||
printf("> %d\n", i);
|
||||
|
||||
results[0].kind = WASM_I32;
|
||||
results[0].of.i32 = (int32_t)i;
|
||||
results->data[0].kind = WASM_I32;
|
||||
results->data[0].of.i32 = (int32_t)i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("callback.wasm", "r");
|
||||
FILE* file = fopen("callback.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -108,11 +108,12 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = {
|
||||
wasm_extern_t* externs[] = {
|
||||
wasm_func_as_extern(print_func), wasm_func_as_extern(closure_func)
|
||||
};
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -140,13 +141,11 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
wasm_val_t args[2];
|
||||
args[0].kind = WASM_I32;
|
||||
args[0].of.i32 = 3;
|
||||
args[1].kind = WASM_I32;
|
||||
args[1].of.i32 = 4;
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(run_func, args, results)) {
|
||||
wasm_val_t as[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) };
|
||||
wasm_val_t rs[1] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(as);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(rs);
|
||||
if (wasm_func_call(run_func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -155,7 +154,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Print result.
|
||||
printf("Printing result...\n");
|
||||
printf("> %u\n", results[0].of.i32);
|
||||
printf("> %u\n", rs[0].of.i32);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
|
||||
@@ -35,7 +35,7 @@ auto operator<<(std::ostream& out, const wasm::Val& val) -> std::ostream& {
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto print_callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl << "> " << args[0] << std::endl;
|
||||
results[0] = args[0].copy();
|
||||
@@ -45,7 +45,7 @@ auto print_callback(
|
||||
|
||||
// A function closure.
|
||||
auto closure_callback(
|
||||
void* env, const wasm::Val args[], wasm::Val results[]
|
||||
void* env, const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
auto i = *reinterpret_cast<int*>(env);
|
||||
std::cout << "Calling back closure..." << std::endl;
|
||||
@@ -103,7 +103,8 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {print_func.get(), closure_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(
|
||||
print_func.get(), closure_func.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
@@ -121,8 +122,8 @@ void run() {
|
||||
|
||||
// Call.
|
||||
std::cout << "Calling export..." << std::endl;
|
||||
wasm::Val args[] = {wasm::Val::i32(3), wasm::Val::i32(4)};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::i32(3), wasm::Val::i32(4));
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (run_func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -20,7 +20,7 @@ void finalize(void* data) {
|
||||
void run_in_store(wasm_store_t* store) {
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("finalize.wasm", "r");
|
||||
FILE* file = fopen("finalize.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
exit(1);
|
||||
@@ -50,8 +50,9 @@ void run_in_store(wasm_store_t* store) {
|
||||
printf("Instantiating modules...\n");
|
||||
for (int i = 0; i <= iterations; ++i) {
|
||||
if (i % (iterations / 10) == 0) printf("%d\n", i);
|
||||
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, NULL, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module %d!\n", i);
|
||||
exit(1);
|
||||
|
||||
@@ -46,7 +46,8 @@ void run_in_store(wasm::Store* store) {
|
||||
std::cout << "Instantiating modules..." << std::endl;
|
||||
for (int i = 0; i <= iterations; ++i) {
|
||||
if (i % (iterations / 10) == 0) std::cout << i << std::endl;
|
||||
auto instance = wasm::Instance::make(store, module.get(), nullptr);
|
||||
auto imports = wasm::vec<wasm::Extern*>::make();
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module " << i << "!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -39,9 +39,11 @@ wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
|
||||
|
||||
#define check_call(func, type, expected) \
|
||||
{ \
|
||||
wasm_val_t results[1]; \
|
||||
wasm_func_call(func, NULL, results); \
|
||||
check(results[0], type, expected); \
|
||||
wasm_val_t vs[1]; \
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC; \
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(vs); \
|
||||
wasm_func_call(func, &args, &results); \
|
||||
check(vs[0], type, expected); \
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +55,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("global.wasm", "r");
|
||||
FILE* file = fopen("global.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -90,16 +92,16 @@ int main(int argc, const char* argv[]) {
|
||||
own wasm_globaltype_t* var_i64_type = wasm_globaltype_new(
|
||||
wasm_valtype_new(WASM_I64), WASM_VAR);
|
||||
|
||||
wasm_val_t val_f32_1 = {.kind = WASM_F32, .of = {.f32 = 1}};
|
||||
wasm_val_t val_f32_1 = WASM_F32_VAL(1);
|
||||
own wasm_global_t* const_f32_import =
|
||||
wasm_global_new(store, const_f32_type, &val_f32_1);
|
||||
wasm_val_t val_i64_2 = {.kind = WASM_I64, .of = {.i64 = 2}};
|
||||
wasm_val_t val_i64_2 = WASM_I64_VAL(2);
|
||||
own wasm_global_t* const_i64_import =
|
||||
wasm_global_new(store, const_i64_type, &val_i64_2);
|
||||
wasm_val_t val_f32_3 = {.kind = WASM_F32, .of = {.f32 = 3}};
|
||||
wasm_val_t val_f32_3 = WASM_F32_VAL(3);
|
||||
own wasm_global_t* var_f32_import =
|
||||
wasm_global_new(store, var_f32_type, &val_f32_3);
|
||||
wasm_val_t val_i64_4 = {.kind = WASM_I64, .of = {.i64 = 4}};
|
||||
wasm_val_t val_i64_4 = WASM_I64_VAL(4);
|
||||
own wasm_global_t* var_i64_import =
|
||||
wasm_global_new(store, var_i64_type, &val_i64_4);
|
||||
|
||||
@@ -110,14 +112,15 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = {
|
||||
wasm_extern_t* externs[] = {
|
||||
wasm_global_as_extern(const_f32_import),
|
||||
wasm_global_as_extern(const_i64_import),
|
||||
wasm_global_as_extern(var_f32_import),
|
||||
wasm_global_as_extern(var_i64_import)
|
||||
};
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -175,13 +178,13 @@ int main(int argc, const char* argv[]) {
|
||||
check_call(get_var_i64_export, i64, 8);
|
||||
|
||||
// Modify variables through API and check again.
|
||||
wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 33}};
|
||||
wasm_val_t val33 = WASM_F32_VAL(33);
|
||||
wasm_global_set(var_f32_import, &val33);
|
||||
wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 34}};
|
||||
wasm_val_t val34 = WASM_I64_VAL(34);
|
||||
wasm_global_set(var_i64_import, &val34);
|
||||
wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 37}};
|
||||
wasm_val_t val37 = WASM_F32_VAL(37);
|
||||
wasm_global_set(var_f32_export, &val37);
|
||||
wasm_val_t val38 = {.kind = WASM_I64, .of = {.i64 = 38}};
|
||||
wasm_val_t val38 = WASM_I64_VAL(38);
|
||||
wasm_global_set(var_i64_export, &val38);
|
||||
|
||||
check_global(var_f32_import, f32, 33);
|
||||
@@ -195,14 +198,19 @@ int main(int argc, const char* argv[]) {
|
||||
check_call(get_var_i64_export, i64, 38);
|
||||
|
||||
// Modify variables through calls and check again.
|
||||
wasm_val_t args73[] = { {.kind = WASM_F32, .of = {.f32 = 73}} };
|
||||
wasm_func_call(set_var_f32_import, args73, NULL);
|
||||
wasm_val_t args74[] = { {.kind = WASM_I64, .of = {.i64 = 74}} };
|
||||
wasm_func_call(set_var_i64_import, args74, NULL);
|
||||
wasm_val_t args77[] = { {.kind = WASM_F32, .of = {.f32 = 77}} };
|
||||
wasm_func_call(set_var_f32_export, args77, NULL);
|
||||
wasm_val_t args78[] = { {.kind = WASM_I64, .of = {.i64 = 78}} };
|
||||
wasm_func_call(set_var_i64_export, args78, NULL);
|
||||
wasm_val_vec_t res = WASM_EMPTY_VEC;
|
||||
wasm_val_t vs73[] = { WASM_F32_VAL(73) };
|
||||
wasm_val_vec_t args73 = WASM_ARRAY_VEC(vs73);
|
||||
wasm_func_call(set_var_f32_import, &args73, &res);
|
||||
wasm_val_t vs74[] = { WASM_I64_VAL(74) };
|
||||
wasm_val_vec_t args74 = WASM_ARRAY_VEC(vs74);
|
||||
wasm_func_call(set_var_i64_import, &args74, &res);
|
||||
wasm_val_t vs77[] = { WASM_F32_VAL(77) };
|
||||
wasm_val_vec_t args77 = WASM_ARRAY_VEC(vs77);
|
||||
wasm_func_call(set_var_f32_export, &args77, &res);
|
||||
wasm_val_t vs78[] = { WASM_I64_VAL(78) };
|
||||
wasm_val_vec_t args78 = WASM_ARRAY_VEC(vs78);
|
||||
wasm_func_call(set_var_i64_export, &args78, &res);
|
||||
|
||||
check_global(var_f32_import, f32, 73);
|
||||
check_global(var_i64_import, i64, 74);
|
||||
|
||||
@@ -32,8 +32,9 @@ void check(T actual, U expected) {
|
||||
}
|
||||
|
||||
auto call(const wasm::Func* func) -> wasm::Val {
|
||||
wasm::Val results[1];
|
||||
if (func->call(nullptr, results)) {
|
||||
auto args = wasm::vec<wasm::Val>::make();
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -41,8 +42,9 @@ auto call(const wasm::Func* func) -> wasm::Val {
|
||||
}
|
||||
|
||||
void call(const wasm::Func* func, wasm::Val&& arg) {
|
||||
wasm::Val args[1] = {std::move(arg)};
|
||||
if (func->call(args)) {
|
||||
auto args = wasm::vec<wasm::Val>::make(std::move(arg));
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -95,10 +97,10 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(
|
||||
const_f32_import.get(), const_i64_import.get(),
|
||||
var_f32_import.get(), var_i64_import.get()
|
||||
};
|
||||
);
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* hello_callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n");
|
||||
printf("> Hello World!\n");
|
||||
@@ -25,7 +25,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("hello.wasm", "r");
|
||||
FILE* file = fopen("hello.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -61,9 +61,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = { wasm_func_as_extern(hello_func) };
|
||||
wasm_extern_t* externs[] = { wasm_func_as_extern(hello_func) };
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -90,7 +91,9 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
if (wasm_func_call(run_func, NULL, NULL)) {
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
if (wasm_func_call(run_func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto hello_callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
std::cout << "> Hello world!" << std::endl;
|
||||
@@ -55,7 +55,7 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {hello_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(hello_func.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
@@ -73,7 +73,9 @@ void run() {
|
||||
|
||||
// Call.
|
||||
std::cout << "Calling export..." << std::endl;
|
||||
if (run_func->call()) {
|
||||
auto args = wasm::vec<wasm::Val>::make();
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (run_func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n> ");
|
||||
printf("> %p\n",
|
||||
args[0].of.ref ? wasm_ref_get_host_info(args[0].of.ref) : NULL);
|
||||
wasm_val_copy(&results[0], &args[0]);
|
||||
args->data[0].of.ref ? wasm_ref_get_host_info(args->data[0].of.ref) : NULL);
|
||||
wasm_val_copy(&results->data[0], &args->data[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -47,21 +47,23 @@ wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) {
|
||||
|
||||
own wasm_ref_t* call_v_r(const wasm_func_t* func) {
|
||||
printf("call_v_r... "); fflush(stdout);
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, NULL, results)) {
|
||||
wasm_val_t rs[] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(rs);
|
||||
if (wasm_func_call(func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("okay\n");
|
||||
return results[0].of.ref;
|
||||
return rs[0].of.ref;
|
||||
}
|
||||
|
||||
void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) {
|
||||
printf("call_r_v... "); fflush(stdout);
|
||||
wasm_val_t args[1];
|
||||
args[0].kind = WASM_ANYREF;
|
||||
args[0].of.ref = ref;
|
||||
if (wasm_func_call(func, args, NULL)) {
|
||||
wasm_val_t vs[1] = { WASM_REF_VAL(ref) };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
if (wasm_func_call(func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -70,26 +72,24 @@ void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) {
|
||||
|
||||
own wasm_ref_t* call_r_r(const wasm_func_t* func, wasm_ref_t* ref) {
|
||||
printf("call_r_r... "); fflush(stdout);
|
||||
wasm_val_t args[1];
|
||||
args[0].kind = WASM_ANYREF;
|
||||
args[0].of.ref = ref;
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, args, results)) {
|
||||
wasm_val_t vs[1] = { WASM_REF_VAL(ref) };
|
||||
wasm_val_t rs[1] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(rs);
|
||||
if (wasm_func_call(func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("okay\n");
|
||||
return results[0].of.ref;
|
||||
return rs[0].of.ref;
|
||||
}
|
||||
|
||||
void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) {
|
||||
printf("call_ir_v... "); fflush(stdout);
|
||||
wasm_val_t args[2];
|
||||
args[0].kind = WASM_I32;
|
||||
args[0].of.i32 = i;
|
||||
args[1].kind = WASM_ANYREF;
|
||||
args[1].of.ref = ref;
|
||||
if (wasm_func_call(func, args, NULL)) {
|
||||
wasm_val_t vs[2] = { WASM_I32_VAL(i), WASM_REF_VAL(ref) };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
if (wasm_func_call(func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -98,16 +98,16 @@ void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) {
|
||||
|
||||
own wasm_ref_t* call_i_r(const wasm_func_t* func, int32_t i) {
|
||||
printf("call_i_r... "); fflush(stdout);
|
||||
wasm_val_t args[1];
|
||||
args[0].kind = WASM_I32;
|
||||
args[0].of.i32 = i;
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, args, results)) {
|
||||
wasm_val_t vs[1] = { WASM_I32_VAL(i) };
|
||||
wasm_val_t rs[1] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(rs);
|
||||
if (wasm_func_call(func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("okay\n");
|
||||
return results[0].of.ref;
|
||||
return rs[0].of.ref;
|
||||
}
|
||||
|
||||
void check(own wasm_ref_t* actual, const wasm_ref_t* expected) {
|
||||
@@ -130,7 +130,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("hostref.wasm", "r");
|
||||
FILE* file = fopen("hostref.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -167,9 +167,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = { wasm_func_as_extern(callback_func) };
|
||||
wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) };
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
std::cout << "> " << (args[0].ref() ? args[0].ref()->get_host_info() : nullptr) << std::endl;
|
||||
@@ -45,8 +45,9 @@ auto get_export_table(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Ta
|
||||
|
||||
void call_r_v(const wasm::Func* func, const wasm::Ref* ref) {
|
||||
std::cout << "call_r_v... " << std::flush;
|
||||
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
|
||||
if (func->call(args, nullptr)) {
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>()));
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -55,8 +56,9 @@ void call_r_v(const wasm::Func* func, const wasm::Ref* ref) {
|
||||
|
||||
auto call_v_r(const wasm::Func* func) -> wasm::own<wasm::Ref> {
|
||||
std::cout << "call_v_r... " << std::flush;
|
||||
wasm::Val results[1];
|
||||
if (func->call(nullptr, results)) {
|
||||
auto args = wasm::vec<wasm::Val>::make();
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -66,8 +68,8 @@ auto call_v_r(const wasm::Func* func) -> wasm::own<wasm::Ref> {
|
||||
|
||||
auto call_r_r(const wasm::Func* func, const wasm::Ref* ref) -> wasm::own<wasm::Ref> {
|
||||
std::cout << "call_r_r... " << std::flush;
|
||||
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>()));
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
@@ -78,8 +80,10 @@ auto call_r_r(const wasm::Func* func, const wasm::Ref* ref) -> wasm::own<wasm::R
|
||||
|
||||
void call_ir_v(const wasm::Func* func, int32_t i, const wasm::Ref* ref) {
|
||||
std::cout << "call_ir_v... " << std::flush;
|
||||
wasm::Val args[2] = {wasm::Val::i32(i), wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
|
||||
if (func->call(args, nullptr)) {
|
||||
auto args = wasm::vec<wasm::Val>::make(
|
||||
wasm::Val::i32(i), wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>()));
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -88,8 +92,8 @@ void call_ir_v(const wasm::Func* func, int32_t i, const wasm::Ref* ref) {
|
||||
|
||||
auto call_i_r(const wasm::Func* func, int32_t i) -> wasm::own<wasm::Ref> {
|
||||
std::cout << "call_i_r... " << std::flush;
|
||||
wasm::Val args[1] = {wasm::Val::i32(i)};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::i32(i));
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
@@ -147,7 +151,7 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {callback_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(callback_func.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
|
||||
@@ -32,49 +32,49 @@ void check(bool success) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_call(wasm_func_t* func, wasm_val_t args[], int32_t expected) {
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, args, results) || results[0].of.i32 != expected) {
|
||||
void check_call(wasm_func_t* func, int i, wasm_val_t args[], int32_t expected) {
|
||||
wasm_val_t r = WASM_INIT_VAL;
|
||||
wasm_val_vec_t args_ = {i, args};
|
||||
wasm_val_vec_t results = {1, &r};
|
||||
if (wasm_func_call(func, &args_, &results) || r.of.i32 != expected) {
|
||||
printf("> Error on result\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_call0(wasm_func_t* func, int32_t expected) {
|
||||
check_call(func, NULL, expected);
|
||||
check_call(func, 0, NULL, expected);
|
||||
}
|
||||
|
||||
void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) {
|
||||
wasm_val_t args[] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
|
||||
check_call(func, args, expected);
|
||||
wasm_val_t args[] = { WASM_I32_VAL(arg) };
|
||||
check_call(func, 1, args, expected);
|
||||
}
|
||||
|
||||
void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_call(func, args, expected);
|
||||
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
|
||||
check_call(func, 2, args, expected);
|
||||
}
|
||||
|
||||
void check_ok(wasm_func_t* func, wasm_val_t args[]) {
|
||||
if (wasm_func_call(func, args, NULL)) {
|
||||
void check_ok(wasm_func_t* func, int i, wasm_val_t args[]) {
|
||||
wasm_val_vec_t args_ = {i, args};
|
||||
wasm_val_vec_t results = {0, NULL};
|
||||
if (wasm_func_call(func, &args_, &results)) {
|
||||
printf("> Error on result, expected empty\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_ok(func, args);
|
||||
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
|
||||
check_ok(func, 2, args);
|
||||
}
|
||||
|
||||
void check_trap(wasm_func_t* func, wasm_val_t args[]) {
|
||||
wasm_val_t results[1];
|
||||
own wasm_trap_t* trap = wasm_func_call(func, args, results);
|
||||
void check_trap(wasm_func_t* func, int i, wasm_val_t args[]) {
|
||||
wasm_val_t r = WASM_INIT_VAL;
|
||||
wasm_val_vec_t args_ = {i, args};
|
||||
wasm_val_vec_t results = {1, &r};
|
||||
own wasm_trap_t* trap = wasm_func_call(func, &args_, &results);
|
||||
if (! trap) {
|
||||
printf("> Error on result, expected trap\n");
|
||||
exit(1);
|
||||
@@ -83,16 +83,13 @@ void check_trap(wasm_func_t* func, wasm_val_t args[]) {
|
||||
}
|
||||
|
||||
void check_trap1(wasm_func_t* func, int32_t arg) {
|
||||
wasm_val_t args[1] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
|
||||
check_trap(func, args);
|
||||
wasm_val_t args[] = { WASM_I32_VAL(arg) };
|
||||
check_trap(func, 1, args);
|
||||
}
|
||||
|
||||
void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_trap(func, args);
|
||||
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
|
||||
check_trap(func, 2, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +101,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("memory.wasm", "r");
|
||||
FILE* file = fopen("memory.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -132,7 +129,9 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
own wasm_instance_t* instance = wasm_instance_new(store, module, NULL, NULL);
|
||||
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
|
||||
@@ -33,8 +33,9 @@ void check(T actual, U expected) {
|
||||
|
||||
template<class... Args>
|
||||
void check_ok(const wasm::Func* func, Args... xs) {
|
||||
wasm::Val args[] = {wasm::Val::i32(xs)...};
|
||||
if (func->call(args)) {
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::i32(xs)...);
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error on result, expected return" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -42,8 +43,9 @@ void check_ok(const wasm::Func* func, Args... xs) {
|
||||
|
||||
template<class... Args>
|
||||
void check_trap(const wasm::Func* func, Args... xs) {
|
||||
wasm::Val args[] = {wasm::Val::i32(xs)...};
|
||||
if (! func->call(args)) {
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::i32(xs)...);
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (! func->call(args, results)) {
|
||||
std::cout << "> Error on result, expected trap" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
@@ -51,8 +53,8 @@ void check_trap(const wasm::Func* func, Args... xs) {
|
||||
|
||||
template<class... Args>
|
||||
auto call(const wasm::Func* func, Args... xs) -> int32_t {
|
||||
wasm::Val args[] = {wasm::Val::i32(xs)...};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(wasm::Val::i32(xs)...);
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error on result, expected return" << std::endl;
|
||||
exit(1);
|
||||
@@ -92,7 +94,8 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
auto instance = wasm::Instance::make(store, module.get(), nullptr);
|
||||
auto imports = wasm::vec<wasm::Extern*>::make();
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -9,31 +9,32 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n> ");
|
||||
printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n",
|
||||
args[0].of.i32, args[1].of.i64, args[2].of.i64, args[3].of.i32);
|
||||
args->data[0].of.i32, args->data[1].of.i64,
|
||||
args->data[2].of.i64, args->data[3].of.i32);
|
||||
printf("\n");
|
||||
|
||||
wasm_val_copy(&results[0], &args[3]);
|
||||
wasm_val_copy(&results[1], &args[1]);
|
||||
wasm_val_copy(&results[2], &args[2]);
|
||||
wasm_val_copy(&results[3], &args[0]);
|
||||
wasm_val_copy(&results->data[0], &args->data[3]);
|
||||
wasm_val_copy(&results->data[1], &args->data[1]);
|
||||
wasm_val_copy(&results->data[2], &args->data[2]);
|
||||
wasm_val_copy(&results->data[3], &args->data[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// A function closure.
|
||||
own wasm_trap_t* closure_callback(
|
||||
void* env, const wasm_val_t args[], wasm_val_t results[]
|
||||
void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
int i = *(int*)env;
|
||||
printf("Calling back closure...\n");
|
||||
printf("> %d\n", i);
|
||||
|
||||
results[0].kind = WASM_I32;
|
||||
results[0].of.i32 = (int32_t)i;
|
||||
results->data[0].kind = WASM_I32;
|
||||
results->data[0].of.i32 = (int32_t)i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("multi.wasm", "r");
|
||||
FILE* file = fopen("multi.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -89,9 +90,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)};
|
||||
wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) };
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -118,17 +120,15 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
wasm_val_t args[4];
|
||||
args[0].kind = WASM_I32;
|
||||
args[0].of.i32 = 1;
|
||||
args[1].kind = WASM_I64;
|
||||
args[1].of.i64 = 2;
|
||||
args[2].kind = WASM_I64;
|
||||
args[2].of.i64 = 3;
|
||||
args[3].kind = WASM_I32;
|
||||
args[3].of.i32 = 4;
|
||||
wasm_val_t results[4];
|
||||
if (wasm_func_call(run_func, args, results)) {
|
||||
wasm_val_t vals[4] = {
|
||||
WASM_I32_VAL(1), WASM_I32_VAL(2), WASM_I32_VAL(3), WASM_I32_VAL(4)
|
||||
};
|
||||
wasm_val_t res[4] = {
|
||||
WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL
|
||||
};
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vals);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(res);
|
||||
if (wasm_func_call(run_func, &args, &results)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -138,13 +138,12 @@ int main(int argc, const char* argv[]) {
|
||||
// Print result.
|
||||
printf("Printing result...\n");
|
||||
printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n",
|
||||
results[0].of.i32, results[1].of.i64,
|
||||
results[2].of.i64, results[3].of.i32);
|
||||
res[0].of.i32, res[1].of.i64, res[2].of.i64, res[3].of.i32);
|
||||
|
||||
assert(results[0].of.i32 == 4);
|
||||
assert(results[1].of.i64 == 3);
|
||||
assert(results[2].of.i64 == 2);
|
||||
assert(results[3].of.i32 == 1);
|
||||
assert(res[0].of.i32 == 4);
|
||||
assert(res[1].of.i64 == 3);
|
||||
assert(res[2].of.i64 == 2);
|
||||
assert(res[3].of.i32 == 1);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
std::cout << "> " << args[0].i32();
|
||||
@@ -66,7 +66,7 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {callback_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(callback_func.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
@@ -84,10 +84,10 @@ void run() {
|
||||
|
||||
// Call.
|
||||
std::cout << "Calling export..." << std::endl;
|
||||
wasm::Val args[] = {
|
||||
auto args = wasm::vec<wasm::Val>::make(
|
||||
wasm::Val::i32(1), wasm::Val::i64(2), wasm::Val::i64(3), wasm::Val::i32(4)
|
||||
};
|
||||
wasm::Val results[4];
|
||||
);
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(4);
|
||||
if (wasm::own<wasm::Trap> trap = run_func->call(args, results)) {
|
||||
std::cout << "> Error calling function! " << trap->message().get() << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -90,7 +90,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("reflect.wasm", "r");
|
||||
FILE* file = fopen("reflect.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -118,7 +118,9 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
own wasm_instance_t* instance = wasm_instance_new(store, module, NULL, NULL);
|
||||
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
|
||||
@@ -101,7 +101,8 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
auto instance = wasm::Instance::make(store, module.get(), nullptr);
|
||||
auto imports = wasm::vec<wasm::Extern*>::make();
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#define own
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* hello_callback(const wasm_val_t args[], wasm_val_t results[]) {
|
||||
own wasm_trap_t* hello_callback(
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n");
|
||||
printf("> Hello World!\n");
|
||||
return NULL;
|
||||
@@ -23,7 +25,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("serialize.wasm", "r");
|
||||
FILE* file = fopen("serialize.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -76,9 +78,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating deserialized module...\n");
|
||||
const wasm_extern_t* imports[] = { wasm_func_as_extern(hello_func) };
|
||||
wasm_extern_t* externs[] = { wasm_func_as_extern(hello_func) };
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, deserialized, imports, NULL);
|
||||
wasm_instance_new(store, deserialized, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -105,7 +108,8 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
if (wasm_func_call(run_func, NULL, NULL)) {
|
||||
wasm_val_vec_t empty = WASM_EMPTY_VEC;
|
||||
if (wasm_func_call(run_func, &empty, &empty)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto hello_callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
std::cout << "> Hello world!" << std::endl;
|
||||
@@ -67,7 +67,7 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating deserialized module..." << std::endl;
|
||||
wasm::Extern* imports[] = {hello_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(hello_func.get());
|
||||
auto instance = wasm::Instance::make(store, deserialized.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
@@ -85,7 +85,9 @@ void run() {
|
||||
|
||||
// Call.
|
||||
std::cout << "Calling export..." << std::endl;
|
||||
if (run_func->call()) {
|
||||
auto args = wasm::vec<wasm::Val>::make();
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
if (run_func->call(args, results)) {
|
||||
std::cout << "> Error calling function!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("start.wasm", "r");
|
||||
FILE* file = fopen("start.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -54,9 +54,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
|
||||
own wasm_trap_t* trap = NULL;
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, NULL, &trap);
|
||||
wasm_instance_new(store, module, &imports, &trap);
|
||||
if (instance || !trap) {
|
||||
printf("> Error instantiating module, expected trap!\n");
|
||||
return 1;
|
||||
|
||||
@@ -47,7 +47,8 @@ void run() {
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::own<wasm::Trap> trap;
|
||||
auto instance = wasm::Instance::make(store, module.get(), nullptr, &trap);
|
||||
auto imports = wasm::vec<wasm::Extern*>::make();
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports, &trap);
|
||||
if (instance || !trap) {
|
||||
std::cout << "> Error instantiating module, expected trap!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* neg_callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n");
|
||||
results[0].kind = WASM_I32;
|
||||
results[0].of.i32 = -args[0].of.i32;
|
||||
results->data[0].kind = WASM_I32;
|
||||
results->data[0].of.i32 = -args->data[0].of.i32;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -49,23 +49,22 @@ void check_table(wasm_table_t* table, int32_t i, bool expect_set) {
|
||||
}
|
||||
|
||||
void check_call(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, args, results) || results[0].of.i32 != expected) {
|
||||
wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
|
||||
wasm_val_t r[1] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(r);
|
||||
if (wasm_func_call(func, &args, &results) || r[0].of.i32 != expected) {
|
||||
printf("> Error on result\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_trap(wasm_func_t* func, int32_t arg1, int32_t arg2) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
own wasm_trap_t* trap = wasm_func_call(func, args, NULL);
|
||||
wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
|
||||
wasm_val_t r[1] = { WASM_INIT_VAL };
|
||||
wasm_val_vec_t args = WASM_ARRAY_VEC(vs);
|
||||
wasm_val_vec_t results = WASM_ARRAY_VEC(r);
|
||||
own wasm_trap_t* trap = wasm_func_call(func, &args, &results);
|
||||
if (! trap) {
|
||||
printf("> Error on result, expected trap\n");
|
||||
exit(1);
|
||||
@@ -82,7 +81,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("table.wasm", "r");
|
||||
FILE* file = fopen("table.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -110,7 +109,9 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
own wasm_instance_t* instance = wasm_instance_new(store, module, NULL, NULL);
|
||||
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto neg_callback(
|
||||
const wasm::Val args[], wasm::Val results[]
|
||||
const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
results[0] = wasm::Val(-args[0].i32());
|
||||
@@ -51,8 +51,8 @@ void check(bool success) {
|
||||
auto call(
|
||||
const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2
|
||||
) -> wasm::Val {
|
||||
wasm::Val args[2] = {std::move(arg1), std::move(arg2)};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(std::move(arg1), std::move(arg2));
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (func->call(args, results)) {
|
||||
std::cout << "> Error on result, expected return" << std::endl;
|
||||
exit(1);
|
||||
@@ -61,8 +61,8 @@ auto call(
|
||||
}
|
||||
|
||||
void check_trap(const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2) {
|
||||
wasm::Val args[2] = {std::move(arg1), std::move(arg2)};
|
||||
wasm::Val results[1];
|
||||
auto args = wasm::vec<wasm::Val>::make(std::move(arg1), std::move(arg2));
|
||||
auto results = wasm::vec<wasm::Val>::make_uninitialized(1);
|
||||
if (! func->call(args, results)) {
|
||||
std::cout << "> Error on result, expected trap" << std::endl;
|
||||
exit(1);
|
||||
@@ -100,7 +100,8 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
auto instance = wasm::Instance::make(store, module.get(), nullptr);
|
||||
auto imports = wasm::vec<wasm::Extern*>::make();
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -13,9 +13,9 @@ const int N_THREADS = 10;
|
||||
const int N_REPS = 3;
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* callback(const wasm_val_t args[], wasm_val_t results[]) {
|
||||
assert(args[0].kind == WASM_I32);
|
||||
printf("> Thread %d running\n", args[0].of.i32);
|
||||
own wasm_trap_t* callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) {
|
||||
assert(args->data[0].kind == WASM_I32);
|
||||
printf("> Thread %d running\n", args->data[0].of.i32);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -42,18 +42,19 @@ void* run(void* args_abs) {
|
||||
own wasm_func_t* func = wasm_func_new(store, func_type, callback);
|
||||
wasm_functype_delete(func_type);
|
||||
|
||||
wasm_val_t val = {.kind = WASM_I32, .of = {.i32 = (int32_t)args->id}};
|
||||
wasm_val_t val = WASM_I32_VAL((int32_t)args->id);
|
||||
own wasm_globaltype_t* global_type =
|
||||
wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST);
|
||||
own wasm_global_t* global = wasm_global_new(store, global_type, &val);
|
||||
wasm_globaltype_delete(global_type);
|
||||
|
||||
// Instantiate.
|
||||
const wasm_extern_t* imports[] = {
|
||||
wasm_extern_t* externs[] = {
|
||||
wasm_func_as_extern(func), wasm_global_as_extern(global),
|
||||
};
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return NULL;
|
||||
@@ -78,7 +79,8 @@ void* run(void* args_abs) {
|
||||
wasm_instance_delete(instance);
|
||||
|
||||
// Call.
|
||||
if (wasm_func_call(run_func, NULL, NULL)) {
|
||||
wasm_val_vec_t empty = WASM_EMPTY_VEC;
|
||||
if (wasm_func_call(run_func, &empty, &empty)) {
|
||||
printf("> Error calling function!\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ int main(int argc, const char *argv[]) {
|
||||
wasm_engine_t* engine = wasm_engine_new();
|
||||
|
||||
// Load binary.
|
||||
FILE* file = fopen("threads.wasm", "r");
|
||||
FILE* file = fopen("threads.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
|
||||
@@ -10,7 +10,7 @@ const int N_REPS = 3;
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto callback(
|
||||
void* env, const wasm::Val args[], wasm::Val results[]
|
||||
void* env, const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
assert(args[0].kind() == wasm::ValKind::I32);
|
||||
std::lock_guard<std::mutex> lock(*reinterpret_cast<std::mutex*>(env));
|
||||
@@ -53,7 +53,7 @@ void run(
|
||||
store, global_type.get(), wasm::Val::i32(i));
|
||||
|
||||
// Instantiate.
|
||||
wasm::Extern* imports[] = {func.get(), global.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(func.get(), global.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
@@ -71,7 +71,8 @@ void run(
|
||||
auto run_func = exports[0]->func();
|
||||
|
||||
// Call.
|
||||
run_func->call();
|
||||
auto empty = wasm::vec<wasm::Val>::make();
|
||||
run_func->call(empty, empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
own wasm_trap_t* fail_callback(
|
||||
void* env, const wasm_val_t args[], wasm_val_t results[]
|
||||
void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
printf("Calling back...\n");
|
||||
own wasm_name_t message;
|
||||
wasm_name_new_from_string(&message, "callback abort");
|
||||
wasm_name_new_from_string_nt(&message, "callback abort");
|
||||
own wasm_trap_t* trap = wasm_trap_new((wasm_store_t*)env, &message);
|
||||
wasm_name_delete(&message);
|
||||
return trap;
|
||||
@@ -38,7 +38,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("trap.wasm", "r");
|
||||
FILE* file = fopen("trap.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
@@ -75,9 +75,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = { wasm_func_as_extern(fail_func) };
|
||||
wasm_extern_t* externs[] = { wasm_func_as_extern(fail_func) };
|
||||
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
||||
own wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
wasm_instance_new(store, module, &imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
@@ -106,7 +107,9 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
|
||||
printf("Calling export %d...\n", i);
|
||||
own wasm_trap_t* trap = wasm_func_call(func, NULL, NULL);
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
own wasm_trap_t* trap = wasm_func_call(func, &args, &results);
|
||||
if (!trap) {
|
||||
printf("> Error calling function, expected trap!\n");
|
||||
return 1;
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
auto fail_callback(
|
||||
void* env, const wasm::Val args[], wasm::Val results[]
|
||||
void* env, const wasm::vec<wasm::Val>& args, wasm::vec<wasm::Val>& results
|
||||
) -> wasm::own<wasm::Trap> {
|
||||
std::cout << "Calling back..." << std::endl;
|
||||
auto store = reinterpret_cast<wasm::Store*>(env);
|
||||
auto message = wasm::Name::make(std::string("callback abort"));
|
||||
auto message = wasm::Name::make_nt(std::string("callback abort"));
|
||||
return wasm::Trap::make(store, message);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ void run() {
|
||||
|
||||
// Instantiate.
|
||||
std::cout << "Instantiating module..." << std::endl;
|
||||
wasm::Extern* imports[] = {fail_func.get()};
|
||||
auto imports = wasm::vec<wasm::Extern*>::make(fail_func.get());
|
||||
auto instance = wasm::Instance::make(store, module.get(), imports);
|
||||
if (!instance) {
|
||||
std::cout << "> Error instantiating module!" << std::endl;
|
||||
@@ -85,7 +85,9 @@ void run() {
|
||||
// Call.
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
std::cout << "Calling export " << i << "..." << std::endl;
|
||||
auto trap = exports[i]->func()->call();
|
||||
auto args = wasm::vec<wasm::Val>::make();
|
||||
auto results = wasm::vec<wasm::Val>::make();
|
||||
auto trap = exports[i]->func()->call(args, results);
|
||||
if (!trap) {
|
||||
std::cout << "> Error calling function, expected trap!" << std::endl;
|
||||
exit(1);
|
||||
|
||||
@@ -105,7 +105,13 @@ typedef wasm_byte_vec_t wasm_name_t;
|
||||
#define wasm_name_delete wasm_byte_vec_delete
|
||||
|
||||
static inline void wasm_name_new_from_string(
|
||||
own wasm_name_t* out, const char* s
|
||||
own wasm_name_t* out, own const char* s
|
||||
) {
|
||||
wasm_name_new(out, strlen(s), s);
|
||||
}
|
||||
|
||||
static inline void wasm_name_new_from_string_nt(
|
||||
own wasm_name_t* out, own const char* s
|
||||
) {
|
||||
wasm_name_new(out, strlen(s) + 1, s);
|
||||
}
|
||||
@@ -408,9 +414,9 @@ WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const
|
||||
WASM_DECLARE_REF(func)
|
||||
|
||||
typedef own wasm_trap_t* (*wasm_func_callback_t)(
|
||||
const wasm_val_t args[], wasm_val_t results[]);
|
||||
const wasm_val_vec_t* args, own wasm_val_vec_t* results);
|
||||
typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)(
|
||||
void* env, const wasm_val_t args[], wasm_val_t results[]);
|
||||
void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results);
|
||||
|
||||
WASM_API_EXTERN own wasm_func_t* wasm_func_new(
|
||||
wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t);
|
||||
@@ -423,7 +429,7 @@ WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*);
|
||||
WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*);
|
||||
|
||||
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(
|
||||
const wasm_func_t*, const wasm_val_t args[], wasm_val_t results[]);
|
||||
const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
|
||||
|
||||
|
||||
// Global Instances
|
||||
@@ -510,7 +516,7 @@ WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_exte
|
||||
WASM_DECLARE_REF(instance)
|
||||
|
||||
WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(
|
||||
wasm_store_t*, const wasm_module_t*, const wasm_extern_t* const imports[],
|
||||
wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports,
|
||||
own wasm_trap_t**
|
||||
);
|
||||
|
||||
@@ -520,6 +526,12 @@ WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_exte
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Convenience
|
||||
|
||||
// Vectors
|
||||
|
||||
#define WASM_EMPTY_VEC {0, NULL}
|
||||
#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array}
|
||||
|
||||
|
||||
// Value Type construction short-hands
|
||||
|
||||
static inline own wasm_valtype_t* wasm_valtype_new_i32() {
|
||||
@@ -692,6 +704,13 @@ static inline void* wasm_val_ptr(const wasm_val_t* val) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}}
|
||||
#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}}
|
||||
#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}}
|
||||
#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}}
|
||||
#define WASM_REF_VAL(r) {.kind = WASM_ANYREF, .of = {.ref = r}}
|
||||
#define WASM_INIT_VAL {.kind = WASM_ANYREF, .of = {.ref = NULL}}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -146,6 +146,12 @@ public:
|
||||
}
|
||||
|
||||
static auto make(std::string s) -> vec<char> {
|
||||
auto v = vec(s.length());
|
||||
if (v) std::strncpy(v.get(), s.data(), s.length());
|
||||
return v;
|
||||
}
|
||||
|
||||
static auto make_nt(std::string s) -> vec<char> {
|
||||
auto v = vec(s.length() + 1);
|
||||
if (v) std::strcpy(v.get(), s.data());
|
||||
return v;
|
||||
@@ -431,11 +437,11 @@ class Val {
|
||||
|
||||
public:
|
||||
Val() : kind_(ValKind::ANYREF) { impl_.ref = nullptr; }
|
||||
Val(int32_t i) : kind_(ValKind::I32) { impl_.i32 = i; }
|
||||
Val(int64_t i) : kind_(ValKind::I64) { impl_.i64 = i; }
|
||||
Val(float32_t z) : kind_(ValKind::F32) { impl_.f32 = z; }
|
||||
Val(float64_t z) : kind_(ValKind::F64) { impl_.f64 = z; }
|
||||
Val(own<Ref>&& r) : kind_(ValKind::ANYREF) { impl_.ref = r.release(); }
|
||||
explicit Val(int32_t i) : kind_(ValKind::I32) { impl_.i32 = i; }
|
||||
explicit Val(int64_t i) : kind_(ValKind::I64) { impl_.i64 = i; }
|
||||
explicit Val(float32_t z) : kind_(ValKind::F32) { impl_.f32 = z; }
|
||||
explicit Val(float64_t z) : kind_(ValKind::F64) { impl_.f64 = z; }
|
||||
explicit Val(own<Ref>&& r) : kind_(ValKind::ANYREF) { impl_.ref = r.release(); }
|
||||
|
||||
Val(Val&& that) : kind_(that.kind_), impl_(that.impl_) {
|
||||
if (is_ref()) that.impl_.ref = nullptr;
|
||||
@@ -648,8 +654,8 @@ public:
|
||||
Func() = delete;
|
||||
~Func();
|
||||
|
||||
using callback = auto (*)(const Val[], Val[]) -> own<Trap>;
|
||||
using callback_with_env = auto (*)(void*, const Val[], Val[]) -> own<Trap>;
|
||||
using callback = auto (*)(const vec<Val>&, vec<Val>&) -> own<Trap>;
|
||||
using callback_with_env = auto (*)(void*, const vec<Val>&, vec<Val>&) -> own<Trap>;
|
||||
|
||||
static auto make(Store*, const FuncType*, callback) -> own<Func>;
|
||||
static auto make(Store*, const FuncType*, callback_with_env,
|
||||
@@ -660,7 +666,7 @@ public:
|
||||
auto param_arity() const -> size_t;
|
||||
auto result_arity() const -> size_t;
|
||||
|
||||
auto call(const Val[] = nullptr, Val[] = nullptr) const -> own<Trap>;
|
||||
auto call(const vec<Val>&, vec<Val>&) const -> own<Trap>;
|
||||
};
|
||||
|
||||
|
||||
@@ -731,7 +737,7 @@ public:
|
||||
~Instance();
|
||||
|
||||
static auto make(
|
||||
Store*, const Module*, const Extern* const[], own<Trap>* = nullptr
|
||||
Store*, const Module*, const vec<Extern*>&, own<Trap>* = nullptr
|
||||
) -> own<Instance>;
|
||||
auto copy() const -> own<Instance>;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ struct borrowed_vec {
|
||||
|
||||
// Vectors
|
||||
|
||||
#define WASM_DEFINE_VEC_BASE(name, Name, vec, ptr_or_none) \
|
||||
#define WASM_DEFINE_VEC_BASE(name, Name, vec, plainvec, ptr_or_none) \
|
||||
static_assert( \
|
||||
sizeof(wasm_##name##_vec_t) == sizeof(vec<Name>), \
|
||||
"C/C++ incompatibility" \
|
||||
@@ -86,6 +86,14 @@ struct borrowed_vec {
|
||||
-> wasm_##name##_t ptr_or_none const* { \
|
||||
return reinterpret_cast<wasm_##name##_t ptr_or_none const*>(v); \
|
||||
} \
|
||||
extern "C++" inline auto reveal_##name##_vec(wasm_##name##_vec_t* v) \
|
||||
-> plainvec<Name*>* { \
|
||||
return reinterpret_cast<plainvec<Name*>*>(v); \
|
||||
} \
|
||||
extern "C++" inline auto reveal_##name##_vec(const wasm_##name##_vec_t* v) \
|
||||
-> const plainvec<Name*>* { \
|
||||
return reinterpret_cast<const plainvec<Name*>*>(v); \
|
||||
} \
|
||||
extern "C++" inline auto reveal_##name##_vec(wasm_##name##_t ptr_or_none* v) \
|
||||
-> vec<Name>::elem_type* { \
|
||||
return reinterpret_cast<vec<Name>::elem_type*>(v); \
|
||||
@@ -134,7 +142,7 @@ struct borrowed_vec {
|
||||
|
||||
// Vectors with no ownership management of elements
|
||||
#define WASM_DEFINE_VEC_PLAIN(name, Name) \
|
||||
WASM_DEFINE_VEC_BASE(name, Name, vec, ) \
|
||||
WASM_DEFINE_VEC_BASE(name, Name, vec, vec, ) \
|
||||
\
|
||||
void wasm_##name##_vec_new( \
|
||||
wasm_##name##_vec_t* out, \
|
||||
@@ -156,7 +164,7 @@ struct borrowed_vec {
|
||||
|
||||
// Vectors that own their elements
|
||||
#define WASM_DEFINE_VEC_OWN(name, Name) \
|
||||
WASM_DEFINE_VEC_BASE(name, Name, ownvec, *) \
|
||||
WASM_DEFINE_VEC_BASE(name, Name, ownvec, vec, *) \
|
||||
\
|
||||
void wasm_##name##_vec_new( \
|
||||
wasm_##name##_vec_t* out, \
|
||||
@@ -620,7 +628,7 @@ inline auto borrow_val(const wasm_val_t* v) -> borrowed_val {
|
||||
} // extern "C++"
|
||||
|
||||
|
||||
WASM_DEFINE_VEC_BASE(val, Val, vec, )
|
||||
WASM_DEFINE_VEC_BASE(val, Val, vec, vec, )
|
||||
|
||||
void wasm_val_vec_new(
|
||||
wasm_val_vec_t* out, size_t size, wasm_val_t const data[]
|
||||
@@ -771,7 +779,9 @@ WASM_DEFINE_REF(func, Func)
|
||||
|
||||
extern "C++" {
|
||||
|
||||
auto wasm_callback(void* env, const Val args[], Val results[]) -> own<Trap> {
|
||||
auto wasm_callback(
|
||||
void* env, const vec<Val>& args, vec<Val>& results
|
||||
) -> own<Trap> {
|
||||
auto f = reinterpret_cast<wasm_func_callback_t>(env);
|
||||
return adopt_trap(f(hide_val_vec(args), hide_val_vec(results)));
|
||||
}
|
||||
@@ -783,7 +793,7 @@ struct wasm_callback_env_t {
|
||||
};
|
||||
|
||||
auto wasm_callback_with_env(
|
||||
void* env, const Val args[], Val results[]
|
||||
void* env, const vec<Val>& args, vec<Val>& results
|
||||
) -> own<Trap> {
|
||||
auto t = static_cast<wasm_callback_env_t*>(env);
|
||||
return adopt_trap(t->callback(t->env, hide_val_vec(args), hide_val_vec(results)));
|
||||
@@ -826,9 +836,11 @@ size_t wasm_func_result_arity(const wasm_func_t* func) {
|
||||
}
|
||||
|
||||
wasm_trap_t* wasm_func_call(
|
||||
const wasm_func_t* func, const wasm_val_t args[], wasm_val_t results[]
|
||||
const wasm_func_t* func, const wasm_val_vec_t* args, wasm_val_vec_t* results
|
||||
) {
|
||||
return release_trap(func->call(reveal_val_vec(args), reveal_val_vec(results)));
|
||||
auto args_ = borrow_val_vec(args);
|
||||
auto results_ = borrow_val_vec(results);
|
||||
return release_trap(func->call(args_.it, results_.it));
|
||||
}
|
||||
|
||||
|
||||
@@ -995,12 +1007,13 @@ WASM_DEFINE_REF(instance, Instance)
|
||||
wasm_instance_t* wasm_instance_new(
|
||||
wasm_store_t* store,
|
||||
const wasm_module_t* module,
|
||||
const wasm_extern_t* const imports[],
|
||||
const wasm_extern_vec_t* imports,
|
||||
wasm_trap_t** trap
|
||||
) {
|
||||
own<Trap> error;
|
||||
auto instance = release_instance(Instance::make(store, module,
|
||||
reinterpret_cast<const Extern* const*>(imports), &error));
|
||||
auto imports_ = reveal_extern_vec(imports);
|
||||
auto instance =
|
||||
release_instance(Instance::make(store, module, *imports_, &error));
|
||||
if (trap) *trap = hide_trap(error.release());
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -229,6 +229,7 @@ DEFINE_VEC(Global, ownvec, GLOBAL)
|
||||
DEFINE_VEC(Table, ownvec, TABLE)
|
||||
DEFINE_VEC(Memory, ownvec, MEMORY)
|
||||
DEFINE_VEC(Extern, ownvec, EXTERN)
|
||||
DEFINE_VEC(Extern*, vec, EXTERN)
|
||||
DEFINE_VEC(Val, vec, VAL)
|
||||
|
||||
#endif // #ifdef WASM_API_DEBUG
|
||||
@@ -1257,7 +1258,7 @@ auto Trap::message() const -> Message {
|
||||
|
||||
auto message = v8::Exception::CreateMessage(isolate, impl(this)->v8_object());
|
||||
v8::String::Utf8Value string(isolate, message->Get());
|
||||
return vec<byte_t>::make(std::string(*string));
|
||||
return vec<byte_t>::make_nt(std::string(*string));
|
||||
}
|
||||
|
||||
auto Trap::origin() const -> own<Frame> {
|
||||
@@ -1687,7 +1688,7 @@ auto Func::result_arity() const -> size_t {
|
||||
return wasm_v8::func_type_result_arity(impl(this)->v8_object());
|
||||
}
|
||||
|
||||
auto Func::call(const Val args[], Val results[]) const -> own<Trap> {
|
||||
auto Func::call(const vec<Val>& args, vec<Val>& results) const -> own<Trap> {
|
||||
auto func = impl(this);
|
||||
auto store = func->store();
|
||||
auto isolate = store->isolate();
|
||||
@@ -1754,17 +1755,17 @@ void FuncData::v8_callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
assert(param_types.size() == info.Length());
|
||||
|
||||
// TODO: cache params and result arrays per thread.
|
||||
auto args = std::unique_ptr<Val[]>(new Val[param_types.size()]);
|
||||
auto results = std::unique_ptr<Val[]>(new Val[result_types.size()]);
|
||||
auto args = vec<Val>::make_uninitialized(param_types.size());
|
||||
auto results = vec<Val>::make_uninitialized(result_types.size());
|
||||
for (size_t i = 0; i < param_types.size(); ++i) {
|
||||
args[i] = v8_to_val(store, info[i], param_types[i].get());
|
||||
}
|
||||
|
||||
own<Trap> trap;
|
||||
if (self->kind == CALLBACK_WITH_ENV) {
|
||||
trap = self->callback_with_env(self->env, args.get(), results.get());
|
||||
trap = self->callback_with_env(self->env, args, results);
|
||||
} else {
|
||||
trap = self->callback(args.get(), results.get());
|
||||
trap = self->callback(args, results);
|
||||
}
|
||||
|
||||
if (trap) {
|
||||
@@ -2019,7 +2020,7 @@ auto Instance::copy() const -> own<Instance> {
|
||||
}
|
||||
|
||||
auto Instance::make(
|
||||
Store* store_abs, const Module* module_abs, const Extern* const imports[],
|
||||
Store* store_abs, const Module* module_abs, const vec<Extern*>& imports,
|
||||
own<Trap>* trap
|
||||
) -> own<Instance> {
|
||||
auto store = impl(store_abs);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// The Wasmer C/C++ header file compatible with the `wasm-c-api` standard API.
|
||||
// This file is generated by lib/c-api/build.rs.
|
||||
|
||||
#if !defined(WASMER_WASM_H_MACROS)
|
||||
|
||||
@@ -144,7 +145,7 @@ void wasi_env_set_memory(wasi_env_t *env, const wasm_memory_t *memory);
|
||||
bool wasi_get_imports(const wasm_store_t *store,
|
||||
const wasm_module_t *module,
|
||||
const wasi_env_t *wasi_env,
|
||||
wasm_extern_t **imports);
|
||||
wasm_extern_vec_t *imports);
|
||||
#endif
|
||||
|
||||
#if defined(WASMER_WASI_ENABLED)
|
||||
@@ -159,8 +160,6 @@ void wasm_config_set_compiler(wasm_config_t *config, wasmer_compiler_t compiler)
|
||||
|
||||
void wasm_config_set_engine(wasm_config_t *config, wasmer_engine_t engine);
|
||||
|
||||
void *wasm_instance_get_vmctx_ptr(const wasm_instance_t *instance);
|
||||
|
||||
void wasm_module_name(const wasm_module_t *module, wasm_name_t *out);
|
||||
|
||||
bool wasm_module_set_name(wasm_module_t *module, const wasm_name_t *name);
|
||||
|
||||
@@ -9,6 +9,8 @@ extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define own
|
||||
|
||||
// TODO: make this define templated so that the Rust code can toggle it on/off
|
||||
#define WASI
|
||||
|
||||
@@ -134,20 +136,24 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(module, &import_types);
|
||||
int num_imports = import_types.size;
|
||||
wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*));
|
||||
|
||||
wasm_extern_vec_t imports;
|
||||
wasm_extern_vec_new_uninitialized(&imports, import_types.size);
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
|
||||
#ifdef WASI
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports);
|
||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, &imports);
|
||||
|
||||
if (!get_imports_result) {
|
||||
fprintf(stderr, "Error getting WASI imports!\n");
|
||||
print_wasmer_error();
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL);
|
||||
wasm_instance_t* instance = wasm_instance_new(store, module, &imports, NULL);
|
||||
|
||||
if (!instance) {
|
||||
fprintf(stderr, "Failed to create instance\n");
|
||||
print_wasmer_error();
|
||||
@@ -158,11 +164,24 @@ int main(int argc, char* argv[]) {
|
||||
wasi_env_set_instance(wasi_env, instance);
|
||||
#endif
|
||||
|
||||
void* vmctx = wasm_instance_get_vmctx_ptr(instance);
|
||||
wasm_val_t* inout[2] = { NULL, NULL };
|
||||
#ifdef WASI
|
||||
own wasm_func_t* start_function = wasi_get_start_function(instance);
|
||||
if (!start_function) {
|
||||
fprintf(stderr, "`_start` function not found\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We're able to call our compiled function directly through a trampoline.
|
||||
wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout);
|
||||
wasm_val_vec_t args = WASM_EMPTY_VEC;
|
||||
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
||||
own wasm_trap_t* trap = wasm_func_call(start_function, &args, &results);
|
||||
if (trap) {
|
||||
fprintf(stderr, "Trap is not NULL: TODO:\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: handle non-WASI start (maybe with invoke?)
|
||||
|
||||
wasm_instance_delete(instance);
|
||||
wasm_module_delete(module);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
unstable_features
|
||||
)]
|
||||
#![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")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
|
||||
@@ -15,7 +15,7 @@ edition = "2018"
|
||||
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha4", features = ["translator"], default-features = false }
|
||||
wasmer-vm = { path = "../vm", version = "1.0.0-alpha4" }
|
||||
wasmer-types = { path = "../wasmer-types", version = "1.0.0-alpha4", default-features = false, features = ["std"] }
|
||||
cranelift-codegen = { version = "0.65", default-features = false }
|
||||
cranelift-codegen = { version = "0.65", default-features = false, features = ["x86", "arm64"] }
|
||||
cranelift-frontend = { version = "0.65", default-features = false }
|
||||
tracing = "0.1"
|
||||
hashbrown = { version = "0.8", optional = true }
|
||||
|
||||
555
lib/compiler-llvm/src/abi/aarch64_systemv.rs
Normal file
555
lib/compiler-llvm/src/abi/aarch64_systemv.rs
Normal file
@@ -0,0 +1,555 @@
|
||||
use crate::abi::Abi;
|
||||
use crate::translator::intrinsics::{type_to_llvm, Intrinsics};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
types::{BasicType, FunctionType, StructType},
|
||||
values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_types::{FunctionType as FuncSig, Type};
|
||||
|
||||
/// Implementation of the [`Abi`] trait for the Aarch64 ABI on Linux.
|
||||
pub struct Aarch64SystemV {}
|
||||
|
||||
impl Abi for Aarch64SystemV {
|
||||
// Given a function definition, retrieve the parameter that is the vmctx pointer.
|
||||
fn get_vmctx_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
|
||||
func_value
|
||||
.get_nth_param(
|
||||
if func_value
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
// Given a wasm function type, produce an llvm function declaration.
|
||||
fn func_type_to_llvm<'ctx>(
|
||||
&self,
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
sig: &FuncSig,
|
||||
) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError> {
|
||||
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
|
||||
|
||||
let param_types =
|
||||
std::iter::once(Ok(intrinsics.ctx_ptr_ty.as_basic_type_enum())).chain(user_param_types);
|
||||
|
||||
Ok(match sig.results() {
|
||||
[] => (
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[_] => {
|
||||
let single_value = sig.results()[0];
|
||||
(
|
||||
type_to_llvm(intrinsics, single_value)?
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[Type::F32, Type::F32] => {
|
||||
let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
|
||||
(
|
||||
context
|
||||
.struct_type(&[f32_ty, f32_ty], false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[Type::F64, Type::F64] => {
|
||||
let f64_ty = intrinsics.f64_ty.as_basic_type_enum();
|
||||
(
|
||||
context
|
||||
.struct_type(&[f64_ty, f64_ty], false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[Type::F32, Type::F32, Type::F32] => {
|
||||
let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
|
||||
(
|
||||
context
|
||||
.struct_type(&[f32_ty, f32_ty, f32_ty], false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[Type::F32, Type::F32, Type::F32, Type::F32] => {
|
||||
let f32_ty = intrinsics.f32_ty.as_basic_type_enum();
|
||||
(
|
||||
context
|
||||
.struct_type(&[f32_ty, f32_ty, f32_ty, f32_ty], false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let sig_returns_bitwidths = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
match sig_returns_bitwidths.as_slice() {
|
||||
[32, 32] => (
|
||||
intrinsics
|
||||
.i64_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 64]
|
||||
| [64, 32]
|
||||
| [64, 64]
|
||||
| [32, 32, 32]
|
||||
| [64, 32, 32]
|
||||
| [32, 32, 64]
|
||||
| [32, 32, 32, 32] => (
|
||||
intrinsics
|
||||
.i64_ty
|
||||
.array_type(2)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
_ => {
|
||||
let basic_types: Vec<_> = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(intrinsics, ty))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let sret = context
|
||||
.struct_type(&basic_types, false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let param_types =
|
||||
std::iter::once(Ok(sret.as_basic_type_enum())).chain(param_types);
|
||||
|
||||
(
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![(
|
||||
context.create_enum_attribute(
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
0,
|
||||
),
|
||||
AttributeLoc::Param(0),
|
||||
)],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Marshall wasm stack values into function parameters.
|
||||
fn args_to_call<'ctx>(
|
||||
&self,
|
||||
alloca_builder: &Builder<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
ctx_ptr: PointerValue<'ctx>,
|
||||
llvm_fn_ty: &FunctionType<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
// If it's an sret, allocate the return space.
|
||||
let sret = if llvm_fn_ty.get_return_type().is_none() && func_sig.results().len() > 1 {
|
||||
Some(
|
||||
alloca_builder.build_alloca(
|
||||
llvm_fn_ty.get_param_types()[0]
|
||||
.into_pointer_type()
|
||||
.get_element_type()
|
||||
.into_struct_type(),
|
||||
"sret",
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let values = std::iter::once(ctx_ptr.as_basic_value_enum()).chain(values.iter().copied());
|
||||
|
||||
if let Some(sret) = sret {
|
||||
std::iter::once(sret.as_basic_value_enum())
|
||||
.chain(values)
|
||||
.collect()
|
||||
} else {
|
||||
values.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Given a CallSite, extract the returned values and return them in a Vec.
|
||||
fn rets_from_call<'ctx>(
|
||||
&self,
|
||||
builder: &Builder<'ctx>,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
call_site: CallSiteValue<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
let split_i64 = |value: IntValue<'ctx>| -> (IntValue<'ctx>, IntValue<'ctx>) {
|
||||
assert!(value.get_type() == intrinsics.i64_ty);
|
||||
let low = builder.build_int_truncate(value, intrinsics.i32_ty, "");
|
||||
let lshr =
|
||||
builder.build_right_shift(value, intrinsics.i64_ty.const_int(32, false), false, "");
|
||||
let high = builder.build_int_truncate(lshr, intrinsics.i32_ty, "");
|
||||
(low, high)
|
||||
};
|
||||
|
||||
let casted = |value: BasicValueEnum<'ctx>, ty: Type| -> BasicValueEnum<'ctx> {
|
||||
match ty {
|
||||
Type::I32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i32_ty, "")
|
||||
}
|
||||
Type::F32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f32_ty, "")
|
||||
}
|
||||
Type::I64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i64_ty, "")
|
||||
}
|
||||
Type::F64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f64_ty, "")
|
||||
}
|
||||
Type::V128 => {
|
||||
assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum());
|
||||
value
|
||||
}
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(basic_value) = call_site.try_as_basic_value().left() {
|
||||
if func_sig.results().len() > 1 {
|
||||
if basic_value.get_type() == intrinsics.i64_ty.as_basic_type_enum() {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
let value = basic_value.into_int_value();
|
||||
let (low, high) = split_i64(value);
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
return vec![low, high];
|
||||
}
|
||||
if basic_value.is_struct_value() {
|
||||
let struct_value = basic_value.into_struct_value();
|
||||
return (0..struct_value.get_type().count_fields())
|
||||
.map(|i| builder.build_extract_value(struct_value, i, "").unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
let array_value = basic_value.into_array_value();
|
||||
let low = builder
|
||||
.build_extract_value(array_value, 0, "")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
let high = builder
|
||||
.build_extract_value(array_value, 1, "")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
match func_sig_returns_bitwidths.as_slice() {
|
||||
[32, 64] => {
|
||||
let (low, _) = split_i64(low);
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
vec![low, high]
|
||||
}
|
||||
[64, 32] => {
|
||||
let (high, _) = split_i64(high);
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
vec![low, high]
|
||||
}
|
||||
[64, 64] => {
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
vec![low, high]
|
||||
}
|
||||
[32, 32, 32] => {
|
||||
let (v1, v2) = split_i64(low);
|
||||
let (v3, _) = split_i64(high);
|
||||
let v1 = casted(v1.into(), func_sig.results()[0]);
|
||||
let v2 = casted(v2.into(), func_sig.results()[1]);
|
||||
let v3 = casted(v3.into(), func_sig.results()[2]);
|
||||
vec![v1, v2, v3]
|
||||
}
|
||||
[32, 32, 64] => {
|
||||
let (v1, v2) = split_i64(low);
|
||||
let v1 = casted(v1.into(), func_sig.results()[0]);
|
||||
let v2 = casted(v2.into(), func_sig.results()[1]);
|
||||
let v3 = casted(high.into(), func_sig.results()[2]);
|
||||
vec![v1, v2, v3]
|
||||
}
|
||||
[64, 32, 32] => {
|
||||
let v1 = casted(low.into(), func_sig.results()[0]);
|
||||
let (v2, v3) = split_i64(high);
|
||||
let v2 = casted(v2.into(), func_sig.results()[1]);
|
||||
let v3 = casted(v3.into(), func_sig.results()[2]);
|
||||
vec![v1, v2, v3]
|
||||
}
|
||||
[32, 32, 32, 32] => {
|
||||
let (v1, v2) = split_i64(low);
|
||||
let (v3, v4) = split_i64(high);
|
||||
let v1 = casted(v1.into(), func_sig.results()[0]);
|
||||
let v2 = casted(v2.into(), func_sig.results()[1]);
|
||||
let v3 = casted(v3.into(), func_sig.results()[2]);
|
||||
let v4 = casted(v4.into(), func_sig.results()[3]);
|
||||
vec![v1, v2, v3, v4]
|
||||
}
|
||||
_ => unreachable!("expected an sret for this type"),
|
||||
}
|
||||
} else {
|
||||
assert!(func_sig.results().len() == 1);
|
||||
vec![basic_value]
|
||||
}
|
||||
} else {
|
||||
assert!(call_site.count_arguments() > 0); // Either sret or vmctx.
|
||||
if call_site
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let sret = call_site
|
||||
.try_as_basic_value()
|
||||
.right()
|
||||
.unwrap()
|
||||
.get_operand(0)
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
let struct_value = builder.build_load(sret, "").into_struct_value();
|
||||
let mut rets: Vec<_> = Vec::new();
|
||||
for i in 0..struct_value.get_type().count_fields() {
|
||||
let value = builder.build_extract_value(struct_value, i, "").unwrap();
|
||||
rets.push(value);
|
||||
}
|
||||
assert!(func_sig.results().len() == rets.len());
|
||||
rets
|
||||
} else {
|
||||
assert!(func_sig.results().is_empty());
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_sret(&self, func_sig: &FuncSig) -> Result<bool, CompileError> {
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => Ok(32),
|
||||
Type::I64 | Type::F64 => Ok(64),
|
||||
Type::V128 => Ok(128),
|
||||
ty => Err(CompileError::Codegen(format!(
|
||||
"is_sret: unimplemented wasmer_types type {:?}",
|
||||
ty
|
||||
))),
|
||||
})
|
||||
.collect::<Result<Vec<i32>, _>>()?;
|
||||
|
||||
Ok(!matches!(func_sig_returns_bitwidths.as_slice(),
|
||||
[]
|
||||
| [_]
|
||||
| [32, 32]
|
||||
| [32, 64]
|
||||
| [64, 32]
|
||||
| [64, 64]
|
||||
| [32, 32, 32]
|
||||
| [32, 32, 64]
|
||||
| [64, 32, 32]
|
||||
| [32, 32, 32, 32]))
|
||||
}
|
||||
|
||||
fn pack_values_for_register_return<'ctx>(
|
||||
&self,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
builder: &Builder<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
func_type: &FunctionType<'ctx>,
|
||||
) -> Result<BasicValueEnum<'ctx>, CompileError> {
|
||||
let is_32 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i32_ty)
|
||||
|| (value.is_float_value()
|
||||
&& value.into_float_value().get_type() == intrinsics.f32_ty)
|
||||
};
|
||||
let is_64 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i64_ty)
|
||||
|| (value.is_float_value()
|
||||
&& value.into_float_value().get_type() == intrinsics.f64_ty)
|
||||
};
|
||||
|
||||
let pack_i32s = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
|
||||
assert!(low.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
assert!(high.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
let (low, high) = (low.into_int_value(), high.into_int_value());
|
||||
let low = builder.build_int_z_extend(low, intrinsics.i64_ty, "");
|
||||
let high = builder.build_int_z_extend(high, intrinsics.i64_ty, "");
|
||||
let high = builder.build_left_shift(high, intrinsics.i64_ty.const_int(32, false), "");
|
||||
builder.build_or(low, high, "").as_basic_value_enum()
|
||||
};
|
||||
|
||||
let to_i64 = |v: BasicValueEnum<'ctx>| {
|
||||
if v.is_float_value() {
|
||||
let v = v.into_float_value();
|
||||
if v.get_type() == intrinsics.f32_ty {
|
||||
let v = builder
|
||||
.build_bitcast(v, intrinsics.i32_ty, "")
|
||||
.into_int_value();
|
||||
let v = builder.build_int_z_extend(v, intrinsics.i64_ty, "");
|
||||
v.as_basic_value_enum()
|
||||
} else {
|
||||
debug_assert!(v.get_type() == intrinsics.f64_ty);
|
||||
let v = builder.build_bitcast(v, intrinsics.i64_ty, "");
|
||||
v.as_basic_value_enum()
|
||||
}
|
||||
} else {
|
||||
let v = v.into_int_value();
|
||||
if v.get_type() == intrinsics.i32_ty {
|
||||
let v = builder.build_int_z_extend(v, intrinsics.i64_ty, "");
|
||||
v.as_basic_value_enum()
|
||||
} else {
|
||||
debug_assert!(v.get_type() == intrinsics.i64_ty);
|
||||
v.as_basic_value_enum()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let build_struct = |ty: StructType<'ctx>, values: &[BasicValueEnum<'ctx>]| {
|
||||
let mut struct_value = ty.get_undef();
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
struct_value = builder
|
||||
.build_insert_value(struct_value, *v, i as u32, "")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
}
|
||||
struct_value.as_basic_value_enum()
|
||||
};
|
||||
|
||||
let build_2xi64 = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
|
||||
let low = to_i64(low);
|
||||
let high = to_i64(high);
|
||||
let value = intrinsics.i64_ty.array_type(2).get_undef();
|
||||
let value = builder.build_insert_value(value, low, 0, "").unwrap();
|
||||
let value = builder.build_insert_value(value, high, 1, "").unwrap();
|
||||
value.as_basic_value_enum()
|
||||
};
|
||||
|
||||
Ok(match *values {
|
||||
[one_value] => one_value,
|
||||
[v1, v2]
|
||||
if v1.is_float_value()
|
||||
&& v2.is_float_value()
|
||||
&& v1.into_float_value().get_type() == v2.into_float_value().get_type() =>
|
||||
{
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, v2],
|
||||
)
|
||||
}
|
||||
[v1, v2] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
pack_i32s(v1, v2)
|
||||
}
|
||||
[v1, v2] => build_2xi64(v1, v2),
|
||||
[v1, v2, v3]
|
||||
if is_32(v1)
|
||||
&& is_32(v2)
|
||||
&& is_32(v3)
|
||||
&& v1.is_float_value()
|
||||
&& v2.is_float_value()
|
||||
&& v3.is_float_value() =>
|
||||
{
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, v2, v3],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
let v1v2_pack = pack_i32s(v1, v2);
|
||||
build_2xi64(v1v2_pack, v3)
|
||||
}
|
||||
[v1, v2, v3] if is_64(v1) && is_32(v2) && is_32(v3) => {
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
let v2v3_pack = pack_i32s(v2, v3);
|
||||
build_2xi64(v1, v2v3_pack)
|
||||
}
|
||||
[v1, v2, v3, v4]
|
||||
if is_32(v1)
|
||||
&& is_32(v2)
|
||||
&& is_32(v3)
|
||||
&& is_32(v4)
|
||||
&& v1.is_float_value()
|
||||
&& v2.is_float_value()
|
||||
&& v3.is_float_value()
|
||||
&& v4.is_float_value() =>
|
||||
{
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, v2, v3, v4],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3, v4] if is_32(v1) && is_32(v2) && is_32(v3) && is_32(v4) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
let v1v2_pack = pack_i32s(v1, v2);
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
let v4 = builder.build_bitcast(v4, intrinsics.i32_ty, "");
|
||||
let v3v4_pack = pack_i32s(v3, v4);
|
||||
build_2xi64(v1v2_pack, v3v4_pack)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("called to perform register return on struct return or void function")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
85
lib/compiler-llvm/src/abi/mod.rs
Normal file
85
lib/compiler-llvm/src/abi/mod.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
// LLVM implements part of the ABI lowering internally, but also requires that
|
||||
// the user pack and unpack values themselves sometimes. This can help the LLVM
|
||||
// optimizer by exposing operations to the optimizer, but it requires that the
|
||||
// frontend know exactly what IR to produce in order to get the right ABI.
|
||||
|
||||
#![deny(dead_code, missing_docs)]
|
||||
|
||||
use crate::translator::intrinsics::Intrinsics;
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
targets::TargetMachine,
|
||||
types::FunctionType,
|
||||
values::{BasicValueEnum, CallSiteValue, FunctionValue, PointerValue},
|
||||
};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_types::FunctionType as FuncSig;
|
||||
|
||||
mod aarch64_systemv;
|
||||
mod x86_64_systemv;
|
||||
|
||||
use aarch64_systemv::Aarch64SystemV;
|
||||
use x86_64_systemv::X86_64SystemV;
|
||||
|
||||
pub fn get_abi(target_machine: &TargetMachine) -> Box<dyn Abi> {
|
||||
if target_machine
|
||||
.get_triple()
|
||||
.as_str()
|
||||
.to_string_lossy()
|
||||
.starts_with("aarch64")
|
||||
{
|
||||
Box::new(Aarch64SystemV {})
|
||||
} else {
|
||||
Box::new(X86_64SystemV {})
|
||||
}
|
||||
}
|
||||
|
||||
/// We need to produce different LLVM IR for different platforms. (Contrary to
|
||||
/// popular knowledge LLVM IR is not intended to be portable in that way.) This
|
||||
/// trait deals with differences between function signatures on different
|
||||
/// targets.
|
||||
pub trait Abi {
|
||||
/// Given a function definition, retrieve the parameter that is the vmctx pointer.
|
||||
fn get_vmctx_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx>;
|
||||
|
||||
/// Given a wasm function type, produce an llvm function declaration.
|
||||
fn func_type_to_llvm<'ctx>(
|
||||
&self,
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
sig: &FuncSig,
|
||||
) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError>;
|
||||
|
||||
/// Marshall wasm stack values into function parameters.
|
||||
fn args_to_call<'ctx>(
|
||||
&self,
|
||||
alloca_builder: &Builder<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
ctx_ptr: PointerValue<'ctx>,
|
||||
llvm_fn_ty: &FunctionType<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
) -> Vec<BasicValueEnum<'ctx>>;
|
||||
|
||||
/// Given a CallSite, extract the returned values and return them in a Vec.
|
||||
fn rets_from_call<'ctx>(
|
||||
&self,
|
||||
builder: &Builder<'ctx>,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
call_site: CallSiteValue<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
) -> Vec<BasicValueEnum<'ctx>>;
|
||||
|
||||
/// Whether the llvm equivalent of this wasm function has an `sret` attribute.
|
||||
fn is_sret(&self, func_sig: &FuncSig) -> Result<bool, CompileError>;
|
||||
|
||||
/// Pack LLVM IR values representing individual wasm values into the return type for the function.
|
||||
fn pack_values_for_register_return<'ctx>(
|
||||
&self,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
builder: &Builder<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
func_type: &FunctionType<'ctx>,
|
||||
) -> Result<BasicValueEnum<'ctx>, CompileError>;
|
||||
}
|
||||
583
lib/compiler-llvm/src/abi/x86_64_systemv.rs
Normal file
583
lib/compiler-llvm/src/abi/x86_64_systemv.rs
Normal file
@@ -0,0 +1,583 @@
|
||||
use crate::abi::Abi;
|
||||
use crate::translator::intrinsics::{type_to_llvm, Intrinsics};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
types::{BasicType, FunctionType, StructType},
|
||||
values::{
|
||||
BasicValue, BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue,
|
||||
PointerValue, VectorValue,
|
||||
},
|
||||
AddressSpace,
|
||||
};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_types::{FunctionType as FuncSig, Type};
|
||||
|
||||
/// Implementation of the [`Abi`] trait for the AMD64 SystemV ABI.
|
||||
pub struct X86_64SystemV {}
|
||||
|
||||
impl Abi for X86_64SystemV {
|
||||
// Given a function definition, retrieve the parameter that is the vmctx pointer.
|
||||
fn get_vmctx_ptr_param<'ctx>(&self, func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
|
||||
func_value
|
||||
.get_nth_param(
|
||||
if func_value
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
// Given a wasm function type, produce an llvm function declaration.
|
||||
fn func_type_to_llvm<'ctx>(
|
||||
&self,
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
sig: &FuncSig,
|
||||
) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError> {
|
||||
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
|
||||
|
||||
let param_types =
|
||||
std::iter::once(Ok(intrinsics.ctx_ptr_ty.as_basic_type_enum())).chain(user_param_types);
|
||||
|
||||
let sig_returns_bitwidths = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
Ok(match sig_returns_bitwidths.as_slice() {
|
||||
[] => (
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[_] => {
|
||||
let single_value = sig.results()[0];
|
||||
(
|
||||
type_to_llvm(intrinsics, single_value)?
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[32, 64] | [64, 32] | [64, 64] => {
|
||||
let basic_types: Vec<_> = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(intrinsics, ty))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
(
|
||||
context
|
||||
.struct_type(&basic_types, false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[32, 32] if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 => (
|
||||
intrinsics
|
||||
.f32_ty
|
||||
.vec_type(2)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32] => (
|
||||
intrinsics
|
||||
.i64_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, _] if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum(),
|
||||
type_to_llvm(intrinsics, sig.results()[2])?,
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, _] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
intrinsics.i64_ty.as_basic_type_enum(),
|
||||
type_to_llvm(intrinsics, sig.results()[2])?,
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[64, 32, 32] if sig.results()[1] == Type::F32 && sig.results()[2] == Type::F32 => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
type_to_llvm(intrinsics, sig.results()[0])?,
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[64, 32, 32] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
type_to_llvm(intrinsics, sig.results()[0])?,
|
||||
intrinsics.i64_ty.as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, 32, 32] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 {
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
} else {
|
||||
intrinsics.i64_ty.as_basic_type_enum()
|
||||
},
|
||||
if sig.results()[2] == Type::F32 && sig.results()[3] == Type::F32 {
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
} else {
|
||||
intrinsics.i64_ty.as_basic_type_enum()
|
||||
},
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
_ => {
|
||||
let basic_types: Vec<_> = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(intrinsics, ty))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let sret = context
|
||||
.struct_type(&basic_types, false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let param_types = std::iter::once(Ok(sret.as_basic_type_enum())).chain(param_types);
|
||||
|
||||
(
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![(
|
||||
context.create_enum_attribute(Attribute::get_named_enum_kind_id("sret"), 0),
|
||||
AttributeLoc::Param(0),
|
||||
)],
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Marshall wasm stack values into function parameters.
|
||||
fn args_to_call<'ctx>(
|
||||
&self,
|
||||
alloca_builder: &Builder<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
ctx_ptr: PointerValue<'ctx>,
|
||||
llvm_fn_ty: &FunctionType<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
// If it's an sret, allocate the return space.
|
||||
let sret = if llvm_fn_ty.get_return_type().is_none() && func_sig.results().len() > 1 {
|
||||
Some(
|
||||
alloca_builder.build_alloca(
|
||||
llvm_fn_ty.get_param_types()[0]
|
||||
.into_pointer_type()
|
||||
.get_element_type()
|
||||
.into_struct_type(),
|
||||
"sret",
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let values = std::iter::once(ctx_ptr.as_basic_value_enum()).chain(values.iter().copied());
|
||||
|
||||
if let Some(sret) = sret {
|
||||
std::iter::once(sret.as_basic_value_enum())
|
||||
.chain(values)
|
||||
.collect()
|
||||
} else {
|
||||
values.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Given a CallSite, extract the returned values and return them in a Vec.
|
||||
fn rets_from_call<'ctx>(
|
||||
&self,
|
||||
builder: &Builder<'ctx>,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
call_site: CallSiteValue<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
let split_i64 = |value: IntValue<'ctx>| -> (IntValue<'ctx>, IntValue<'ctx>) {
|
||||
assert!(value.get_type() == intrinsics.i64_ty);
|
||||
let low = builder.build_int_truncate(value, intrinsics.i32_ty, "");
|
||||
let lshr =
|
||||
builder.build_right_shift(value, intrinsics.i64_ty.const_int(32, false), false, "");
|
||||
let high = builder.build_int_truncate(lshr, intrinsics.i32_ty, "");
|
||||
(low, high)
|
||||
};
|
||||
|
||||
let f32x2_ty = intrinsics.f32_ty.vec_type(2).as_basic_type_enum();
|
||||
let extract_f32x2 = |value: VectorValue<'ctx>| -> (FloatValue<'ctx>, FloatValue<'ctx>) {
|
||||
assert!(value.get_type() == f32x2_ty.into_vector_type());
|
||||
let ret0 = builder
|
||||
.build_extract_element(value, intrinsics.i32_ty.const_int(0, false), "")
|
||||
.into_float_value();
|
||||
let ret1 = builder
|
||||
.build_extract_element(value, intrinsics.i32_ty.const_int(1, false), "")
|
||||
.into_float_value();
|
||||
(ret0, ret1)
|
||||
};
|
||||
|
||||
let casted = |value: BasicValueEnum<'ctx>, ty: Type| -> BasicValueEnum<'ctx> {
|
||||
match ty {
|
||||
Type::I32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i32_ty, "")
|
||||
}
|
||||
Type::F32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f32_ty, "")
|
||||
}
|
||||
Type::I64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i64_ty, "")
|
||||
}
|
||||
Type::F64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f64_ty, "")
|
||||
}
|
||||
Type::V128 => {
|
||||
assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum());
|
||||
value
|
||||
}
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(basic_value) = call_site.try_as_basic_value().left() {
|
||||
if func_sig.results().len() > 1 {
|
||||
if basic_value.get_type() == intrinsics.i64_ty.as_basic_type_enum() {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
let value = basic_value.into_int_value();
|
||||
let (low, high) = split_i64(value);
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
return vec![low, high];
|
||||
}
|
||||
if basic_value.get_type() == f32x2_ty {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
let (ret0, ret1) = extract_f32x2(basic_value.into_vector_value());
|
||||
return vec![ret0.into(), ret1.into()];
|
||||
}
|
||||
let struct_value = basic_value.into_struct_value();
|
||||
let rets = (0..struct_value.get_type().count_fields())
|
||||
.map(|i| builder.build_extract_value(struct_value, i, "").unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
match func_sig_returns_bitwidths.as_slice() {
|
||||
[32, 64] | [64, 32] | [64, 64] => {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
vec![rets[0], rets[1]]
|
||||
}
|
||||
[32, 32, _]
|
||||
if rets[0].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum() =>
|
||||
{
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets0, rets1) = extract_f32x2(rets[0].into_vector_value());
|
||||
vec![rets0.into(), rets1.into(), rets[1]]
|
||||
}
|
||||
[32, 32, _] => {
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (low, high) = split_i64(rets[0].into_int_value());
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
vec![low, high, rets[1]]
|
||||
}
|
||||
[64, 32, 32]
|
||||
if rets[1].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum() =>
|
||||
{
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets1, rets2) = extract_f32x2(rets[1].into_vector_value());
|
||||
vec![rets[0], rets1.into(), rets2.into()]
|
||||
}
|
||||
[64, 32, 32] => {
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets1, rets2) = split_i64(rets[1].into_int_value());
|
||||
let rets1 = casted(rets1.into(), func_sig.results()[1]);
|
||||
let rets2 = casted(rets2.into(), func_sig.results()[2]);
|
||||
vec![rets[0], rets1, rets2]
|
||||
}
|
||||
[32, 32, 32, 32] => {
|
||||
assert!(func_sig.results().len() == 4);
|
||||
let (low0, high0) = if rets[0].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
{
|
||||
let (x, y) = extract_f32x2(rets[0].into_vector_value());
|
||||
(x.into(), y.into())
|
||||
} else {
|
||||
let (x, y) = split_i64(rets[0].into_int_value());
|
||||
(x.into(), y.into())
|
||||
};
|
||||
let (low1, high1) = if rets[1].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
{
|
||||
let (x, y) = extract_f32x2(rets[1].into_vector_value());
|
||||
(x.into(), y.into())
|
||||
} else {
|
||||
let (x, y) = split_i64(rets[1].into_int_value());
|
||||
(x.into(), y.into())
|
||||
};
|
||||
let low0 = casted(low0, func_sig.results()[0]);
|
||||
let high0 = casted(high0, func_sig.results()[1]);
|
||||
let low1 = casted(low1, func_sig.results()[2]);
|
||||
let high1 = casted(high1, func_sig.results()[3]);
|
||||
vec![low0, high0, low1, high1]
|
||||
}
|
||||
_ => unreachable!("expected an sret for this type"),
|
||||
}
|
||||
} else {
|
||||
assert!(func_sig.results().len() == 1);
|
||||
vec![basic_value]
|
||||
}
|
||||
} else {
|
||||
assert!(call_site.count_arguments() > 0); // Either sret or vmctx.
|
||||
if call_site
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let sret = call_site
|
||||
.try_as_basic_value()
|
||||
.right()
|
||||
.unwrap()
|
||||
.get_operand(0)
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
let struct_value = builder.build_load(sret, "").into_struct_value();
|
||||
let mut rets: Vec<_> = Vec::new();
|
||||
for i in 0..struct_value.get_type().count_fields() {
|
||||
let value = builder.build_extract_value(struct_value, i, "").unwrap();
|
||||
rets.push(value);
|
||||
}
|
||||
assert!(func_sig.results().len() == rets.len());
|
||||
rets
|
||||
} else {
|
||||
assert!(func_sig.results().is_empty());
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_sret(&self, func_sig: &FuncSig) -> Result<bool, CompileError> {
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => Ok(32),
|
||||
Type::I64 | Type::F64 => Ok(64),
|
||||
Type::V128 => Ok(128),
|
||||
ty => Err(CompileError::Codegen(format!(
|
||||
"is_sret: unimplemented wasmer_types type {:?}",
|
||||
ty
|
||||
))),
|
||||
})
|
||||
.collect::<Result<Vec<i32>, _>>()?;
|
||||
|
||||
Ok(!matches!(func_sig_returns_bitwidths.as_slice(),
|
||||
[]
|
||||
| [_]
|
||||
| [32, 32]
|
||||
| [32, 64]
|
||||
| [64, 32]
|
||||
| [64, 64]
|
||||
| [32, 32, 32]
|
||||
| [32, 32, 64]
|
||||
| [64, 32, 32]
|
||||
| [32, 32, 32, 32]))
|
||||
}
|
||||
|
||||
fn pack_values_for_register_return<'ctx>(
|
||||
&self,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
builder: &Builder<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
func_type: &FunctionType<'ctx>,
|
||||
) -> Result<BasicValueEnum<'ctx>, CompileError> {
|
||||
let is_32 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i32_ty)
|
||||
|| (value.is_float_value()
|
||||
&& value.into_float_value().get_type() == intrinsics.f32_ty)
|
||||
};
|
||||
let is_64 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i64_ty)
|
||||
|| (value.is_float_value()
|
||||
&& value.into_float_value().get_type() == intrinsics.f64_ty)
|
||||
};
|
||||
let is_f32 = |value: BasicValueEnum| {
|
||||
value.is_float_value() && value.into_float_value().get_type() == intrinsics.f32_ty
|
||||
};
|
||||
|
||||
let pack_i32s = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
|
||||
assert!(low.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
assert!(high.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
let (low, high) = (low.into_int_value(), high.into_int_value());
|
||||
let low = builder.build_int_z_extend(low, intrinsics.i64_ty, "");
|
||||
let high = builder.build_int_z_extend(high, intrinsics.i64_ty, "");
|
||||
let high = builder.build_left_shift(high, intrinsics.i64_ty.const_int(32, false), "");
|
||||
builder.build_or(low, high, "").as_basic_value_enum()
|
||||
};
|
||||
|
||||
let pack_f32s = |first: BasicValueEnum<'ctx>,
|
||||
second: BasicValueEnum<'ctx>|
|
||||
-> BasicValueEnum<'ctx> {
|
||||
assert!(first.get_type() == intrinsics.f32_ty.as_basic_type_enum());
|
||||
assert!(second.get_type() == intrinsics.f32_ty.as_basic_type_enum());
|
||||
let (first, second) = (first.into_float_value(), second.into_float_value());
|
||||
let vec_ty = intrinsics.f32_ty.vec_type(2);
|
||||
let vec =
|
||||
builder.build_insert_element(vec_ty.get_undef(), first, intrinsics.i32_zero, "");
|
||||
builder
|
||||
.build_insert_element(vec, second, intrinsics.i32_ty.const_int(1, false), "")
|
||||
.as_basic_value_enum()
|
||||
};
|
||||
|
||||
let build_struct = |ty: StructType<'ctx>, values: &[BasicValueEnum<'ctx>]| {
|
||||
let mut struct_value = ty.get_undef();
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
struct_value = builder
|
||||
.build_insert_value(struct_value, *v, i as u32, "")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
}
|
||||
struct_value.as_basic_value_enum()
|
||||
};
|
||||
|
||||
Ok(match *values {
|
||||
[one_value] => one_value,
|
||||
[v1, v2] if is_f32(v1) && is_f32(v2) => pack_f32s(v1, v2),
|
||||
[v1, v2] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
pack_i32s(v1, v2)
|
||||
}
|
||||
[v1, v2] => {
|
||||
assert!(!(is_32(v1) && is_32(v2)));
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, v2],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3] if is_f32(v1) && is_f32(v2) => build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[pack_f32s(v1, v2), v3],
|
||||
),
|
||||
[v1, v2, v3] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[pack_i32s(v1, v2), v3],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3] if is_64(v1) && is_f32(v2) && is_f32(v3) => build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, pack_f32s(v2, v3)],
|
||||
),
|
||||
[v1, v2, v3] if is_64(v1) && is_32(v2) && is_32(v3) => {
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, pack_i32s(v2, v3)],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3, v4] if is_32(v1) && is_32(v2) && is_32(v3) && is_32(v4) => {
|
||||
let v1v2_pack = if is_f32(v1) && is_f32(v2) {
|
||||
pack_f32s(v1, v2)
|
||||
} else {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
pack_i32s(v1, v2)
|
||||
};
|
||||
let v3v4_pack = if is_f32(v3) && is_f32(v4) {
|
||||
pack_f32s(v3, v4)
|
||||
} else {
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
let v4 = builder.build_bitcast(v4, intrinsics.i32_ty, "");
|
||||
pack_i32s(v3, v4)
|
||||
};
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1v2_pack, v3v4_pack],
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("called to perform register return on struct return or void function")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl SymbolRegistry for ShortNames {
|
||||
Ok(v) => v,
|
||||
Err(_) => return None,
|
||||
};
|
||||
match ty.chars().nth(0).unwrap() {
|
||||
match ty.chars().next().unwrap() {
|
||||
'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))),
|
||||
's' => Some(Symbol::Section(SectionIndex::from_u32(idx))),
|
||||
't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(
|
||||
@@ -267,7 +267,7 @@ impl Compiler for LLVMCompiler {
|
||||
self.config(),
|
||||
memory_styles,
|
||||
&table_styles,
|
||||
&mut ShortNames {},
|
||||
&ShortNames {},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
)]
|
||||
#![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")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")]
|
||||
|
||||
mod abi;
|
||||
mod compiler;
|
||||
mod config;
|
||||
mod object_file;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::abi::{get_abi, Abi};
|
||||
use crate::config::{CompiledKind, LLVM};
|
||||
use crate::object_file::{load_object_file, CompiledFunction};
|
||||
use crate::translator::abi::{
|
||||
func_type_to_llvm, get_vmctx_ptr_param, is_sret, pack_values_for_register_return,
|
||||
rets_from_call,
|
||||
};
|
||||
use crate::translator::intrinsics::{type_to_llvm, type_to_llvm_ptr, Intrinsics};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
@@ -23,15 +20,18 @@ use wasmer_types::{FunctionType, LocalFunctionIndex};
|
||||
pub struct FuncTrampoline {
|
||||
ctx: Context,
|
||||
target_machine: TargetMachine,
|
||||
abi: Box<dyn Abi>,
|
||||
}
|
||||
|
||||
const FUNCTION_SECTION: &str = "__TEXT,wasmer_trmpl"; // Needs to be between 1 and 16 chars
|
||||
|
||||
impl FuncTrampoline {
|
||||
pub fn new(target_machine: TargetMachine) -> Self {
|
||||
let abi = get_abi(&target_machine);
|
||||
Self {
|
||||
ctx: Context::create(),
|
||||
target_machine,
|
||||
abi,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ impl FuncTrampoline {
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||
|
||||
let (callee_ty, callee_attrs) = func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
|
||||
let (callee_ty, callee_attrs) = self.abi.func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
|
||||
let trampoline_ty = intrinsics.void_ty.fn_type(
|
||||
&[
|
||||
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
|
||||
@@ -72,7 +72,7 @@ impl FuncTrampoline {
|
||||
trampoline_func
|
||||
.as_global_value()
|
||||
.set_dll_storage_class(DLLStorageClass::Export);
|
||||
generate_trampoline(trampoline_func, ty, &callee_attrs, &self.ctx, &intrinsics)?;
|
||||
self.generate_trampoline(trampoline_func, ty, &callee_attrs, &self.ctx, &intrinsics)?;
|
||||
|
||||
if let Some(ref callbacks) = config.callbacks {
|
||||
callbacks.preopt_ir(&function, &module);
|
||||
@@ -180,7 +180,8 @@ impl FuncTrampoline {
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||
|
||||
let (trampoline_ty, trampoline_attrs) = func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
|
||||
let (trampoline_ty, trampoline_attrs) =
|
||||
self.abi.func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
|
||||
let trampoline_func = module.add_function(name, trampoline_ty, Some(Linkage::External));
|
||||
for (attr, attr_loc) in trampoline_attrs {
|
||||
trampoline_func.add_attribute(attr_loc, attr);
|
||||
@@ -194,7 +195,7 @@ impl FuncTrampoline {
|
||||
trampoline_func
|
||||
.as_global_value()
|
||||
.set_dll_storage_class(DLLStorageClass::Export);
|
||||
generate_dynamic_trampoline(trampoline_func, ty, &self.ctx, &intrinsics)?;
|
||||
self.generate_dynamic_trampoline(trampoline_func, ty, &self.ctx, &intrinsics)?;
|
||||
|
||||
if let Some(ref callbacks) = config.callbacks {
|
||||
callbacks.preopt_ir(&function, &module);
|
||||
@@ -286,9 +287,9 @@ impl FuncTrampoline {
|
||||
unwind_info: compiled_function.body.unwind_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_trampoline<'ctx>(
|
||||
&self,
|
||||
trampoline_func: FunctionValue,
|
||||
func_sig: &FunctionType,
|
||||
func_attrs: &[(Attribute, AttributeLoc)],
|
||||
@@ -299,16 +300,8 @@ fn generate_trampoline<'ctx>(
|
||||
let builder = context.create_builder();
|
||||
builder.position_at_end(entry_block);
|
||||
|
||||
/*
|
||||
// TODO: remove debugging
|
||||
builder.build_call(
|
||||
intrinsics.debug_trap,
|
||||
&[],
|
||||
"");
|
||||
*/
|
||||
|
||||
let (callee_vmctx_ptr, func_ptr, args_rets_ptr) = match *trampoline_func.get_params().as_slice()
|
||||
{
|
||||
let (callee_vmctx_ptr, func_ptr, args_rets_ptr) =
|
||||
match *trampoline_func.get_params().as_slice() {
|
||||
[callee_vmctx_ptr, func_ptr, args_rets_ptr] => (
|
||||
callee_vmctx_ptr,
|
||||
func_ptr.into_pointer_value(),
|
||||
@@ -323,7 +316,7 @@ fn generate_trampoline<'ctx>(
|
||||
|
||||
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
|
||||
|
||||
if is_sret(func_sig)? {
|
||||
if self.abi.is_sret(func_sig)? {
|
||||
let basic_types: Vec<_> = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
@@ -355,7 +348,9 @@ fn generate_trampoline<'ctx>(
|
||||
call_site.add_attribute(*attr_loc, *attr);
|
||||
}
|
||||
|
||||
let rets = rets_from_call(&builder, intrinsics, call_site, func_sig);
|
||||
let rets = self
|
||||
.abi
|
||||
.rets_from_call(&builder, intrinsics, call_site, func_sig);
|
||||
let mut idx = 0;
|
||||
rets.iter().for_each(|v| {
|
||||
let ptr = unsafe {
|
||||
@@ -365,7 +360,8 @@ fn generate_trampoline<'ctx>(
|
||||
"",
|
||||
)
|
||||
};
|
||||
let ptr = builder.build_pointer_cast(ptr, v.get_type().ptr_type(AddressSpace::Generic), "");
|
||||
let ptr =
|
||||
builder.build_pointer_cast(ptr, v.get_type().ptr_type(AddressSpace::Generic), "");
|
||||
builder.build_store(ptr, *v);
|
||||
if v.get_type() == intrinsics.i128_ty.as_basic_type_enum() {
|
||||
idx += 1;
|
||||
@@ -378,6 +374,7 @@ fn generate_trampoline<'ctx>(
|
||||
}
|
||||
|
||||
fn generate_dynamic_trampoline<'ctx>(
|
||||
&self,
|
||||
trampoline_func: FunctionValue,
|
||||
func_sig: &FunctionType,
|
||||
context: &'ctx Context,
|
||||
@@ -397,7 +394,7 @@ fn generate_dynamic_trampoline<'ctx>(
|
||||
);
|
||||
|
||||
// Copy params to 'values'.
|
||||
let first_user_param = if is_sret(func_sig)? { 2 } else { 1 };
|
||||
let first_user_param = if self.abi.is_sret(func_sig)? { 2 } else { 1 };
|
||||
for i in 0..func_sig.params().len() {
|
||||
let ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
@@ -430,7 +427,7 @@ fn generate_dynamic_trampoline<'ctx>(
|
||||
false,
|
||||
)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
let vmctx = get_vmctx_ptr_param(&trampoline_func);
|
||||
let vmctx = self.abi.get_vmctx_ptr_param(&trampoline_func);
|
||||
let callee = builder
|
||||
.build_load(
|
||||
builder
|
||||
@@ -468,12 +465,13 @@ fn generate_dynamic_trampoline<'ctx>(
|
||||
"",
|
||||
)
|
||||
};
|
||||
let ptr = builder.build_pointer_cast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "");
|
||||
let ptr =
|
||||
builder.build_pointer_cast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "");
|
||||
Ok(builder.build_load(ptr, ""))
|
||||
})
|
||||
.collect::<Result<Vec<_>, CompileError>>()?;
|
||||
|
||||
if is_sret(func_sig)? {
|
||||
if self.abi.is_sret(func_sig)? {
|
||||
let sret = trampoline_func
|
||||
.get_first_param()
|
||||
.unwrap()
|
||||
@@ -497,7 +495,7 @@ fn generate_dynamic_trampoline<'ctx>(
|
||||
builder.build_store(sret, struct_value);
|
||||
builder.build_return(None);
|
||||
} else {
|
||||
builder.build_return(Some(&pack_values_for_register_return(
|
||||
builder.build_return(Some(&self.abi.pack_values_for_register_return(
|
||||
&intrinsics,
|
||||
&builder,
|
||||
&results.as_slice(),
|
||||
@@ -508,3 +506,4 @@ fn generate_dynamic_trampoline<'ctx>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
// LLVM implements part of the ABI lowering internally, but also requires that
|
||||
// the user pack and unpack values themselves sometimes. This can help the LLVM
|
||||
// optimizer by exposing operations to the optimizer, but it requires that the
|
||||
// frontend know exactly what IR to produce in order to get the right ABI.
|
||||
//
|
||||
// So far, this is an implementation of the SysV AMD64 ABI.
|
||||
|
||||
#![deny(dead_code, missing_docs)]
|
||||
|
||||
use crate::translator::intrinsics::{type_to_llvm, Intrinsics};
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
types::{BasicType, FunctionType, StructType},
|
||||
values::{
|
||||
BasicValue, BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue,
|
||||
PointerValue, VectorValue,
|
||||
},
|
||||
AddressSpace,
|
||||
};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_types::{FunctionType as FuncSig, Type};
|
||||
|
||||
// Given a function definition, retrieve the parameter that is the vmctx pointer.
|
||||
pub fn get_vmctx_ptr_param<'ctx>(func_value: &FunctionValue<'ctx>) -> PointerValue<'ctx> {
|
||||
func_value
|
||||
.get_nth_param(
|
||||
if func_value
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
// Given a wasm function type, produce an llvm function declaration.
|
||||
pub fn func_type_to_llvm<'ctx>(
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
sig: &FuncSig,
|
||||
) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError> {
|
||||
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
|
||||
|
||||
let param_types =
|
||||
std::iter::once(Ok(intrinsics.ctx_ptr_ty.as_basic_type_enum())).chain(user_param_types);
|
||||
|
||||
let sig_returns_bitwidths = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
Ok(match sig_returns_bitwidths.as_slice() {
|
||||
[] => (
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[_] => {
|
||||
let single_value = sig.results()[0];
|
||||
(
|
||||
type_to_llvm(intrinsics, single_value)?
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[32, 64] | [64, 32] | [64, 64] => {
|
||||
let basic_types: Vec<_> = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(intrinsics, ty))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
(
|
||||
context
|
||||
.struct_type(&basic_types, false)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
[32, 32] if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 => (
|
||||
intrinsics
|
||||
.f32_ty
|
||||
.vec_type(2)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32] => (
|
||||
intrinsics
|
||||
.i64_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, _] if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum(),
|
||||
type_to_llvm(intrinsics, sig.results()[2])?,
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, _] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
intrinsics.i64_ty.as_basic_type_enum(),
|
||||
type_to_llvm(intrinsics, sig.results()[2])?,
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[64, 32, 32] if sig.results()[1] == Type::F32 && sig.results()[2] == Type::F32 => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
type_to_llvm(intrinsics, sig.results()[0])?,
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[64, 32, 32] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
type_to_llvm(intrinsics, sig.results()[0])?,
|
||||
intrinsics.i64_ty.as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
[32, 32, 32, 32] => (
|
||||
context
|
||||
.struct_type(
|
||||
&[
|
||||
if sig.results()[0] == Type::F32 && sig.results()[1] == Type::F32 {
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
} else {
|
||||
intrinsics.i64_ty.as_basic_type_enum()
|
||||
},
|
||||
if sig.results()[2] == Type::F32 && sig.results()[3] == Type::F32 {
|
||||
intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
} else {
|
||||
intrinsics.i64_ty.as_basic_type_enum()
|
||||
},
|
||||
],
|
||||
false,
|
||||
)
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![],
|
||||
),
|
||||
_ => {
|
||||
let basic_types: Vec<_> = sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(intrinsics, ty))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let sret = context
|
||||
.struct_type(&basic_types, false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let param_types = std::iter::once(Ok(sret.as_basic_type_enum())).chain(param_types);
|
||||
|
||||
(
|
||||
intrinsics
|
||||
.void_ty
|
||||
.fn_type(¶m_types.collect::<Result<Vec<_>, _>>()?, false),
|
||||
vec![(
|
||||
context.create_enum_attribute(Attribute::get_named_enum_kind_id("sret"), 0),
|
||||
AttributeLoc::Param(0),
|
||||
)],
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Marshall wasm stack values into function parameters.
|
||||
pub fn args_to_call<'ctx>(
|
||||
alloca_builder: &Builder<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
ctx_ptr: PointerValue<'ctx>,
|
||||
llvm_fn_ty: &FunctionType<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
// If it's an sret, allocate the return space.
|
||||
let sret = if llvm_fn_ty.get_return_type().is_none() && func_sig.results().len() > 1 {
|
||||
Some(
|
||||
alloca_builder.build_alloca(
|
||||
llvm_fn_ty.get_param_types()[0]
|
||||
.into_pointer_type()
|
||||
.get_element_type()
|
||||
.into_struct_type(),
|
||||
"sret",
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let values = std::iter::once(ctx_ptr.as_basic_value_enum()).chain(values.iter().copied());
|
||||
|
||||
if let Some(sret) = sret {
|
||||
std::iter::once(sret.as_basic_value_enum())
|
||||
.chain(values)
|
||||
.collect()
|
||||
} else {
|
||||
values.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Given a CallSite, extract the returned values and return them in a Vec.
|
||||
pub fn rets_from_call<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
call_site: CallSiteValue<'ctx>,
|
||||
func_sig: &FuncSig,
|
||||
) -> Vec<BasicValueEnum<'ctx>> {
|
||||
let split_i64 = |value: IntValue<'ctx>| -> (IntValue<'ctx>, IntValue<'ctx>) {
|
||||
assert!(value.get_type() == intrinsics.i64_ty);
|
||||
let low = builder.build_int_truncate(value, intrinsics.i32_ty, "");
|
||||
let lshr =
|
||||
builder.build_right_shift(value, intrinsics.i64_ty.const_int(32, false), false, "");
|
||||
let high = builder.build_int_truncate(lshr, intrinsics.i32_ty, "");
|
||||
(low, high)
|
||||
};
|
||||
|
||||
let f32x2_ty = intrinsics.f32_ty.vec_type(2).as_basic_type_enum();
|
||||
let extract_f32x2 = |value: VectorValue<'ctx>| -> (FloatValue<'ctx>, FloatValue<'ctx>) {
|
||||
assert!(value.get_type() == f32x2_ty.into_vector_type());
|
||||
let ret0 = builder
|
||||
.build_extract_element(value, intrinsics.i32_ty.const_int(0, false), "")
|
||||
.into_float_value();
|
||||
let ret1 = builder
|
||||
.build_extract_element(value, intrinsics.i32_ty.const_int(1, false), "")
|
||||
.into_float_value();
|
||||
(ret0, ret1)
|
||||
};
|
||||
|
||||
let casted = |value: BasicValueEnum<'ctx>, ty: Type| -> BasicValueEnum<'ctx> {
|
||||
match ty {
|
||||
Type::I32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i32_ty, "")
|
||||
}
|
||||
Type::F32 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f32_ty, "")
|
||||
}
|
||||
Type::I64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.i64_ty, "")
|
||||
}
|
||||
Type::F64 => {
|
||||
assert!(
|
||||
value.get_type() == intrinsics.i64_ty.as_basic_type_enum()
|
||||
|| value.get_type() == intrinsics.f64_ty.as_basic_type_enum()
|
||||
);
|
||||
builder.build_bitcast(value, intrinsics.f64_ty, "")
|
||||
}
|
||||
Type::V128 => {
|
||||
assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum());
|
||||
value
|
||||
}
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(basic_value) = call_site.try_as_basic_value().left() {
|
||||
if func_sig.results().len() > 1 {
|
||||
if basic_value.get_type() == intrinsics.i64_ty.as_basic_type_enum() {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
let value = basic_value.into_int_value();
|
||||
let (low, high) = split_i64(value);
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
return vec![low, high];
|
||||
}
|
||||
if basic_value.get_type() == f32x2_ty {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
let (ret0, ret1) = extract_f32x2(basic_value.into_vector_value());
|
||||
return vec![ret0.into(), ret1.into()];
|
||||
}
|
||||
let struct_value = basic_value.into_struct_value();
|
||||
let rets = (0..struct_value.get_type().count_fields())
|
||||
.map(|i| builder.build_extract_value(struct_value, i, "").unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => 32,
|
||||
Type::I64 | Type::F64 => 64,
|
||||
Type::V128 => 128,
|
||||
Type::ExternRef => unimplemented!("externref in the llvm backend"),
|
||||
Type::FuncRef => unimplemented!("funcref in the llvm backend"),
|
||||
})
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
match func_sig_returns_bitwidths.as_slice() {
|
||||
[32, 64] | [64, 32] | [64, 64] => {
|
||||
assert!(func_sig.results().len() == 2);
|
||||
vec![rets[0], rets[1]]
|
||||
}
|
||||
[32, 32, _]
|
||||
if rets[0].get_type() == intrinsics.f32_ty.vec_type(2).as_basic_type_enum() =>
|
||||
{
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets0, rets1) = extract_f32x2(rets[0].into_vector_value());
|
||||
vec![rets0.into(), rets1.into(), rets[1]]
|
||||
}
|
||||
[32, 32, _] => {
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (low, high) = split_i64(rets[0].into_int_value());
|
||||
let low = casted(low.into(), func_sig.results()[0]);
|
||||
let high = casted(high.into(), func_sig.results()[1]);
|
||||
vec![low, high, rets[1]]
|
||||
}
|
||||
[64, 32, 32]
|
||||
if rets[1].get_type() == intrinsics.f32_ty.vec_type(2).as_basic_type_enum() =>
|
||||
{
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets1, rets2) = extract_f32x2(rets[1].into_vector_value());
|
||||
vec![rets[0], rets1.into(), rets2.into()]
|
||||
}
|
||||
[64, 32, 32] => {
|
||||
assert!(func_sig.results().len() == 3);
|
||||
let (rets1, rets2) = split_i64(rets[1].into_int_value());
|
||||
let rets1 = casted(rets1.into(), func_sig.results()[1]);
|
||||
let rets2 = casted(rets2.into(), func_sig.results()[2]);
|
||||
vec![rets[0], rets1, rets2]
|
||||
}
|
||||
[32, 32, 32, 32] => {
|
||||
assert!(func_sig.results().len() == 4);
|
||||
let (low0, high0) = if rets[0].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
{
|
||||
let (x, y) = extract_f32x2(rets[0].into_vector_value());
|
||||
(x.into(), y.into())
|
||||
} else {
|
||||
let (x, y) = split_i64(rets[0].into_int_value());
|
||||
(x.into(), y.into())
|
||||
};
|
||||
let (low1, high1) = if rets[1].get_type()
|
||||
== intrinsics.f32_ty.vec_type(2).as_basic_type_enum()
|
||||
{
|
||||
let (x, y) = extract_f32x2(rets[1].into_vector_value());
|
||||
(x.into(), y.into())
|
||||
} else {
|
||||
let (x, y) = split_i64(rets[1].into_int_value());
|
||||
(x.into(), y.into())
|
||||
};
|
||||
let low0 = casted(low0, func_sig.results()[0]);
|
||||
let high0 = casted(high0, func_sig.results()[1]);
|
||||
let low1 = casted(low1, func_sig.results()[2]);
|
||||
let high1 = casted(high1, func_sig.results()[3]);
|
||||
vec![low0, high0, low1, high1]
|
||||
}
|
||||
_ => unreachable!("expected an sret for this type"),
|
||||
}
|
||||
} else {
|
||||
assert!(func_sig.results().len() == 1);
|
||||
vec![basic_value]
|
||||
}
|
||||
} else {
|
||||
assert!(call_site.count_arguments() > 0); // Either sret or vmctx.
|
||||
if call_site
|
||||
.get_enum_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
Attribute::get_named_enum_kind_id("sret"),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let sret = call_site
|
||||
.try_as_basic_value()
|
||||
.right()
|
||||
.unwrap()
|
||||
.get_operand(0)
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
let struct_value = builder.build_load(sret, "").into_struct_value();
|
||||
let mut rets: Vec<_> = Vec::new();
|
||||
for i in 0..struct_value.get_type().count_fields() {
|
||||
let value = builder.build_extract_value(struct_value, i, "").unwrap();
|
||||
rets.push(value);
|
||||
}
|
||||
assert!(func_sig.results().len() == rets.len());
|
||||
rets
|
||||
} else {
|
||||
assert!(func_sig.results().is_empty());
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sret(func_sig: &FuncSig) -> Result<bool, CompileError> {
|
||||
let func_sig_returns_bitwidths = func_sig
|
||||
.results()
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 | Type::F32 => Ok(32),
|
||||
Type::I64 | Type::F64 => Ok(64),
|
||||
Type::V128 => Ok(128),
|
||||
ty => Err(CompileError::Codegen(format!(
|
||||
"is_sret: unimplemented wasmer_types type {:?}",
|
||||
ty
|
||||
))),
|
||||
})
|
||||
.collect::<Result<Vec<i32>, _>>()?;
|
||||
|
||||
Ok(match func_sig_returns_bitwidths.as_slice() {
|
||||
[]
|
||||
| [_]
|
||||
| [32, 64]
|
||||
| [64, 32]
|
||||
| [64, 64]
|
||||
| [32, 32]
|
||||
| [32, 32, 32]
|
||||
| [32, 32, 64]
|
||||
| [64, 32, 32]
|
||||
| [32, 32, 32, 32] => false,
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pack_values_for_register_return<'ctx>(
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
builder: &Builder<'ctx>,
|
||||
values: &[BasicValueEnum<'ctx>],
|
||||
func_type: &FunctionType<'ctx>,
|
||||
) -> Result<BasicValueEnum<'ctx>, CompileError> {
|
||||
let is_32 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i32_ty)
|
||||
|| (value.is_float_value() && value.into_float_value().get_type() == intrinsics.f32_ty)
|
||||
};
|
||||
let is_64 = |value: BasicValueEnum| {
|
||||
(value.is_int_value() && value.into_int_value().get_type() == intrinsics.i64_ty)
|
||||
|| (value.is_float_value() && value.into_float_value().get_type() == intrinsics.f64_ty)
|
||||
};
|
||||
let is_f32 = |value: BasicValueEnum| {
|
||||
value.is_float_value() && value.into_float_value().get_type() == intrinsics.f32_ty
|
||||
};
|
||||
|
||||
let pack_i32s = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
|
||||
assert!(low.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
assert!(high.get_type() == intrinsics.i32_ty.as_basic_type_enum());
|
||||
let (low, high) = (low.into_int_value(), high.into_int_value());
|
||||
let low = builder.build_int_z_extend(low, intrinsics.i64_ty, "");
|
||||
let high = builder.build_int_z_extend(high, intrinsics.i64_ty, "");
|
||||
let high = builder.build_left_shift(high, intrinsics.i64_ty.const_int(32, false), "");
|
||||
builder.build_or(low, high, "").as_basic_value_enum()
|
||||
};
|
||||
|
||||
let pack_f32s = |first: BasicValueEnum<'ctx>,
|
||||
second: BasicValueEnum<'ctx>|
|
||||
-> BasicValueEnum<'ctx> {
|
||||
assert!(first.get_type() == intrinsics.f32_ty.as_basic_type_enum());
|
||||
assert!(second.get_type() == intrinsics.f32_ty.as_basic_type_enum());
|
||||
let (first, second) = (first.into_float_value(), second.into_float_value());
|
||||
let vec_ty = intrinsics.f32_ty.vec_type(2);
|
||||
let vec = builder.build_insert_element(vec_ty.get_undef(), first, intrinsics.i32_zero, "");
|
||||
builder
|
||||
.build_insert_element(vec, second, intrinsics.i32_ty.const_int(1, false), "")
|
||||
.as_basic_value_enum()
|
||||
};
|
||||
|
||||
let build_struct = |ty: StructType<'ctx>, values: &[BasicValueEnum<'ctx>]| {
|
||||
let mut struct_value = ty.get_undef();
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
struct_value = builder
|
||||
.build_insert_value(struct_value, *v, i as u32, "")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
}
|
||||
struct_value.as_basic_value_enum()
|
||||
};
|
||||
|
||||
Ok(match *values {
|
||||
[one_value] => one_value,
|
||||
[v1, v2] if is_f32(v1) && is_f32(v2) => pack_f32s(v1, v2),
|
||||
[v1, v2] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
pack_i32s(v1, v2)
|
||||
}
|
||||
[v1, v2] => {
|
||||
assert!(!(is_32(v1) && is_32(v2)));
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, v2],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3] if is_f32(v1) && is_f32(v2) => build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[pack_f32s(v1, v2), v3],
|
||||
),
|
||||
[v1, v2, v3] if is_32(v1) && is_32(v2) => {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[pack_i32s(v1, v2), v3],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3] if is_64(v1) && is_f32(v2) && is_f32(v3) => build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, pack_f32s(v2, v3)],
|
||||
),
|
||||
[v1, v2, v3] if is_64(v1) && is_32(v2) && is_32(v3) => {
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1, pack_i32s(v2, v3)],
|
||||
)
|
||||
}
|
||||
[v1, v2, v3, v4] if is_32(v1) && is_32(v2) && is_32(v3) && is_32(v4) => {
|
||||
let v1v2_pack = if is_f32(v1) && is_f32(v2) {
|
||||
pack_f32s(v1, v2)
|
||||
} else {
|
||||
let v1 = builder.build_bitcast(v1, intrinsics.i32_ty, "");
|
||||
let v2 = builder.build_bitcast(v2, intrinsics.i32_ty, "");
|
||||
pack_i32s(v1, v2)
|
||||
};
|
||||
let v3v4_pack = if is_f32(v3) && is_f32(v4) {
|
||||
pack_f32s(v3, v4)
|
||||
} else {
|
||||
let v3 = builder.build_bitcast(v3, intrinsics.i32_ty, "");
|
||||
let v4 = builder.build_bitcast(v4, intrinsics.i32_ty, "");
|
||||
pack_i32s(v3, v4)
|
||||
};
|
||||
build_struct(
|
||||
func_type.get_return_type().unwrap().into_struct_type(),
|
||||
&[v1v2_pack, v3v4_pack],
|
||||
)
|
||||
}
|
||||
_ => unreachable!("called to perform register return on struct return or void function"),
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::{
|
||||
abi,
|
||||
intrinsics::{
|
||||
tbaa_label, type_to_llvm, CtxType, FunctionCache, GlobalCache, Intrinsics, MemoryCache,
|
||||
},
|
||||
@@ -22,6 +21,7 @@ use inkwell::{
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::abi::{get_abi, Abi};
|
||||
use crate::config::{CompiledKind, LLVM};
|
||||
use crate::object_file::{load_object_file, CompiledFunction};
|
||||
use wasmer_compiler::wasmparser::{MemoryImmediate, Operator};
|
||||
@@ -57,13 +57,16 @@ fn const_zero(ty: BasicTypeEnum) -> BasicValueEnum {
|
||||
pub struct FuncTranslator {
|
||||
ctx: Context,
|
||||
target_machine: TargetMachine,
|
||||
abi: Box<dyn Abi>,
|
||||
}
|
||||
|
||||
impl FuncTranslator {
|
||||
pub fn new(target_machine: TargetMachine) -> Self {
|
||||
let abi = get_abi(&target_machine);
|
||||
Self {
|
||||
ctx: Context::create(),
|
||||
target_machine,
|
||||
abi,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +102,9 @@ impl FuncTranslator {
|
||||
.unwrap();
|
||||
|
||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||
let (func_type, func_attrs) = abi::func_type_to_llvm(&self.ctx, &intrinsics, wasm_fn_type)?;
|
||||
let (func_type, func_attrs) =
|
||||
self.abi
|
||||
.func_type_to_llvm(&self.ctx, &intrinsics, wasm_fn_type)?;
|
||||
|
||||
let func = module.add_function(&function_name, func_type, Some(Linkage::External));
|
||||
for (attr, attr_loc) in &func_attrs {
|
||||
@@ -203,7 +208,7 @@ impl FuncTranslator {
|
||||
state,
|
||||
function: func,
|
||||
locals: params_locals,
|
||||
ctx: CtxType::new(wasm_module, &func, &cache_builder),
|
||||
ctx: CtxType::new(wasm_module, &func, &cache_builder, &*self.abi),
|
||||
unreachable_depth: 0,
|
||||
memory_styles,
|
||||
_table_styles,
|
||||
@@ -211,6 +216,7 @@ impl FuncTranslator {
|
||||
module_translation,
|
||||
wasm_module,
|
||||
symbol_registry,
|
||||
abi: &*self.abi,
|
||||
};
|
||||
fcg.ctx.add_func(
|
||||
func_index,
|
||||
@@ -1171,7 +1177,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
.map(|(v, i)| self.apply_pending_canonicalization(v, i));
|
||||
if wasm_fn_type.results().is_empty() {
|
||||
self.builder.build_return(None);
|
||||
} else if abi::is_sret(wasm_fn_type)? {
|
||||
} else if self.abi.is_sret(wasm_fn_type)? {
|
||||
let sret = self
|
||||
.function
|
||||
.get_first_param()
|
||||
@@ -1198,7 +1204,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
self.builder.build_return(None);
|
||||
} else {
|
||||
self.builder
|
||||
.build_return(Some(&abi::pack_values_for_register_return(
|
||||
.build_return(Some(&self.abi.pack_values_for_register_return(
|
||||
&self.intrinsics,
|
||||
&self.builder,
|
||||
&results.collect::<Vec<_>>(),
|
||||
@@ -1318,6 +1324,7 @@ pub struct LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
module_translation: &'a ModuleTranslationState,
|
||||
wasm_module: &'a ModuleInfo,
|
||||
symbol_registry: &'a dyn SymbolRegistry,
|
||||
abi: &'a dyn Abi,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
@@ -2138,7 +2145,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
_ => *v,
|
||||
});
|
||||
|
||||
let params = abi::args_to_call(
|
||||
let params = self.abi.args_to_call(
|
||||
&self.alloca_builder,
|
||||
func_type,
|
||||
callee_vmctx.into_pointer_value(),
|
||||
@@ -2186,7 +2193,8 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
}
|
||||
*/
|
||||
|
||||
abi::rets_from_call(&self.builder, &self.intrinsics, call_site, func_type)
|
||||
self.abi
|
||||
.rets_from_call(&self.builder, &self.intrinsics, call_site, func_type)
|
||||
.iter()
|
||||
.for_each(|ret| self.state.push1(*ret));
|
||||
}
|
||||
@@ -2357,7 +2365,8 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
self.builder.position_at_end(continue_block);
|
||||
|
||||
let (llvm_func_type, llvm_func_attrs) =
|
||||
abi::func_type_to_llvm(&self.context, &self.intrinsics, func_type)?;
|
||||
self.abi
|
||||
.func_type_to_llvm(&self.context, &self.intrinsics, func_type)?;
|
||||
|
||||
let params = self.state.popn_save_extra(func_type.params().len())?;
|
||||
|
||||
@@ -2381,7 +2390,7 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
_ => *v,
|
||||
});
|
||||
|
||||
let params = abi::args_to_call(
|
||||
let params = self.abi.args_to_call(
|
||||
&self.alloca_builder,
|
||||
func_type,
|
||||
ctx_ptr.into_pointer_value(),
|
||||
@@ -2436,7 +2445,8 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
}
|
||||
*/
|
||||
|
||||
abi::rets_from_call(&self.builder, &self.intrinsics, call_site, func_type)
|
||||
self.abi
|
||||
.rets_from_call(&self.builder, &self.intrinsics, call_site, func_type)
|
||||
.iter()
|
||||
.for_each(|ret| self.state.push1(*ret));
|
||||
}
|
||||
@@ -6286,6 +6296,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
4,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let store = self.builder.build_store(effective_address, value);
|
||||
self.annotate_user_memaccess(memory_index, memarg, 1, store)?;
|
||||
}
|
||||
@@ -6300,6 +6317,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
8,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let store = self.builder.build_store(effective_address, value);
|
||||
self.annotate_user_memaccess(memory_index, memarg, 1, store)?;
|
||||
}
|
||||
@@ -6315,6 +6339,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
4,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let store = self.builder.build_store(effective_address, v);
|
||||
self.annotate_user_memaccess(memory_index, memarg, 1, store)?;
|
||||
}
|
||||
@@ -6330,6 +6361,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
8,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let store = self.builder.build_store(effective_address, v);
|
||||
self.annotate_user_memaccess(memory_index, memarg, 1, store)?;
|
||||
}
|
||||
@@ -6345,6 +6383,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
16,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let store = self.builder.build_store(effective_address, v);
|
||||
self.annotate_user_memaccess(memory_index, memarg, 1, store)?;
|
||||
}
|
||||
@@ -6603,6 +6648,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
1,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let narrow_value =
|
||||
self.builder
|
||||
.build_int_truncate(value, self.intrinsics.i8_ty, "");
|
||||
@@ -6620,6 +6672,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
2,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let narrow_value =
|
||||
self.builder
|
||||
.build_int_truncate(value, self.intrinsics.i16_ty, "");
|
||||
@@ -6637,6 +6696,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
offset,
|
||||
4,
|
||||
)?;
|
||||
let dead_load = self.builder.build_load(effective_address, "");
|
||||
self.annotate_user_memaccess(
|
||||
memory_index,
|
||||
memarg,
|
||||
1,
|
||||
dead_load.as_instruction_value().unwrap(),
|
||||
)?;
|
||||
let narrow_value =
|
||||
self.builder
|
||||
.build_int_truncate(value, self.intrinsics.i32_ty, "");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! [llvm-intrinsics]: https://llvm.org/docs/LangRef.html#intrinsic-functions
|
||||
|
||||
use super::abi;
|
||||
use crate::abi::Abi;
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
builder::Builder,
|
||||
@@ -536,6 +536,7 @@ pub struct CtxType<'ctx, 'a> {
|
||||
|
||||
wasm_module: &'a WasmerCompilerModule,
|
||||
cache_builder: &'a Builder<'ctx>,
|
||||
abi: &'a dyn Abi,
|
||||
|
||||
cached_memories: HashMap<MemoryIndex, MemoryCache<'ctx>>,
|
||||
cached_tables: HashMap<TableIndex, TableCache<'ctx>>,
|
||||
@@ -553,12 +554,14 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> {
|
||||
wasm_module: &'a WasmerCompilerModule,
|
||||
func_value: &FunctionValue<'ctx>,
|
||||
cache_builder: &'a Builder<'ctx>,
|
||||
abi: &'a dyn Abi,
|
||||
) -> CtxType<'ctx, 'a> {
|
||||
CtxType {
|
||||
ctx_ptr_value: abi::get_vmctx_ptr_param(func_value),
|
||||
ctx_ptr_value: abi.get_vmctx_ptr_param(func_value),
|
||||
|
||||
wasm_module,
|
||||
cache_builder,
|
||||
abi,
|
||||
|
||||
cached_memories: HashMap::new(),
|
||||
cached_tables: HashMap::new(),
|
||||
@@ -937,7 +940,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> {
|
||||
Entry::Vacant(entry) => {
|
||||
debug_assert!(module.get_function(function_name).is_none());
|
||||
let (llvm_func_type, llvm_func_attrs) =
|
||||
abi::func_type_to_llvm(context, intrinsics, func_type)?;
|
||||
self.abi.func_type_to_llvm(context, intrinsics, func_type)?;
|
||||
let func =
|
||||
module.add_function(function_name, llvm_func_type, Some(Linkage::External));
|
||||
for (attr, attr_loc) in &llvm_func_attrs {
|
||||
@@ -970,7 +973,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> {
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
let (llvm_func_type, llvm_func_attrs) =
|
||||
abi::func_type_to_llvm(context, intrinsics, func_type)?;
|
||||
self.abi.func_type_to_llvm(context, intrinsics, func_type)?;
|
||||
debug_assert!(wasm_module.local_func_index(function_index).is_none());
|
||||
let offset = offsets.vmctx_vmfunction_import(function_index);
|
||||
let offset = intrinsics.i32_ty.const_int(offset.into(), false);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod abi;
|
||||
mod code;
|
||||
pub mod intrinsics;
|
||||
//mod stackmap;
|
||||
|
||||
@@ -74,10 +74,7 @@ impl<'ctx> ControlFrame<'ctx> {
|
||||
}
|
||||
|
||||
pub fn is_loop(&self) -> bool {
|
||||
match self {
|
||||
ControlFrame::Loop { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, ControlFrame::Loop { .. })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
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")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
@@ -20,12 +20,15 @@ use wasmer_types::{
|
||||
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
};
|
||||
use wasmer_vm::{FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex};
|
||||
use wasmer_vm::{
|
||||
FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, VMTrampoline,
|
||||
};
|
||||
|
||||
/// A compiled wasm module, ready to be instantiated.
|
||||
pub struct JITArtifact {
|
||||
serializable: SerializableModule,
|
||||
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
|
||||
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
frame_info_registration: Mutex<Option<GlobalFrameInfoRegistration>>,
|
||||
@@ -150,7 +153,7 @@ impl JITArtifact {
|
||||
) -> Result<Self, CompileError> {
|
||||
let (
|
||||
finished_functions,
|
||||
_finished_function_call_trampolines,
|
||||
finished_function_call_trampolines,
|
||||
finished_dynamic_function_trampolines,
|
||||
custom_sections,
|
||||
) = inner_jit.allocate(
|
||||
@@ -201,6 +204,8 @@ impl JITArtifact {
|
||||
inner_jit.publish_eh_frame(eh_frame)?;
|
||||
|
||||
let finished_functions = finished_functions.into_boxed_slice();
|
||||
let finished_function_call_trampolines =
|
||||
finished_function_call_trampolines.into_boxed_slice();
|
||||
let finished_dynamic_function_trampolines =
|
||||
finished_dynamic_function_trampolines.into_boxed_slice();
|
||||
let signatures = signatures.into_boxed_slice();
|
||||
@@ -208,6 +213,7 @@ impl JITArtifact {
|
||||
Ok(Self {
|
||||
serializable,
|
||||
finished_functions,
|
||||
finished_function_call_trampolines,
|
||||
finished_dynamic_function_trampolines,
|
||||
signatures,
|
||||
frame_info_registration: Mutex::new(None),
|
||||
@@ -270,6 +276,10 @@ impl Artifact for JITArtifact {
|
||||
&self.finished_functions
|
||||
}
|
||||
|
||||
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
|
||||
&self.finished_function_call_trampolines
|
||||
}
|
||||
|
||||
// TODO: return *const instead of *mut
|
||||
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
|
||||
&self.finished_dynamic_function_trampolines
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! JIT compilation.
|
||||
|
||||
use crate::{CodeMemory, JITArtifact};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::Compiler;
|
||||
@@ -33,7 +32,6 @@ impl JITEngine {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(JITEngineInner {
|
||||
compiler: Some(compiler),
|
||||
function_call_trampolines: HashMap::new(),
|
||||
code_memory: vec![],
|
||||
signatures: SignatureRegistry::new(),
|
||||
features,
|
||||
@@ -61,7 +59,6 @@ impl JITEngine {
|
||||
inner: Arc::new(Mutex::new(JITEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: None,
|
||||
function_call_trampolines: HashMap::new(),
|
||||
code_memory: vec![],
|
||||
signatures: SignatureRegistry::new(),
|
||||
features: Features::default(),
|
||||
@@ -98,11 +95,6 @@ impl Engine for JITEngine {
|
||||
compiler.signatures().lookup(sig)
|
||||
}
|
||||
|
||||
/// Retrieves a trampoline given a signature
|
||||
fn function_call_trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.inner().function_call_trampoline(sig)
|
||||
}
|
||||
|
||||
/// Validates a WebAssembly module
|
||||
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
|
||||
self.inner().validate(binary)
|
||||
@@ -150,8 +142,6 @@ pub struct JITEngineInner {
|
||||
/// The compiler
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: Option<Box<dyn Compiler + Send>>,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
function_call_trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
/// The features to compile the Wasm module with
|
||||
features: Features,
|
||||
/// The code memory is responsible of publishing the compiled
|
||||
@@ -196,7 +186,7 @@ impl JITEngineInner {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn allocate(
|
||||
&mut self,
|
||||
module: &ModuleInfo,
|
||||
_module: &ModuleInfo,
|
||||
functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
|
||||
function_call_trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
|
||||
dynamic_function_trampolines: &PrimaryMap<FunctionIndex, FunctionBody>,
|
||||
@@ -204,7 +194,7 @@ impl JITEngineInner {
|
||||
) -> Result<
|
||||
(
|
||||
PrimaryMap<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
PrimaryMap<SignatureIndex, FunctionBodyPtr>,
|
||||
PrimaryMap<SignatureIndex, VMTrampoline>,
|
||||
PrimaryMap<FunctionIndex, FunctionBodyPtr>,
|
||||
PrimaryMap<SectionIndex, SectionBodyPtr>,
|
||||
),
|
||||
@@ -241,20 +231,15 @@ impl JITEngineInner {
|
||||
.map(|slice| FunctionBodyPtr(slice as *mut [_]))
|
||||
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
|
||||
|
||||
let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, FunctionBodyPtr> =
|
||||
let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
|
||||
PrimaryMap::new();
|
||||
for ((sig_index, _), ptr) in function_call_trampolines.iter().zip(
|
||||
allocated_functions
|
||||
for ptr in allocated_functions
|
||||
.drain(0..function_call_trampolines.len())
|
||||
.map(|slice| FunctionBodyPtr(slice as *mut [_])),
|
||||
) {
|
||||
let func_type = &module.signatures[sig_index];
|
||||
let index = self.signatures.register(&func_type);
|
||||
allocated_function_call_trampolines.push(ptr);
|
||||
let trampoline = unsafe {
|
||||
std::mem::transmute::<*const VMFunctionBody, VMTrampoline>((**ptr).as_ptr())
|
||||
};
|
||||
self.function_call_trampolines.insert(index, trampoline);
|
||||
.map(|slice| slice.as_ptr())
|
||||
{
|
||||
let trampoline =
|
||||
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
|
||||
allocated_function_call_trampolines.push(trampoline);
|
||||
}
|
||||
|
||||
let allocated_dynamic_function_trampolines = allocated_functions
|
||||
@@ -309,9 +294,4 @@ impl JITEngineInner {
|
||||
pub fn signatures(&self) -> &SignatureRegistry {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
/// Gets the trampoline pre-registered for a particular signature
|
||||
pub fn function_call_trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.function_call_trampolines.get(&sig).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ use wasmer_compiler::{CompileError, Features, OperatingSystem, Symbol, SymbolReg
|
||||
use wasmer_compiler::{
|
||||
CompileModuleInfo, FunctionBodyData, ModuleEnvironment, ModuleTranslationState,
|
||||
};
|
||||
use wasmer_engine::{
|
||||
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
|
||||
};
|
||||
use wasmer_engine::{Artifact, DeserializeError, InstantiationError, SerializeError};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_engine::{Engine, Tunables};
|
||||
#[cfg(feature = "compiler")]
|
||||
@@ -42,9 +40,8 @@ use wasmer_vm::{
|
||||
pub struct NativeArtifact {
|
||||
sharedobject_path: PathBuf,
|
||||
metadata: ModuleMetadata,
|
||||
#[allow(dead_code)]
|
||||
library: Option<Library>,
|
||||
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
|
||||
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
}
|
||||
@@ -324,14 +321,17 @@ impl NativeArtifact {
|
||||
sharedobject_path: PathBuf,
|
||||
) -> Result<Self, CompileError> {
|
||||
let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
|
||||
let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
|
||||
PrimaryMap::new();
|
||||
let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
|
||||
PrimaryMap::new();
|
||||
let signatures: PrimaryMap<SignatureIndex, VMSharedSignatureIndex> = PrimaryMap::new();
|
||||
Ok(Self {
|
||||
sharedobject_path,
|
||||
metadata,
|
||||
library: None,
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
@@ -367,14 +367,17 @@ impl NativeArtifact {
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve function call trampolines (for all signatures in the module)
|
||||
for (sig_index, func_type) in metadata.compile_info.module.signatures.iter() {
|
||||
// Retrieve function call trampolines
|
||||
let mut finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
|
||||
PrimaryMap::with_capacity(metadata.compile_info.module.signatures.len());
|
||||
for sig_index in metadata.compile_info.module.signatures.keys() {
|
||||
let function_name = metadata.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
|
||||
unsafe {
|
||||
let trampoline: LibrarySymbol<VMTrampoline> = lib
|
||||
.get(function_name.as_bytes())
|
||||
.map_err(to_compile_error)?;
|
||||
engine_inner.add_trampoline(&func_type, *trampoline);
|
||||
let raw = *trampoline.into_raw();
|
||||
finished_function_call_trampolines.push(raw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,21 +421,23 @@ impl NativeArtifact {
|
||||
|
||||
// Compute indices into the shared signature table.
|
||||
let signatures = {
|
||||
let signature_registry = engine_inner.signatures();
|
||||
metadata
|
||||
.compile_info
|
||||
.module
|
||||
.signatures
|
||||
.values()
|
||||
.map(|sig| signature_registry.register(sig))
|
||||
.map(|sig| engine_inner.signatures().register(sig))
|
||||
.collect::<PrimaryMap<_, _>>()
|
||||
};
|
||||
|
||||
engine_inner.add_library(lib);
|
||||
|
||||
Ok(Self {
|
||||
sharedobject_path,
|
||||
metadata,
|
||||
library: Some(lib),
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
@@ -571,6 +576,10 @@ impl Artifact for NativeArtifact {
|
||||
&self.finished_functions
|
||||
}
|
||||
|
||||
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
|
||||
&self.finished_function_call_trampolines
|
||||
}
|
||||
|
||||
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
|
||||
&self.finished_dynamic_function_trampolines
|
||||
}
|
||||
@@ -580,11 +589,6 @@ impl Artifact for NativeArtifact {
|
||||
}
|
||||
|
||||
fn preinstantiate(&self) -> Result<(), InstantiationError> {
|
||||
if self.library.is_none() {
|
||||
return Err(InstantiationError::Link(LinkError::Trap(
|
||||
RuntimeError::new("Cross compiled artifacts can't be instantiated."),
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Native Engine.
|
||||
|
||||
use crate::NativeArtifact;
|
||||
use std::collections::HashMap;
|
||||
use libloading::Library;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
@@ -12,7 +12,7 @@ use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_types::Features;
|
||||
use wasmer_types::FunctionType;
|
||||
use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
|
||||
use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex};
|
||||
#[cfg(feature = "compiler")]
|
||||
use which::which;
|
||||
|
||||
@@ -49,12 +49,12 @@ impl NativeEngine {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(NativeEngineInner {
|
||||
compiler: Some(compiler),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
features,
|
||||
is_cross_compiling,
|
||||
linker,
|
||||
libraries: vec![],
|
||||
})),
|
||||
target: Arc::new(target),
|
||||
engine_id: EngineId::default(),
|
||||
@@ -81,11 +81,11 @@ impl NativeEngine {
|
||||
compiler: None,
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features::default(),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
is_cross_compiling: false,
|
||||
linker: Linker::None,
|
||||
libraries: vec![],
|
||||
})),
|
||||
target: Arc::new(Target::default()),
|
||||
engine_id: EngineId::default(),
|
||||
@@ -137,11 +137,6 @@ impl Engine for NativeEngine {
|
||||
compiler.signatures().lookup(sig)
|
||||
}
|
||||
|
||||
/// Retrieves a trampoline given a signature
|
||||
fn function_call_trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.inner().trampoline(sig)
|
||||
}
|
||||
|
||||
/// Validates a WebAssembly module
|
||||
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
|
||||
self.inner().validate(binary)
|
||||
@@ -222,8 +217,6 @@ pub struct NativeEngineInner {
|
||||
/// The WebAssembly features to use
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
/// The signature registry is used mainly to operate with trampolines
|
||||
/// performantly.
|
||||
signatures: SignatureRegistry,
|
||||
@@ -235,6 +228,8 @@ pub struct NativeEngineInner {
|
||||
is_cross_compiling: bool,
|
||||
/// The linker to use.
|
||||
linker: Linker,
|
||||
/// List of libraries loaded by this engine.
|
||||
libraries: Vec<Library>,
|
||||
}
|
||||
|
||||
impl NativeEngineInner {
|
||||
@@ -283,19 +278,6 @@ impl NativeEngineInner {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
/// Gets the trampoline pre-registered for a particular signature
|
||||
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.trampolines.get(&sig).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn add_trampoline(&mut self, func_type: &FunctionType, trampoline: VMTrampoline) {
|
||||
let index = self.signatures.register(&func_type);
|
||||
// We always use (for now) the latest trampoline compiled
|
||||
// TODO: we need to deallocate trampolines as the compiled modules
|
||||
// where they belong become unallocated.
|
||||
self.trampolines.insert(index, trampoline);
|
||||
}
|
||||
|
||||
pub(crate) fn is_cross_compiling(&self) -> bool {
|
||||
self.is_cross_compiling
|
||||
}
|
||||
@@ -303,4 +285,8 @@ impl NativeEngineInner {
|
||||
pub(crate) fn linker(&self) -> Linker {
|
||||
self.linker
|
||||
}
|
||||
|
||||
pub(crate) fn add_library(&mut self, library: Library) {
|
||||
self.libraries.push(library);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define own
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -100,13 +102,19 @@ int main() {
|
||||
wasi_env_set_instance(wasi_env, instance);
|
||||
|
||||
// WASI is now set up.
|
||||
|
||||
void* vmctx = wasm_instance_get_vmctx_ptr(instance);
|
||||
wasm_val_t* inout[2] = { NULL, NULL };
|
||||
own wasm_func_t* start_function = wasi_get_start_function(instance);
|
||||
if (!start_function) {
|
||||
fprintf(stderr, "`_start` function not found\n");
|
||||
print_wasmer_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
// We're able to call our compiled function directly through a trampoline.
|
||||
wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout);
|
||||
own wasm_trap_t* trap = wasm_func_call(start_function, NULL, NULL);
|
||||
if (trap) {
|
||||
fprintf(stderr, "Trap is not NULL: TODO:\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wasm_instance_delete(instance);
|
||||
wasm_module_delete(module);
|
||||
|
||||
@@ -34,6 +34,7 @@ pub struct ObjectFileArtifact {
|
||||
metadata: ModuleMetadata,
|
||||
module_bytes: Vec<u8>,
|
||||
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
|
||||
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
/// Length of the serialized metadata
|
||||
@@ -247,6 +248,8 @@ impl ObjectFileArtifact {
|
||||
metadata_length: usize,
|
||||
) -> Result<Self, CompileError> {
|
||||
let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
|
||||
let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
|
||||
PrimaryMap::new();
|
||||
let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
|
||||
PrimaryMap::new();
|
||||
let signature_registry = engine_inner.signatures();
|
||||
@@ -262,6 +265,8 @@ impl ObjectFileArtifact {
|
||||
metadata,
|
||||
module_bytes,
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
@@ -307,7 +312,7 @@ impl ObjectFileArtifact {
|
||||
len: usize,
|
||||
}
|
||||
|
||||
let mut engine_inner = engine.inner_mut();
|
||||
let engine_inner = engine.inner();
|
||||
let signature_registry = engine_inner.signatures();
|
||||
let mut sig_map: BTreeMap<SignatureIndex, VMSharedSignatureIndex> = BTreeMap::new();
|
||||
|
||||
@@ -339,19 +344,17 @@ impl ObjectFileArtifact {
|
||||
}
|
||||
|
||||
// read trampolines in order
|
||||
let mut finished_function_call_trampolines = PrimaryMap::new();
|
||||
byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
let num_function_trampolines = usize::from_ne_bytes(byte_buffer);
|
||||
for i in 0..num_function_trampolines {
|
||||
for _ in 0..num_function_trampolines {
|
||||
byte_buffer[0..WORD_SIZE]
|
||||
.clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
|
||||
cur_offset += WORD_SIZE;
|
||||
let trampoline_ptr_bytes = usize::from_ne_bytes(byte_buffer);
|
||||
let trampoline = mem::transmute::<usize, VMTrampoline>(trampoline_ptr_bytes);
|
||||
|
||||
let func_type = &metadata.compile_info.module.signatures[SignatureIndex::new(i)];
|
||||
|
||||
engine_inner.add_trampoline(func_type, trampoline);
|
||||
finished_function_call_trampolines.push(trampoline);
|
||||
// TODO: we can read back the length here if we serialize it. This will improve debug output.
|
||||
}
|
||||
|
||||
@@ -377,6 +380,8 @@ impl ObjectFileArtifact {
|
||||
metadata,
|
||||
module_bytes: bytes.to_owned(),
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
finished_function_call_trampolines: finished_function_call_trampolines
|
||||
.into_boxed_slice(),
|
||||
finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
|
||||
.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
@@ -432,6 +437,10 @@ impl Artifact for ObjectFileArtifact {
|
||||
&self.finished_functions
|
||||
}
|
||||
|
||||
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
|
||||
&self.finished_function_call_trampolines
|
||||
}
|
||||
|
||||
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
|
||||
&self.finished_dynamic_function_trampolines
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::ObjectFileArtifact;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -10,7 +9,7 @@ use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_types::Features;
|
||||
use wasmer_types::FunctionType;
|
||||
use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex, VMTrampoline};
|
||||
use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex};
|
||||
|
||||
/// A WebAssembly `ObjectFile` Engine.
|
||||
#[derive(Clone)]
|
||||
@@ -28,7 +27,6 @@ impl ObjectFileEngine {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(ObjectFileEngineInner {
|
||||
compiler: Some(compiler),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
features,
|
||||
@@ -58,7 +56,6 @@ impl ObjectFileEngine {
|
||||
compiler: None,
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features::default(),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
})),
|
||||
@@ -112,11 +109,6 @@ impl Engine for ObjectFileEngine {
|
||||
compiler.signatures().lookup(sig)
|
||||
}
|
||||
|
||||
/// Retrieves a trampoline given a signature
|
||||
fn function_call_trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.inner().trampoline(sig)
|
||||
}
|
||||
|
||||
/// Validates a WebAssembly module
|
||||
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
|
||||
self.inner().validate(binary)
|
||||
@@ -180,8 +172,6 @@ pub struct ObjectFileEngineInner {
|
||||
/// The WebAssembly features to use
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
/// The signature registry is used mainly to operate with trampolines
|
||||
/// performantly.
|
||||
signatures: SignatureRegistry,
|
||||
@@ -236,17 +226,4 @@ impl ObjectFileEngineInner {
|
||||
pub fn signatures(&self) -> &SignatureRegistry {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
/// Gets the trampoline pre-registered for a particular signature
|
||||
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
|
||||
self.trampolines.get(&sig).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn add_trampoline(&mut self, func_type: &FunctionType, trampoline: VMTrampoline) {
|
||||
let index = self.signatures.register(&func_type);
|
||||
// We always use (for now) the latest trampoline compiled
|
||||
// TODO: we need to deallocate trampolines as the compiled modules
|
||||
// where they belong become unallocated.
|
||||
self.trampolines.insert(index, trampoline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use wasmer_types::{
|
||||
};
|
||||
use wasmer_vm::{
|
||||
FunctionBodyPtr, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex,
|
||||
VMTrampoline,
|
||||
};
|
||||
|
||||
/// An `Artifact` is the product that the `Engine`
|
||||
@@ -54,8 +55,12 @@ pub trait Artifact: Send + Sync + Upcastable {
|
||||
/// ready to be run.
|
||||
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>;
|
||||
|
||||
/// Returns the function call trampolines allocated in memory of this
|
||||
/// `Artifact`, ready to be run.
|
||||
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline>;
|
||||
|
||||
/// Returns the dynamic function trampolines allocated in memory
|
||||
/// for this `Artifact`, ready to be run.
|
||||
/// of this `Artifact`, ready to be run.
|
||||
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr>;
|
||||
|
||||
/// Returns the associated VM signatures for this `Artifact`.
|
||||
@@ -125,6 +130,7 @@ pub trait Artifact: Send + Sync + Upcastable {
|
||||
offsets,
|
||||
module,
|
||||
self.finished_functions().clone(),
|
||||
self.finished_function_call_trampolines().clone(),
|
||||
finished_memories,
|
||||
finished_tables,
|
||||
finished_globals,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user