mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-07 21:28:21 +00:00
Merge branch 'master' into feat-deprecated-runtime-core
This commit is contained in:
10
.github/workflows/main.yaml
vendored
10
.github/workflows/main.yaml
vendored
@@ -92,10 +92,12 @@ jobs:
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
curl https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-apple-darwin.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
|
||||
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 }}"
|
||||
env:
|
||||
LLVM_DIR: ${{ github.workspace }}/llvm-10
|
||||
- name: Install LLVM (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2149,6 +2149,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"rustc_version",
|
||||
"structopt",
|
||||
"tempfile",
|
||||
"test-generator",
|
||||
"wasm-common",
|
||||
"wasmer",
|
||||
|
||||
47
Cargo.toml
47
Cargo.toml
@@ -70,6 +70,7 @@ blake3 = "0.3"
|
||||
criterion = "0.3"
|
||||
lazy_static = "1.4"
|
||||
wasmer-engine-dummy = { path = "tests/lib/engine-dummy" }
|
||||
tempfile = "3.1"
|
||||
|
||||
[features]
|
||||
# Don't add the compiler features in default, please add them on the Makefile
|
||||
@@ -98,6 +99,7 @@ wasi = ["wasmer-wasi"]
|
||||
emscripten = ["wasmer-emscripten"]
|
||||
wat = ["wasmer/wat"]
|
||||
compiler = [
|
||||
"wasmer-compiler/translator",
|
||||
"wasmer-engine-jit/compiler",
|
||||
"wasmer-engine-native/compiler"
|
||||
]
|
||||
@@ -135,3 +137,48 @@ test-llvm = [
|
||||
[[bench]]
|
||||
name = "static_and_dynamic_functions"
|
||||
harness = false
|
||||
|
||||
[[example]]
|
||||
name = "engine-jit"
|
||||
path = "examples/engine_jit.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "engine-native"
|
||||
path = "examples/engine_native.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "engine-headless"
|
||||
path = "examples/engine_headless.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "cross-compilation"
|
||||
path = "examples/engine_cross_compilation.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "compiler-singlepass"
|
||||
path = "examples/compiler_singlepass.rs"
|
||||
required-features = ["singlepass"]
|
||||
|
||||
[[example]]
|
||||
name = "compiler-cranelift"
|
||||
path = "examples/compiler_cranelift.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "compiler-llvm"
|
||||
path = "examples/compiler_llvm.rs"
|
||||
required-features = ["llvm"]
|
||||
|
||||
[[example]]
|
||||
name = "exported-function"
|
||||
path = "examples/exports_function.rs"
|
||||
required-features = ["cranelift"]
|
||||
|
||||
[[example]]
|
||||
name = "wasi"
|
||||
path = "examples/wasi.rs"
|
||||
required-features = ["cranelift", "wasi"]
|
||||
|
||||
7
Makefile
7
Makefile
@@ -96,7 +96,7 @@ build-capi-llvm:
|
||||
# Testing #
|
||||
###########
|
||||
|
||||
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages
|
||||
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples
|
||||
|
||||
test-singlepass:
|
||||
cargo test --release $(compiler_features) --features "test-singlepass"
|
||||
@@ -112,6 +112,8 @@ test-packages:
|
||||
cargo test -p wasmer-vm --release
|
||||
cargo test -p wasm-common --release
|
||||
cargo test -p wasmer-wasi --release
|
||||
cargo test -p wasmer-object --release
|
||||
cargo test -p wasmer-engine-native --release --no-default-features
|
||||
|
||||
test-capi-singlepass: build-capi-singlepass
|
||||
cargo test --manifest-path lib/c-api/Cargo.toml --release \
|
||||
@@ -130,6 +132,9 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm
|
||||
test-wasi-unit:
|
||||
cargo test --manifest-path lib/wasi/Cargo.toml --release
|
||||
|
||||
test-examples:
|
||||
cargo test --release $(compiler_features) --features wasi --examples
|
||||
|
||||
#############
|
||||
# Packaging #
|
||||
#############
|
||||
|
||||
16
README.md
16
README.md
@@ -28,7 +28,7 @@
|
||||
|
||||
<br />
|
||||
|
||||
[Wasmer](https://wasmer.io/) is the fastest and most popular [WebAssembly](https://webassembly.org/) runtime:
|
||||
[Wasmer](https://wasmer.io/) is a standalone [WebAssembly](https://webassembly.org/) runtime:
|
||||
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
|
||||
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
|
||||
* **Pluggable**: Wasmer can be used from almost **any programming language**
|
||||
@@ -52,19 +52,20 @@ Get started with Wasmer:
|
||||
```sh
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
```
|
||||
> Note: *Wasmer is also [available on Windows](https://github.com/wasmerio/wasmer/releases)*
|
||||
|
||||
<details>
|
||||
<summary><b>Alternative</b>: Install with Homebrew</summary>
|
||||
<summary>With PowerShell</summary>
|
||||
<p>
|
||||
|
||||
```sh
|
||||
brew install wasmer
|
||||
```powershell
|
||||
iwr https://win.wasmer.io -useb | iex
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
> *Check all available installation methods here: https://github.com/wasmerio/wasmer-install*
|
||||
|
||||
#### 2. Use Wasmer
|
||||
|
||||
Download a WASM file, and use it universally! You can start with QuickJS: [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm)
|
||||
@@ -88,14 +89,15 @@ Wasmer runtime can be used as a library embedded in different languages, so you
|
||||
|
||||
| | Language | Docs | Author(s) | Maintenance | Release | Stars |
|
||||
|-|-|-|-|-|-|-|
|
||||
|  | [**Rust**](https://github.com/wasmerio/wasmer-rust-example) | [Docs](https://wasmerio.github.io/wasmer/crates/wasmer_vm/) | Wasmer | actively developed | <a href="https://crates.io/crates/wasmer-vm/" target="_blank"></a> |  |
|
||||
|  | [**Rust**](https://github.com/wasmerio/wasmer-rust-example) | [Docs](https://wasmerio.github.io/wasmer/crates/wasmer_runtime/) | Wasmer | actively developed | <a href="https://crates.io/crates/wasmer/" target="_blank"></a> |  |
|
||||
|  | [**C/C++**](https://github.com/wasmerio/wasmer-c-api) | [Docs](https://wasmerio.github.io/wasmer/c/runtime-c-api/) | Wasmer | actively developed | <a href="https://github.com/wasmerio/wasmer-c-api/" target="_blank"></a> |  |
|
||||
|  | [**Python**](https://github.com/wasmerio/python-ext-wasm) | [Docs](https://github.com/wasmerio/python-ext-wasm#api-of-the-wasmer-extensionmodule) | Wasmer | actively developed | <a href="https://pypi.org/project/wasmer/" target="_blank"></a> |  |
|
||||
|  | [**Go**](https://github.com/wasmerio/go-ext-wasm) | [Docs](https://github.com/wasmerio/go-ext-wasm#basic-example-exported-function) | Wasmer | actively developed | <a href="https://github.com/wasmerio/go-ext-wasm" target="_blank"></a> |  |
|
||||
|  | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | [Docs](https://wasmerio.github.io/php-ext-wasm/wasm/) | Wasmer | actively developed | <a href="https://pecl.php.net/package/wasm" target="_blank"></a> |  |
|
||||
|  | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | [Docs](https://www.rubydoc.info/gems/wasmer/) | Wasmer | actively developed | <a href="https://rubygems.org/gems/wasmer" target="_blank"></a> |  |
|
||||
|  | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | | Wasmer | actively developed | <a href="https://github.com/wasmerio/postgres-ext-wasm" target="_blank"></a> |  |
|
||||
|  | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | [Docs](https://docs.wasmer.io/wasmer-js/wasmer-js) | Wasmer | actively developed | <a href="https://www.npmjs.com/package/@wasmer/wasi" target="_blank"></a> |  |
|
||||
|  | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | [Docs](https://docs.wasmer.io/wasmer-js/wasmer-js) | Wasmer | actively developed | <a href="https://www.npmjs.com/package/@wasmer/wasi" target="_blank"></a> |  |
|
||||
|  | [**Java**](https://github.com/wasmerio/java-ext-wasm) | [Docs](https://github.com/wasmerio/java-ext-wasm/#api-of-the-wasmer-library) | Wasmer | actively developed | <a href="https://bintray.com/wasmer/wasmer-jni/wasmer-jni" target="_blank"></a> |  |
|
||||
|  | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Docs](https://migueldeicaza.github.io/WasmerSharp/) |[Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | <a href="https://www.nuget.org/packages/WasmerSharp/" target="_blank"></a> |  |
|
||||
|  | [**R**](https://github.com/dirkschumacher/wasmr) | [Docs](https://github.com/dirkschumacher/wasmr#example) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | |  |
|
||||
|  | [**Elixir**](https://github.com/tessi/wasmex) | [Docs](https://hexdocs.pm/wasmex/api-reference.html) | [Philipp Tessenow](https://github.com/tessi) | actively developed | <a href="https://hex.pm/packages/wasmex" target="_blank"></a> |  |
|
||||
|
||||
167
examples/README.md
Normal file
167
examples/README.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Wasmer Examples
|
||||
|
||||
This directory contains a collection of examples. This isn't an
|
||||
exhaustive collection though, if one example is missing, please ask,
|
||||
we will be happy to fulfill your needs!
|
||||
|
||||
## Examples
|
||||
|
||||
The examples are written in a difficulty/discovery order. Concepts that
|
||||
are explained in an example is not necessarily re-explained in a next
|
||||
example.
|
||||
|
||||
### Engines
|
||||
|
||||
1. [**JIT engine**][engine-jit], explains what an engine is, what the
|
||||
JIT engine is, and how to set it up. The example completes itself
|
||||
with the compilation of the Wasm module, its instantiation, and
|
||||
finally, by calling an exported function.
|
||||
|
||||
_Keywords_: JIT, engine, in-memory, executable code.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example engine-jit --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
2. [**Native engine**][engine-native], explains what a native engine
|
||||
is, and how to set it up. The example completes itself with the
|
||||
compilation of the Wasm module, its instantiation, and finally, by
|
||||
calling an exported function.
|
||||
|
||||
_Keywords_: native, engine, shared library, dynamic library,
|
||||
executable code.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example engine-native --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
3. [**Headless engines**][engine-headless], explains what a headless
|
||||
engine is, what problem it does solve, and what are the benefits of
|
||||
it. The example completes itself with the instantiation of a
|
||||
pre-compiled Wasm module, and finally, by calling an exported
|
||||
function.
|
||||
|
||||
_Keywords_: native, engine, constrained environment, ahead-of-time
|
||||
compilation, cross-compilation, executable code, serialization.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example engine-headless --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
4. [**Cross-compilation**][cross-compilation], illustrates the power
|
||||
of the abstraction over the engines and the compilers, such as it
|
||||
is possible to cross-compile a Wasm module for a custom target.
|
||||
|
||||
_Keywords_: engine, compiler, cross-compilation.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example cross-compilation --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Compilers
|
||||
|
||||
5. [**Singlepass Compiler**][compiler-singlepass], explains how to use
|
||||
the [Singlepass compiler](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-singlepass).
|
||||
|
||||
_Keywords_: engine, compiler, singlepass.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example compiler-singlepass --release --features "singlepass"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
6. [**Cranelift Compiler**][compiler-cranelift], explains how to use
|
||||
the [Cranelift compiler](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-cranelift).
|
||||
|
||||
_Keywords_: engine, compiler, cranelift.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example compiler-cranelift --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
7. [**LLVM Compiler**][compiler-llvm], explains how to use
|
||||
the [LLVM compiler](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-llvm).
|
||||
|
||||
_Keywords_: engine, compiler, llvm.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example compiler-llvm --release --features "llvm"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Exports
|
||||
|
||||
8. [**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.
|
||||
|
||||
_Keywords_: export, function, dynamic, static, native.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example exported-function --release --features "cranelift"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Integrations
|
||||
|
||||
9. [**WASI integration**][integration-wasi], 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.
|
||||
|
||||
_Keywords_: export, function, dynamic, static, native.
|
||||
|
||||
<details>
|
||||
<summary><em>Execute the example</em></summary>
|
||||
|
||||
```shell
|
||||
$ cargo run --example wasi --release --features "cranelift,wasi"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
[engine-jit]: ./engine_jit.rs
|
||||
[engine-native]: ./engine_native.rs
|
||||
[engine-headless]: ./engine_headless.rs
|
||||
[compiler-singlepass]: ./compiler_singlepass.rs
|
||||
[compiler-cranelift]: ./compiler_cranelift.rs
|
||||
[compiler-llvm]: ./compiler_llvm.rs
|
||||
[cross-compilation]: ./engine_cross_compilation.rs
|
||||
[exported-function]: ./exports_function.rs
|
||||
[integration-wasi]: ./wasi.rs
|
||||
66
examples/compiler_cranelift.rs
Normal file
66
examples/compiler_cranelift.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
//! A Wasm module can be compiled with multiple compilers.
|
||||
//!
|
||||
//! This example illustrates how to use the Cranelift compiler.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example compiler-cranelift --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, 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(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Use Cranelift compiler with the default settings
|
||||
let compiler = Cranelift::default();
|
||||
|
||||
// Create the store
|
||||
let store = Store::new(&JIT::new(&compiler).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 sum = instance.exports.get_function("sum")?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// Let's call the `sum` exported function. The parameters are a
|
||||
// slice of `Value`s. The results are a boxed slice of `Value`s.
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "cranelift")]
|
||||
fn test_compiler_cranelift() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
66
examples/compiler_llvm.rs
Normal file
66
examples/compiler_llvm.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
//! A Wasm module can be compiled with multiple compilers.
|
||||
//!
|
||||
//! This example illustrates how to use the LLVM compiler.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example compiler-llvm --release --features "llvm"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Instance, Module, Store, Value};
|
||||
use wasmer_compiler_llvm::LLVM;
|
||||
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(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Use LLVM compiler with the default settings
|
||||
let compiler = LLVM::default();
|
||||
|
||||
// Create the store
|
||||
let store = Store::new(&JIT::new(&compiler).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 sum = instance.exports.get_function("sum")?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// Let's call the `sum` exported function. The parameters are a
|
||||
// slice of `Value`s. The results are a boxed slice of `Value`s.
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "llvm")]
|
||||
fn test_compiler_llvm() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
66
examples/compiler_singlepass.rs
Normal file
66
examples/compiler_singlepass.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
//! A Wasm module can be compiled with multiple compilers.
|
||||
//!
|
||||
//! This example illustrates how to use the Singlepass compiler.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example compiler-singlepass --release --features "singlepass"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Instance, Module, Store, Value};
|
||||
use wasmer_compiler_singlepass::Singlepass;
|
||||
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(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Use Singlepass compiler with the default settings
|
||||
let compiler = Singlepass::default();
|
||||
|
||||
// Create the store
|
||||
let store = Store::new(&JIT::new(&compiler).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 sum = instance.exports.get_function("sum")?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// Let's call the `sum` exported function. The parameters are a
|
||||
// slice of `Value`s. The results are a boxed slice of `Value`s.
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "singlepass")]
|
||||
fn test_compiler_singlepass() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
106
examples/engine_cross_compilation.rs
Normal file
106
examples/engine_cross_compilation.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
//! Defining an engine in Wasmer is one of the fundamental steps.
|
||||
//!
|
||||
//! As a reminder, an engine applies roughly 2 steps:
|
||||
//!
|
||||
//! 1. It compiles the Wasm module bytes to executable code, through
|
||||
//! the intervention of a compiler,
|
||||
//! 2. It stores the executable code somewhere.
|
||||
//!
|
||||
//! This example focuses on the first step: the compiler. It
|
||||
//! illustrates how the abstraction over the compiler is so powerful
|
||||
//! that it is possible to cross-compile a Wasm module.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example cross-compilation --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use std::str::FromStr;
|
||||
use wasmer::{wat2wasm, Module, RuntimeError, Store};
|
||||
use wasmer_compiler::{CpuFeature, Target, Triple};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_native::Native;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Define a compiler configuration.
|
||||
//
|
||||
// In this situation, the compiler is
|
||||
// `wasmer_compiler_cranelift`. The compiler is responsible to
|
||||
// compile the Wasm module into executable code.
|
||||
let mut compiler_config = Cranelift::default();
|
||||
|
||||
// Here we go.
|
||||
//
|
||||
// Let's define the target “triple”. Historically, such things had
|
||||
// three fields, though additional fields have been added over
|
||||
// time.
|
||||
let triple = Triple::from_str("x86_64-linux-musl")
|
||||
.map_err(|error| RuntimeError::new(error.to_string()))?;
|
||||
|
||||
// Here we go again.
|
||||
//
|
||||
// Let's define a CPU feature.
|
||||
let mut cpu_feature = CpuFeature::set();
|
||||
cpu_feature.insert(CpuFeature::from_str("sse2")?);
|
||||
|
||||
// Here we go finally.
|
||||
//
|
||||
// Let's build the target.
|
||||
let target = Target::new(triple, cpu_feature);
|
||||
println!("Chosen target: {:?}", target);
|
||||
|
||||
// Define the engine that will drive everything.
|
||||
//
|
||||
// In this case, the engine is `wasmer_engine_native` which means
|
||||
// that a native object is going to be generated.
|
||||
//
|
||||
// That's where we specify the target for the compiler.
|
||||
// Use the native engine.
|
||||
let engine = Native::new(&mut compiler_config)
|
||||
// Here we go.
|
||||
// Pass the target to the engine! The engine will share
|
||||
// this information with the compiler.
|
||||
.target(target)
|
||||
// Get the engine.
|
||||
.engine();
|
||||
|
||||
// Create a store, that holds the engine.
|
||||
let store = Store::new(&engine);
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let _module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
println!("Module compiled successfully.");
|
||||
|
||||
// Congrats, the Wasm module is cross-compiled!
|
||||
//
|
||||
// What to do with that? It is possible to use an engine (probably
|
||||
// a headless engine) to execute the cross-compiled Wasm module an
|
||||
// the targeted platform.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_cross_compilation() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
153
examples/engine_headless.rs
Normal file
153
examples/engine_headless.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
//! Defining an engine in Wasmer is one of the fundamental steps.
|
||||
//!
|
||||
//! This example illustrates a neat feature of engines: their ability
|
||||
//! to run in a headless mode. At the time of writing, all engines
|
||||
//! have a headless mode, but it's not a requirement of the `Engine`
|
||||
//! trait (defined in the `wasmer_engine` crate).
|
||||
//!
|
||||
//! What problem does it solve, and what does it mean?
|
||||
//!
|
||||
//! Once a Wasm module is compiled into executable code and stored
|
||||
//! somewhere (e.g. in memory with the JIT engine, or in a native
|
||||
//! object with the native engine), the module can be instantiated and
|
||||
//! executed. But imagine for a second the following scenario:
|
||||
//!
|
||||
//! * Modules are compiled ahead of time, to be instantiated later
|
||||
//! on.
|
||||
//! * Modules are cross-compiled on a machine ahead of time
|
||||
//! to be run on another machine later one.
|
||||
//!
|
||||
//! In both scenarios, the environment where the compiled Wasm module
|
||||
//! will be executed can be very constrained. For such particular
|
||||
//! contexts, Wasmer can be compiled _without_ the compilers, so that
|
||||
//! the `wasmer` binary is as small as possible. Indeed, there is no
|
||||
//! need for a compiler since the Wasm module is already compiled. All
|
||||
//! we need is an engine that _only_ drives the instantiation and
|
||||
//! execution of the Wasm module.
|
||||
//!
|
||||
//! And that, that's a headless engine.
|
||||
//!
|
||||
//! To achieve such a scenario, a Wasm module must be compiled, then
|
||||
//! serialized —for example into a file—, then later, potentially on
|
||||
//! another machine, deserialized. The next steps are classical: The
|
||||
//! Wasm module is instantiated and executed.
|
||||
//!
|
||||
//! This example uses a `compiler` because it illustrates the entire
|
||||
//! workflow, but keep in mind the compiler isn't required after the
|
||||
//! compilation step.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example engine-headless --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
use wasmer::imports;
|
||||
use wasmer::wat2wasm;
|
||||
use wasmer::Instance;
|
||||
use wasmer::Module;
|
||||
use wasmer::Store;
|
||||
use wasmer::Value;
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_native::Native;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// First step, let's compile the Wasm module and serialize it.
|
||||
// Note: we need a compiler here.
|
||||
let serialized_module_file = {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Define a compiler configuration.
|
||||
//
|
||||
// In this situation, the compiler is
|
||||
// `wasmer_compiler_cranelift`. The compiler is responsible to
|
||||
// compile the Wasm module into executable code.
|
||||
let mut compiler_config = Cranelift::default();
|
||||
|
||||
println!("Creating Native engine...");
|
||||
// Define the engine that will drive everything.
|
||||
//
|
||||
// In this case, the engine is `wasmer_engine_native` which
|
||||
// means that a native object is going to be generated. So
|
||||
// when we are going to serialize the compiled Wasm module, we
|
||||
// are going to store it in a file with the `.so` extension
|
||||
// for example (or `.dylib`, or `.dll` depending of the
|
||||
// platform).
|
||||
let engine = Native::new(&mut compiler_config).engine();
|
||||
|
||||
// Create a store, that holds the engine.
|
||||
let store = Store::new(&engine);
|
||||
|
||||
println!("Compiling module...");
|
||||
// Let's compile the Wasm module.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
println!("Serializing module...");
|
||||
// Here we go. Let's serialize the compiled Wasm module in a
|
||||
// file.
|
||||
let serialized_module_file = NamedTempFile::new()?;
|
||||
module.serialize_to_file(&serialized_module_file)?;
|
||||
|
||||
serialized_module_file
|
||||
};
|
||||
|
||||
// Second step, deserialize the compiled Wasm module, and execute
|
||||
// it, for example with Wasmer without a compiler.
|
||||
{
|
||||
println!("Creating headless Native engine...");
|
||||
// We create a headless Native engine.
|
||||
let engine = Native::headless().engine();
|
||||
let store = Store::new(&engine);
|
||||
|
||||
println!("Deserializing module...");
|
||||
// Here we go.
|
||||
//
|
||||
// Deserialize the compiled Wasm module. This code is unsafe
|
||||
// because Wasmer can't assert the bytes are valid (see the
|
||||
// `wasmer::Module::deserialize`'s documentation to learn
|
||||
// more).
|
||||
let module = unsafe { Module::deserialize_from_file(&store, serialized_module_file) }?;
|
||||
|
||||
// Congrats, the Wasm module has been deserialized! Now let's
|
||||
// execute it for the sake of having a complete example.
|
||||
|
||||
// Create an import object. Since our Wasm module didn't declare
|
||||
// any imports, it's an empty object.
|
||||
let import_object = imports! {};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// The Wasm module exports a function called `sum`.
|
||||
let sum = instance.exports.get_function("sum")?;
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_engine_headless() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
91
examples/engine_jit.rs
Normal file
91
examples/engine_jit.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
//! Defining an engine in Wasmer is one of the fundamental steps.
|
||||
//!
|
||||
//! This example illustrates how to use the `wasmer_engine_jit`, aka
|
||||
//! the JIT engine. An engine applies roughly 2 steps:
|
||||
//!
|
||||
//! 1. It compiles the Wasm module bytes to executable code, through
|
||||
//! the intervention of a compiler,
|
||||
//! 2. It stores the executable code somewhere.
|
||||
//!
|
||||
//! In the particular context of the JIT engine, the executable code
|
||||
//! is stored in memory.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example engine-jit --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, 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(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Define a compiler configuration.
|
||||
//
|
||||
// In this situation, the compiler is
|
||||
// `wasmer_compiler_cranelift`. The compiler is responsible to
|
||||
// compile the Wasm module into executable code.
|
||||
let mut compiler_config = Cranelift::default();
|
||||
|
||||
println!("Creating JIT engine...");
|
||||
// Define the engine that will drive everything.
|
||||
//
|
||||
// In this case, the engine is `wasmer_engine_jit` which roughly
|
||||
// means that the executable code will live in memory.
|
||||
let engine = JIT::new(&mut compiler_config).engine();
|
||||
|
||||
// Create a store, that holds the engine.
|
||||
let store = Store::new(&engine);
|
||||
|
||||
println!("Compiling module...");
|
||||
// Here we go.
|
||||
//
|
||||
// Let's compile the Wasm module. It is at this step that the Wasm
|
||||
// text is transformed into Wasm bytes (if necessary), and then
|
||||
// compiled to executable code by the compiler, which is then
|
||||
// stored in memory by the engine.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Congrats, the Wasm module is compiled! Now let's execute it for
|
||||
// the sake of having a complete example.
|
||||
|
||||
// Create an import object. Since our Wasm module didn't declare
|
||||
// any imports, it's an empty object.
|
||||
let import_object = imports! {};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// And here we go again. Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// The Wasm module exports a function called `sum`.
|
||||
let sum = instance.exports.get_function("sum")?;
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_engine_jit() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
92
examples/engine_native.rs
Normal file
92
examples/engine_native.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! Defining an engine in Wasmer is one of the fundamental steps.
|
||||
//!
|
||||
//! This example illustrates how to use the `wasmer_engine_native`,
|
||||
//! aka the native engine. An engine applies roughly 2 steps:
|
||||
//!
|
||||
//! 1. It compiles the Wasm module bytes to executable code, through
|
||||
//! the intervention of a compiler,
|
||||
//! 2. It stores the executable code somewhere.
|
||||
//!
|
||||
//! In the particular context of the native engine, the executable
|
||||
//! code is stored in a native object, more precisely in a dynamic
|
||||
//! library.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example engine-native --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, Instance, Module, Store, Value};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_native::Native;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = wat2wasm(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// Define a compiler configuration.
|
||||
//
|
||||
// In this situation, the compiler is
|
||||
// `wasmer_compiler_cranelift`. The compiler is responsible to
|
||||
// compile the Wasm module into executable code.
|
||||
let mut compiler_config = Cranelift::default();
|
||||
|
||||
println!("Creating Native engine...");
|
||||
// Define the engine that will drive everything.
|
||||
//
|
||||
// In this case, the engine is `wasmer_engine_native` which means
|
||||
// that a native object is going to be generated.
|
||||
let engine = Native::new(&mut compiler_config).engine();
|
||||
|
||||
// Create a store, that holds the engine.
|
||||
let store = Store::new(&engine);
|
||||
|
||||
println!("Compiling module...");
|
||||
// Here we go.
|
||||
//
|
||||
// Let's compile the Wasm module. It is at this step that the Wasm
|
||||
// text is transformed into Wasm bytes (if necessary), and then
|
||||
// compiled to executable code by the compiler, which is then
|
||||
// stored into a native object by the engine.
|
||||
let module = Module::new(&store, wasm_bytes)?;
|
||||
|
||||
// Congrats, the Wasm module is compiled! Now let's execute it for
|
||||
// the sake of having a complete example.
|
||||
|
||||
// Create an import object. Since our Wasm module didn't declare
|
||||
// any imports, it's an empty object.
|
||||
let import_object = imports! {};
|
||||
|
||||
println!("Instantiating module...");
|
||||
// And here we go again. Let's instantiate the Wasm module.
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// The Wasm module exports a function called `sum`.
|
||||
let sum = instance.exports.get_function("sum")?;
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_engine_native() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
111
examples/exports_function.rs
Normal file
111
examples/exports_function.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
//! A Wasm module can export entities, like functions, memories,
|
||||
//! globals and tables.
|
||||
//!
|
||||
//! This example illustrates how to use exported 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:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example exported-function --release --features "cranelift"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{imports, wat2wasm, 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(
|
||||
r#"
|
||||
(module
|
||||
(type $sum_t (func (param i32 i32) (result i32)))
|
||||
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add)
|
||||
(export "sum" (func $sum_f)))
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
// 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 a function called `sum`. Let's get
|
||||
// it. Note that
|
||||
//
|
||||
// ```
|
||||
// get_function(name)
|
||||
// ```
|
||||
//
|
||||
// is just an alias to
|
||||
//
|
||||
// ```
|
||||
// get::<Function>(name)`.
|
||||
// ```
|
||||
let sum = instance.exports.get_function("sum")?;
|
||||
|
||||
println!("Calling `sum` function...");
|
||||
// Let's call the `sum` exported function. The parameters are a
|
||||
// slice of `Value`s. The results are a boxed slice of `Value`s.
|
||||
let results = sum.call(&[Value::I32(1), Value::I32(2)])?;
|
||||
|
||||
println!("Results: {:?}", results);
|
||||
assert_eq!(results.to_vec(), vec![Value::I32(3)]);
|
||||
|
||||
// That was fun. But what if we can get rid of the `Value`s? Well,
|
||||
// that's possible with the `NativeFunction` API. The function
|
||||
// will use native Rust values.
|
||||
//
|
||||
// Note that `native` takes 2 generic parameters: `Args` and
|
||||
// `Rets`, respectively for the parameters and the results. If
|
||||
// those values don't match the exported function signature, an
|
||||
// error will be raised.
|
||||
let sum = sum.native::<(i32, i32), i32>()?;
|
||||
|
||||
println!("Calling `sum` function (natively)...");
|
||||
// Let's call the `sum` exported function. The parameters are
|
||||
// statically typed Rust values of type `i32` and `i32`. The
|
||||
// result, in this case particular case, in a unit of type `i32`.
|
||||
let result = sum.call(1, 2)?;
|
||||
|
||||
println!("Results: {:?}", result);
|
||||
assert_eq!(result, 3);
|
||||
|
||||
// Much nicer, isn't it?
|
||||
//
|
||||
// Those two API exist because they addres different needs. The
|
||||
// former has a more dynamic approach, while the second has a more
|
||||
// static approach.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exported_function() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
69
examples/wasi.rs
Normal file
69
examples/wasi.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
//! Running a WASI compiled WebAssembly module with Wasmer.
|
||||
//!
|
||||
//! This example illustrates how to run WASI modules with
|
||||
//! Wasmer. To run WASI we have to have to do mainly 3 steps:
|
||||
//!
|
||||
//! 1. Create a `WasiEnv` instance
|
||||
//! 2. Attach the imports from the `WasiEnv` to a new instance
|
||||
//! 3. Run the `WASI` module.
|
||||
//!
|
||||
//! You can run the example directly by executing in Wasmer root:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run --example wasi --release --features "cranelift,wasi"
|
||||
//! ```
|
||||
//!
|
||||
//! Ready?
|
||||
|
||||
use wasmer::{Instance, Module, Store};
|
||||
use wasmer_compiler_cranelift::Cranelift;
|
||||
use wasmer_engine_jit::JIT;
|
||||
use wasmer_wasi::WasiState;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let wasm_path = format!(
|
||||
"{}/tests/wasi-wast/wasi/unstable/hello.wasm",
|
||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
||||
);
|
||||
// Let's declare the Wasm module with the text representation.
|
||||
let wasm_bytes = std::fs::read(wasm_path)?;
|
||||
|
||||
// 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)?;
|
||||
|
||||
println!("Creating `WasiEnv`...");
|
||||
// First, we create the `WasiEnv`
|
||||
let mut wasi_env = WasiState::new("hello")
|
||||
// .args(&["world"])
|
||||
// .env("KEY", "Value")
|
||||
.finalize()?;
|
||||
|
||||
println!("Instantiating module with WASI imports...");
|
||||
// Then, we get the import object related to our WASI
|
||||
// and attach it to the Wasm instance.
|
||||
let import_object = wasi_env.import_object(&module)?;
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
// WASI requires to explicitly set the memory for the `WasiEnv`
|
||||
wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());
|
||||
|
||||
println!("Call WASI `_start` function...");
|
||||
// And we just call the `_start` function!
|
||||
let start = instance.exports.get_function("_start")?;
|
||||
start.call(&[])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "wasi")]
|
||||
fn test_wasi() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main()
|
||||
}
|
||||
23
lib/api/src/externals/function.rs
vendored
23
lib/api/src/externals/function.rs
vendored
@@ -37,7 +37,17 @@ pub enum FunctionDefinition {
|
||||
Host(HostFunctionDefinition),
|
||||
}
|
||||
|
||||
/// A WebAssembly `function`.
|
||||
/// A WebAssembly `function` instance.
|
||||
///
|
||||
/// A function instance is the runtime representation of a function.
|
||||
/// It effectively is a closure of the original function (defined in either
|
||||
/// the host or the WebAssembly module) over the runtime `Instance` of its
|
||||
/// originating `Module`.
|
||||
///
|
||||
/// The module instance is used to resolve references to other definitions
|
||||
/// during execution of the function.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#function-instances
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Function {
|
||||
pub(crate) store: Store,
|
||||
@@ -46,7 +56,7 @@ pub struct Function {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Creates a new `Function` that is:
|
||||
/// Creates a new host `Function` that is:
|
||||
///
|
||||
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
|
||||
/// unique _statically declared type_. The outputs can be
|
||||
@@ -77,7 +87,7 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
/// Creates a new host `Function` that is:
|
||||
///
|
||||
/// 1. Static/Monomorphic, i.e. all inputs and outputs have a
|
||||
/// unique statically declared type. The outputs can be wrapped
|
||||
@@ -115,7 +125,7 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
/// Creates a new host `Function` that is:
|
||||
///
|
||||
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
|
||||
/// slice of `Val` (the set of all Wasm values), and all
|
||||
@@ -150,7 +160,7 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Function` that is:
|
||||
/// Creates a new host `Function` that is:
|
||||
///
|
||||
/// 1. Dynamic/Polymorphic, i.e. all inputs are received in a
|
||||
/// slice of `Val` (the set of all Wasm values), and all
|
||||
@@ -187,11 +197,12 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying type of this function.
|
||||
/// Returns the [`FunctionType`] of the `Function`.
|
||||
pub fn ty(&self) -> &FunctionType {
|
||||
&self.exported.signature
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Function` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
18
lib/api/src/externals/global.rs
vendored
18
lib/api/src/externals/global.rs
vendored
@@ -8,6 +8,12 @@ use crate::RuntimeError;
|
||||
use std::fmt;
|
||||
use wasmer_vm::{Export, ExportGlobal, VMGlobalDefinition};
|
||||
|
||||
/// A WebAssembly `global` instance.
|
||||
///
|
||||
/// A global instance is the runtime representation of a global variable.
|
||||
/// It consists of an individual value and a flag indicating whether it is mutable.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#global-instances
|
||||
#[derive(Clone)]
|
||||
pub struct Global {
|
||||
store: Store,
|
||||
@@ -15,12 +21,14 @@ pub struct Global {
|
||||
}
|
||||
|
||||
impl Global {
|
||||
/// Create a new `Global` with the initial value [`Val`].
|
||||
pub fn new(store: &Store, val: Val) -> Global {
|
||||
// Note: we unwrap because the provided type should always match
|
||||
// the value type, so it's safe to unwrap.
|
||||
Self::from_type(store, GlobalType::new(val.ty(), Mutability::Const), val).unwrap()
|
||||
}
|
||||
|
||||
/// Create a mutable `Global` with the initial value [`Val`].
|
||||
pub fn new_mut(store: &Store, val: Val) -> Global {
|
||||
// Note: we unwrap because the provided type should always match
|
||||
// the value type, so it's safe to unwrap.
|
||||
@@ -52,14 +60,17 @@ impl Global {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`GlobalType`] of the `Global`.
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
&self.exported.global
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Global` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Retrieves the current value [`Val`] that the Global has.
|
||||
pub fn get(&self) -> Val {
|
||||
unsafe {
|
||||
let definition = &mut *self.exported.definition;
|
||||
@@ -73,6 +84,13 @@ impl Global {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a custom value [`Val`] to the runtime Global.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will error if:
|
||||
/// * The global is not mutable
|
||||
/// * The type of the `Val` doesn't matches the Global type.
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if self.ty().mutability != Mutability::Var {
|
||||
return Err(RuntimeError::new(
|
||||
|
||||
28
lib/api/src/externals/memory.rs
vendored
28
lib/api/src/externals/memory.rs
vendored
@@ -7,6 +7,20 @@ use std::sync::Arc;
|
||||
use wasm_common::{Pages, ValueType};
|
||||
use wasmer_vm::{Export, ExportMemory, Memory as RuntimeMemory, MemoryError};
|
||||
|
||||
/// A WebAssembly `memory` instance.
|
||||
///
|
||||
/// A memory instance is the runtime representation of a linear memory.
|
||||
/// It consists of a vector of bytes and an optional maximum size.
|
||||
///
|
||||
/// The length of the vector always is a multiple of the WebAssembly
|
||||
/// page size, which is defined to be the constant 65536 – abbreviated 64Ki.
|
||||
/// Like in a memory type, the maximum size in a memory instance is
|
||||
/// given in units of this page size.
|
||||
///
|
||||
/// A memory created by the host or in WebAssembly code will be accessible and
|
||||
/// mutable from both host and WebAssembly.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
|
||||
#[derive(Clone)]
|
||||
pub struct Memory {
|
||||
store: Store,
|
||||
@@ -14,6 +28,9 @@ pub struct Memory {
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
/// Creates a new host `Memory` from the provided [`MemoryType`].
|
||||
///
|
||||
/// This function will construct the `Memory` using the store [`Tunables`].
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Result<Memory, MemoryError> {
|
||||
let tunables = store.tunables();
|
||||
let style = tunables.memory_style(&ty);
|
||||
@@ -25,10 +42,12 @@ impl Memory {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`MemoryType`] of the `Memory`.
|
||||
pub fn ty(&self) -> &MemoryType {
|
||||
self.memory.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Memory` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
@@ -55,22 +74,31 @@ impl Memory {
|
||||
slice::from_raw_parts_mut(def.base, def.current_length)
|
||||
}
|
||||
|
||||
/// Returns the pointer to the raw bytes of the `Memory`.
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
let definition = self.memory.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
def.base
|
||||
}
|
||||
|
||||
/// Returns the size (in bytes) of the `Memory`.
|
||||
pub fn data_size(&self) -> usize {
|
||||
let definition = self.memory.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
def.current_length
|
||||
}
|
||||
|
||||
/// Returns the size (in [`Pages`]) of the `Memory`.
|
||||
pub fn size(&self) -> Pages {
|
||||
self.memory.size()
|
||||
}
|
||||
|
||||
/// Grow memory by the specified amount of WebAssembly [`Pages`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if memory can't be grown by the specified amount
|
||||
/// of pages.
|
||||
pub fn grow<IntoPages>(&self, delta: IntoPages) -> Result<Pages, MemoryError>
|
||||
where
|
||||
IntoPages: Into<Pages>,
|
||||
|
||||
9
lib/api/src/externals/mod.rs
vendored
9
lib/api/src/externals/mod.rs
vendored
@@ -15,15 +15,24 @@ use crate::store::{Store, StoreObject};
|
||||
use crate::ExternType;
|
||||
use wasmer_vm::Export;
|
||||
|
||||
/// An `Extern` is the runtime representation of an entity that
|
||||
/// can be imported or exported.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#external-values
|
||||
#[derive(Clone)]
|
||||
pub enum Extern {
|
||||
/// A external [`Function`].
|
||||
Function(Function),
|
||||
/// A external [`Global`].
|
||||
Global(Global),
|
||||
/// A external [`Table`].
|
||||
Table(Table),
|
||||
/// A external [`Memory`].
|
||||
Memory(Memory),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
/// Return the undelying type of the inner `Extern`.
|
||||
pub fn ty(&self) -> ExternType {
|
||||
match self {
|
||||
Extern::Function(ft) => ExternType::Function(ft.ty().clone()),
|
||||
|
||||
9
lib/api/src/externals/table.rs
vendored
9
lib/api/src/externals/table.rs
vendored
@@ -7,11 +7,15 @@ use crate::TableType;
|
||||
use std::sync::Arc;
|
||||
use wasmer_vm::{Export, ExportTable, Table as RuntimeTable, VMCallerCheckedAnyfunc};
|
||||
|
||||
/// A WebAssembly `table` instance.
|
||||
///
|
||||
/// The `Table` struct is an array-like structure representing a WebAssembly Table,
|
||||
/// which stores function references.
|
||||
///
|
||||
/// A table created by the host or in WebAssembly code will be accessible and
|
||||
/// mutable from both host and WebAssembly.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#table-instances
|
||||
#[derive(Clone)]
|
||||
pub struct Table {
|
||||
store: Store,
|
||||
@@ -30,6 +34,8 @@ impl Table {
|
||||
/// Creates a new `Table` with the provided [`TableType`] definition.
|
||||
///
|
||||
/// All the elements in the table will be set to the `init` value.
|
||||
///
|
||||
/// This function will construct the `Table` using the store [`Tunables`].
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
|
||||
let item = init.into_checked_anyfunc(store)?;
|
||||
let tunables = store.tunables();
|
||||
@@ -49,11 +55,12 @@ impl Table {
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the underlying [`TableType`].
|
||||
/// Returns the [`TableType`] of the `Table`.
|
||||
pub fn ty(&self) -> &TableType {
|
||||
self.table.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Table` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ use wasmer_vm::InstanceHandle;
|
||||
/// Instance objects contain all the exported WebAssembly
|
||||
/// functions, memories, tables and globals that allow
|
||||
/// interacting with WebAssembly.
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#module-instances
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
handle: InstanceHandle,
|
||||
@@ -94,6 +96,7 @@ impl Instance {
|
||||
&self.module
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Instance` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
self.module.store()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
//! Wasmer API
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(
|
||||
missing_docs,
|
||||
trivial_numeric_casts,
|
||||
unused_extern_crates,
|
||||
intra_doc_link_resolution_failure
|
||||
)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
|
||||
mod exports;
|
||||
mod externals;
|
||||
|
||||
@@ -108,6 +108,7 @@ impl Module {
|
||||
Module::from_binary(store, bytes.as_ref())
|
||||
}
|
||||
|
||||
/// Creates a new WebAssembly module from a file path.
|
||||
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Module, IoCompileError> {
|
||||
let file_ref = file.as_ref();
|
||||
let canonical = file_ref.canonicalize()?;
|
||||
@@ -394,6 +395,7 @@ impl Module {
|
||||
self.artifact.module_ref().custom_sections(name)
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Instance` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ use wasmer_vm::{
|
||||
ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind,
|
||||
};
|
||||
|
||||
/// A WebAssembly function that can be called natively
|
||||
/// (using the Native ABI).
|
||||
pub struct NativeFunc<'a, Args = (), Rets = ()> {
|
||||
definition: FunctionDefinition,
|
||||
store: Store,
|
||||
|
||||
@@ -6,6 +6,16 @@ use wasmer_engine::Tunables as BaseTunables;
|
||||
use std::sync::Arc;
|
||||
use wasmer_engine::Engine;
|
||||
|
||||
/// The store represents all global state that can be manipulated by
|
||||
/// WebAssembly programs. It consists of the runtime representation
|
||||
/// of all instances of functions, tables, memories, and globals that
|
||||
/// have been allocated during the lifetime of the abstract machine.
|
||||
///
|
||||
/// The `Store` holds the engine (that is —amongst many things— used to compile
|
||||
/// the Wasm bytes into a valid module artifact), in addition to the
|
||||
/// [`Tunables`] (that are used to create the memories, tables and globals).
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#store
|
||||
#[derive(Clone)]
|
||||
pub struct Store {
|
||||
engine: Arc<dyn Engine + Send + Sync>,
|
||||
@@ -13,52 +23,59 @@ pub struct Store {
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new<E>(engine: &E) -> Store
|
||||
/// Creates a new `Store` with a specific [`Engine`].
|
||||
pub fn new<E>(engine: &E) -> Self
|
||||
where
|
||||
E: Engine + ?Sized,
|
||||
{
|
||||
Store {
|
||||
Self {
|
||||
engine: engine.cloned(),
|
||||
tunables: Arc::new(Tunables::for_target(engine.target())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Store` with a specific [`Engine`] and [`Tunables`].
|
||||
pub fn new_with_tunables<E>(
|
||||
engine: &E,
|
||||
tunables: impl BaseTunables + Send + Sync + 'static,
|
||||
) -> Store
|
||||
) -> Self
|
||||
where
|
||||
E: Engine + ?Sized,
|
||||
{
|
||||
Store {
|
||||
Self {
|
||||
engine: engine.cloned(),
|
||||
tunables: Arc::new(tunables),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Tunables`].
|
||||
pub fn tunables(&self) -> &dyn BaseTunables {
|
||||
self.tunables.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the [`Engine`].
|
||||
pub fn engine(&self) -> &Arc<dyn Engine + Send + Sync> {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
pub fn same(a: &Store, b: &Store) -> bool {
|
||||
/// Checks whether two stores are identical. A store is considered
|
||||
/// equal to another store if both have the same engine. The
|
||||
/// tunables are excluded from the logic.
|
||||
pub fn same(a: &Self, b: &Self) -> bool {
|
||||
a.engine.id() == b.engine.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Store {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Store::same(self, other)
|
||||
Self::same(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
// We only implement default if we have assigned a default compiler and engine
|
||||
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
|
||||
impl Default for Store {
|
||||
fn default() -> Store {
|
||||
fn default() -> Self {
|
||||
// We store them on a function that returns to make
|
||||
// sure this function doesn't emit a compile error even if
|
||||
// more than one compiler is enabled.
|
||||
@@ -102,6 +119,8 @@ impl Default for Store {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait represinting any object that lives in the `Store`.
|
||||
pub trait StoreObject {
|
||||
/// Return true if the object `Store` is the same as the provided `Store`.
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ pub use wasm_common::{
|
||||
MemoryType, Mutability, TableType, Type as ValType,
|
||||
};
|
||||
|
||||
/// WebAssembly computations manipulate values of basic value types:
|
||||
/// * Integers (32 or 64 bit width)
|
||||
/// * Floating-point (32 or 64 bit width)
|
||||
/// * Vectors (128 bits, with 32 or 64 bit lanes)
|
||||
///
|
||||
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#values
|
||||
pub type Val = Value<Function>;
|
||||
|
||||
impl StoreObject for Val {
|
||||
|
||||
1
lib/cache/src/lib.rs
vendored
1
lib/cache/src/lib.rs
vendored
@@ -4,7 +4,6 @@
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "std", deny(unstable_features))]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#.*
|
||||
|
||||
## When to use Cranelift
|
||||
|
||||
We recommend using this compiler crate **only for development proposes**.
|
||||
|
||||
@@ -52,14 +52,6 @@ impl Cranelift {
|
||||
}
|
||||
}
|
||||
|
||||
/// Should the Cranelift verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated Cranelift IR is valid.
|
||||
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_verifier = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
@@ -197,6 +189,10 @@ impl CompilerConfig for Cranelift {
|
||||
self.enable_pic = true;
|
||||
}
|
||||
|
||||
fn enable_verifier(&mut self) {
|
||||
self.enable_verifier = true;
|
||||
}
|
||||
|
||||
/// Transform it into the compiler
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
Box::new(CraneliftCompiler::new(&self))
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
//! Cranelift is a fast IR generator created by Mozilla for usage in
|
||||
//! Firefox as a next JS compiler generator.
|
||||
//!
|
||||
//! Compared to LLVM, Cranelit is a bit faster and made enterely in Rust.
|
||||
//! Compared to LLVM, Cranelit is a bit faster and made entirely in Rust.
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#.*
|
||||
|
||||
## When to use LLVM
|
||||
|
||||
We recommend using LLVM as the default compiler when running WebAssembly
|
||||
|
||||
@@ -63,14 +63,6 @@ impl LLVM {
|
||||
}
|
||||
}
|
||||
|
||||
/// Should the LLVM verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated LLVM IR is valid.
|
||||
pub fn verify_ir(&mut self, enable: bool) -> &mut Self {
|
||||
self.enable_verifier = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
@@ -184,6 +176,11 @@ impl CompilerConfig for LLVM {
|
||||
self.is_pic = true;
|
||||
}
|
||||
|
||||
/// Whether to verify compiler IR.
|
||||
fn enable_verifier(&mut self) {
|
||||
self.enable_verifier = true;
|
||||
}
|
||||
|
||||
/// Transform it into the compiler.
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
Box::new(LLVMCompiler::new(&self))
|
||||
|
||||
@@ -1630,13 +1630,15 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
let current_block = self.builder.get_insert_block().ok_or_else(|| {
|
||||
CompileError::Codegen("not currently in a block".to_string())
|
||||
})?;
|
||||
self.builder.build_unconditional_branch(*frame.code_after());
|
||||
|
||||
for phi in frame.phis().to_vec().iter().rev() {
|
||||
let (value, info) = self.state.pop1_extra()?;
|
||||
let value = self.apply_pending_canonicalization(value, info);
|
||||
phi.add_incoming(&[(&value, current_block)])
|
||||
}
|
||||
|
||||
let frame = self.state.frame_at_depth(0)?;
|
||||
self.builder.build_unconditional_branch(*frame.code_after());
|
||||
}
|
||||
|
||||
let (if_else_block, if_else_state) = if let ControlFrame::IfElse {
|
||||
@@ -1721,12 +1723,13 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
|
||||
.ok_or_else(|| CompileError::Codegen("not currently in a block".to_string()))?;
|
||||
|
||||
let frame = self.state.outermost_frame()?;
|
||||
self.builder.build_unconditional_branch(*frame.br_dest());
|
||||
for phi in frame.phis().to_vec().iter().rev() {
|
||||
let (arg, info) = self.state.pop1_extra()?;
|
||||
let arg = self.apply_pending_canonicalization(arg, info);
|
||||
phi.add_incoming(&[(&arg, current_block)]);
|
||||
}
|
||||
let frame = self.state.outermost_frame()?;
|
||||
self.builder.build_unconditional_branch(*frame.br_dest());
|
||||
|
||||
self.state.reachable = false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ let compiler = Singlepass::new();
|
||||
let store = Store::new(&JIT::new(&compiler).engine());
|
||||
```
|
||||
|
||||
*Note: you can find a [full working example using Singlepass compiler here](https://github.com/wasmerio/wasmer-reborn/blob/master/examples/compiler-singlepass.rs).*
|
||||
|
||||
## When to use Singlepass
|
||||
|
||||
Singlepass is designed to emit compiled code at linear time, as such
|
||||
|
||||
@@ -26,6 +26,15 @@ pub trait CompilerConfig {
|
||||
// in case they do something special for emitting PIC code.
|
||||
}
|
||||
|
||||
/// Enable compiler IR verification.
|
||||
///
|
||||
/// For compilers capable of doing so, this enables internal consistency
|
||||
/// checking.
|
||||
fn enable_verifier(&mut self) {
|
||||
// By default we do nothing, each backend will need to customize this
|
||||
// in case they create an IR that they can verify.
|
||||
}
|
||||
|
||||
/// Gets the custom compiler config
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send>;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use thiserror::Error;
|
||||
/// [compiler-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/CompileError
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompileError {
|
||||
/// A wasm translation error occured.
|
||||
/// A Wasm translation error occured.
|
||||
#[error("WebAssembly translation error: {0}")]
|
||||
Wasm(#[from] WasmError),
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ use crate::trap::TrapInformation;
|
||||
use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap, JumpTableOffsets, Relocation};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{FunctionIndex, LocalFunctionIndex, SignatureIndex};
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "std", deny(unstable_features))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
|
||||
@@ -15,7 +15,7 @@ lazy_static = "1.4"
|
||||
libc = "0.2.72"
|
||||
log = "0.4"
|
||||
time = "0.1"
|
||||
wasmer = { path = "../api", version = "1.0.0-alpha.1" }
|
||||
wasmer = { path = "../api", version = "1.0.0-alpha.1", default-features = false }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
getrandom = "0.1"
|
||||
|
||||
@@ -6,6 +6,8 @@ After the compiler process the result, the JIT pushes it into
|
||||
memory and links it's contents so it can be usable by the
|
||||
`wasmer` api.
|
||||
|
||||
*Note: you can find a [full working example using the JIT engine here](https://github.com/wasmerio/wasmer-reborn/blob/master/examples/engine-jit.rs).*
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
This project borrowed some of the code of the code memory and unwind tables from the [wasmtime-jit](https://crates.io/crates/wasmtime-jit), the code since then has evolved significantly.
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
allow(clippy::new_without_default, clippy::new_without_default)
|
||||
|
||||
@@ -12,7 +12,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasm-common = { path = "../wasm-common", version = "1.0.0-alpha.1" }
|
||||
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false }
|
||||
wasmer-compiler = { path = "../compiler", version = "1.0.0-alpha.1", default-features = false, features = ["enable-serde"] }
|
||||
wasmer-vm = { path = "../vm", version = "1.0.0-alpha.1" }
|
||||
wasmer-engine = { path = "../engine", version = "1.0.0-alpha.1" }
|
||||
wasmer-object = { path = "../object", version = "1.0.0-alpha.1" }
|
||||
|
||||
@@ -10,6 +10,8 @@ so it can be usable by the `wasmer` API.
|
||||
|
||||
This allows Wasmer to achieve *blazing fast* native startup times.
|
||||
|
||||
*Note: you can find a [full working example using the JIT engine here](https://github.com/wasmerio/wasmer-reborn/blob/master/examples/engine-jit.rs).*
|
||||
|
||||
## Requirements
|
||||
|
||||
The `wasmer-engine-native` crate requires a linker available on your
|
||||
|
||||
@@ -15,23 +15,23 @@ use tempfile::NamedTempFile;
|
||||
#[cfg(feature = "compiler")]
|
||||
use tracing::trace;
|
||||
use wasm_common::entity::{BoxedSlice, PrimaryMap};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasm_common::DataInitializer;
|
||||
use wasm_common::{
|
||||
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer,
|
||||
SignatureIndex, TableIndex,
|
||||
FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
|
||||
TableIndex,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{
|
||||
Compilation, CompileModuleInfo, Compiler, ModuleEnvironment, OperatingSystem, Target, Triple,
|
||||
};
|
||||
use wasmer_compiler::{CompileError, Features};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_engine::Engine;
|
||||
use wasmer_compiler::{Compilation, CompileModuleInfo, Compiler, ModuleEnvironment, Target};
|
||||
use wasmer_compiler::{CompileError, Features, OperatingSystem, Triple};
|
||||
use wasmer_engine::{
|
||||
Artifact, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
|
||||
Tunables,
|
||||
};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_object::{emit_compilation, emit_data, get_object_for_target, CompilationNamer};
|
||||
use wasmer_engine::{Engine, Tunables};
|
||||
use wasmer_object::CompilationNamer;
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_object::{emit_compilation, emit_data, get_object_for_target};
|
||||
use wasmer_vm::{MemoryStyle, TableStyle};
|
||||
use wasmer_vm::{ModuleInfo, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline};
|
||||
|
||||
@@ -50,6 +50,8 @@ fn to_compile_error(err: impl Error) -> CompileError {
|
||||
CompileError::Codegen(format!("{}", err))
|
||||
}
|
||||
|
||||
const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
|
||||
|
||||
impl NativeArtifact {
|
||||
// Mach-O header in Mac
|
||||
#[allow(dead_code)]
|
||||
@@ -91,6 +93,7 @@ impl NativeArtifact {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
/// Generate a compilation
|
||||
pub fn generate_compilation<'data>(
|
||||
data: &'data [u8],
|
||||
@@ -154,7 +157,7 @@ impl NativeArtifact {
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let target_triple = target.triple().clone();
|
||||
let target_triple = target.triple();
|
||||
|
||||
// We construct the function body lengths
|
||||
let function_body_lengths = compilation
|
||||
@@ -179,8 +182,9 @@ impl NativeArtifact {
|
||||
.expect("Should write number");
|
||||
metadata_binary.extend(serialized_data);
|
||||
|
||||
emit_data(&mut obj, b"WASMER_METADATA", &metadata_binary).map_err(to_compile_error)?;
|
||||
emit_compilation(&mut obj, compilation, &metadata).map_err(to_compile_error)?;
|
||||
emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary).map_err(to_compile_error)?;
|
||||
emit_compilation(&mut obj, compilation, &metadata, &target_triple)
|
||||
.map_err(to_compile_error)?;
|
||||
|
||||
let filepath = {
|
||||
let file = tempfile::Builder::new()
|
||||
@@ -211,7 +215,7 @@ impl NativeArtifact {
|
||||
};
|
||||
|
||||
let host_target = Triple::host();
|
||||
let is_cross_compiling = target_triple != host_target;
|
||||
let is_cross_compiling = target_triple != &host_target;
|
||||
let cross_compiling_args: Vec<String> = if is_cross_compiling {
|
||||
vec![
|
||||
format!("--target={}", target_triple),
|
||||
@@ -472,7 +476,7 @@ impl NativeArtifact {
|
||||
// to take the first element of the data to construct the slice from
|
||||
// it.
|
||||
let symbol: LibrarySymbol<*mut [u8; 10 + 1]> =
|
||||
lib.get(b"WASMER_METADATA").map_err(|e| {
|
||||
lib.get(WASMER_METADATA_SYMBOL).map_err(|e| {
|
||||
DeserializeError::CorruptedBinary(format!(
|
||||
"The provided object file doesn't seem to be generated by Wasmer: {}",
|
||||
e
|
||||
|
||||
@@ -9,9 +9,11 @@ pub struct Native<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Native<'a> {
|
||||
#[cfg(feature = "compiler")]
|
||||
/// Create a new Native
|
||||
pub fn new(compiler_config: &'a mut dyn CompilerConfig) -> Self {
|
||||
compiler_config.enable_pic();
|
||||
|
||||
Self {
|
||||
compiler_config: Some(compiler_config),
|
||||
target: None,
|
||||
@@ -42,15 +44,70 @@ impl<'a> Native<'a> {
|
||||
|
||||
/// Build the `NativeEngine` for this configuration
|
||||
pub fn engine(self) -> NativeEngine {
|
||||
if let Some(_compiler_config) = self.compiler_config {
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
let compiler_config = _compiler_config;
|
||||
let target = self.target.unwrap_or_default();
|
||||
if let Some(compiler_config) = self.compiler_config {
|
||||
let features = self
|
||||
.features
|
||||
.unwrap_or_else(|| compiler_config.default_features_for_target(&target));
|
||||
let compiler = compiler_config.compiler();
|
||||
NativeEngine::new(compiler, target, features)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
{
|
||||
unreachable!("Cannot call `NativeEngine::new` without the `compiler` feature")
|
||||
}
|
||||
} else {
|
||||
NativeEngine::headless()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "compiler")]
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::{Compiler, FunctionMiddlewareGenerator};
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
#[derive(Default)]
|
||||
pub struct TestCompilerConfig {
|
||||
pub enabled_pic: bool,
|
||||
pub middlewares: Vec<Arc<dyn FunctionMiddlewareGenerator>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
impl CompilerConfig for TestCompilerConfig {
|
||||
fn enable_pic(&mut self) {
|
||||
self.enabled_pic = true;
|
||||
}
|
||||
|
||||
fn compiler(&self) -> Box<dyn Compiler + Send> {
|
||||
unimplemented!("compiler not implemented");
|
||||
}
|
||||
|
||||
fn push_middleware(&mut self, middleware: Arc<dyn FunctionMiddlewareGenerator>) {
|
||||
self.middlewares.push(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
#[test]
|
||||
#[should_panic(expected = "compiler not implemented")]
|
||||
fn build_engine() {
|
||||
let mut compiler_config = TestCompilerConfig::default();
|
||||
let native = Native::new(&mut compiler_config);
|
||||
let _engine = native.engine();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_headless_engine() {
|
||||
let native = Native::headless();
|
||||
let _engine = native.engine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use wasm_common::{Features, FunctionType};
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasm_common::Features;
|
||||
use wasm_common::FunctionType;
|
||||
#[cfg(feature = "compiler")]
|
||||
use wasmer_compiler::Compiler;
|
||||
use wasmer_compiler::{CompileError, Target};
|
||||
@@ -56,10 +58,11 @@ impl NativeEngine {
|
||||
inner: Arc::new(Mutex::new(NativeEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features::default(),
|
||||
trampolines: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
prefixer: None,
|
||||
features: Features::default(),
|
||||
})),
|
||||
target: Arc::new(Target::default()),
|
||||
engine_id: EngineId::default(),
|
||||
@@ -122,6 +125,7 @@ impl Engine for NativeEngine {
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
#[cfg(feature = "compiler")]
|
||||
fn compile(
|
||||
&self,
|
||||
binary: &[u8],
|
||||
@@ -130,6 +134,19 @@ impl Engine for NativeEngine {
|
||||
Ok(Arc::new(NativeArtifact::new(&self, binary, tunables)?))
|
||||
}
|
||||
|
||||
/// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled).
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
fn compile(
|
||||
&self,
|
||||
_binary: &[u8],
|
||||
_tunables: &dyn Tunables,
|
||||
) -> Result<Arc<dyn Artifact>, CompileError> {
|
||||
Err(CompileError::Codegen(
|
||||
"The `NativeEngine` is operating in headless mode, so it cannot compile a module."
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module (binary content of a Shared Object file)
|
||||
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
|
||||
Ok(Arc::new(NativeArtifact::deserialize(&self, &bytes)?))
|
||||
@@ -161,6 +178,7 @@ pub struct NativeEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
compiler: Option<Box<dyn Compiler + Send>>,
|
||||
/// The WebAssembly features to use
|
||||
#[cfg(feature = "compiler")]
|
||||
features: Features,
|
||||
/// Pointers to trampoline functions used to enter particular signatures
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
@@ -178,7 +196,7 @@ impl NativeEngineInner {
|
||||
#[cfg(feature = "compiler")]
|
||||
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
|
||||
if self.compiler.is_none() {
|
||||
return Err(CompileError::Codegen("The NativeEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
|
||||
return Err(CompileError::Codegen("The `NativeEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
|
||||
}
|
||||
Ok(&**self
|
||||
.compiler
|
||||
@@ -186,6 +204,7 @@ impl NativeEngineInner {
|
||||
.expect("Can't get compiler reference"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String {
|
||||
if let Some(prefixer) = &self.prefixer {
|
||||
prefixer(&bytes)
|
||||
@@ -194,6 +213,7 @@ impl NativeEngineInner {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "compiler")]
|
||||
pub(crate) fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
@@ -208,7 +228,7 @@ impl NativeEngineInner {
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
|
||||
Err(CompileError::Validate(
|
||||
"The NativeEngine is not compiled with compiler support, which is required for validating".to_string(),
|
||||
"The `NativeEngine` is not compiled with compiler support, which is required for validating".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
//! it generates a shared object file (`.so` or `.dylib` depending on
|
||||
//! the target), saves it temporarily to disk and uses it natively
|
||||
//! via `dlopen` and `dlsym` (using the `libloading` library).
|
||||
//!
|
||||
//! Note: `.dll` generation for Windows is not yet supported
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#
|
||||
* [Native](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/engine-native)
|
||||
|
||||
## Example Implementation
|
||||
|
||||
Please check [`wasmer-engine-dummy`](../../tests/lib/engine-dummy/) for an example
|
||||
implementation for an Engine.
|
||||
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
This project borrowed some of the code of the trap implementation from the [wasmtime-api](https://crates.io/crates/wasmtime), the code since then has evolved significantly.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#. Given a compilation result, i.e. the result
|
||||
of `wasmer_compiler::Compiler::compile_module`, this crate exposes
|
||||
functions to create an `Object` file for a given target. It is a
|
||||
useful thin layer on top of [the `object`
|
||||
crate](https://github.com/gimli-rs/object).
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
|
||||
@@ -22,7 +22,21 @@ pub trait CompilationNamer {
|
||||
fn get_dynamic_function_trampoline_name(&self, index: &FunctionIndex) -> String;
|
||||
}
|
||||
|
||||
/// Create an object for a given target [`Triple`]
|
||||
/// Create an object for a given target `Triple`.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// # use wasmer_compiler::Triple;
|
||||
/// # use wasmer_object::ObjectError;
|
||||
/// use wasmer_object::get_object_for_target;
|
||||
///
|
||||
/// # fn generate_object_for_target(triple: &Triple) -> Result<(), ObjectError> {
|
||||
/// let mut object = get_object_for_target(&triple)?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn get_object_for_target(triple: &Triple) -> Result<Object, ObjectError> {
|
||||
let obj_binary_format = match triple.binary_format {
|
||||
BinaryFormat::Elf => object::BinaryFormat::Elf,
|
||||
@@ -45,13 +59,14 @@ pub fn get_object_for_target(triple: &Triple) -> Result<Object, ObjectError> {
|
||||
)));
|
||||
}
|
||||
};
|
||||
let obj_endianness = match triple.endianness() {
|
||||
Ok(Endianness::Little) => object::Endianness::Little,
|
||||
Ok(Endianness::Big) => object::Endianness::Big,
|
||||
Err(_) => {
|
||||
return Err(ObjectError::UnknownEndianness);
|
||||
}
|
||||
let obj_endianness = match triple
|
||||
.endianness()
|
||||
.map_err(|_| ObjectError::UnknownEndianness)?
|
||||
{
|
||||
Endianness::Little => object::Endianness::Little,
|
||||
Endianness::Big => object::Endianness::Big,
|
||||
};
|
||||
|
||||
Ok(Object::new(
|
||||
obj_binary_format,
|
||||
obj_architecture,
|
||||
@@ -59,7 +74,22 @@ pub fn get_object_for_target(triple: &Triple) -> Result<Object, ObjectError> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Emit data in into an object
|
||||
/// Write data into an existing object.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// # use wasmer_compiler::Triple;
|
||||
/// # use wasmer_object::ObjectError;
|
||||
/// use wasmer_object::{get_object_for_target, emit_data};
|
||||
///
|
||||
/// # fn emit_data_into_object(triple: &Triple) -> Result<(), ObjectError> {
|
||||
/// let mut object = get_object_for_target(&triple)?;
|
||||
/// emit_data(&mut object, b"WASMER_METADATA", &b"Hello, World!"[..])?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn emit_data(obj: &mut Object, name: &[u8], data: &[u8]) -> Result<(), ObjectError> {
|
||||
let symbol_id = obj.add_symbol(Symbol {
|
||||
name: name.to_vec(),
|
||||
@@ -73,14 +103,34 @@ pub fn emit_data(obj: &mut Object, name: &[u8], data: &[u8]) -> Result<(), Objec
|
||||
});
|
||||
let section_id = obj.section_id(StandardSection::Data);
|
||||
obj.add_symbol_data(symbol_id, section_id, &data, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit the compilation into an object
|
||||
/// Emit the compilation result into an existing object.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```rust
|
||||
/// # use wasmer_compiler::{Compilation, Triple};
|
||||
/// # use wasmer_object::{CompilationNamer, ObjectError};
|
||||
/// use wasmer_object::{get_object_for_target, emit_compilation};
|
||||
///
|
||||
/// # fn emit_compilation_into_object(
|
||||
/// # triple: &Triple,
|
||||
/// # compilation: Compilation,
|
||||
/// # compilation_namer: impl CompilationNamer,
|
||||
/// # ) -> Result<(), ObjectError> {
|
||||
/// let mut object = get_object_for_target(&triple)?;
|
||||
/// emit_compilation(&mut object, compilation, &compilation_namer, &triple)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn emit_compilation(
|
||||
obj: &mut Object,
|
||||
compilation: Compilation,
|
||||
namer: &impl CompilationNamer,
|
||||
triple: &Triple,
|
||||
) -> Result<(), ObjectError> {
|
||||
let function_bodies = compilation.get_function_bodies();
|
||||
let function_relocations = compilation.get_relocations();
|
||||
@@ -166,22 +216,39 @@ pub fn emit_compilation(
|
||||
}
|
||||
|
||||
// Add relocations (function and sections)
|
||||
let (relocation_size, relocation_kind, relocation_encoding) = match triple.architecture {
|
||||
Architecture::X86_64 => (
|
||||
32,
|
||||
RelocationKind::PltRelative,
|
||||
RelocationEncoding::X86Branch,
|
||||
),
|
||||
architecture => {
|
||||
return Err(ObjectError::UnsupportedArchitecture(
|
||||
architecture.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut all_relocations = Vec::new();
|
||||
|
||||
for (function_local_index, relocations) in function_relocations.into_iter() {
|
||||
let function_name = namer.get_function_name(&function_local_index);
|
||||
let symbol_id = obj.symbol_id(function_name.as_bytes()).unwrap();
|
||||
all_relocations.push((symbol_id, relocations))
|
||||
}
|
||||
|
||||
for (section_index, relocations) in custom_section_relocations.into_iter() {
|
||||
let section_name = namer.get_section_name(§ion_index);
|
||||
let symbol_id = obj.symbol_id(section_name.as_bytes()).unwrap();
|
||||
all_relocations.push((symbol_id, relocations))
|
||||
}
|
||||
|
||||
for (symbol_id, relocations) in all_relocations.into_iter() {
|
||||
let (_symbol_id, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap();
|
||||
let section_id = obj.section_id(StandardSection::Text);
|
||||
|
||||
for r in relocations {
|
||||
let relocation_address = section_offset + r.offset as u64;
|
||||
|
||||
match r.reloc_target {
|
||||
RelocationTarget::LocalFunc(index) => {
|
||||
let target_name = namer.get_function_name(&index);
|
||||
@@ -190,12 +257,9 @@ pub fn emit_compilation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
size: relocation_size,
|
||||
kind: relocation_kind,
|
||||
encoding: relocation_encoding,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
@@ -221,12 +285,9 @@ pub fn emit_compilation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
size: relocation_size,
|
||||
kind: relocation_kind,
|
||||
encoding: relocation_encoding,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
@@ -240,12 +301,9 @@ pub fn emit_compilation(
|
||||
section_id,
|
||||
Relocation {
|
||||
offset: relocation_address,
|
||||
size: 32, // FIXME for all targets
|
||||
kind: RelocationKind::PltRelative,
|
||||
encoding: RelocationEncoding::X86Branch,
|
||||
// size: 64, // FIXME for all targets
|
||||
// kind: RelocationKind::Absolute,
|
||||
// encoding: RelocationEncoding::Generic,
|
||||
size: relocation_size,
|
||||
kind: relocation_kind,
|
||||
encoding: relocation_encoding,
|
||||
symbol: target_symbol,
|
||||
addend: r.addend,
|
||||
},
|
||||
@@ -258,5 +316,6 @@ pub fn emit_compilation(
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#.*
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#![deny(missing_docs, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
|
||||
38
src/store.rs
38
src/store.rs
@@ -16,27 +16,31 @@ use wasmer_compiler::CompilerConfig;
|
||||
#[derive(Debug, Clone, StructOpt)]
|
||||
/// The compiler options
|
||||
pub struct StoreOptions {
|
||||
/// Use Singlepass compiler
|
||||
/// Use Singlepass compiler.
|
||||
#[structopt(long, conflicts_with_all = &["cranelift", "llvm", "backend"])]
|
||||
singlepass: bool,
|
||||
|
||||
/// Use Cranelift compiler
|
||||
/// Use Cranelift compiler.
|
||||
#[structopt(long, conflicts_with_all = &["singlepass", "llvm", "backend"])]
|
||||
cranelift: bool,
|
||||
|
||||
/// Use LLVM compiler
|
||||
/// Use LLVM compiler.
|
||||
#[structopt(long, conflicts_with_all = &["singlepass", "cranelift", "backend"])]
|
||||
llvm: bool,
|
||||
|
||||
/// Enable compiler internal verification.
|
||||
#[structopt(long)]
|
||||
enable_verifier: bool,
|
||||
|
||||
/// LLVM debug directory, where IR and object files will be written to.
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
llvm_debug_dir: Option<PathBuf>,
|
||||
|
||||
/// Use JIT Engine
|
||||
/// Use JIT Engine.
|
||||
#[structopt(long, conflicts_with_all = &["native"])]
|
||||
jit: bool,
|
||||
|
||||
/// Use Native Engine
|
||||
/// Use Native Engine.
|
||||
#[structopt(long, conflicts_with_all = &["jit"])]
|
||||
native: bool,
|
||||
|
||||
@@ -46,8 +50,6 @@ pub struct StoreOptions {
|
||||
|
||||
#[structopt(flatten)]
|
||||
features: WasmFeatures,
|
||||
// #[structopt(flatten)]
|
||||
// llvm_options: LLVMCLIOptions,
|
||||
}
|
||||
|
||||
/// The compiler used for the store
|
||||
@@ -153,8 +155,7 @@ impl StoreOptions {
|
||||
}
|
||||
|
||||
/// Get the Target architecture
|
||||
pub fn get_features(&self) -> Result<Features> {
|
||||
let mut features = Features::default();
|
||||
pub fn get_features(&self, mut features: Features) -> Result<Features> {
|
||||
if self.features.threads || self.features.all {
|
||||
features.threads(true);
|
||||
}
|
||||
@@ -181,12 +182,18 @@ impl StoreOptions {
|
||||
CompilerType::Headless => bail!("The headless engine can't be chosen"),
|
||||
#[cfg(feature = "singlepass")]
|
||||
CompilerType::Singlepass => {
|
||||
let config = wasmer_compiler_singlepass::Singlepass::new();
|
||||
let mut config = wasmer_compiler_singlepass::Singlepass::new();
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "cranelift")]
|
||||
CompilerType::Cranelift => {
|
||||
let config = wasmer_compiler_cranelift::Cranelift::new();
|
||||
let mut config = wasmer_compiler_cranelift::Cranelift::new();
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "llvm")]
|
||||
@@ -276,6 +283,9 @@ impl StoreOptions {
|
||||
if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
|
||||
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
|
||||
}
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm",)))]
|
||||
@@ -284,6 +294,8 @@ impl StoreOptions {
|
||||
compiler.to_string()
|
||||
),
|
||||
};
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Ok((compiler_config, compiler))
|
||||
}
|
||||
|
||||
@@ -310,7 +322,7 @@ impl StoreOptions {
|
||||
mut compiler_config: Box<dyn CompilerConfig>,
|
||||
) -> Result<(Box<dyn Engine + Send + Sync>, EngineType)> {
|
||||
let engine_type = self.get_engine()?;
|
||||
let features = self.get_features()?;
|
||||
let features = self.get_features(compiler_config.default_features_for_target(&target))?;
|
||||
let engine: Box<dyn Engine + Send + Sync> = match engine_type {
|
||||
#[cfg(feature = "jit")]
|
||||
EngineType::JIT => Box::new(
|
||||
@@ -378,7 +390,7 @@ impl StoreOptions {
|
||||
/// Get the store (headless engine)
|
||||
pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> {
|
||||
let (engine, engine_type) = self.get_engine_headless()?;
|
||||
let store = Store::new(&engine);
|
||||
let store = Store::new(&*engine);
|
||||
Ok((store, engine_type, CompilerType::Headless))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
use wasmer::{CompilerConfig, Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
|
||||
use wasmer::{Features, FunctionMiddlewareGenerator, Store, Triple, Tunables};
|
||||
use wasmer_compiler::CompilerConfig;
|
||||
use wasmer_engine::Engine;
|
||||
use wasmer_engine_jit::JIT;
|
||||
|
||||
@@ -13,14 +14,17 @@ pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
|
||||
} else if #[cfg(feature = "test-cranelift")] {
|
||||
let mut compiler = wasmer_compiler_cranelift::Cranelift::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-llvm")] {
|
||||
let mut compiler = wasmer_compiler_llvm::LLVM::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-singlepass")] {
|
||||
let mut compiler = wasmer_compiler_singlepass::Singlepass::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else {
|
||||
compile_error!("No compiler chosen for the tests")
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![deny(unstable_features)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
|
||||
@@ -192,3 +192,30 @@
|
||||
(assert_return (invoke "nan-canonicalization-f64-func-call-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000))
|
||||
(assert_return (invoke "nan-canonicalization-f64-func-call-indirect" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000001))
|
||||
(assert_return (invoke "nan-canonicalization-f64-func-call-indirect-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000))
|
||||
|
||||
;; Test canonicalization is done before branch in `else` operator.
|
||||
(module
|
||||
(func (;0;)
|
||||
(local f64)
|
||||
i32.const 1
|
||||
if (result f64)
|
||||
local.get 0
|
||||
f64.const 0x1p+0 (;=1;)
|
||||
f64.add
|
||||
else
|
||||
f64.const 0x0p+0 (;=0;)
|
||||
end
|
||||
return
|
||||
)
|
||||
)
|
||||
|
||||
;; Test canonicalization is done before branch in `return` operator.
|
||||
(module
|
||||
(func (;0;) (result f64)
|
||||
(local f64)
|
||||
f64.const 0x0p+0
|
||||
local.get 0
|
||||
f64.mul
|
||||
return
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user