Merge branch 'master' into add-c-api

This commit is contained in:
Mark McCaskey
2020-05-05 16:22:37 -07:00
committed by GitHub
141 changed files with 3460 additions and 2296 deletions

View File

@@ -1,29 +1,24 @@
# Wasmer Attributions
Wasmer is a community effort and makes use of code from various other
projects. Listed below are notable sections of code that are licensed
projects ❤️.
Listed below are notable sections of code that are licensed
from other projects and the relevant license of those projects.
These are the projects that were used as inspiration and/or that we are using code from:
These are the projects that were used as inspiration and/or that we are using code from.
Each of the subcrates we have have an `Aknowledgements` section with more details.
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework
- [wasmtime](https://github.com/CraneStation/wasmtime):
For their wast, environ, jit, runtime and debugging with
the `__jit_debug_register_code` function
in Rust, the structure of using Cranelift with the GDB JIT
interface including implementation details regarding the structure
of generating debug information for each function with Cranelift
(for example, the sorting of the extended basic blocks before
processing the instructions), and the API for transforming DWARF
see [wasm-debug's attribution file](https://github.com/wasmerio/wasm-debug/blob/master/ATTRIBUTIONS.md)
for more information.
Projects:
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility - [LICENSE](#emscripten)
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime - [LICENSE](#nebulet)
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility
- [The WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for implementation details of WebAssembly and spectests
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework - [LICENSE](#wavm)
- [wasmtime](https://github.com/CraneStation/wasmtime): for their API and internal documentation, as well as some internal implementations - [LICENSE](#wasmtime)
- [WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for the spectests implementation
Please let us know if you believe there is an error or omission in
this list and we will do our best to correct it.
🙏 Please let us know if you believe there is an error or omission in
this list and we will correct.
## Licenses

175
Cargo.lock generated
View File

@@ -124,6 +124,12 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytesize"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010"
[[package]]
name = "cast"
version = "0.2.3"
@@ -370,6 +376,18 @@ dependencies = [
"generic-array",
]
[[package]]
name = "distance"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240"
[[package]]
name = "downcast-rs"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
[[package]]
name = "either"
version = "1.5.3"
@@ -398,32 +416,6 @@ dependencies = [
"syn",
]
[[package]]
name = "env_logger"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "erased-serde"
version = "0.3.11"
@@ -433,16 +425,6 @@ dependencies = [
"serde",
]
[[package]]
name = "file-per-thread-logger"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9"
dependencies = [
"env_logger 0.6.2",
"log",
]
[[package]]
name = "fnv"
version = "1.0.6"
@@ -557,15 +539,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -861,16 +834,6 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger 0.7.1",
"log",
]
[[package]]
name = "proc-macro-error"
version = "1.0.2"
@@ -906,12 +869,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.3"
@@ -1291,9 +1248,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "strsim"
@@ -1379,15 +1336,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]]
name = "test-generator"
version = "0.1.0"
@@ -1455,12 +1403,33 @@ dependencies = [
]
[[package]]
name = "toml"
version = "0.5.6"
name = "tracing"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
checksum = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab"
dependencies = [
"serde",
"cfg-if",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbad39da2f9af1cae3016339ad7f2c7a9e870f12e8fd04c4fd7ef35b30c0d2b"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715"
dependencies = [
"lazy_static",
]
[[package]]
@@ -1543,11 +1512,10 @@ version = "0.16.2"
dependencies = [
"anyhow",
"cfg-if",
"file-per-thread-logger",
"indexmap",
"libc",
"pretty_env_logger",
"rayon",
"more-asserts",
"target-lexicon",
"tempfile",
"thiserror",
"wasm-common",
@@ -1555,7 +1523,8 @@ dependencies = [
"wasmer-compiler-cranelift",
"wasmer-compiler-llvm",
"wasmer-compiler-singlepass",
"wasmer-jit",
"wasmer-engine",
"wasmer-engine-jit",
"wasmer-runtime",
"wat",
"winapi",
@@ -1567,6 +1536,8 @@ version = "0.16.2"
dependencies = [
"anyhow",
"atty",
"bytesize",
"distance",
"glob",
"rustc_version",
"structopt",
@@ -1578,7 +1549,8 @@ dependencies = [
"wasmer-compiler-cranelift",
"wasmer-compiler-llvm",
"wasmer-compiler-singlepass",
"wasmer-jit",
"wasmer-engine",
"wasmer-engine-jit",
"wasmer-wasi",
"wasmer-wasi-experimental-io-devices",
"wasmer-wast",
@@ -1619,11 +1591,11 @@ dependencies = [
"cranelift-frontend",
"hashbrown",
"lazy_static",
"log",
"more-asserts",
"rayon",
"serde",
"target-lexicon",
"tracing",
"wasm-common",
"wasmer-compiler",
"wasmer-runtime",
@@ -1656,7 +1628,6 @@ name = "wasmer-compiler-singlepass"
version = "0.16.2"
dependencies = [
"hashbrown",
"log",
"more-asserts",
"rayon",
"serde",
@@ -1666,7 +1637,28 @@ dependencies = [
]
[[package]]
name = "wasmer-jit"
name = "wasmer-engine"
version = "0.16.2"
dependencies = [
"backtrace",
"bincode",
"downcast-rs",
"lazy_static",
"more-asserts",
"region",
"rustc-demangle",
"serde",
"serde_bytes",
"target-lexicon",
"thiserror",
"wasm-common",
"wasmer-compiler",
"wasmer-runtime",
"winapi",
]
[[package]]
name = "wasmer-engine-jit"
version = "0.16.2"
dependencies = [
"backtrace",
@@ -1676,10 +1668,12 @@ dependencies = [
"region",
"rustc-demangle",
"serde",
"serde_bytes",
"target-lexicon",
"thiserror",
"wasm-common",
"wasmer-compiler",
"wasmer-engine",
"wasmer-runtime",
"winapi",
]
@@ -1721,10 +1715,10 @@ dependencies = [
"generational-arena",
"getrandom",
"libc",
"log",
"serde",
"thiserror",
"time",
"tracing",
"typetag",
"wasmer",
"winapi",
@@ -1734,10 +1728,10 @@ dependencies = [
name = "wasmer-wasi-experimental-io-devices"
version = "0.16.2"
dependencies = [
"log",
"minifb",
"ref_thread_local",
"serde",
"tracing",
"typetag",
"wasmer-wasi",
]
@@ -1792,15 +1786,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@@ -20,19 +20,24 @@ publish = false
autoexamples = false
[dependencies]
wasmer = { path = "lib/api" }
wasmer-compiler = { path = "lib/compiler" }
wasmer-compiler-cranelift = { path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { path = "lib/compiler-llvm", optional = true }
wasmer-jit = { path = "lib/jit" }
wasmer-wasi = { path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { path = "tests/lib/wast", optional = true }
wasmer-cache = { path = "lib/cache", optional = true }
wasmer = { version = "0.16.2", path = "lib/api", default-features = false }
wasmer-compiler = { version = "0.16.2", path = "lib/compiler" }
wasmer-compiler-cranelift = { version = "0.16.2", path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { version = "0.16.2", path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "0.16.2", path = "lib/compiler-llvm", optional = true }
wasmer-engine = { version = "0.16.2", path = "lib/engine" }
wasmer-engine-jit = { version = "0.16.2", path = "lib/engine-jit", optional = true }
wasmer-wasi = { version = "0.16.2", path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { version = "0.16.2", path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { version = "0.16.2", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "0.16.2", path = "lib/cache", optional = true }
atty = "0.2"
anyhow = "1.0.28"
structopt = { version = "0.3", features = ["suggestions"] }
# For the function names autosuggestion
distance = "0.4"
# For the inspect subcommand
bytesize = "1.0.0"
[workspace]
members = [
@@ -48,34 +53,40 @@ rustc_version = "0.2"
[dev-dependencies]
anyhow = "1.0.28"
test-utils = { path = "tests/lib/test-utils" }
wasmer = { path = "lib/api" }
wasmer-compiler = { path = "lib/compiler" }
wasmer-jit = { path = "lib/jit" }
wasmer-wast = { path = "tests/lib/wast" }
[features]
# Don't add the backend features in default, please add them on the Makefile
# Don't add the compiler features in default, please add them on the Makefile
# since we might want to autoconfigure them depending on the availability on the host.
# default = ["wasi"]
default = ["wast", "wasi", "experimental-io-devices", "compiler-cranelift", "cache"]
default = ["wat", "wast", "wasi", "cranelift", "cache", "jit"]
engine = []
jit = [
"wasmer-engine-jit",
"engine",
]
cache = ["wasmer-cache"]
wast = ["wasmer-wast"]
wasi = ["wasmer-wasi"]
wat = ["wasmer/wat"]
compiler = ["wasmer-engine-jit/compiler",]
experimental-io-devices = [
"wasmer-wasi-experimental-io-devices"
"wasmer-wasi-experimental-io-devices",
"wasi"
]
compiler-singlepass = [
"test-utils/compiler-singlepass",
"wasmer/compiler-singlepass",
singlepass = [
"wasmer-compiler-singlepass",
"test-utils/singlepass",
"compiler",
]
compiler-cranelift = [
"test-utils/compiler-cranelift",
"wasmer/compiler-cranelift",
cranelift = [
"wasmer-compiler-cranelift",
"test-utils/cranelift",
"compiler",
]
compiler-llvm = [
"test-utils/compiler-llvm",
"wasmer/compiler-llvm",
llvm = [
"wasmer-compiler-llvm",
"test-utils/llvm",
"compiler",
]
# [profile.release]
# lto = "fat"

View File

@@ -10,7 +10,7 @@ use std::path::PathBuf;
use std::process::Command;
use test_generator::{
build_ignores_from_textfile, test_directory, test_directory_module, wast_processor,
with_backends, with_test_module, Testsuite,
with_features, with_test_module, Testsuite,
};
fn is_truthy_env(name: &str) -> bool {
@@ -34,9 +34,15 @@ fn main() -> anyhow::Result<()> {
};
let backends = vec!["singlepass", "cranelift", "llvm"];
with_backends(&mut spectests, &backends, |mut spectests| {
with_features(&mut spectests, &backends, |mut spectests| {
with_test_module(&mut spectests, "spec", |spectests| {
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
test_directory_module(
spectests,
"tests/wast/spec/proposals/multi-value",
wast_processor,
)?;
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
Ok(())
})?;
with_test_module(&mut spectests, "wasmer", |spectests| {

View File

@@ -14,12 +14,15 @@ wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "0.16.
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "0.16.2", optional = true }
wasmer-compiler-llvm = { path = "../compiler-llvm", version = "0.16.2", optional = true }
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
wasmer-jit = { path = "../jit", version = "0.16.2" }
wasmer-engine = { path = "../engine", version = "0.16.2" }
wasmer-engine-jit = { path = "../engine-jit", version = "0.16.2", optional = true }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
indexmap = { version = "1.3.2", features = ["serde-1"] }
cfg-if = "0.1.10"
wat = { version = "1.0.15", optional = true }
thiserror = "1.0.16"
more-asserts = "0.2.1"
target-lexicon = { version = "0.10.0", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.8"
@@ -27,9 +30,6 @@ winapi = "0.3.8"
[dev-dependencies]
# for the binary wasmer.rs
libc = "0.2"
pretty_env_logger = "0.4.0"
rayon = "1.3.0"
file-per-thread-logger = "0.1.2"
wat = "1.0.15"
tempfile = "3.1"
anyhow = "1.0.28"
@@ -38,22 +38,19 @@ anyhow = "1.0.28"
maintenance = { status = "actively-developed" }
[features]
default = ["wat", "default-compiler-cranelift"]
compiler-singlepass = [
"wasmer-compiler-singlepass"
default = ["wat", "cranelift", "jit"]
compiler = ["wasmer-engine-jit/compiler"]
engine = []
jit = ["wasmer-engine-jit"]
singlepass = [
"wasmer-compiler-singlepass",
"compiler",
]
compiler-cranelift = [
"wasmer-compiler-cranelift"
cranelift = [
"wasmer-compiler-cranelift",
"compiler",
]
compiler-llvm = [
"wasmer-compiler-llvm"
]
default-compiler-singlepass = [
"compiler-singlepass"
]
default-compiler-cranelift = [
"compiler-cranelift"
]
default-compiler-llvm = [
"compiler-llvm"
llvm = [
"wasmer-compiler-llvm",
"compiler",
]

View File

@@ -1,5 +1,72 @@
## Wasmer API
# Wasmer [![Build Status](https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square)](https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE)
The `wasmer` crate is an embedding API of the `wasmer` WebAssembly runtime.
This is intended to be used in Rust projects and provides a high-level API of
working with WebAssembly modules.
[`Wasmer`](https://wasmer.io/) is the most popular [WebAssembly](https://webassembly.org/)
runtime for Rust. It supports JIT (Just in Time) and AOT (Ahead of time)
compilation as well as mulitple compiler implementations.
It's designed to be safe and secure, and runnable in any kind of environment.
## Usage
Add to your `Cargo.toml`
```
[dependencies]
wasmer = "0.16.2"
```
```rust
use wasmer::{Instance, Function, Value, imports, DefaultStore as _};
fn main() -> anyhow::Result<()> {
let module_wat = r#"
(module
(type $t0 (func (param i32) (result i32)))
(func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
get_local $p0
i32.const 1
i32.add))
"#;
let module = Module::new(&module_wat);
// The module doesn't import anything, so we create an empty import object.
let import_object = imports! {};
let instance = Instance::new(module, &import_object)?;
let add_one = instance.exports.get_function("add_one")?;
let result = add_one.call([Value::I32(42)])?;
assert_eq!(result[0], Value::I32(43));
Ok(())
}
```
## Features
Wasmer is not only fast, but also designed to be highly customizable:
* **Pluggable Engines**: do you have a fancy `dlopen` implementation? This is for you!
* **Pluggable Compilers**: you want to emit code with DynASM or other compiler? We got you!
* **Headless mode**: that means that no compilers will be required
to run a `serialized` Module (via `Module::deserialize()`).
* **Cross-compilation**: You can pre-compile a module and serialize it
to then run it in other platform (via `Module::serialize()`).
## Config flags
Wasmer has the following configuration flags:
* `wat` (enabled by default): It allows to read WebAssembly files in their text format.
*This feature is normally used only in development environments*
* Compilers (mutually exclusive):
- `singlepass`: it will use `wasmer-compiler-singlepass` as the default
compiler.
- `cranelift`: it will use `wasmer-compiler-cranelift` as the default
compiler.
- `llvm`: it will use `wasmer-compiler-llvm` as the default
compiler.
> Note: if you want to use multiple compilers at the same time, it's also possible!
> You will need to import them directly via each of the compiler crates.
---
Made with ❤️ by the Wasmer team, for the community

View File

@@ -1,4 +1,4 @@
use crate::externals::{Extern, Func, Global, Memory, Table};
use crate::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace;
use indexmap::IndexMap;
use std::iter::FromIterator;
@@ -16,9 +16,11 @@ use wasmer_runtime::Export;
///
/// // This results with an error: `ExportError::IncompatibleType`.
/// let missing_import: &Global = my_instance.exports.get("func")?;
/// let missing_import = my_instance.exports.get_global("func")?;
///
/// // This results with an error: `ExportError::Missing`.
/// let missing_import: &Func = my_instance.exports.get("unknown")?;
/// let missing_import: &Function = my_instance.exports.get("unknown")?;
/// let missing_import = my_instance.exports.get_function("unknown")?;
/// ```
#[derive(Error, Debug)]
pub enum ExportError {
@@ -103,7 +105,7 @@ impl Exports {
}
/// Get an export as a `Func`.
pub fn get_func(&self, name: &str) -> Result<&Func, ExportError> {
pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
self.get(name)
}

View File

@@ -4,10 +4,11 @@ use crate::store::{Store, StoreObject};
use crate::types::{Val, ValAnyFunc};
use crate::Mutability;
use crate::RuntimeError;
use crate::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
use crate::{ExternType, FunctionType, GlobalType, MemoryType, TableType, ValType};
use std::cmp::max;
use std::slice;
use wasm_common::{Bytes, HostFunction, Pages, ValueType, WasmTypeList, WithEnv, WithoutEnv};
use wasmer_engine::Engine as _;
use wasmer_runtime::{
wasmer_call_trampoline, Export, ExportFunction, ExportGlobal, ExportMemory, ExportTable,
LinearMemory, Table as RuntimeTable, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody,
@@ -16,7 +17,7 @@ use wasmer_runtime::{
#[derive(Clone)]
pub enum Extern {
Func(Func),
Function(Function),
Global(Global),
Table(Table),
Memory(Memory),
@@ -25,7 +26,7 @@ pub enum Extern {
impl Extern {
pub fn ty(&self) -> ExternType {
match self {
Extern::Func(ft) => ExternType::Func(ft.ty().clone()),
Extern::Function(ft) => ExternType::Function(ft.ty().clone()),
Extern::Memory(ft) => ExternType::Memory(ft.ty().clone()),
Extern::Table(tt) => ExternType::Table(tt.ty().clone()),
Extern::Global(gt) => ExternType::Global(gt.ty().clone()),
@@ -34,7 +35,7 @@ impl Extern {
pub(crate) fn from_export(store: &Store, export: Export) -> Extern {
match export {
Export::Function(f) => Extern::Func(Func::from_export(store, f)),
Export::Function(f) => Extern::Function(Function::from_export(store, f)),
Export::Memory(m) => Extern::Memory(Memory::from_export(store, m)),
Export::Global(g) => Extern::Global(Global::from_export(store, g)),
Export::Table(t) => Extern::Table(Table::from_export(store, t)),
@@ -45,7 +46,7 @@ impl Extern {
impl<'a> Exportable<'a> for Extern {
fn to_export(&self) -> Export {
match self {
Extern::Func(f) => f.to_export(),
Extern::Function(f) => f.to_export(),
Extern::Global(g) => g.to_export(),
Extern::Memory(m) => m.to_export(),
Extern::Table(t) => t.to_export(),
@@ -61,7 +62,7 @@ impl<'a> Exportable<'a> for Extern {
impl StoreObject for Extern {
fn comes_from_same_store(&self, store: &Store) -> bool {
let my_store = match self {
Extern::Func(f) => f.store(),
Extern::Function(f) => f.store(),
Extern::Global(g) => g.store(),
Extern::Memory(m) => m.store(),
Extern::Table(t) => t.store(),
@@ -70,9 +71,9 @@ impl StoreObject for Extern {
}
}
impl From<Func> for Extern {
fn from(r: Func) -> Self {
Extern::Func(r)
impl From<Function> for Extern {
fn from(r: Function) -> Self {
Extern::Function(r)
}
}
@@ -113,15 +114,10 @@ impl Global {
Self::from_type(store, GlobalType::new(val.ty(), Mutability::Var), val).unwrap()
}
pub fn from_type(store: &Store, ty: GlobalType, val: Val) -> Result<Global, RuntimeError> {
fn from_type(store: &Store, ty: GlobalType, val: Val) -> Result<Global, RuntimeError> {
if !val.comes_from_same_store(store) {
return Err(RuntimeError::new("cross-`Store` globals are not supported"));
}
if val.ty() != ty.ty.clone() {
return Err(RuntimeError::new(
"value provided does not match the type of this global",
));
}
let mut definition = VMGlobalDefinition::new();
unsafe {
match val {
@@ -231,7 +227,9 @@ fn set_table_item(
impl Table {
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
let item = init.into_checked_anyfunc(store)?;
let table = store.engine().create_table(&ty);
let tunables = store.engine().tunables();
let table_plan = tunables.table_plan(ty);
let table = tunables.create_table(table_plan).unwrap();
let definition = table.vmtable();
for i in 0..definition.current_elements {
@@ -345,7 +343,10 @@ pub struct Memory {
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Memory {
let memory = store.engine().create_memory(&ty).unwrap();
let tunables = store.engine().tunables();
let memory_plan = tunables.memory_plan(ty);
let memory = tunables.create_memory(memory_plan).unwrap();
let definition = memory.vmmemory();
Memory {
@@ -492,7 +493,7 @@ pub enum InnerFunc {
/// A WebAssembly `function`.
#[derive(Clone, PartialEq)]
pub struct Func {
pub struct Function {
store: Store,
// If the Function is owned by the Store, not the instance
inner: InnerFunc,
@@ -500,12 +501,12 @@ pub struct Func {
exported: ExportFunction,
}
impl Func {
impl Function {
/// Creates a new `Func` with the given parameters.
///
/// * `store` - a global cache to store information in
/// * `func` - the function.
pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Func
pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Self
where
F: HostFunction<Args, Rets, WithoutEnv, Env>,
Args: WasmTypeList,
@@ -517,7 +518,7 @@ impl Func {
let vmctx = (func.env().unwrap_or(std::ptr::null_mut()) as *mut _) as *mut VMContext;
let func_type = func.ty();
let signature = store.engine().register_signature(&func_type);
Func {
Self {
store: store.clone(),
owned_by_store: true,
inner: InnerFunc::Host(HostFunc {
@@ -536,7 +537,7 @@ impl Func {
/// * `store` - a global cache to store information in.
/// * `env` - the function environment.
/// * `func` - the function.
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: &mut Env, func: F) -> Func
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: &mut Env, func: F) -> Self
where
F: HostFunction<Args, Rets, WithEnv, Env>,
Args: WasmTypeList,
@@ -548,7 +549,7 @@ impl Func {
let vmctx = (func.env().unwrap_or(std::ptr::null_mut()) as *mut _) as *mut VMContext;
let func_type = func.ty();
let signature = store.engine().register_signature(&func_type);
Func {
Self {
store: store.clone(),
owned_by_store: true,
inner: InnerFunc::Host(HostFunc {
@@ -563,7 +564,7 @@ impl Func {
}
/// Returns the underlying type of this function.
pub fn ty(&self) -> FuncType {
pub fn ty(&self) -> FunctionType {
self.store
.engine()
.lookup_signature(self.exported.signature)
@@ -644,7 +645,7 @@ impl Func {
self.ty().results().len()
}
/// Call the [`Func`] function.
/// Call the [`Function`] function.
///
/// Depending on where the Function is defined, it will call it.
/// 1. If the function is defined inside a WebAssembly, it will call the trampoline
@@ -662,9 +663,9 @@ impl Func {
Ok(results.into_boxed_slice())
}
pub(crate) fn from_export(store: &Store, wasmer_export: ExportFunction) -> Func {
pub(crate) fn from_export(store: &Store, wasmer_export: ExportFunction) -> Self {
let trampoline = store.engine().trampoline(wasmer_export.signature).unwrap();
Func {
Self {
store: store.clone(),
owned_by_store: false,
inner: InnerFunc::Wasm(WasmFunc { trampoline }),
@@ -681,19 +682,19 @@ impl Func {
}
}
impl<'a> Exportable<'a> for Func {
impl<'a> Exportable<'a> for Function {
fn to_export(&self) -> Export {
self.exported.clone().into()
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Func(func) => Ok(func),
Extern::Function(func) => Ok(func),
_ => Err(ExportError::IncompatibleType),
}
}
}
impl std::fmt::Debug for Func {
impl std::fmt::Debug for Function {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}

View File

@@ -8,7 +8,7 @@ use std::{
ffi::c_void,
sync::{Arc, Mutex},
};
use wasmer_jit::Resolver;
use wasmer_engine::Resolver;
use wasmer_runtime::Export;
/// The `LikeNamespace` trait represents objects that act as a namespace for imports.
@@ -194,13 +194,15 @@ impl Extend<((String, String), Export)> for ImportObject {
///
///
/// # Usage:
/// ```ignore
/// use wasmer::{imports, func};
/// ```
/// # use wasmer::{Func, Store};
/// # let store = Store::default();
/// use wasmer::imports;
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(foo),
/// },
/// "foo" => Function::new(&store, foo)
/// }
/// };
///
/// fn foo(n: i32) -> i32 {
@@ -224,24 +226,8 @@ macro_rules! imports {
}};
}
/// Generate an [`Namespace`] easily with the `namespace!` macro.
///
/// [`Namespace`]: struct.Namespace.html
///
///
/// # Usage:
/// ```ignore
/// use wasmer::{namespace, func};
///
/// let env = namespace! {
/// "foo" => func!(foo),
/// };
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
($( $imp_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!({ $( $imp_name => $import_item, )* })

View File

@@ -3,7 +3,7 @@ use crate::externals::Extern;
use crate::module::Module;
use crate::store::Store;
use crate::InstantiationError;
use wasmer_jit::Resolver;
use wasmer_engine::Resolver;
use wasmer_runtime::InstanceHandle;
/// A WebAssembly Instance is a stateful, executable
@@ -25,7 +25,8 @@ impl Instance {
/// set of imports resolved by the [`Resolver`].
///
/// The resolver can be anything that implements the [`Resolver`] trait,
/// so you can plug custom resolution for the imports.
/// so you can plug custom resolution for the imports, if you wish not
/// to use [`ImportObject`].
///
/// The [`ImportObject`] is the easiest way to provide imports to the instance.
///
@@ -53,9 +54,7 @@ impl Instance {
pub fn new(module: &Module, resolver: &dyn Resolver) -> Result<Instance, InstantiationError> {
let store = module.store();
let handle = store
.engine()
.instantiate(module.compiled_module(), resolver)?;
let handle = module.instantiate(resolver)?;
let exports = module
.exports()

View File

@@ -9,35 +9,60 @@ mod memory_view;
mod module;
mod ptr;
mod store;
mod tunables;
mod types;
pub use crate::exports::{ExportError, Exportable, Exports};
pub use crate::externals::{Extern, Func, Global, Memory, Table};
pub use crate::externals::{Extern, Function, Global, Memory, Table};
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::instance::Instance;
pub use crate::memory_view::MemoryView;
pub use crate::module::Module;
pub use crate::ptr::{Array, Item, WasmPtr};
pub use crate::store::{Engine, Store, StoreObject};
pub use crate::store::{Store, StoreObject};
pub use crate::tunables::Tunables;
pub use crate::types::{
AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType,
AnyRef, ExportType, ExternType, FunctionType, GlobalType, HostInfo, HostRef, ImportType,
MemoryType, Mutability, TableType, Val, ValType,
};
pub use wasm_common::{Bytes, Pages, ValueType, WasmExternType, WasmTypeList};
#[cfg(feature = "compiler")]
pub use wasmer_compiler::CompilerConfig;
pub use wasmer_jit::{
DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
pub use wasmer_compiler::{Features, Target};
pub use wasmer_engine::{
DeserializeError, Engine, InstantiationError, LinkError, RuntimeError, SerializeError,
};
#[cfg(feature = "compiler-singlepass")]
// The compilers are mutually exclusive
#[cfg(any(
all(feature = "llvm", any(feature = "cranelift", feature = "singlepass")),
all(feature = "cranelift", feature = "singlepass")
))]
compile_error!(
r#"The `singlepass`, `cranelift` and `llvm` features are mutually exclusive.
If you wish to use more than one compiler, you can simply import it from it's own crate. Eg.:
```
use wasmer::{Store, Engine};
use wasmer_compiler_singlepass::SinglepassConfig;
let engine = Engine::new(SinglepassConfig::default());
let store = Store::new_config(&engine);
```"#
);
#[cfg(feature = "singlepass")]
pub use wasmer_compiler_singlepass::SinglepassConfig;
#[cfg(feature = "compiler-cranelift")]
#[cfg(feature = "cranelift")]
pub use wasmer_compiler_cranelift::CraneliftConfig;
#[cfg(feature = "compiler-llvm")]
#[cfg(feature = "llvm")]
pub use wasmer_compiler_llvm::LLVMConfig;
#[cfg(feature = "jit")]
pub use wasmer_engine_jit::JITEngine;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -1,11 +1,14 @@
use crate::store::Store;
use crate::types::{ExportType, ImportType};
use crate::InstantiationError;
use std::borrow::Borrow;
use std::io;
use std::path::Path;
use std::sync::Arc;
use thiserror::Error;
use wasmer_compiler::{CompileError, WasmError};
use wasmer_jit::{CompiledModule, DeserializeError, SerializeError};
use wasmer_engine::{CompiledModule, DeserializeError, Engine, Resolver, SerializeError};
use wasmer_runtime::{ExportsIterator, ImportsIterator, InstanceHandle, Module as ModuleInfo};
#[derive(Error, Debug)]
pub enum IoCompileError {
@@ -23,12 +26,17 @@ pub enum IoCompileError {
///
/// ## Cloning a module
///
/// Cloning a moudle is cheap: it does a shallow copy of the compiled
/// Cloning a module is cheap: it does a shallow copy of the compiled
/// contents rather than a deep copy.
#[derive(Clone)]
pub struct Module {
store: Store,
compiled: Arc<CompiledModule>,
compiled: Arc<dyn CompiledModule>,
#[cfg(feature = "wat")]
#[doc(hidden)]
// If the module was compiled from a wat file.
pub from_wat: bool,
}
impl Module {
@@ -67,28 +75,23 @@ impl Module {
/// let module = Module::new(&store, bytes)?;
/// ```
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> {
// We try to parse it with WAT: it will be a no-op on
// wasm files.
if bytes.as_ref().starts_with(b"\0asm") {
return Module::from_binary(store, bytes.as_ref());
}
#[cfg(feature = "wat")]
{
let bytes = wat::parse_bytes(bytes.as_ref())
.map_err(|e| CompileError::Wasm(WasmError::Generic(format!("{}", e))))?;
// We can assume the binary is valid WebAssembly if returned
// without errors from from wat. However, by skipping validation
// we are not checking if it's using WebAssembly features not enabled
// in the store.
// This is a good tradeoff, as we can assume the "wat" feature is only
// going to be used in development mode.
return unsafe { Module::from_binary_unchecked(store, bytes.as_ref()) };
let might_be_wat = !bytes.as_ref().starts_with(b"\0asm");
let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}",
e
)))
})?;
let mut module = Module::from_binary(store, bytes.as_ref())?;
// We can assume it was a wat file if is not "wasm" looking
// and the previous step succeeded.
module.from_wat = might_be_wat;
return Ok(module);
}
Err(CompileError::Validate(
"The module is not a valid WebAssembly file.".to_string(),
))
Module::from_binary(store, bytes.as_ref())
}
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Module, IoCompileError> {
@@ -153,7 +156,7 @@ impl Module {
/// let serialized = module.serialize()?;
/// ```
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
self.store.engine().serialize(self.compiled_module())
self.store.engine().serialize(self.compiled.borrow())
}
/// Deserializes a a serialized Module binary into a `Module`.
@@ -181,15 +184,33 @@ impl Module {
Ok(Self::from_compiled_module(store, compiled))
}
fn from_compiled_module(store: &Store, compiled: CompiledModule) -> Self {
fn from_compiled_module(store: &Store, compiled: Arc<CompiledModule>) -> Self {
Module {
store: store.clone(),
compiled: Arc::new(compiled),
compiled,
#[cfg(feature = "wat")]
from_wat: false,
}
}
pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.compiled
pub(crate) fn instantiate(
&self,
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
unsafe {
let instance_handle = self
.store
.engine()
.instantiate(self.compiled.borrow(), resolver)?;
// After the instance handle is created, we need to initialize
// the data, call the start function and so. However, if any
// of this steps traps, we still need to keep the instance alive
// as some of the Instance elements may have placed in other
// instance tables.
self.compiled.finish_instantiation(&instance_handle)?;
Ok(instance_handle)
}
}
/// Returns the name of the current module.
@@ -223,7 +244,7 @@ impl Module {
/// ```
pub fn set_name(&mut self, name: &str) {
let compiled = Arc::get_mut(&mut self.compiled).unwrap();
Arc::get_mut(compiled.module_mut()).unwrap().name = Some(name.to_string());
compiled.module_mut().name = Some(name.to_string());
}
/// Returns an iterator over the imported types in the Module.
@@ -246,8 +267,8 @@ impl Module {
/// import.ty();
/// }
/// ```
pub fn imports<'a>(&'a self) -> impl Iterator<Item = ImportType> + 'a {
self.compiled.module_ref().imports()
pub fn imports<'a>(&'a self) -> ImportsIterator<impl Iterator<Item = ImportType> + 'a> {
self.compiled.module().imports()
}
/// Returns an iterator over the exported types in the Module.
@@ -269,11 +290,21 @@ impl Module {
/// export.ty();
/// }
/// ```
pub fn exports<'a>(&'a self) -> impl Iterator<Item = ExportType> + 'a {
self.compiled.module_ref().exports()
pub fn exports<'a>(&'a self) -> ExportsIterator<impl Iterator<Item = ExportType> + 'a> {
self.compiled.module().exports()
}
pub fn store(&self) -> &Store {
&self.store
}
// The ABI of the ModuleInfo is very unstable, we refactor it very often.
// This funciton is public because in some cases it can be useful to get some
// extra information from the module.
//
// However, the usage is highly discouraged.
#[doc(hidden)]
pub fn info(&self) -> &ModuleInfo {
&self.compiled.module()
}
}

View File

@@ -1,21 +0,0 @@
use wasmer_jit::Resolver;
use std::iter::FromIterator;
pub struct IndexResolver {
externs: Vec<Extern>,
}
impl Resolver for IndexResolver {
fn resolve(&self, index: u32, _module: &str, _name: &str) -> Option<Export> {
self.externs.get(index as usize).map(|extern_| extern_.to_export())
}
}
impl FromIterator<Extern> for IndexResolver {
fn from_iter<I: IntoIterator<Item = Extern>>(iter: I) -> Self {
let mut externs = Vec::new();
for extern_ in iter {
externs.push(extern_);
}
IndexResolver { externs }
}
}

View File

@@ -1,61 +1,26 @@
use crate::tunables::Tunables;
use std::sync::Arc;
#[cfg(feature = "compiler")]
use wasmer_compiler::CompilerConfig;
use wasmer_jit::JITEngine;
pub type Engine = JITEngine;
use wasmer_engine::Engine;
#[derive(Clone)]
pub struct Store {
engine: Arc<Engine>,
engine: Arc<dyn Engine>,
}
impl Store {
pub fn new(engine: &Engine) -> Store {
Store {
engine: Arc::new(engine.clone()),
}
pub fn new(engine: Arc<dyn Engine>) -> Store {
Store { engine }
}
pub fn engine(&self) -> &Engine {
pub fn engine(&self) -> &Arc<dyn Engine> {
&self.engine
}
pub fn same(a: &Store, b: &Store) -> bool {
Arc::ptr_eq(&a.engine, &b.engine)
}
#[cfg(any(
feature = "default-compiler-singlepass",
feature = "default-compiler-cranelift",
feature = "default-compiler-llvm",
))]
pub fn default_compiler_config() -> impl CompilerConfig {
#[cfg(any(
all(
feature = "default-compiler-llvm",
any(
feature = "default-compiler-cranelift",
feature = "default-compiler-singlepass"
)
),
all(
feature = "default-compiler-cranelift",
feature = "default-compiler-singlepass"
)
))]
compile_error!(
"The `default-compiler-X` features are mutually exclusive. Please choose just one"
);
#[cfg(feature = "default-compiler-cranelift")]
return wasmer_compiler_cranelift::CraneliftConfig::default();
#[cfg(feature = "default-compiler-llvm")]
return wasmer_compiler_llvm::LLVMConfig::default();
#[cfg(feature = "default-compiler-singlepass")]
return wasmer_compiler_singlepass::SinglepassConfig::default();
}
}
impl PartialEq for Store {
@@ -64,15 +29,36 @@ impl PartialEq for Store {
}
}
// We only implement default if we have assigned a default compiler
#[cfg(any(
feature = "default-compiler-singlepass",
feature = "default-compiler-cranelift",
feature = "default-compiler-llvm",
))]
// We only implement default if we have assigned a default compiler and engine
#[cfg(all(feature = "compiler", feature = "engine"))]
impl Default for Store {
fn default() -> Store {
Store::new(&Engine::new(&Self::default_compiler_config()))
// 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.
#[allow(unreachable_code)]
fn get_config() -> Box<dyn CompilerConfig> {
#[cfg(feature = "cranelift")]
return Box::new(wasmer_compiler_cranelift::CraneliftConfig::default());
#[cfg(feature = "llvm")]
return Box::new(wasmer_compiler_llvm::LLVMConfig::default());
#[cfg(feature = "singlepass")]
return Box::new(wasmer_compiler_singlepass::SinglepassConfig::default());
}
#[allow(unreachable_code)]
fn get_engine(config: Box<dyn CompilerConfig>) -> Arc<dyn Engine> {
let tunables = Tunables::for_target(config.target().triple());
#[cfg(feature = "jit")]
return Arc::new(wasmer_engine_jit::JITEngine::new(&config, tunables));
}
let config = get_config();
let engine = get_engine(config);
Store::new(config)
}
}

View File

@@ -2,6 +2,8 @@ use more_asserts::assert_ge;
use std::cmp::min;
use target_lexicon::{OperatingSystem, PointerWidth, Triple, HOST};
use wasm_common::{MemoryType, Pages, TableType};
use wasmer_engine::Tunables as BaseTunables;
use wasmer_runtime::{LinearMemory, Table};
use wasmer_runtime::{MemoryPlan, MemoryStyle, TablePlan, TableStyle};
/// Tunable parameters for WebAssembly compilation.
@@ -54,9 +56,11 @@ impl Tunables {
dynamic_memory_offset_guard_size,
}
}
}
impl BaseTunables for Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType`
pub fn memory_plan(&self, memory: MemoryType) -> MemoryPlan {
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan {
// A heap with a maximum that doesn't exceed the static memory bound specified by the
// tunables make it static.
//
@@ -81,16 +85,26 @@ impl Tunables {
}
/// Get a `TablePlan` for the provided `TableType`
pub fn table_plan(&self, table: TableType) -> TablePlan {
fn table_plan(&self, table: TableType) -> TablePlan {
TablePlan {
table,
style: TableStyle::CallerChecksSignature,
}
}
/// Create a memory given a memory type
fn create_memory(&self, plan: MemoryPlan) -> Result<LinearMemory, String> {
LinearMemory::new(&plan)
}
/// Create a memory given a memory type
fn create_table(&self, plan: TablePlan) -> Result<Table, String> {
Table::new(&plan)
}
}
impl Default for Tunables {
fn default() -> Self {
Tunables::for_target(&HOST)
Self::for_target(&HOST)
}
}

View File

@@ -1,14 +1,14 @@
use crate::externals::Func;
use crate::externals::Function;
use crate::store::{Store, StoreObject};
use crate::RuntimeError;
use std::ptr;
use wasm_common::Value;
pub use wasm_common::{
AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType,
AnyRef, ExportType, ExternType, FunctionType, GlobalType, HostInfo, HostRef, ImportType,
MemoryType, Mutability, TableType, Type as ValType,
};
pub type Val = Value<Func>;
pub type Val = Value<Function>;
impl StoreObject for Val {
fn comes_from_same_store(&self, store: &Store) -> bool {
@@ -21,8 +21,8 @@ impl StoreObject for Val {
}
}
impl From<Func> for Val {
fn from(val: Func) -> Val {
impl From<Function> for Val {
fn from(val: Function) -> Val {
Val::FuncRef(val)
}
}
@@ -59,14 +59,14 @@ impl ValAnyFunc for Val {
fn from_checked_anyfunc(item: wasmer_runtime::VMCallerCheckedAnyfunc, store: &Store) -> Val {
if item.type_index == wasmer_runtime::VMSharedSignatureIndex::default() {
Val::AnyRef(AnyRef::Null);
return Val::AnyRef(AnyRef::Null);
}
let export = wasmer_runtime::ExportFunction {
address: item.func_ptr,
signature: item.type_index,
vmctx: item.vmctx,
};
let f = Func::from_export(store, export);
let f = Function::from_export(store, export);
Val::FuncRef(f)
}
}

View File

@@ -3,7 +3,7 @@ name = "wasmer-cache"
version = "0.16.2"
description = "Cache system for WebAssembly"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
categories = ["wasm"]
readme = "README.md"
@@ -11,7 +11,7 @@ keywords = ["webassembly", "wasm", "cache"]
edition = "2018"
[dependencies]
wasmer = { path = "../api", version = "0.16.2" }
wasmer = { path = "../api", version = "0.16.2", default-features = false }
memmap = "0.7"
hex = "0.4"
thiserror = "1"

4
lib/cache/README.md vendored Normal file
View File

@@ -0,0 +1,4 @@
# Wasmer Cache
The `wasmer-cache` crate allows easily caching WebAssembly
modules in your system.

View File

@@ -13,10 +13,10 @@ edition = "2018"
[dependencies]
hashbrown = { version = "0.7.2", optional = true }
log = { version = "0.4.8", default-features = false }
tracing = "0.1"
cranelift-codegen = { version = "0.62.0", default-features = false }
cranelift-frontend = { version = "0.62.0", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
rayon = "1.3.0"

View File

@@ -2,3 +2,9 @@
This is the `wasmer-compiler-cranelift` crate, which contains a
compiler implementation based on Cranelift.
### Acknowledgments
This project borrowed some of the function lowering from [cranelift-wasm](https://crates.io/crates/cranelift-wasm).
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

View File

@@ -3,145 +3,31 @@
use crate::address_map::get_function_address_map;
use crate::config::CraneliftConfig;
use crate::func_environ::{get_func_name, FuncEnvironment};
use crate::sink::{RelocSink, TrapSink};
use crate::trampoline::{make_wasm_trampoline, FunctionBuilderContext};
use crate::translator::{
irlibcall_to_libcall, irreloc_to_relocationkind, signature_to_cranelift_ir, FuncTranslator,
compiled_function_unwind_info, irlibcall_to_libcall, irreloc_to_relocationkind,
signature_to_cranelift_ir, transform_jump_table, FuncTranslator,
};
use crate::unwind::compiled_function_unwind_info;
use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, isa, Context};
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
Features, FuncIndex, FuncType, LocalFuncIndex, MemoryIndex, SignatureIndex, TableIndex,
Features, FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, SignatureIndex,
TableIndex,
};
use wasmer_compiler::CompileError;
use wasmer_compiler::FunctionBodyData;
use wasmer_compiler::{
Compilation, CompiledFunction, Compiler, JumpTable, SourceLoc, TrapInformation,
Compilation, CompiledFunction, CompiledFunctionFrameInfo, Compiler, FunctionBody,
FunctionBodyData, SourceLoc, TrapInformation,
};
use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target};
use wasmer_compiler::{Relocation, RelocationTarget};
use wasmer_runtime::TrapCode;
use wasmer_runtime::{MemoryPlan, Module, TablePlan};
/// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink {
/// Current function index.
func_index: FuncIndex,
/// Relocations recorded for the function.
pub func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(irlibcall_to_libcall(libcall))
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
kind: irreloc_to_relocationkind(reloc),
reloc_target,
offset,
addend,
});
}
fn reloc_constant(
&mut self,
_code_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_constant_offset: ir::ConstantOffset,
) {
// Do nothing for now: cranelift emits constant data after the function code and also emits
// function code with correct relative offsets to the constant data.
}
fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
self.func_relocs.push(Relocation {
kind: irreloc_to_relocationkind(reloc),
reloc_target: RelocationTarget::JumpTable(self.func_index, JumpTable::new(jt.index())),
offset,
addend: 0,
});
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
pub fn new(func_index: FuncIndex) -> Self {
Self {
func_index,
func_relocs: Vec::new(),
}
}
}
struct TrapSink {
pub traps: Vec<TrapInformation>,
}
impl TrapSink {
fn new() -> Self {
Self { traps: Vec::new() }
}
}
impl binemit::TrapSink for TrapSink {
fn trap(
&mut self,
code_offset: binemit::CodeOffset,
source_loc: ir::SourceLoc,
trap_code: ir::TrapCode,
) {
self.traps.push(TrapInformation {
code_offset,
source_loc: SourceLoc::new(source_loc.bits()),
// TODO: Translate properly environment Trapcode into cranelift IR
trap_code: translate_ir_trapcode(trap_code),
});
}
}
/// Translates the Cranelift IR TrapCode into generic Trap Code
fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
match trap {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
ir::TrapCode::OutOfBounds => TrapCode::OutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature,
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt,
ir::TrapCode::User(user_code) => TrapCode::User(user_code),
}
}
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
/// optimizing it and then translating to assembly.
pub struct CraneliftCompiler {
@@ -187,7 +73,7 @@ impl Compiler for CraneliftCompiler {
&self,
module: &Module,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'_>>,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
@@ -201,7 +87,7 @@ impl Compiler for CraneliftCompiler {
let functions = function_body_inputs
.into_iter()
.collect::<Vec<(LocalFuncIndex, &FunctionBodyData<'_>)>>()
.collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
.par_iter()
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
let func_index = module.func_index(*i);
@@ -229,7 +115,7 @@ impl Compiler for CraneliftCompiler {
)?;
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new(func_index);
let mut reloc_sink = RelocSink::new(module, func_index);
let mut trap_sink = TrapSink::new();
let mut stackmap_sink = binemit::NullStackmapSink {};
context
@@ -252,25 +138,31 @@ impl Compiler for CraneliftCompiler {
let func_jt_offsets = transform_jump_table(context.func.jt_offsets);
Ok(CompiledFunction {
body: code_buf,
body: FunctionBody {
body: code_buf,
unwind_info,
},
jt_offsets: func_jt_offsets,
unwind_info,
address_map,
relocations: reloc_sink.func_relocs,
traps: trap_sink.traps,
frame_info: CompiledFunctionFrameInfo {
address_map,
traps: trap_sink.traps,
},
})
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.collect::<PrimaryMap<LocalFuncIndex, _>>();
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
Ok(Compilation::new(functions))
let custom_sections = PrimaryMap::new();
Ok(Compilation::new(functions, custom_sections))
}
fn compile_wasm_trampolines(
&self,
signatures: &[FuncType],
) -> Result<Vec<CompiledFunction>, CompileError> {
signatures: &[FunctionType],
) -> Result<Vec<FunctionBody>, CompileError> {
signatures
.par_iter()
.map_init(FunctionBuilderContext::new, |mut cx, sig| {
@@ -279,16 +171,3 @@ impl Compiler for CraneliftCompiler {
.collect::<Result<Vec<_>, CompileError>>()
}
}
/// Transforms Cranelift JumpTable's into runtime JumpTables
pub fn transform_jump_table(
jt_offsets: SecondaryMap<ir::JumpTable, u32>,
) -> SecondaryMap<JumpTable, u32> {
let mut func_jt_offsets = SecondaryMap::with_capacity(jt_offsets.capacity());
for (key, value) in jt_offsets.iter() {
let new_key = JumpTable::new(key.index());
func_jt_offsets[new_key] = *value;
}
func_jt_offsets
}

View File

@@ -47,13 +47,13 @@ pub struct CraneliftConfig {
impl CraneliftConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
pub fn new(features: Features, target: Target) -> Self {
Self {
enable_nan_canonicalization: false,
enable_verifier: false,
opt_level: OptLevel::Speed,
features: Default::default(),
target: Default::default(),
features,
target,
}
}
@@ -172,23 +172,12 @@ impl CompilerConfig for CraneliftConfig {
&self.features
}
/// Gets the WebAssembly features, mutable
fn features_mut(&mut self) -> &mut Features {
&mut self.features
}
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Gets the target that we will use for compiling
/// the WebAssembly module, mutable
fn target_mut(&mut self) -> &mut Target {
&mut self.target
}
/// Transform it into the compiler
fn compiler(&self) -> Box<dyn Compiler> {
Box::new(CraneliftCompiler::new(&self))
@@ -197,6 +186,6 @@ impl CompilerConfig for CraneliftConfig {
impl Default for CraneliftConfig {
fn default() -> Self {
Self::new()
Self::new(Default::default(), Default::default())
}
}

View File

@@ -3,13 +3,13 @@
use cranelift_codegen::ir;
use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFuncIndex;
use wasm_common::LocalFunctionIndex;
/// Value ranges for functions.
pub type ValueLabelsRanges = PrimaryMap<LocalFuncIndex, cranelift_codegen::ValueLabelsRanges>;
pub type ValueLabelsRanges = PrimaryMap<LocalFunctionIndex, cranelift_codegen::ValueLabelsRanges>;
/// Stack slots for functions.
pub type StackSlots = PrimaryMap<LocalFuncIndex, ir::StackSlots>;
pub type StackSlots = PrimaryMap<LocalFunctionIndex, ir::StackSlots>;
/// Memory definition offset in the VMContext structure.
#[derive(Debug, Clone)]

View File

@@ -1,7 +1,7 @@
use cranelift_codegen::isa::CallConv;
use serde::{Deserialize, Serialize};
use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFuncIndex;
use wasm_common::LocalFunctionIndex;
pub use cranelift_codegen::ir::FrameLayoutChange;
@@ -18,4 +18,4 @@ pub struct FrameLayout {
}
/// Functions frame layouts.
pub type FrameLayouts = PrimaryMap<LocalFuncIndex, FrameLayout>;
pub type FrameLayouts = PrimaryMap<LocalFunctionIndex, FrameLayout>;

View File

@@ -11,14 +11,14 @@ use cranelift_codegen::isa::TargetFrontendConfig;
use std::convert::TryFrom;
use wasm_common::entity::EntityRef;
use wasm_common::entity::PrimaryMap;
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasm_common::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::{WasmError, WasmResult};
use wasmer_runtime::VMBuiltinFunctionIndex;
use wasmer_runtime::VMOffsets;
use wasmer_runtime::{MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle};
/// Compute an `ir::ExternalName` for a given wasm function index.
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
pub fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
ir::ExternalName::user(0, func_index.as_u32())
}
@@ -731,7 +731,7 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro
fn make_direct_func(
&mut self,
func: &mut ir::Function,
index: FuncIndex,
index: FunctionIndex,
) -> WasmResult<ir::FuncRef> {
let sigidx = self.module.functions[index];
let signature = func.import_signature(self.signatures[sigidx].clone());
@@ -823,7 +823,7 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro
fn translate_call(
&mut self,
mut pos: FuncCursor<'_>,
callee_index: FuncIndex,
callee_index: FunctionIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {

View File

@@ -50,16 +50,15 @@ mod compiler;
mod config;
mod debug;
mod func_environ;
mod sink;
mod trampoline;
mod translator;
mod unwind;
pub use crate::compiler::{transform_jump_table, CraneliftCompiler};
pub use crate::compiler::CraneliftCompiler;
pub use crate::config::CraneliftConfig;
pub use crate::debug::{FrameLayout, FrameLayoutChange, FrameLayouts};
pub use crate::debug::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges};
pub use crate::trampoline::make_wasm_trampoline;
pub use crate::unwind::compiled_function_unwind_info;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -0,0 +1,142 @@
//! Support for compiling with Cranelift.
use crate::translator::{
irlibcall_to_libcall, irreloc_to_relocationkind, signature_to_cranelift_ir,
transform_jump_table, FuncTranslator,
};
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName};
use wasm_common::entity::EntityRef;
use wasm_common::{FunctionIndex, LocalFunctionIndex};
use wasmer_compiler::{JumpTable, Relocation, RelocationTarget, SourceLoc, TrapInformation};
use wasmer_runtime::{Module, TrapCode};
/// Implementation of a relocation sink that just saves all the information for later
pub(crate) struct RelocSink<'a> {
module: &'a Module,
/// Current function index.
local_func_index: LocalFunctionIndex,
/// Relocations recorded for the function.
pub func_relocs: Vec<Relocation>,
}
impl<'a> binemit::RelocSink for RelocSink<'a> {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
RelocationTarget::LocalFunc(
self.module
.local_func_index(FunctionIndex::from_u32(index))
.expect("The provided function should be local"),
)
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(irlibcall_to_libcall(libcall))
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
kind: irreloc_to_relocationkind(reloc),
reloc_target,
offset,
addend,
});
}
fn reloc_constant(
&mut self,
_code_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_constant_offset: ir::ConstantOffset,
) {
// Do nothing for now: cranelift emits constant data after the function code and also emits
// function code with correct relative offsets to the constant data.
}
fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
self.func_relocs.push(Relocation {
kind: irreloc_to_relocationkind(reloc),
reloc_target: RelocationTarget::JumpTable(
self.local_func_index,
JumpTable::new(jt.index()),
),
offset,
addend: 0,
});
}
}
impl<'a> RelocSink<'a> {
/// Return a new `RelocSink` instance.
pub fn new(module: &'a Module, func_index: FunctionIndex) -> Self {
let local_func_index = module
.local_func_index(func_index)
.expect("The provided function should be local");
Self {
module,
local_func_index,
func_relocs: Vec::new(),
}
}
}
pub(crate) struct TrapSink {
pub traps: Vec<TrapInformation>,
}
impl TrapSink {
pub fn new() -> Self {
Self { traps: Vec::new() }
}
}
impl binemit::TrapSink for TrapSink {
fn trap(
&mut self,
code_offset: binemit::CodeOffset,
source_loc: ir::SourceLoc,
trap_code: ir::TrapCode,
) {
self.traps.push(TrapInformation {
code_offset,
source_loc: SourceLoc::new(source_loc.bits()),
// TODO: Translate properly environment Trapcode into cranelift IR
trap_code: translate_ir_trapcode(trap_code),
});
}
}
/// Translates the Cranelift IR TrapCode into generic Trap Code
fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
match trap {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
ir::TrapCode::OutOfBounds => TrapCode::OutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature,
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt,
ir::TrapCode::User(user_code) => unimplemented!("User trap code not supported"),
// ir::TrapCode::User(user_code) => TrapCode::User(user_code),
}
}

View File

@@ -6,25 +6,25 @@
//! my_func.call([1, 2])
//! ```
use super::binemit::TrampolineRelocSink;
use crate::translator::signature_to_cranelift_ir;
use crate::{compiled_function_unwind_info, transform_jump_table};
use crate::translator::{
compiled_function_unwind_info, signature_to_cranelift_ir, transform_jump_table,
};
use cranelift_codegen::ir::InstBuilder;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use wasm_common::FuncType;
use wasmer_compiler::FunctionAddressMap;
use wasmer_compiler::{CompileError, CompiledFunction};
use wasm_common::FunctionType;
use wasmer_compiler::{CompileError, CompiledFunction, CompiledFunctionFrameInfo, FunctionBody};
/// Create a trampoline for invoking a WebAssembly function.
pub fn make_wasm_trampoline(
isa: &dyn TargetIsa,
fn_builder_ctx: &mut FunctionBuilderContext,
func_type: &FuncType,
func_type: &FunctionType,
value_size: usize,
) -> Result<CompiledFunction, CompileError> {
) -> Result<FunctionBody, CompileError> {
let pointer_type = isa.pointer_type();
let frontend_config = isa.frontend_config();
let signature = signature_to_cranelift_ir(func_type, &frontend_config);
@@ -121,15 +121,10 @@ pub fn make_wasm_trampoline(
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, Some(isa), error)))?;
let unwind_info = compiled_function_unwind_info(isa, &context);
// let address_map = get_function_address_map(&context, input, code_buf.len(), isa);
let address_map = FunctionAddressMap::default(); // get_function_address_map(&context, input, code_buf.len(), isa);
Ok(CompiledFunction {
Ok(FunctionBody {
body: code_buf,
jt_offsets: transform_jump_table(context.func.jt_offsets),
unwind_info,
address_map,
relocations: vec![],
traps: vec![],
// jt_offsets: transform_jump_table(context.func.jt_offsets),
})
}

View File

@@ -37,7 +37,7 @@ use cranelift_codegen::ir::{
use cranelift_codegen::packed_option::ReservedValue;
use cranelift_frontend::{FunctionBuilder, Variable};
use std::vec::Vec;
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasm_common::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::wasmparser::{MemoryImmediate, Operator};
use wasmer_compiler::{to_wasm_error, WasmResult};
use wasmer_compiler::{wasm_unsupported, ModuleTranslationState};
@@ -508,7 +508,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
let call = environ.translate_call(
builder.cursor(),
FuncIndex::from_u32(*function_index),
FunctionIndex::from_u32(*function_index),
fref,
args,
)?;

View File

@@ -9,7 +9,7 @@ use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_frontend::FunctionBuilder;
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasm_common::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::wasmparser::Operator;
use wasmer_compiler::WasmResult;
@@ -146,7 +146,7 @@ pub trait FuncEnvironment: TargetEnvironment {
fn make_direct_func(
&mut self,
func: &mut ir::Function,
index: FuncIndex,
index: FunctionIndex,
) -> WasmResult<ir::FuncRef>;
/// Translate a `call_indirect` WebAssembly instruction at `pos`.
@@ -180,7 +180,7 @@ pub trait FuncEnvironment: TargetEnvironment {
fn translate_call(
&mut self,
mut pos: FuncCursor,
_callee_index: FuncIndex,
_callee_index: FunctionIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {

View File

@@ -10,7 +10,7 @@ use super::func_environ::{FuncEnvironment, GlobalVariable};
use crate::{HashMap, Occupied, Vacant};
use cranelift_codegen::ir::{self, Block, Inst, Value};
use std::vec::Vec;
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasm_common::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::WasmResult;
/// Information about the presence of an associated `else` for an `if`, or the
@@ -213,7 +213,7 @@ pub struct FuncTranslationState {
// Imported and local functions that have been created by
// `FuncEnvironment::make_direct_func()`.
// Stores both the function reference and the number of WebAssembly arguments
functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
functions: HashMap<FunctionIndex, (ir::FuncRef, usize)>,
}
// Public methods that are exposed to non-`wasmer_compiler` API consumers.
@@ -490,7 +490,7 @@ impl FuncTranslationState {
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::FuncRef, usize)> {
let index = FuncIndex::from_u32(index);
let index = FunctionIndex::from_u32(index);
match self.functions.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {

View File

@@ -12,7 +12,7 @@ use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use log::info;
use tracing::info;
use wasmer_compiler::wasmparser::{self, BinaryReader};
use wasmer_compiler::{to_wasm_error, wasm_unsupported, ModuleTranslationState, WasmResult};

View File

@@ -5,11 +5,13 @@ mod func_environ;
mod func_state;
mod func_translator;
mod translation_utils;
mod unwind;
pub use self::func_environ::{FuncEnvironment, GlobalVariable, ReturnMode, TargetEnvironment};
pub use self::func_state::FuncTranslationState;
pub use self::func_translator::FuncTranslator;
pub use self::translation_utils::{
get_vmctx_value_label, irlibcall_to_libcall, irreloc_to_relocationkind,
signature_to_cranelift_ir, type_to_irtype,
signature_to_cranelift_ir, transform_jump_table, type_to_irtype,
};
pub use self::unwind::compiled_function_unwind_info;

View File

@@ -7,16 +7,17 @@ use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::{self, AbiParam};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_frontend::FunctionBuilder;
use wasm_common::{FuncType, Type};
use wasm_common::entity::{EntityRef, SecondaryMap};
use wasm_common::{FunctionType, Type};
use wasmer_compiler::wasm_unsupported;
use wasmer_compiler::wasmparser;
use wasmer_compiler::RelocationKind;
use wasmer_compiler::{JumpTable, RelocationKind};
use wasmer_compiler::{WasmError, WasmResult};
use wasmer_runtime::libcalls::LibCall;
/// Helper function translate a Funciton signature into Cranelift Ir
pub fn signature_to_cranelift_ir(
signature: &FuncType,
signature: &FunctionType,
target_config: &TargetFrontendConfig,
) -> ir::Signature {
let mut sig = ir::Signature::new(target_config.default_call_conv);
@@ -147,3 +148,16 @@ pub fn get_vmctx_value_label() -> ir::ValueLabel {
const VMCTX_LABEL: u32 = 0xffff_fffe;
ir::ValueLabel::from_u32(VMCTX_LABEL)
}
/// Transforms Cranelift JumpTable's into runtime JumpTables
pub fn transform_jump_table(
jt_offsets: SecondaryMap<ir::JumpTable, u32>,
) -> SecondaryMap<JumpTable, u32> {
let mut func_jt_offsets = SecondaryMap::with_capacity(jt_offsets.capacity());
for (key, value) in jt_offsets.iter() {
let new_key = JumpTable::new(key.index());
func_jt_offsets[new_key] = *value;
}
func_jt_offsets
}

View File

@@ -8,7 +8,7 @@ use wasmer_compiler::{CompiledFunctionUnwindInfo, FDERelocEntry};
pub fn compiled_function_unwind_info(
isa: &dyn isa::TargetIsa,
context: &Context,
) -> CompiledFunctionUnwindInfo {
) -> Option<CompiledFunctionUnwindInfo> {
use cranelift_codegen::binemit::{FrameUnwindKind, FrameUnwindOffset, FrameUnwindSink, Reloc};
use cranelift_codegen::isa::CallConv;
@@ -45,7 +45,7 @@ pub fn compiled_function_unwind_info(
CallConv::SystemV | CallConv::Fast | CallConv::Cold => FrameUnwindKind::Libunwind,
CallConv::WindowsFastcall => FrameUnwindKind::Fastcall,
_ => {
return CompiledFunctionUnwindInfo::None;
return None;
}
};
@@ -54,11 +54,13 @@ pub fn compiled_function_unwind_info(
let Sink(data, offset, relocs) = sink;
if data.is_empty() {
return CompiledFunctionUnwindInfo::None;
return None;
}
match kind {
FrameUnwindKind::Fastcall => CompiledFunctionUnwindInfo::Windows(data),
FrameUnwindKind::Libunwind => CompiledFunctionUnwindInfo::FrameLayout(data, offset, relocs),
FrameUnwindKind::Fastcall => Some(CompiledFunctionUnwindInfo::Windows(data)),
FrameUnwindKind::Libunwind => Some(CompiledFunctionUnwindInfo::FrameLayout(
data, offset, relocs,
)),
}
}

View File

@@ -2,7 +2,7 @@
name = "wasmer-compiler-llvm"
version = "0.16.2"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
keywords = ["webassembly", "wasm", "compiler", "llvm"]
categories = ["wasm"]
@@ -10,7 +10,7 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"] }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
target-lexicon = { version = "0.10.0", default-features = false }

View File

@@ -8,15 +8,17 @@ use crate::translator::FuncTranslator;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
use wasm_common::Features;
use wasm_common::{FuncIndex, FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasmer_compiler::FunctionBodyData;
use wasmer_compiler::TrapInformation;
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler};
use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target};
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_compiler::{
Compilation, CompileError, CompiledFunction, Compiler, CompilerConfig, CustomSection,
CustomSectionProtection, FunctionBody, FunctionBodyData, ModuleTranslationState, Relocation,
RelocationTarget, SectionBody, SectionIndex, Target, TrapInformation,
};
use wasmer_runtime::{MemoryPlan, Module, TablePlan, TrapCode};
use inkwell::targets::{InitializationConfig, Target as InkwellTarget};
use std::collections::HashMap;
use std::sync::{Arc, Mutex}; // TODO: remove
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
@@ -56,12 +58,22 @@ impl Compiler for LLVMCompiler {
&self,
module: &'module Module,
_module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
//let data = Arc::new(Mutex::new(0));
let mut func_names = SecondaryMap::new();
// We're going to "link" the sections by simply appending all compatible
// sections, then building the new relocations.
// TODO: merge constants.
let mut used_readonly_section = false;
let mut readonly_section = CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::default(),
};
for (func_index, _) in &module.functions {
func_names[func_index] = module
.func_names
@@ -69,9 +81,9 @@ impl Compiler for LLVMCompiler {
.cloned()
.unwrap_or_else(|| format!("fn{}", func_index.index()));
}
let functions = function_body_inputs
let mut functions = function_body_inputs
.into_iter()
.collect::<Vec<(LocalFuncIndex, &FunctionBodyData<'_>)>>()
.collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
.par_iter()
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
// TODO: remove (to serialize)
@@ -88,15 +100,49 @@ impl Compiler for LLVMCompiler {
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.collect::<PrimaryMap<LocalFuncIndex, _>>();
.map(|(mut function, local_relocations, custom_sections)| {
/// We collect the sections data
for (local_idx, custom_section) in custom_sections.iter().enumerate() {
let local_idx = local_idx as u32;
// TODO: these section numbers are potentially wrong, if there's
// no Read and only a ReadExecute then ReadExecute is 0.
let (ref mut section, section_num) = match &custom_section.protection {
CustomSectionProtection::Read => {
(&mut readonly_section, SectionIndex::from_u32(0))
}
};
let offset = section.bytes.len() as i64;
section.bytes.append(&custom_section.bytes);
// TODO: we're needlessly rescanning the whole list.
for local_relocation in &local_relocations {
if local_relocation.local_section_index == local_idx {
used_readonly_section = true;
function.relocations.push(Relocation {
kind: local_relocation.kind,
reloc_target: RelocationTarget::CustomSection(section_num),
offset: local_relocation.offset,
addend: local_relocation.addend + offset,
});
}
}
}
Ok(function)
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
Ok(Compilation::new(functions))
let mut custom_sections = PrimaryMap::new();
if used_readonly_section {
custom_sections.push(readonly_section);
}
Ok(Compilation::new(functions, custom_sections))
}
fn compile_wasm_trampolines(
&self,
signatures: &[FuncType],
) -> Result<Vec<CompiledFunction>, CompileError> {
signatures: &[FunctionType],
) -> Result<Vec<FunctionBody>, CompileError> {
signatures
.par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, sig| {

View File

@@ -46,13 +46,13 @@ pub struct LLVMConfig {
impl LLVMConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
pub fn new(features: Features, target: Target) -> Self {
Self {
enable_nan_canonicalization: true,
enable_verifier: false,
opt_level: OptimizationLevel::Aggressive,
features: Default::default(),
target: Default::default(),
features,
target,
}
}
fn reloc_mode(&self) -> RelocMode {
@@ -135,23 +135,12 @@ impl CompilerConfig for LLVMConfig {
&self.features
}
/// Gets the WebAssembly features, mutable
fn features_mut(&mut self) -> &mut Features {
&mut self.features
}
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Gets the target that we will use for compiling
/// the WebAssembly module, mutable
fn target_mut(&mut self) -> &mut Target {
&mut self.target
}
/// Transform it into the compiler
fn compiler(&self) -> Box<dyn Compiler> {
Box::new(LLVMCompiler::new(&self))
@@ -160,6 +149,6 @@ impl CompilerConfig for LLVMConfig {
impl Default for LLVMConfig {
fn default() -> LLVMConfig {
LLVMConfig::new()
Self::new(Default::default(), Default::default())
}
}

View File

@@ -1,23 +1,11 @@
use crate::config::LLVMConfig;
use crate::translator::intrinsics::{func_type_to_llvm, type_to_llvm, Intrinsics};
use crate::translator::intrinsics::{func_type_to_llvm, Intrinsics};
use inkwell::{
context::Context,
module::{Linkage, Module},
passes::PassManager,
targets::FileType,
types::{BasicType, FunctionType},
values::FunctionValue,
AddressSpace,
context::Context, module::Linkage, passes::PassManager, targets::FileType, types::BasicType,
values::FunctionValue, AddressSpace,
};
use wasm_common::entity::SecondaryMap;
use wasm_common::{FuncType, Type};
use wasmer_compiler::{
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler,
FunctionAddressMap, SourceLoc,
};
use std::fs;
use std::io::Write;
use wasm_common::{FunctionType, Type};
use wasmer_compiler::{CompileError, FunctionBody};
pub struct FuncTrampoline {
ctx: Context,
@@ -32,9 +20,9 @@ impl FuncTrampoline {
pub fn trampoline(
&mut self,
ty: &FuncType,
ty: &FunctionType,
config: &LLVMConfig,
) -> Result<CompiledFunction, CompileError> {
) -> Result<FunctionBody, CompileError> {
let mut module = self.ctx.create_module("");
let target_triple = config.target_triple();
let target_machine = config.target_machine();
@@ -107,28 +95,16 @@ impl FuncTrampoline {
// TODO: remove debugging
//dbg!(&bytes);
let address_map = FunctionAddressMap {
instructions: vec![],
start_srcloc: SourceLoc::default(),
end_srcloc: SourceLoc::default(),
body_offset: 0,
body_len: 0, // TODO
};
Ok(CompiledFunction {
address_map,
Ok(FunctionBody {
body: bytes,
jt_offsets: SecondaryMap::new(),
unwind_info: CompiledFunctionUnwindInfo::None,
relocations: vec![],
traps: vec![],
unwind_info: None,
})
}
}
fn generate_trampoline<'ctx>(
trampoline_func: FunctionValue,
func_sig: &FuncType,
func_sig: &FunctionType,
context: &'ctx Context,
intrinsics: &Intrinsics<'ctx>,
) -> Result<(), CompileError> {

View File

@@ -14,9 +14,7 @@ use inkwell::{
passes::PassManager,
//targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple},
targets::FileType,
types::{
BasicType, BasicTypeEnum, FloatMathType, FunctionType, IntType, PointerType, VectorType,
},
types::{BasicType, BasicTypeEnum, FloatMathType, IntType, PointerType, VectorType},
values::{
BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue,
VectorValue,
@@ -30,20 +28,22 @@ use inkwell::{
};
use smallvec::SmallVec;
use std::any::Any;
use std::collections::HashMap;
use crate::config::LLVMConfig;
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
use wasm_common::{
FuncIndex, FuncType, GlobalIndex, LocalFuncIndex, MemoryIndex, SignatureIndex, TableIndex, Type,
FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex,
TableIndex, Type,
};
use wasmer_compiler::wasmparser::{self, BinaryReader, MemoryImmediate, Operator};
use wasmer_compiler::{
to_wasm_error, wasm_unsupported, CompileError, CompiledFunction, WasmResult,
};
use wasmer_compiler::{
CompiledFunctionUnwindInfo, FunctionAddressMap, FunctionBodyData, Relocation, RelocationKind,
RelocationTarget, SourceLoc,
to_wasm_error, wasm_unsupported, Addend, CodeOffset, CompileError, CompiledFunction,
CompiledFunctionFrameInfo, CustomSection, CustomSectionProtection, FunctionAddressMap,
FunctionBody, FunctionBodyData, InstructionAddressMap, Relocation, RelocationKind,
RelocationTarget, SectionBody, SourceLoc, WasmResult,
};
use wasmer_runtime::libcalls::LibCall;
use wasmer_runtime::Module as WasmerCompilerModule;
use wasmer_runtime::{MemoryPlan, MemoryStyle, TablePlan};
@@ -83,6 +83,15 @@ fn const_zero<'ctx>(ty: BasicTypeEnum<'ctx>) -> BasicValueEnum<'ctx> {
}
}
// Relocation against a per-function section.
#[derive(Debug)]
pub struct LocalRelocation {
pub kind: RelocationKind,
pub local_section_index: u32,
pub offset: CodeOffset,
pub addend: Addend,
}
impl FuncTranslator {
pub fn new() -> Self {
Self {
@@ -93,14 +102,14 @@ impl FuncTranslator {
pub fn translate(
&mut self,
wasm_module: &WasmerCompilerModule,
func_index: &LocalFuncIndex,
local_func_index: &LocalFunctionIndex,
function_body: &FunctionBodyData,
config: &LLVMConfig,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: &PrimaryMap<TableIndex, TablePlan>,
func_names: &SecondaryMap<FuncIndex, String>,
) -> Result<CompiledFunction, CompileError> {
let func_index = wasm_module.func_index(*func_index);
func_names: &SecondaryMap<FunctionIndex, String>,
) -> Result<(CompiledFunction, Vec<LocalRelocation>, Vec<CustomSection>), CompileError> {
let func_index = wasm_module.func_index(*local_func_index);
let func_name = func_names.get(func_index).unwrap();
let module_name = match wasm_module.name.as_ref() {
None => format!("<anonymous module> function {}", func_name),
@@ -166,15 +175,17 @@ impl FuncTranslator {
let num_locals = reader.read_local_count().map_err(to_wasm_error)?;
for _ in 0..num_locals {
let mut counter = 0;
let (_count, ty) = reader
let (count, ty) = reader
.read_local_decl(&mut counter)
.map_err(to_wasm_error)?;
let ty = wptype_to_type(ty)?;
let ty = type_to_llvm(&intrinsics, ty);
// TODO: don't interleave allocas and stores.
let alloca = cache_builder.build_alloca(ty, "local");
cache_builder.build_store(alloca, const_zero(ty));
locals.push(alloca);
for _ in 0..count {
let alloca = cache_builder.build_alloca(ty, "local");
cache_builder.build_store(alloca, const_zero(ty));
locals.push(alloca);
}
}
let mut params_locals = params.clone();
@@ -200,6 +211,18 @@ impl FuncTranslator {
fcg.translate_operator(op, wasm_module, pos)?;
}
// TODO: use phf?
let mut libcalls = HashMap::new();
libcalls.insert("vm.exception.trap".to_string(), LibCall::RaiseTrap);
libcalls.insert("truncf".to_string(), LibCall::TruncF32);
libcalls.insert("trunc".to_string(), LibCall::TruncF64);
libcalls.insert("ceilf".to_string(), LibCall::CeilF32);
libcalls.insert("ceil".to_string(), LibCall::CeilF64);
libcalls.insert("floorf".to_string(), LibCall::FloorF32);
libcalls.insert("floor".to_string(), LibCall::FloorF64);
libcalls.insert("nearbyintf".to_string(), LibCall::NearestF32);
libcalls.insert("nearbyint".to_string(), LibCall::NearestF64);
let results = fcg.state.popn_save_extra(wasm_fn_type.results().len())?;
match results.as_slice() {
[] => {
@@ -283,78 +306,163 @@ impl FuncTranslator {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
let object = memory_buffer.create_object_file().map_err(|()| {
CompileError::Codegen("failed to create object file from llvm ir".to_string())
})?;
let mem_buf_slice = memory_buffer.as_slice();
let object = goblin::Object::parse(&mem_buf_slice).unwrap();
let elf = match object {
goblin::Object::Elf(elf) => elf,
_ => unimplemented!("native object file type not supported"),
};
let get_section_name = |section: &goblin::elf::section_header::SectionHeader| {
if section.sh_name == goblin::elf::section_header::SHN_UNDEF as _ {
return None;
}
let name = elf.strtab.get(section.sh_name);
if name.is_none() {
return None;
}
let name = name.unwrap();
if name.is_err() {
return None;
}
Some(name.unwrap())
};
let wasmer_function_idx = elf
.section_headers
.iter()
.enumerate()
.filter(|(_, section)| get_section_name(section) == Some("wasmer_function"))
.map(|(idx, _)| idx)
.take(1)
.collect::<Vec<_>>();
// TODO: handle errors here instead of asserting.
assert!(wasmer_function_idx.len() == 1);
let wasmer_function_idx = wasmer_function_idx[0];
let bytes = elf.section_headers[wasmer_function_idx].file_range();
let bytes = mem_buf_slice[bytes.start..bytes.end].to_vec();
let mut bytes = vec![];
let mut relocations = vec![];
for section in object.get_sections() {
match section.get_name().map(std::ffi::CStr::to_bytes) {
Some(b"wasmer_function") => {
bytes.extend(section.get_contents().to_vec());
}
Some(b".relawasmer_function") | Some(b".relwasmer_function") => {
for rel in section.get_relocations() {
let (i, cs) = rel.get_type();
let kind = match (i, cs.to_bytes()) {
(1, b"R_X86_64_64") => RelocationKind::Abs8,
_ => unimplemented!("unknown relocation kind {:?}", rel.get_type()),
};
let mut reloc_target = None;
let mut target = None;
// TODO: fix inkwell API so we don't have a for loop
// which always breaks after the first entry.
for symbol in rel.get_symbols() {
target = symbol
.get_name()
.map(|cs| String::from(cs.to_str().unwrap()));
break;
}
// TODO: use a lookup table instead of a linear scan
if let Some(ref target) = &target {
if let Some((index, _)) =
func_names.iter().find(|(_, name)| *name == target)
{
reloc_target = Some(RelocationTarget::UserFunc(index));
}
}
if reloc_target.is_none() {
unimplemented!("reference to non-user function {:?}", &target);
}
let reloc_target = reloc_target.unwrap();
let mut local_relocations = vec![];
let mut required_custom_sections = HashMap::new();
let relocation = Relocation {
for (section_index, reloc_section) in &elf.shdr_relocs {
let section_name = get_section_name(&elf.section_headers[*section_index]);
if section_name != Some(".relawasmer_function")
&& section_name != Some(".relwasmer_function")
{
continue;
}
for reloc in reloc_section.iter() {
let kind = match reloc.r_type {
// TODO: these constants are not per-arch, we'll need to
// make the whole match per-arch.
goblin::elf::reloc::R_X86_64_64 => RelocationKind::Abs8,
_ => unimplemented!("unknown relocation {}", reloc.r_type),
};
let offset = reloc.r_offset as u32;
let addend = reloc.r_addend.unwrap_or(0);
let target = reloc.r_sym;
// TODO: error handling
let target = elf.syms.get(target).unwrap();
if target.st_type() == goblin::elf::sym::STT_SECTION {
let len = required_custom_sections.len();
let entry = required_custom_sections.entry(target.st_shndx);
let local_section_index = *entry.or_insert(len) as _;
local_relocations.push(LocalRelocation {
kind,
local_section_index,
offset,
addend,
});
} else if target.st_type() == goblin::elf::sym::STT_FUNC
&& target.st_shndx == wasmer_function_idx
{
// This is a function referencing its own byte stream.
relocations.push(Relocation {
kind,
reloc_target: RelocationTarget::LocalFunc(*local_func_index),
offset,
addend,
});
} else if target.st_type() == goblin::elf::sym::STT_NOTYPE
&& target.st_shndx == goblin::elf::section_header::SHN_UNDEF as _
{
// Not defined in this .o file. Maybe another local function?
let name = target.st_name;
let name = elf.strtab.get(name).unwrap().unwrap();
if let Some((index, _)) =
func_names.iter().find(|(_, func_name)| *func_name == name)
{
let local_index = wasm_module
.local_func_index(index)
.expect("Relocation to non-local function");
relocations.push(Relocation {
kind,
reloc_target,
offset: rel.get_offset() as u32,
// TODO: it appears the LLVM C API has no way to
// retrieve the addend.
addend: 0,
};
relocations.push(relocation);
reloc_target: RelocationTarget::LocalFunc(local_index),
offset,
addend,
});
// Maybe a libcall then?
} else if let Some(libcall) = libcalls.get(name) {
relocations.push(Relocation {
kind,
reloc_target: RelocationTarget::LibCall(*libcall),
offset,
addend,
});
} else {
unimplemented!("reference to unknown symbol {}", name);
}
} else {
unimplemented!("unknown relocation {:?} with target {:?}", reloc, target);
}
_ => {}
};
}
}
let mut custom_sections = vec![];
custom_sections.resize(
required_custom_sections.len(),
CustomSection {
protection: CustomSectionProtection::Read,
bytes: SectionBody::default(),
},
);
for (section_idx, local_section_idx) in required_custom_sections {
let bytes = elf.section_headers[section_idx as usize].file_range();
let bytes = &mem_buf_slice[bytes.start..bytes.end];
custom_sections[local_section_idx].bytes.extend(bytes);
}
let address_map = FunctionAddressMap {
instructions: vec![],
instructions: vec![InstructionAddressMap {
srcloc: SourceLoc::default(),
code_offset: 0,
code_len: bytes.len(),
}],
start_srcloc: SourceLoc::default(),
end_srcloc: SourceLoc::default(),
body_offset: 0,
body_len: 0, // TODO
body_len: bytes.len(),
};
Ok(CompiledFunction {
address_map,
body: bytes,
jt_offsets: SecondaryMap::new(),
unwind_info: CompiledFunctionUnwindInfo::None,
relocations,
traps: vec![],
})
Ok((
CompiledFunction {
body: FunctionBody {
body: bytes,
unwind_info: None,
},
jt_offsets: SecondaryMap::new(),
relocations,
frame_info: CompiledFunctionFrameInfo {
address_map,
traps: vec![],
},
},
local_relocations,
custom_sections,
))
}
}
@@ -1238,8 +1346,8 @@ pub struct LLVMModuleCodeGenerator<'ctx> {
intrinsics: Option<Intrinsics<'ctx>>,
functions: Vec<LLVMFunctionCodeGenerator<'ctx>>,
signatures: Map<SignatureIndex, FunctionType<'ctx>>,
function_signatures: Option<Arc<Map<FuncIndex, SignatureIndex>>>,
llvm_functions: Rc<RefCell<HashMap<FuncIndex, FunctionValue<'ctx>>>>,
function_signatures: Option<Arc<Map<FunctionIndex, SignatureIndex>>>,
llvm_functions: Rc<RefCell<HashMap<FunctionIndex, FunctionValue<'ctx>>>>,
func_import_count: usize,
personality_func: ManuallyDrop<FunctionValue<'ctx>>,
module: ManuallyDrop<Rc<RefCell<Module<'ctx>>>>,
@@ -2077,15 +2185,21 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> {
state.push1_extra(res, info);
}
Operator::Call { function_index } => {
let func_index = FuncIndex::from_u32(function_index);
let func_index = FunctionIndex::from_u32(function_index);
let sigindex = module.functions.get(func_index).unwrap();
let func_type = module.signatures.get(*sigindex).unwrap();
let func_name = module.func_names.get(&func_index).unwrap();
let llvm_func_type = func_type_to_llvm(&self.context, &intrinsics, func_type);
let func =
let func = self.module.get_function(func_name);
// TODO: we could do this by comparing function indices instead
// of going through LLVM APIs and string comparisons.
let func = if func.is_none() {
self.module
.add_function(func_name, llvm_func_type, Some(Linkage::External));
.add_function(func_name, llvm_func_type, Some(Linkage::External))
} else {
func.unwrap()
};
let params: Vec<_> = std::iter::once(ctx.basic())
.chain(
@@ -9012,7 +9126,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Com
),
};
let func_index = FuncIndex::new(self.func_import_count + self.functions.len());
let func_index = FunctionIndex::new(self.func_import_count + self.functions.len());
let sig_id = self.function_signatures.as_ref().unwrap()[func_index];
let func_sig = module_info.read().unwrap().signatures[sig_id].clone();
@@ -9221,7 +9335,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Com
fn feed_function_signatures(
&mut self,
assoc: Map<FuncIndex, SignatureIndex>,
assoc: Map<FunctionIndex, SignatureIndex>,
) -> Result<(), CompileError> {
for (index, sig_id) in &assoc {
if index.index() >= self.func_import_count {

View File

@@ -26,7 +26,7 @@ use wasmer_runtime_core::{
module::ModuleInfo,
structures::TypedIndex,
types::{
GlobalIndex, ImportedFuncIndex, LocalOrImport, MemoryIndex, SignatureIndex, TableIndex, Type,
GlobalIndex, ImportedFunctionIndex, LocalOrImport, MemoryIndex, SignatureIndex, TableIndex, Type,
},
units::Pages,
vm::{Ctx, INTERNALS_SIZE},
@@ -34,11 +34,11 @@ use wasmer_runtime_core::{
*/
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
FuncIndex, FuncType, GlobalIndex, MemoryIndex, Mutability, Pages, SignatureIndex, TableIndex,
Type,
FunctionIndex, FunctionType as FuncType, GlobalIndex, MemoryIndex, Mutability, Pages,
SignatureIndex, TableIndex, Type,
};
use wasmer_runtime::Module as WasmerCompilerModule;
use wasmer_runtime::{MemoryPlan, MemoryStyle, VMOffsets};
use wasmer_runtime::{MemoryPlan, MemoryStyle, TrapCode, VMOffsets};
fn type_to_llvm_ptr<'ctx>(intrinsics: &Intrinsics<'ctx>, ty: Type) -> PointerType<'ctx> {
match ty {
@@ -462,12 +462,26 @@ impl<'ctx> Intrinsics<'ctx> {
f32x4_zero,
f64x2_zero,
trap_unreachable: i32_zero.as_basic_value_enum(),
trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(),
trap_call_indirect_oob: i32_ty.const_int(3, false).as_basic_value_enum(),
trap_memory_oob: i32_ty.const_int(2, false).as_basic_value_enum(),
trap_illegal_arithmetic: i32_ty.const_int(4, false).as_basic_value_enum(),
trap_misaligned_atomic: i32_ty.const_int(5, false).as_basic_value_enum(),
trap_unreachable: i32_ty
.const_int(TrapCode::UnreachableCodeReached as _, false)
.as_basic_value_enum(),
trap_call_indirect_sig: i32_ty
.const_int(TrapCode::BadSignature as _, false)
.as_basic_value_enum(),
trap_call_indirect_oob: i32_ty
.const_int(TrapCode::OutOfBounds as _, false)
.as_basic_value_enum(),
trap_memory_oob: i32_ty
.const_int(TrapCode::OutOfBounds as _, false)
.as_basic_value_enum(),
// TODO: split out div-by-zero and float-to-int
trap_illegal_arithmetic: i32_ty
.const_int(TrapCode::IntegerOverflow as _, false)
.as_basic_value_enum(),
// TODO: add misaligned atomic traps to wasmer runtime
trap_misaligned_atomic: i32_ty
.const_int(TrapCode::Interrupt as _, false)
.as_basic_value_enum(),
// VM intrinsics.
memory_grow_dynamic_local: module.add_function(
@@ -637,7 +651,7 @@ pub struct CtxType<'ctx, 'a> {
cached_tables: HashMap<TableIndex, TableCache<'ctx>>,
cached_sigindices: HashMap<SignatureIndex, IntValue<'ctx>>,
cached_globals: HashMap<GlobalIndex, GlobalCache<'ctx>>,
cached_imported_functions: HashMap<FuncIndex, ImportedFuncCache<'ctx>>,
cached_imported_functions: HashMap<FunctionIndex, ImportedFuncCache<'ctx>>,
offsets: VMOffsets,
}
@@ -1114,7 +1128,7 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> {
pub fn imported_func(
&mut self,
index: FuncIndex,
index: FunctionIndex,
intrinsics: &Intrinsics<'ctx>,
module: &Module<'ctx>,
) -> (PointerValue<'ctx>, PointerValue<'ctx>) {

View File

@@ -13,8 +13,7 @@ edition = "2018"
[dependencies]
hashbrown = { version = "0.7.2", optional = true }
log = { version = "0.4.8", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
rayon = "1.3.0"

View File

@@ -0,0 +1,12 @@
# Wasmer Compiler - Singlepass
This is the `wasmer-compiler-singlepass` crate, which contains a
compiler implementation based on Singlepass.
Singlepass is designed to emit compiled code at linear time, as such
is not prone to JIT bombs and also offers great compilation performance
orders of magnitude faster than `wasmer-compiler-cranelift` and
`wasmer-compiler-llvm`, however with a bit slower runtime speed.
> Note: this crate requires on Rust nightly to be compiled, as depends on
`dynasm-rs` and that crate can only be compiled in Nigthly.

View File

@@ -6,10 +6,10 @@ use crate::config::SinglepassConfig;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::Features;
use wasm_common::{FuncIndex, FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasm_common::{FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_compiler::FunctionBodyData;
use wasmer_compiler::TrapInformation;
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler};
use wasmer_compiler::{Compilation, CompileError, Compiler, FunctionBody};
use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target};
use wasmer_runtime::Module;
use wasmer_runtime::TrapCode;
@@ -52,7 +52,7 @@ impl Compiler for SinglepassCompiler {
&self,
_module: &Module,
_module_translation: &ModuleTranslationState,
_function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'_>>,
_function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
_memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
@@ -67,8 +67,8 @@ impl Compiler for SinglepassCompiler {
fn compile_wasm_trampolines(
&self,
_signatures: &[FuncType],
) -> Result<Vec<CompiledFunction>, CompileError> {
_signatures: &[FunctionType],
) -> Result<Vec<FunctionBody>, CompileError> {
// Note: do not implement this yet
Err(CompileError::Codegen(
"Singlepass trampoline compilation not supported yet".to_owned(),

View File

@@ -19,11 +19,11 @@ pub struct SinglepassConfig {
impl SinglepassConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
pub fn new(features: Features, target: Target) -> Self {
Self {
enable_nan_canonicalization: true,
features: Default::default(),
target: Default::default(),
features,
target,
}
}
}
@@ -34,23 +34,12 @@ impl CompilerConfig for SinglepassConfig {
&self.features
}
/// Gets the WebAssembly features, mutable
fn features_mut(&mut self) -> &mut Features {
&mut self.features
}
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target {
&self.target
}
/// Gets the target that we will use for compiling
/// the WebAssembly module, mutable
fn target_mut(&mut self) -> &mut Target {
&mut self.target
}
/// Transform it into the compiler
fn compiler(&self) -> Box<dyn Compiler> {
Box::new(SinglepassCompiler::new(&self))
@@ -59,6 +48,6 @@ impl CompilerConfig for SinglepassConfig {
impl Default for SinglepassConfig {
fn default() -> SinglepassConfig {
SinglepassConfig::new()
Self::new(Default::default(), Default::default())
}
}

View File

@@ -3,7 +3,7 @@ name = "wasmer-compiler"
version = "0.16.2"
description = "Base compiler abstraction for WebAssembly"
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
categories = ["no-std", "wasm"]
readme = "README.md"
@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
enumset = "1.0.0"
target-lexicon = { version = "0.10.0", default-features = false }
wasmparser = { version = "0.51.4", default-features = false }
wasmparser = { version = "0.51.4", optional = true, default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
hashbrown = { version = "0.7.2", optional = true }
@@ -26,6 +26,10 @@ raw-cpuid = "7.0.3"
[features]
default = ["std", "enable-serde"]
# This feature is for compiler implementors, it enables using `Compiler` and
# `CompilerConfig`, as well as the included wasmparser.
# Disable this feature if you just want a headless engine.
translator = ["wasmparser"]
std = []
core = ["hashbrown"]
enable-serde = ["serde", "serde_bytes"]

View File

@@ -4,3 +4,9 @@ This crate is the base for Compiler implementations.
It performs the translation from a Wasm module into a basic Module,
but leaves the Wasm function bytecode translation to the compiler implementor.
### Acknowledgments
This project borrowed some of the code strucutre from the [cranelift-wasm](https://crates.io/crates/cranelift-wasm), however it's been adapted to not depend on any specific IR and be abstract of any compiler.
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

View File

@@ -1,18 +1,35 @@
//! This module mainly outputs the `Compiler` trait that custom
//! compilers will need to implement.
use crate::config::Target;
use crate::error::CompileError;
use crate::function::{Compilation, CompiledFunction};
use crate::function::{Compilation, FunctionBody};
use crate::std::boxed::Box;
use crate::std::vec::Vec;
use crate::target::Target;
use crate::FunctionBodyData;
use crate::ModuleTranslationState;
use wasm_common::entity::PrimaryMap;
use wasm_common::{Features, FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasm_common::{Features, FunctionType, LocalFunctionIndex, MemoryIndex, TableIndex};
use wasmer_runtime::Module;
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
/// The compiler configuration options.
///
/// This options must have WebAssembly `Features` and a specific
/// `Target` to compile to.
pub trait CompilerConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features;
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target;
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler>;
}
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
pub trait Compiler {
/// Gets the target associated with this compiler
@@ -47,7 +64,7 @@ pub trait Compiler {
module: &'module Module,
module_translation: &ModuleTranslationState,
// The list of function bodies
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
// The plans for the module memories (imported and local)
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
// The plans for the module tables (imported and local)
@@ -65,6 +82,6 @@ pub trait Compiler {
/// ```
fn compile_wasm_trampolines(
&self,
signatures: &[FuncType],
) -> Result<Vec<CompiledFunction>, CompileError>;
signatures: &[FunctionType],
) -> Result<Vec<FunctionBody>, CompileError>;
}

View File

@@ -1,5 +1,4 @@
use crate::std::string::String;
use crate::translator::WasmError;
use thiserror::Error;
// Compilation Errors
@@ -28,3 +27,39 @@ pub enum CompileError {
#[error("Insufficient resources: {0}")]
Resource(String),
}
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Error, Debug)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[error("Invalid input WebAssembly code at offset {offset}: {message}")]
InvalidWebAssembly {
/// A string describing the validation error.
message: String,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[error("Unsupported feature: {0}")]
Unsupported(String),
/// An implementation limit was exceeded.
#[error("Implementation limit exceeded")]
ImplLimitExceeded,
/// A generic error.
#[error("{0}")]
Generic(String),
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;

View File

@@ -5,16 +5,38 @@
//! * `jit`: to generate a JIT
//! * `obj`: to generate a native object
use crate::std::ops::Range;
use crate::section::{CustomSection, SectionBody, SectionIndex};
use crate::std::vec::Vec;
use crate::trap::TrapInformation;
use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap, JumpTableOffsets, Relocation};
use serde::{Deserialize, Serialize};
use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFuncIndex;
use wasm_common::LocalFunctionIndex;
type FunctionBody = Vec<u8>;
/// The frame info for a Compiled function.
///
/// This structure is only used for reconstructing
/// the frame information after a `Trap`.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct CompiledFunctionFrameInfo {
/// The traps (in the function body)
pub traps: Vec<TrapInformation>,
/// The address map.
pub address_map: FunctionAddressMap,
}
/// The function body.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct FunctionBody {
/// The function body bytes.
#[serde(with = "serde_bytes")]
pub body: Vec<u8>,
/// The function unwind info
pub unwind_info: Option<CompiledFunctionUnwindInfo>,
}
/// The result of compiling a WebAssembly function.
///
@@ -24,80 +46,46 @@ type FunctionBody = Vec<u8>;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CompiledFunction {
/// The function body.
#[serde(with = "serde_bytes")]
pub body: FunctionBody,
/// The relocations (in the body)
pub relocations: Vec<Relocation>,
/// The traps (in the body)
pub traps: Vec<TrapInformation>,
/// The jump tables offsets (in the body).
pub jt_offsets: JumpTableOffsets,
/// The unwind information.
pub unwind_info: CompiledFunctionUnwindInfo,
/// The address map.
///
/// TODO: Make it optional as it's not required for trampoline generation.
pub address_map: FunctionAddressMap,
/// The frame information.
pub frame_info: CompiledFunctionFrameInfo,
}
/// The compiled functions map (index in the Wasm -> function)
pub type Functions = PrimaryMap<LocalFuncIndex, CompiledFunction>;
pub type Functions = PrimaryMap<LocalFunctionIndex, CompiledFunction>;
/// The custom sections for a Compilation.
pub type CustomSections = PrimaryMap<SectionIndex, CustomSection>;
/// The result of compiling a WebAssembly module's functions.
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct Compilation {
/// Compiled code for the function bodies.
functions: Functions,
/// Custom sections for the module.
/// It will hold the data, for example, for constants used in a
/// function, global variables, rodata_64, hot/cold function partitioning, ...
custom_sections: CustomSections,
}
impl Compilation {
/// Creates a compilation artifact from a contiguous function buffer and a set of ranges
pub fn new(functions: Functions) -> Self {
Self { functions }
}
/// Allocates the compilation result with the given function bodies.
pub fn from_buffer(
buffer: Vec<u8>,
functions: impl IntoIterator<
Item = (
Range<usize>,
JumpTableOffsets,
Range<usize>,
Vec<Relocation>,
Vec<TrapInformation>,
FunctionAddressMap,
),
>,
) -> Self {
Self::new(
functions
.into_iter()
.map(
|(body_range, jt_offsets, unwind_range, relocations, traps, address_map)| {
CompiledFunction {
body: buffer[body_range].to_vec(),
jt_offsets,
unwind_info: CompiledFunctionUnwindInfo::Windows(
buffer[unwind_range].to_vec(),
),
address_map,
relocations,
traps,
}
},
)
.collect(),
)
pub fn new(functions: Functions, custom_sections: CustomSections) -> Self {
Self {
functions,
custom_sections,
}
}
/// Gets the bytes of a single function
pub fn get(&self, func: LocalFuncIndex) -> &CompiledFunction {
pub fn get(&self, func: LocalFunctionIndex) -> &CompiledFunction {
&self.functions[func]
}
@@ -112,35 +100,43 @@ impl Compilation {
}
/// Gets functions jump table offsets.
pub fn get_jt_offsets(&self) -> PrimaryMap<LocalFuncIndex, JumpTableOffsets> {
self.functions
.iter()
.map(|(_, func)| func.jt_offsets.clone())
.collect::<PrimaryMap<LocalFuncIndex, _>>()
}
/// Gets functions jump table offsets.
pub fn get_relocations(&self) -> PrimaryMap<LocalFuncIndex, Vec<Relocation>> {
pub fn get_relocations(&self) -> PrimaryMap<LocalFunctionIndex, Vec<Relocation>> {
self.functions
.iter()
.map(|(_, func)| func.relocations.clone())
.collect::<PrimaryMap<LocalFuncIndex, _>>()
}
/// Gets functions address maps.
pub fn get_address_maps(&self) -> PrimaryMap<LocalFuncIndex, FunctionAddressMap> {
self.functions
.iter()
.map(|(_, func)| func.address_map.clone())
.collect::<PrimaryMap<LocalFuncIndex, _>>()
.collect::<PrimaryMap<LocalFunctionIndex, _>>()
}
/// Gets functions jump table offsets.
pub fn get_traps(&self) -> PrimaryMap<LocalFuncIndex, Vec<TrapInformation>> {
pub fn get_function_bodies(&self) -> PrimaryMap<LocalFunctionIndex, FunctionBody> {
self.functions
.iter()
.map(|(_, func)| func.traps.clone())
.collect::<PrimaryMap<LocalFuncIndex, _>>()
.map(|(_, func)| func.body.clone())
.collect::<PrimaryMap<LocalFunctionIndex, _>>()
}
/// Gets functions jump table offsets.
pub fn get_jt_offsets(&self) -> PrimaryMap<LocalFunctionIndex, JumpTableOffsets> {
self.functions
.iter()
.map(|(_, func)| func.jt_offsets.clone())
.collect::<PrimaryMap<LocalFunctionIndex, _>>()
}
/// Gets functions jump table offsets.
pub fn get_frame_info(&self) -> PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo> {
self.functions
.iter()
.map(|(_, func)| func.frame_info.clone())
.collect::<PrimaryMap<LocalFunctionIndex, _>>()
}
/// Gets custom section data.
pub fn get_custom_sections(&self) -> PrimaryMap<SectionIndex, SectionBody> {
self.custom_sections
.iter()
.map(|(_, section)| section.bytes.clone())
.collect::<PrimaryMap<SectionIndex, _>>()
}
}

View File

@@ -38,36 +38,46 @@ use hashbrown::HashMap;
use std::collections::HashMap;
mod address_map;
#[cfg(feature = "translator")]
mod compiler;
mod config;
mod error;
mod function;
mod jump_table;
mod relocation;
mod target;
mod trap;
mod unwind;
#[cfg(feature = "translator")]
#[macro_use]
mod translator;
mod section;
mod sourceloc;
pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
pub use crate::compiler::Compiler;
pub use crate::config::{
Architecture, CallingConvention, CompilerConfig, CpuFeature, Features, OperatingSystem, Target,
Triple,
#[cfg(feature = "translator")]
pub use crate::compiler::{Compiler, CompilerConfig};
pub use crate::error::{CompileError, WasmError, WasmResult};
pub use crate::function::{
Compilation, CompiledFunction, CompiledFunctionFrameInfo, CustomSections, FunctionBody,
Functions,
};
pub use crate::error::CompileError;
pub use crate::function::{Compilation, CompiledFunction, Functions};
pub use crate::jump_table::{JumpTable, JumpTableOffsets};
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex};
pub use crate::sourceloc::SourceLoc;
pub use crate::target::{
Architecture, CallingConvention, CpuFeature, OperatingSystem, Target, Triple,
};
#[cfg(feature = "translator")]
pub use crate::translator::{
to_wasm_error, translate_module, FunctionBodyData, ModuleEnvironment, ModuleTranslation,
ModuleTranslationState, WasmError, WasmResult,
ModuleTranslationState,
};
pub use crate::trap::TrapInformation;
pub use crate::unwind::{CompiledFunctionUnwindInfo, FDERelocEntry, FunctionTableReloc};
pub use wasm_common::Features;
/// wasmparser is exported as a module to slim compiler dependencies
pub mod wasmparser {
pub use wasmparser::*;

View File

@@ -9,12 +9,13 @@
//! the generated machine code, so a given frontend (JIT or native) can
//! do the corresponding work to run it.
use crate::section::SectionIndex;
use crate::std::vec::Vec;
use crate::{Addend, CodeOffset, JumpTable};
use serde::{Deserialize, Serialize};
use std::fmt;
use wasm_common::entity::PrimaryMap;
use wasm_common::{FuncIndex, LocalFuncIndex};
use wasm_common::{FunctionIndex, LocalFunctionIndex};
use wasmer_runtime::libcalls::LibCall;
/// Relocation kinds for every ISA.
@@ -88,13 +89,15 @@ pub struct Relocation {
/// Destination function. Can be either user function or some special one, like `memory.grow`.
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum RelocationTarget {
/// The user function index.
UserFunc(FuncIndex),
/// A relocation to a function defined locally in the wasm (not an imported one).
LocalFunc(LocalFunctionIndex),
/// A compiler-generated libcall.
LibCall(LibCall),
/// Jump table index.
JumpTable(FuncIndex, JumpTable),
JumpTable(LocalFunctionIndex, JumpTable),
/// Custom sections generated by the compiler
CustomSection(SectionIndex),
}
/// Relocations to apply to function bodies.
pub type Relocations = PrimaryMap<LocalFuncIndex, Vec<Relocation>>;
pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;

View File

@@ -0,0 +1,71 @@
//! This module define the required structures to emit custom
//! Sections in a `Compilation`.
//!
//! The functions that access a custom [`CustomSection`] would need
//! to emit a custom relocation: `RelocationTarget::CustomSection`, so
//! it can be patched later by the engine (native or JIT).
use crate::std::vec::Vec;
use serde::{Deserialize, Serialize};
use wasm_common::entity::entity_impl;
/// Index type of a Section defined inside a WebAssembly `Compilation`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct SectionIndex(u32);
entity_impl!(SectionIndex);
/// Custom section Protection.
///
/// Determines how a custom section may be used.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CustomSectionProtection {
/// A custom section with read permissions,
Read,
// We don't include `ReadWrite` here because it would complicate freeze
// and resumption of executing Modules.
// We also currently don't include `ReadExecute` as we don't have a way
// to represent relocations for this kind of section.
}
/// A Section for a `Compilation`.
///
/// This is used so compilers can store arbitrary information
/// in the emitted module.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CustomSection {
/// The protection
pub protection: CustomSectionProtection,
/// The bytes corresponding to this section.
///
/// > Note: These bytes have to be at-least 8-byte aligned
/// > (the start of the memory pointer).
/// > We might need to create another field for alignment in case it's
/// > needed in the future.
pub bytes: SectionBody,
}
/// The bytes in the section.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct SectionBody(#[serde(with = "serde_bytes")] Vec<u8>);
impl SectionBody {
/// Extend the section with the bytes given.
pub fn extend(&mut self, contents: &[u8]) {
self.0.extend(contents);
}
/// Extends the section by appending bytes from another section.
pub fn append(&mut self, body: &SectionBody) {
self.0.extend(&body.0);
}
/// Returns a raw pointer to the section's buffer.
pub fn as_ptr(&self) -> *const u8 {
self.0.as_ptr()
}
/// Returns the length of this section in bytes.
pub fn len(&self) -> usize {
self.0.len()
}
}

View File

@@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize};
/// that can't be given a real source location.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct SourceLoc(u32);
impl SourceLoc {
@@ -51,6 +52,7 @@ impl fmt::Display for SourceLoc {
#[cfg(test)]
mod tests {
use super::SourceLoc;
use crate::std::string::ToString;
#[test]
fn display() {

View File

@@ -1,10 +1,8 @@
//! The configuration for the
use crate::compiler::Compiler;
use crate::std::boxed::Box;
//! Target configuration
use enumset::{EnumSet, EnumSetType};
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple};
pub use wasm_common::Features;
use crate::std::boxed::Box;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use raw_cpuid::CpuId;
@@ -140,26 +138,3 @@ impl Default for Target {
}
}
}
/// The compiler configuration options.
///
/// This options must have WebAssembly `Features` and a specific
/// `Target` to compile to.
pub trait CompilerConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features;
/// Gets the WebAssembly features, mutable
fn features_mut(&mut self) -> &mut Features;
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target;
/// Gets the target that we will use for compiling
/// the WebAssembly module, mutable
fn target_mut(&mut self) -> &mut Target;
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler>;
}

View File

@@ -1,17 +1,17 @@
use super::error::{WasmError, WasmResult};
use super::module::translate_module;
use super::state::ModuleTranslationState;
use crate::std::borrow::ToOwned;
use crate::std::string::ToString;
use crate::std::{boxed::Box, string::String, vec::Vec};
use crate::{WasmError, WasmResult};
use std::convert::TryFrom;
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::FuncType;
use wasm_common::FunctionType;
use wasm_common::{
DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, ExportIndex, FuncIndex,
GlobalIndex, GlobalType, ImportIndex, LocalFuncIndex, MemoryIndex, MemoryType, SignatureIndex,
TableIndex, TableType,
DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, ExportIndex, FunctionIndex,
GlobalIndex, GlobalInit, GlobalType, ImportIndex, LocalFunctionIndex, MemoryIndex, MemoryType,
SignatureIndex, TableIndex, TableType,
};
use wasmer_runtime::{Module, TableElements};
@@ -34,7 +34,7 @@ pub struct ModuleTranslation<'data> {
pub module: Module,
/// References to the function bodies.
pub function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,
pub function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
/// References to the data initializers.
pub data_initializers: Vec<DataInitializer<'data>>,
@@ -102,7 +102,7 @@ impl<'data> ModuleEnvironment<'data> {
Ok(())
}
pub(crate) fn declare_signature(&mut self, sig: FuncType) -> WasmResult<()> {
pub(crate) fn declare_signature(&mut self, sig: FunctionType) -> WasmResult<()> {
// TODO: Deduplicate signatures.
self.result.module.signatures.push(sig);
Ok(())
@@ -120,7 +120,7 @@ impl<'data> ModuleEnvironment<'data> {
"Imported functions must be declared first"
);
self.declare_import(
ImportIndex::Function(FuncIndex::from_u32(
ImportIndex::Function(FunctionIndex::from_u32(
self.result.module.num_imported_funcs as _,
)),
module,
@@ -263,8 +263,13 @@ impl<'data> ModuleEnvironment<'data> {
Ok(())
}
pub(crate) fn declare_global(&mut self, global: GlobalType) -> WasmResult<()> {
pub(crate) fn declare_global(
&mut self,
global: GlobalType,
initializer: GlobalInit,
) -> WasmResult<()> {
self.result.module.globals.push(global);
self.result.module.global_initializers.push(initializer);
Ok(())
}
@@ -278,7 +283,7 @@ impl<'data> ModuleEnvironment<'data> {
pub(crate) fn declare_func_export(
&mut self,
func_index: FuncIndex,
func_index: FunctionIndex,
name: &str,
) -> WasmResult<()> {
self.declare_export(ExportIndex::Function(func_index), name)
@@ -308,7 +313,7 @@ impl<'data> ModuleEnvironment<'data> {
self.declare_export(ExportIndex::Global(global_index), name)
}
pub(crate) fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
pub(crate) fn declare_start_func(&mut self, func_index: FunctionIndex) -> WasmResult<()> {
debug_assert!(self.result.module.start_func.is_none());
self.result.module.start_func = Some(func_index);
Ok(())
@@ -327,7 +332,7 @@ impl<'data> ModuleEnvironment<'data> {
table_index: TableIndex,
base: Option<GlobalIndex>,
offset: usize,
elements: Box<[FuncIndex]>,
elements: Box<[FunctionIndex]>,
) -> WasmResult<()> {
self.result.module.table_elements.push(TableElements {
table_index,
@@ -341,7 +346,7 @@ impl<'data> ModuleEnvironment<'data> {
pub(crate) fn declare_passive_element(
&mut self,
elem_index: ElemIndex,
segments: Box<[FuncIndex]>,
segments: Box<[FunctionIndex]>,
) -> WasmResult<()> {
let old = self
.result
@@ -423,7 +428,7 @@ impl<'data> ModuleEnvironment<'data> {
pub(crate) fn declare_func_name(
&mut self,
func_index: FuncIndex,
func_index: FunctionIndex,
name: &'data str,
) -> WasmResult<()> {
self.result

View File

@@ -1,39 +1,7 @@
use crate::std::string::String;
use crate::WasmError;
use thiserror::Error;
use wasmparser::BinaryReaderError;
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Error, Debug)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[error("Invalid input WebAssembly code at offset {offset}: {message}")]
InvalidWebAssembly {
/// A string describing the validation error.
message: String,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[error("Unsupported feature: {0}")]
Unsupported(String),
/// An implementation limit was exceeded.
#[error("Implementation limit exceeded")]
ImplLimitExceeded,
/// A generic error.
#[error("{0}")]
Generic(String),
}
/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!`
/// on the arguments to this macro.
#[macro_export]
@@ -48,6 +16,3 @@ pub fn to_wasm_error(e: BinaryReaderError) -> WasmError {
offset: e.offset(),
}
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;

View File

@@ -13,6 +13,6 @@ mod error;
mod sections;
pub use self::environ::{FunctionBodyData, ModuleEnvironment, ModuleTranslation};
pub use self::error::{to_wasm_error, WasmError, WasmResult};
pub use self::error::to_wasm_error;
pub use self::module::translate_module;
pub use self::state::ModuleTranslationState;

View File

@@ -1,13 +1,14 @@
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use super::environ::ModuleEnvironment;
use super::error::{to_wasm_error, WasmResult};
use super::error::to_wasm_error;
use super::sections::{
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
parse_name_section, parse_start_section, parse_table_section, parse_type_section,
};
use super::state::ModuleTranslationState;
use crate::WasmResult;
use wasmparser::{CustomSectionContent, ModuleReader, SectionContent};
/// Translate a sequence of bytes forming a valid Wasm binary into a

View File

@@ -8,24 +8,26 @@
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
//! interpreted on the fly.
use super::environ::ModuleEnvironment;
use super::error::{to_wasm_error, WasmError, WasmResult};
use super::error::to_wasm_error;
use super::state::ModuleTranslationState;
use crate::{wasm_unsupported, HashMap};
use crate::{WasmError, WasmResult};
use core::convert::TryFrom;
use std::boxed::Box;
use std::vec::Vec;
use wasm_common::entity::packed_option::ReservedValue;
use wasm_common::entity::EntityRef;
use wasm_common::{
DataIndex, ElemIndex, FuncIndex, FuncType, GlobalIndex, GlobalInit, GlobalType, MemoryIndex,
MemoryType, Pages, SignatureIndex, TableIndex, TableType, Type, V128,
DataIndex, ElemIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalInit, GlobalType,
MemoryIndex, MemoryType, Pages, SignatureIndex, TableIndex, TableType, Type, V128,
};
use wasmparser::{
self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems,
ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind,
FuncType as WPFuncType, FunctionSectionReader, GlobalSectionReader, GlobalType as WPGlobalType,
ImportSectionEntryType, ImportSectionReader, MemorySectionReader, MemoryType as WPMemoryType,
NameSectionReader, Naming, NamingReader, Operator, TableSectionReader, TypeSectionReader,
FuncType as WPFunctionType, FunctionSectionReader, GlobalSectionReader,
GlobalType as WPGlobalType, ImportSectionEntryType, ImportSectionReader, MemorySectionReader,
MemoryType as WPMemoryType, NameSectionReader, Naming, NamingReader, Operator,
TableSectionReader, TypeSectionReader,
};
/// Helper function translating wasmparser types to Wasm Type.
@@ -56,7 +58,7 @@ pub fn parse_type_section(
for entry in types {
match entry.map_err(to_wasm_error)? {
WPFuncType {
WPFunctionType {
form: wasmparser::Type::Func,
params,
returns,
@@ -75,7 +77,7 @@ pub fn parse_type_section(
.expect("only numeric types are supported in function signatures")
})
.collect();
let sig = FuncType::new(sig_params, sig_returns);
let sig = FunctionType::new(sig_params, sig_returns);
environ.declare_signature(sig)?;
module_translation_state.wasm_types.push((params, returns));
}
@@ -129,7 +131,6 @@ pub fn parse_import_section<'data>(
GlobalType {
ty: wptype_to_type(ty.content_type).unwrap(),
mutability: ty.mutable.into(),
initializer: GlobalInit::Import,
},
module_name,
field_name,
@@ -238,7 +239,7 @@ pub fn parse_global_section(
}
Operator::RefNull => GlobalInit::RefNullConst,
Operator::RefFunc { function_index } => {
GlobalInit::RefFunc(FuncIndex::from_u32(function_index))
GlobalInit::RefFunc(FunctionIndex::from_u32(function_index))
}
Operator::GlobalGet { global_index } => {
GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
@@ -253,9 +254,8 @@ pub fn parse_global_section(
let global = GlobalType {
ty: wptype_to_type(content_type).unwrap(),
mutability: mutable.into(),
initializer,
};
environ.declare_global(global)?;
environ.declare_global(global, initializer)?;
}
Ok(())
@@ -280,7 +280,9 @@ pub fn parse_export_section<'data>(
// becomes a concern here.
let index = index as usize;
match *kind {
ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), field)?,
ExternalKind::Function => {
environ.declare_func_export(FunctionIndex::new(index), field)?
}
ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
ExternalKind::Memory => {
environ.declare_memory_export(MemoryIndex::new(index), field)?
@@ -297,17 +299,17 @@ pub fn parse_export_section<'data>(
/// Parses the Start section of the wasm module.
pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
environ.declare_start_func(FuncIndex::from_u32(index))?;
environ.declare_start_func(FunctionIndex::from_u32(index))?;
Ok(())
}
fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
fn read_elems(items: &ElementItems) -> WasmResult<Box<[FunctionIndex]>> {
let items_reader = items.get_items_reader().map_err(to_wasm_error)?;
let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
for item in items_reader {
let elem = match item.map_err(to_wasm_error)? {
ElementItem::Null => FuncIndex::reserved_value(),
ElementItem::Func(index) => FuncIndex::from_u32(index),
ElementItem::Null => FunctionIndex::reserved_value(),
ElementItem::Func(index) => FunctionIndex::from_u32(index),
};
elems.push(elem);
}
@@ -461,7 +463,7 @@ pub fn parse_name_section<'data>(
fn parse_function_name_subsection(
mut naming_reader: NamingReader<'_>,
) -> Option<HashMap<FuncIndex, &str>> {
) -> Option<HashMap<FunctionIndex, &str>> {
let mut function_names = HashMap::new();
for _ in 0..naming_reader.get_count() {
let Naming { index, name } = naming_reader.read().ok()?;
@@ -471,7 +473,7 @@ fn parse_function_name_subsection(
}
if function_names
.insert(FuncIndex::from_u32(index), name)
.insert(FunctionIndex::from_u32(index), name)
.is_some()
{
// If the function index has been previously seen, then we

View File

@@ -31,9 +31,6 @@ pub struct FunctionTableReloc {
/// [unwind info]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CompiledFunctionUnwindInfo {
/// No unwind information.
None,
/// Windows UNWIND_INFO.
Windows(Vec<u8>),
@@ -45,7 +42,6 @@ impl CompiledFunctionUnwindInfo {
/// Retuns true is no unwind info data.
pub fn is_empty(&self) -> bool {
match self {
CompiledFunctionUnwindInfo::None => true,
CompiledFunctionUnwindInfo::Windows(d) => d.is_empty(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.is_empty(),
}
@@ -54,7 +50,6 @@ impl CompiledFunctionUnwindInfo {
/// Returns size of serilized unwind info.
pub fn len(&self) -> usize {
match self {
CompiledFunctionUnwindInfo::None => 0,
CompiledFunctionUnwindInfo::Windows(d) => d.len(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.len(),
}
@@ -63,7 +58,6 @@ impl CompiledFunctionUnwindInfo {
/// Serializes data into byte array.
pub fn serialize(&self, dest: &mut [u8], relocs: &mut Vec<FunctionTableReloc>) {
match self {
CompiledFunctionUnwindInfo::None => (),
CompiledFunctionUnwindInfo::Windows(d) => {
dest.copy_from_slice(d);
}

39
lib/engine-jit/Cargo.toml Normal file
View File

@@ -0,0 +1,39 @@
[package]
name = "wasmer-engine-jit"
version = "0.16.2"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer JIT Engine"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/wasmerio/wasmer"
readme = "README.md"
edition = "2018"
[dependencies]
wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasmer-engine = { path = "../engine", version = "0.16.2" }
target-lexicon = { version = "0.10.0", default-features = false }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
backtrace = "0.3.46"
rustc-demangle = "0.1.16"
more-asserts = "0.2.1"
thiserror = "1.0.16"
region = "2.1.2"
serde = { version = "1.0.106", sfeatures = ["derive", "rc"] }
serde_bytes = { version = "0.11.3" }
bincode = "1.2.1"
lazy_static = "1.4"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
[features]
# Enable the `compiler` feature if you want the engine to compile
# and not be only on headless mode.
compiler = []
[badges]
maintenance = { status = "actively-developed" }

13
lib/engine-jit/README.md Normal file
View File

@@ -0,0 +1,13 @@
# Wasmer JIT
The Wasmer JIT is usable with any compiler implementation
based on `wasmer-compiler`.
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.
### 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.
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

View File

@@ -4,8 +4,8 @@ use region;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFuncIndex;
use wasmer_compiler::{Compilation, CompiledFunction};
use wasm_common::LocalFunctionIndex;
use wasmer_compiler::FunctionBody;
use wasmer_runtime::{Mmap, VMFunctionBody};
struct CodeMemoryEntry {
@@ -60,12 +60,12 @@ impl CodeMemory {
/// Allocate a continuous memory blocks for a single compiled function.
pub fn allocate_functions(
&mut self,
compilation: &Compilation,
) -> Result<PrimaryMap<LocalFuncIndex, *mut [VMFunctionBody]>, String> {
let fat_ptrs = self.allocate_for_compilation(compilation)?;
functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
) -> Result<PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]>, String> {
let fat_ptrs = self.allocate_for_compilation(functions)?;
// Second, create a PrimaryMap from result vector of pointers.
let mut result = PrimaryMap::with_capacity(compilation.len());
let mut result = PrimaryMap::with_capacity(functions.len());
for i in 0..fat_ptrs.len() {
let fat_ptr: *mut [VMFunctionBody] = fat_ptrs[i];
result.push(fat_ptr);
@@ -78,7 +78,7 @@ impl CodeMemory {
/// mmap region rather than into a Vec that we need to copy in.
pub fn allocate_for_function(
&mut self,
func: &CompiledFunction,
func: &FunctionBody,
) -> Result<&mut [VMFunctionBody], String> {
let size = Self::function_allocation_size(func);
@@ -94,17 +94,17 @@ impl CodeMemory {
/// Allocates memory for both the function bodies as well as function unwind data.
pub fn allocate_for_compilation(
&mut self,
compilation: &Compilation,
compilation: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
let total_len = compilation
.into_iter()
.values()
.fold(0, |acc, func| acc + Self::function_allocation_size(func));
let (mut buf, mut table, start) = self.allocate(total_len)?;
let mut result = Vec::with_capacity(compilation.len());
let mut start = start as u32;
for func in compilation.into_iter() {
for func in compilation.values() {
let (next_start, next_buf, next_table, vmfunc) =
Self::copy_function(func, start, buf, table);
@@ -167,12 +167,12 @@ impl CodeMemory {
}
/// Calculates the allocation size of the given compiled function.
fn function_allocation_size(func: &CompiledFunction) -> usize {
if func.unwind_info.is_empty() {
func.body.len()
} else {
fn function_allocation_size(func: &FunctionBody) -> usize {
if let Some(unwind_info) = &func.unwind_info {
// Account for necessary unwind information alignment padding (32-bit)
((func.body.len() + 3) & !3) + func.unwind_info.len()
((func.body.len() + 3) & !3) + unwind_info.len()
} else {
func.body.len()
}
}
@@ -180,7 +180,7 @@ impl CodeMemory {
///
/// This will also add the function to the current function table.
fn copy_function<'a>(
func: &CompiledFunction,
func: &FunctionBody,
func_start: u32,
buf: &'a mut [u8],
table: &'a mut FunctionTable,
@@ -196,19 +196,19 @@ impl CodeMemory {
body.copy_from_slice(&func.body);
let vmfunc = Self::view_as_mut_vmfunc_slice(body);
if func.unwind_info.is_empty() {
if func.unwind_info.is_none() {
return (func_end, remainder, table, vmfunc);
}
let unwind_info = func.unwind_info.as_ref().unwrap();
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
let padding = ((func.body.len() + 3) & !3) - func.body.len();
let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len());
let (unwind, remainder) = remainder.split_at_mut(padding + unwind_info.len());
let mut relocs = Vec::new();
func.unwind_info
.serialize(&mut unwind[padding..], &mut relocs);
unwind_info.serialize(&mut unwind[padding..], &mut relocs);
let unwind_start = func_end + (padding as u32);
let unwind_end = unwind_start + (func.unwind_info.len() as u32);
let unwind_end = unwind_start + (unwind_info.len() as u32);
relocs.iter_mut().for_each(move |r| {
r.offset += unwind_start;

View File

@@ -0,0 +1,256 @@
//! JIT compilation.
use crate::{CodeMemory, CompiledModule};
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{FunctionType, LocalFunctionIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::{Compilation, CompileError, FunctionBody, Target};
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_engine::{
CompiledModule as BaseCompiledModule, DeserializeError, Engine, InstantiationError, Resolver,
SerializeError, Tunables,
};
use wasmer_runtime::{
InstanceHandle, MemoryPlan, Module, SignatureRegistry, TablePlan, VMFunctionBody,
VMSharedSignatureIndex, VMTrampoline,
};
/// A WebAssembly `JIT` Engine.
#[derive(Clone)]
pub struct JITEngine {
inner: Arc<RefCell<JITEngineInner>>,
tunables: Arc<Box<dyn Tunables>>,
}
impl JITEngine {
const MAGIC_HEADER: &'static [u8] = b"\0wasmer-jit";
/// Create a new `JITEngine` with the given config
#[cfg(feature = "compiler")]
pub fn new<C: CompilerConfig>(config: &C, tunables: impl Tunables + 'static) -> Self
where
C: ?Sized,
{
let compiler = config.compiler();
Self {
inner: Arc::new(RefCell::new(JITEngineInner {
compiler: Some(compiler),
trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
})),
tunables: Arc::new(Box::new(tunables)),
}
}
/// Create a headless `JITEngine`
///
/// A headless engine is an engine without any compiler attached.
/// This is useful for assuring a minimal runtime for running
/// WebAssembly modules.
///
/// For example, for running in IoT devices where compilers are very
/// expensive, or also to optimize startup speed.
///
/// # Important
///
/// Headless engines can't compile or validate any modules,
/// they just take already processed Modules (via `Module::serialize`).
pub fn headless(tunables: impl Tunables + 'static) -> Self {
Self {
inner: Arc::new(RefCell::new(JITEngineInner {
#[cfg(feature = "compiler")]
compiler: None,
trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
})),
tunables: Arc::new(Box::new(tunables)),
}
}
pub(crate) fn compiler(&self) -> std::cell::Ref<'_, JITEngineInner> {
self.inner.borrow()
}
pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, JITEngineInner> {
self.inner.borrow_mut()
}
/// Check if the provided bytes look like a serialized
/// module by the `JITEngine` implementation.
pub fn is_deserializable(bytes: &[u8]) -> bool {
bytes.starts_with(Self::MAGIC_HEADER)
}
}
impl Engine for JITEngine {
/// Get the tunables
fn tunables(&self) -> &dyn Tunables {
&**self.tunables
}
/// Register a signature
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
let compiler = self.compiler();
compiler.signatures().register(func_type)
}
/// Lookup a signature
fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
let compiler = self.compiler();
compiler.signatures().lookup(sig)
}
/// Retrieves a trampoline given a signature
fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
self.compiler().trampoline(sig)
}
/// Validates a WebAssembly module
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
self.compiler().validate(binary)
}
/// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn BaseCompiledModule>, CompileError> {
Ok(Arc::new(CompiledModule::new(&self, binary)?))
}
/// Instantiates a WebAssembly module
unsafe fn instantiate(
&self,
compiled_module: &dyn BaseCompiledModule,
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
let compiled_module = compiled_module.downcast_ref::<CompiledModule>().unwrap();
unsafe { compiled_module.instantiate(&self, resolver, Box::new(())) }
}
/// Serializes a WebAssembly module
fn serialize(
&self,
compiled_module: &dyn BaseCompiledModule,
) -> Result<Vec<u8>, SerializeError> {
let compiled_module = compiled_module.downcast_ref::<CompiledModule>().unwrap();
// We append the header
let mut serialized = Self::MAGIC_HEADER.to_vec();
serialized.extend(compiled_module.serialize()?);
Ok(serialized)
}
/// Deserializes a WebAssembly module
fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn BaseCompiledModule>, DeserializeError> {
if !Self::is_deserializable(bytes) {
return Err(DeserializeError::Incompatible(
"The provided bytes are not wasmer-jit".to_string(),
));
}
Ok(Arc::new(CompiledModule::deserialize(
&self,
&bytes[Self::MAGIC_HEADER.len()..],
)?))
}
}
/// The inner contents of `JITEngine`
pub struct JITEngineInner {
/// The compiler
#[cfg(feature = "compiler")]
compiler: Option<Box<dyn Compiler>>,
/// Pointers to trampoline functions used to enter particular signatures
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The code memory is responsible of publishing the compiled
/// functions to memory.
code_memory: CodeMemory,
/// The signature registry is used mainly to operate with trampolines
/// performantly.
signatures: SignatureRegistry,
}
impl JITEngineInner {
/// Gets the compiler associated to this JIT
#[cfg(feature = "compiler")]
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
if self.compiler.is_none() {
return Err(CompileError::Codegen("The JITEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
}
Ok(&**self.compiler.as_ref().unwrap())
}
/// Validate the module
#[cfg(feature = "compiler")]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.compiler()?.validate_module(data)
}
/// Validate the module
#[cfg(not(feature = "compiler"))]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
Err(CompileError::Validate(
"Validation is only enabled with the compiler feature".to_string(),
))
}
/// Compile the given function bodies.
pub(crate) fn allocate<'data>(
&mut self,
module: &Module,
functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
) -> Result<PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]>, CompileError> {
// Allocate all of the compiled functions into executable memory,
// copying over their contents.
let allocated_functions =
self.code_memory
.allocate_functions(&functions)
.map_err(|message| {
CompileError::Resource(format!(
"failed to allocate memory for functions: {}",
message
))
})?;
for (sig_index, compiled_function) in trampolines.iter() {
let func_type = module.signatures.get(sig_index).unwrap();
let index = self.signatures.register(&func_type);
if self.trampolines.contains_key(&index) {
// We don't need to allocate the trampoline in case
// it's signature is already allocated.
continue;
}
let ptr = self
.code_memory
.allocate_for_function(&compiled_function)
.map_err(|message| {
CompileError::Resource(format!(
"failed to allocate memory for trampolines: {}",
message
))
})?
.as_ptr();
let trampoline =
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
self.trampolines.insert(index, trampoline);
}
Ok(allocated_functions)
}
/// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.publish();
}
/// Shared signature registry.
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
}
/// Gets the trampoline pre-registered for a particular signature
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
self.trampolines.get(&sig).cloned()
}
}

View File

@@ -27,27 +27,16 @@
mod code_memory;
mod engine;
mod error;
mod function_table;
mod instantiate;
mod link;
mod resolver;
mod trap;
mod tunables;
mod module;
mod serialize;
pub use crate::code_memory::CodeMemory;
pub use crate::engine::JITEngine;
pub use crate::error::{
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
};
pub use crate::function_table::FunctionTable;
pub use crate::instantiate::CompiledModule;
pub use crate::link::link_module;
pub use crate::resolver::{resolve_imports, NullResolver, Resolver};
pub use crate::trap::*;
pub use crate::tunables::Tunables;
pub use wasmer_compiler::CompilerConfig;
pub use crate::module::CompiledModule;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -2,43 +2,41 @@
use std::ptr::write_unaligned;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::LocalFuncIndex;
use wasmer_compiler::{JumpTable, JumpTableOffsets, RelocationKind, RelocationTarget, Relocations};
use wasm_common::LocalFunctionIndex;
use wasmer_compiler::{
JumpTable, JumpTableOffsets, RelocationKind, RelocationTarget, Relocations, SectionBody,
SectionIndex,
};
use wasmer_runtime::Module;
use wasmer_runtime::VMFunctionBody;
/// Links a module that has been compiled with `compiled_module` in `wasmer-compiler::Compiler`.
///
/// Performs all required relocations inside the function code, provided the necessary metadata.
/// Links a module, patching the allocated functions with the
/// required relocations and jump tables.
pub fn link_module(
module: &Module,
allocated_functions: &PrimaryMap<LocalFuncIndex, *mut [VMFunctionBody]>,
jt_offsets: &PrimaryMap<LocalFuncIndex, JumpTableOffsets>,
allocated_functions: &PrimaryMap<LocalFunctionIndex, *mut [VMFunctionBody]>,
jt_offsets: &PrimaryMap<LocalFunctionIndex, JumpTableOffsets>,
relocations: Relocations,
allocated_sections: &PrimaryMap<SectionIndex, SectionBody>,
) {
for (i, function_relocs) in relocations.into_iter() {
for r in function_relocs {
let target_func_address: usize = match r.reloc_target {
RelocationTarget::UserFunc(index) => match module.local_func_index(index) {
Some(f) => {
let fatptr: *const [VMFunctionBody] = allocated_functions[f];
fatptr as *const VMFunctionBody as usize
}
None => panic!("direct call to import"),
},
RelocationTarget::LocalFunc(index) => {
let fatptr: *const [VMFunctionBody] = allocated_functions[index];
fatptr as *const VMFunctionBody as usize
}
RelocationTarget::LibCall(libcall) => libcall.function_pointer(),
RelocationTarget::CustomSection(custom_section) => {
allocated_sections[custom_section].as_ptr() as usize
}
RelocationTarget::JumpTable(func_index, jt) => {
match module.local_func_index(func_index) {
Some(f) => {
let offset = *jt_offsets
.get(f)
.and_then(|ofs| ofs.get(JumpTable::new(jt.index())))
.expect("func jump table");
let fatptr: *const [VMFunctionBody] = allocated_functions[f];
fatptr as *const VMFunctionBody as usize + offset as usize
}
None => panic!("func index of jump table"),
}
let offset = *jt_offsets
.get(func_index)
.and_then(|ofs| ofs.get(JumpTable::new(jt.index())))
.expect("func jump table");
let fatptr: *const [VMFunctionBody] = allocated_functions[func_index];
fatptr as *const VMFunctionBody as usize + offset as usize
}
};
@@ -73,10 +71,8 @@ pub fn link_module(
.wrapping_add(reloc_addend as u32);
write_unaligned(reloc_address as *mut u32, reloc_delta_u32);
}
RelocationKind::X86PCRelRodata4 => {
// ignore
}
_ => panic!("unsupported reloc kind"),
RelocationKind::X86PCRelRodata4 => {}
_ => panic!("Relocation kind unsupported"),
}
}
}

View File

@@ -0,0 +1,296 @@
//! Define `CompiledModule` to allow compiling and instantiating to be
//! done as separate steps.
use crate::engine::{JITEngine, JITEngineInner};
use crate::link::link_module;
use crate::serialize::{SerializableCompilation, SerializableModule};
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::sync::{Arc, Mutex};
use wasm_common::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasm_common::{
DataInitializer, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex,
MemoryIndex, OwnedDataInitializer, SignatureIndex, TableIndex,
};
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_engine::{
register_frame_info, resolve_imports, CompiledModule as BaseCompiledModule, DeserializeError,
Engine, GlobalFrameInfoRegistration, InstantiationError, LinkError, Resolver, RuntimeError,
SerializableFunctionFrameInfo, SerializeError, Tunables,
};
use wasmer_runtime::{
InstanceHandle, LinearMemory, Module, SignatureRegistry, Table, VMFunctionBody,
VMGlobalDefinition, VMSharedSignatureIndex,
};
use wasmer_runtime::{MemoryPlan, TablePlan};
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
serializable: SerializableModule,
finished_functions: BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
frame_info_registration: Mutex<Option<Option<GlobalFrameInfoRegistration>>>,
}
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
#[cfg(feature = "compiler")]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
let environ = ModuleEnvironment::new();
let mut jit_compiler = jit.compiler_mut();
let tunables = jit.tunables();
let translation = environ
.translate(data)
.map_err(|error| CompileError::Wasm(error))?;
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
.module
.memories
.iter()
.map(|(_index, memory_type)| tunables.memory_plan(*memory_type))
.collect();
let table_plans: PrimaryMap<TableIndex, TablePlan> = translation
.module
.tables
.iter()
.map(|(_index, table_type)| tunables.table_plan(*table_type))
.collect();
let compiler = jit_compiler.compiler()?;
// Compile the Module
let compilation = compiler.compile_module(
&translation.module,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?;
// Compile the trampolines
let func_types = translation
.module
.signatures
.values()
.cloned()
.collect::<Vec<_>>();
let trampolines = compiler
.compile_wasm_trampolines(&func_types)?
.into_iter()
.collect::<PrimaryMap<SignatureIndex, _>>();
let data_initializers = translation
.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
let frame_infos = compilation
.get_frame_info()
.values()
.map(|frame_info| SerializableFunctionFrameInfo::Processed(frame_info.clone()))
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
let serializable_compilation = SerializableCompilation {
function_bodies: compilation.get_function_bodies(),
function_relocations: compilation.get_relocations(),
function_jt_offsets: compilation.get_jt_offsets(),
function_frame_info: frame_infos,
trampolines,
custom_sections: compilation.get_custom_sections(),
};
let serializable = SerializableModule {
compilation: serializable_compilation,
module: Arc::new(translation.module),
features: jit_compiler.compiler()?.features().clone(),
data_initializers,
memory_plans,
table_plans,
};
Self::from_parts(&mut jit_compiler, serializable)
}
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
#[cfg(not(feature = "compiler"))]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
Err(CompileError::Codegen(
"Compilation is not enabled in the engine".to_string(),
))
}
/// Serialize a CompiledModule
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
// let mut s = flexbuffers::FlexbufferSerializer::new();
// self.serializable.serialize(&mut s).map_err(|e| SerializeError::Generic(format!("{:?}", e)));
// Ok(s.take_buffer())
bincode::serialize(&self.serializable)
.map_err(|e| SerializeError::Generic(format!("{:?}", e)))
}
/// Deserialize a CompiledModule
pub fn deserialize(jit: &JITEngine, bytes: &[u8]) -> Result<CompiledModule, DeserializeError> {
// let r = flexbuffers::Reader::get_root(bytes).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
// let serializable = SerializableModule::deserialize(r).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
let serializable: SerializableModule = bincode::deserialize(bytes)
.map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
Self::from_parts(&mut jit.compiler_mut(), serializable)
.map_err(|e| DeserializeError::Compiler(e))
}
/// Construct a `CompiledModule` from component parts.
pub fn from_parts(
jit_compiler: &mut JITEngineInner,
serializable: SerializableModule,
) -> Result<Self, CompileError> {
let finished_functions = jit_compiler.allocate(
&serializable.module,
&serializable.compilation.function_bodies,
&serializable.compilation.trampolines,
)?;
link_module(
&serializable.module,
&finished_functions,
&serializable.compilation.function_jt_offsets,
serializable.compilation.function_relocations.clone(),
&serializable.compilation.custom_sections,
);
// Compute indices into the shared signature table.
let signatures = {
let signature_registry = jit_compiler.signatures();
serializable
.module
.signatures
.values()
.map(|sig| signature_registry.register(sig))
.collect::<PrimaryMap<_, _>>()
};
// Make all code compiled thus far executable.
jit_compiler.publish_compiled_code();
Ok(Self {
serializable,
finished_functions: finished_functions.into_boxed_slice(),
signatures: signatures.into_boxed_slice(),
frame_info_registration: Mutex::new(None),
})
}
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.serializable.memory_plans
}
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
&self.serializable.table_plans
}
/// Crate an `Instance` from this `CompiledModule`.
///
/// # Unsafety
///
/// See `InstanceHandle::new`
pub unsafe fn instantiate(
&self,
jit: &JITEngine,
resolver: &dyn Resolver,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
let jit_compiler = jit.compiler();
let tunables = jit.tunables();
let sig_registry: &SignatureRegistry = jit_compiler.signatures();
let imports = resolve_imports(
&self.module(),
&sig_registry,
resolver,
self.memory_plans(),
self.table_plans(),
)
.map_err(InstantiationError::Link)?;
let finished_memories = tunables
.create_memories(&self.module(), self.memory_plans())
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_tables = tunables
.create_tables(&self.module(), self.table_plans())
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_globals = tunables
.create_globals(&self.module())
.map_err(InstantiationError::Link)?
.into_boxed_slice();
// Register the frame info for the module
self.register_frame_info();
InstanceHandle::new(
self.serializable.module.clone(),
self.finished_functions.clone(),
finished_memories,
finished_tables,
finished_globals,
imports,
self.signatures.clone(),
host_state,
)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
/// Returns data initializers to pass to `InstanceHandle::initialize`
pub fn data_initializers(&self) -> Vec<DataInitializer<'_>> {
self.serializable
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>()
}
/// Register this module's stack frame information into the global scope.
///
/// This is required to ensure that any traps can be properly symbolicated.
fn register_frame_info(&self) {
let mut info = self.frame_info_registration.lock().unwrap();
if info.is_some() {
return;
}
let frame_infos = &self.serializable.compilation.function_frame_info;
let finished_functions = &self.finished_functions;
*info = Some(register_frame_info(
self.serializable.module.clone(),
finished_functions,
frame_infos.clone(),
));
}
}
impl BaseCompiledModule for CompiledModule {
unsafe fn finish_instantiation(
&self,
handle: &InstanceHandle,
) -> Result<(), InstantiationError> {
let is_bulk_memory: bool = self.serializable.features.bulk_memory;
handle
.finish_instantiation(is_bulk_memory, &self.data_initializers())
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
fn module(&self) -> &Module {
&self.serializable.module
}
fn module_mut(&mut self) -> &mut Module {
Arc::get_mut(&mut self.serializable.module).unwrap()
}
}

View File

@@ -0,0 +1,37 @@
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{
Features, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex, TableIndex,
};
use wasmer_compiler::{FunctionBody, JumpTableOffsets, Relocation, SectionBody, SectionIndex};
use wasmer_engine::SerializableFunctionFrameInfo;
use wasmer_runtime::Module;
use wasmer_runtime::{MemoryPlan, TablePlan};
/// The compilation related data for a serialized modules
#[derive(Serialize, Deserialize)]
pub struct SerializableCompilation {
pub function_bodies: PrimaryMap<LocalFunctionIndex, FunctionBody>,
pub function_relocations: PrimaryMap<LocalFunctionIndex, Vec<Relocation>>,
pub function_jt_offsets: PrimaryMap<LocalFunctionIndex, JumpTableOffsets>,
// This is `SerializableFunctionFrameInfo` instead of `CompiledFunctionFrameInfo`,
// to allow lazy frame_info deserialization, we convert it to it's lazy binary
// format upon serialization.
pub function_frame_info: PrimaryMap<LocalFunctionIndex, SerializableFunctionFrameInfo>,
pub trampolines: PrimaryMap<SignatureIndex, FunctionBody>,
pub custom_sections: PrimaryMap<SectionIndex, SectionBody>,
}
/// Serializable struct that is able to serialize from and to
/// a `CompiledModule`.
#[derive(Serialize, Deserialize)]
pub struct SerializableModule {
pub compilation: SerializableCompilation,
pub features: Features,
pub module: Arc<Module>,
pub data_initializers: Box<[OwnedDataInitializer]>,
// Plans for that module
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
}

View File

@@ -1,8 +1,8 @@
[package]
name = "wasmer-jit"
name = "wasmer-engine"
version = "0.16.2"
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
description = "Wasmer JIT frontend"
description = "Wasmer Engine abstraction"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
@@ -15,14 +15,17 @@ wasm-common = { path = "../wasm-common", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
target-lexicon = { version = "0.10.0", default-features = false }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
backtrace = "0.3.46"
rustc-demangle = "0.1.16"
more-asserts = "0.2.1"
thiserror = "1.0.16"
region = "2.1.2"
serde = { version = "1.0.106", sfeatures = ["derive", "rc"] }
serde_bytes = { version = "0.11.3" }
bincode = "1.2.1"
lazy_static = "1.4"
downcast-rs = "1.1.1"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }

12
lib/engine/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Wasmer Engine
The Wasmer Engine is the general abstraction for Engines in Wasmer.
It currently has two implementations:
* Wasmer JIT
* Wasmer Native
### 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.
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

46
lib/engine/src/engine.rs Normal file
View File

@@ -0,0 +1,46 @@
//! JIT compilation.
use crate::error::InstantiationError;
use crate::resolver::Resolver;
use crate::tunables::Tunables;
use crate::{CompiledModule, DeserializeError, SerializeError};
use std::sync::Arc;
use wasm_common::FunctionType;
use wasmer_compiler::CompileError;
use wasmer_runtime::{InstanceHandle, VMSharedSignatureIndex, VMTrampoline};
/// A unimplemented Wasmer `Engine`.
/// This trait is used by implementors to implement custom engines,
/// such as: JIT or Native.
pub trait Engine {
/// Get the tunables
fn tunables(&self) -> &Tunables;
/// Register a signature
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex;
/// Lookup a signature
fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType>;
/// Retrieves a trampoline given a signature
fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline>;
/// Validates a WebAssembly module
fn validate(&self, binary: &[u8]) -> Result<(), CompileError>;
/// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<CompiledModule>, CompileError>;
/// Instantiates a WebAssembly module
unsafe fn instantiate(
&self,
compiled_module: &dyn CompiledModule,
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError>;
/// Serializes a WebAssembly module
fn serialize(&self, compiled_module: &dyn CompiledModule) -> Result<Vec<u8>, SerializeError>;
/// Deserializes a WebAssembly module
fn deserialize(&self, bytes: &[u8]) -> Result<Arc<CompiledModule>, DeserializeError>;
}

View File

@@ -1,4 +1,3 @@
// TODO: Move this errors into a common engine crate.
//! The WebAssembly possible errors
use crate::trap::RuntimeError;
use thiserror::Error;
@@ -47,7 +46,7 @@ pub enum ImportError {
/// Unknown Import.
/// This error occurs when an import was expected but not provided.
#[error("unknown import. Expected {0:?}")]
Unknown(ExternType),
UnknownImport(ExternType),
}
/// The WebAssembly.LinkError object indicates an error during
@@ -59,9 +58,6 @@ pub enum ImportError {
#[derive(Error, Debug)]
#[error("Link error: {0}")]
pub enum LinkError {
/// A wasm translation error occured.
Generic(String),
/// An error occurred when checking the import types.
#[error("Error while importing {0:?}.{1:?}: {2}")]
Import(String, String, ImportError),

43
lib/engine/src/lib.rs Normal file
View File

@@ -0,0 +1,43 @@
//! Generic Engine abstraction for Wasmer Engines.
#![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)
)]
#![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 engine;
mod error;
mod module;
mod resolver;
mod serialize;
mod trap;
mod tunables;
pub use crate::engine::Engine;
pub use crate::error::{
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
};
pub use crate::module::CompiledModule;
pub use crate::resolver::{resolve_imports, NullResolver, Resolver};
pub use crate::serialize::SerializableFunctionFrameInfo;
pub use crate::trap::*;
pub use crate::tunables::Tunables;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

28
lib/engine/src/module.rs Normal file
View File

@@ -0,0 +1,28 @@
use crate::error::InstantiationError;
use std::sync::Arc;
use wasmer_runtime::InstanceHandle;
use wasmer_runtime::Module;
use downcast_rs::{impl_downcast, Downcast};
/// The `CompiledModule` trait is used by engine implementors, such
/// as a JIT or Native execution.
pub trait CompiledModule: Downcast {
/// Finish instantiation of a `InstanceHandle`
///
/// # Unsafety
///
/// See `InstanceHandle::finish_instantiation`
unsafe fn finish_instantiation(
&self,
handle: &InstanceHandle,
) -> Result<(), InstantiationError>;
/// Return a reference-counting pointer to a module.
fn module(&self) -> &Module;
/// Return a reference-counting pointer to a module.
fn module_mut(&mut self) -> &mut Module;
}
impl_downcast!(CompiledModule); // `sync` => also produce `Arc` downcasts.

View File

@@ -3,7 +3,6 @@
use crate::error::{ImportError, LinkError};
use more_asserts::assert_ge;
use std::collections::HashSet;
use wasm_common::entity::PrimaryMap;
use wasm_common::{ExternType, ImportIndex, MemoryIndex, TableIndex};
use wasmer_runtime::{
@@ -41,7 +40,7 @@ fn get_extern_from_import(module: &Module, import_index: &ImportIndex) -> Extern
match import_index {
ImportIndex::Function(index) => {
let func = module.signatures[module.functions[*index]].clone();
ExternType::Func(func)
ExternType::Function(func)
}
ImportIndex::Table(index) => {
let table = module.tables[*index].clone();
@@ -67,7 +66,7 @@ fn get_extern_from_export(
match export {
Export::Function(ref f) => {
let func = signatures.lookup(f.signature).unwrap().clone();
ExternType::Func(func)
ExternType::Function(func)
}
Export::Table(ref t) => {
let table = t.plan().table.clone();
@@ -95,8 +94,6 @@ pub fn resolve_imports(
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> Result<Imports, LinkError> {
let dependencies = HashSet::new();
let mut function_imports = PrimaryMap::with_capacity(module.num_imported_funcs);
let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
@@ -110,7 +107,7 @@ pub fn resolve_imports(
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::Unknown(import_extern),
ImportError::UnknownImport(import_extern),
));
}
Some(r) => r,
@@ -125,16 +122,12 @@ pub fn resolve_imports(
}
match resolved {
Export::Function(ref f) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
function_imports.push(VMFunctionImport {
body: f.address,
vmctx: f.vmctx,
});
}
Export::Table(ref t) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
table_imports.push(VMTableImport {
definition: t.definition,
from: t.from,
@@ -168,16 +161,13 @@ pub fn resolve_imports(
}
}
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
memory_imports.push(VMMemoryImport {
definition: m.definition,
from: m.from,
});
}
Export::Global(ref g) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
global_imports.push(VMGlobalImport {
definition: g.definition,
});
@@ -186,7 +176,6 @@ pub fn resolve_imports(
}
Ok(Imports::new(
dependencies,
function_imports,
table_imports,
memory_imports,

107
lib/engine/src/serialize.rs Normal file
View File

@@ -0,0 +1,107 @@
use serde::de::{Deserializer, Visitor};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use std::fmt;
use wasmer_compiler::CompiledFunctionFrameInfo;
/// This is the unserialized verison of `CompiledFunctionFrameInfo`.
#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct UnprocessedFunctionFrameInfo {
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
}
impl UnprocessedFunctionFrameInfo {
/// Converts the `UnprocessedFunctionFrameInfo` to a `CompiledFunctionFrameInfo`
pub fn deserialize(&self) -> CompiledFunctionFrameInfo {
// let r = flexbuffers::Reader::get_root(&self.bytes).expect("Can't deserialize the info");
// CompiledFunctionFrameInfo::deserialize(r).expect("Can't deserialize the info")
bincode::deserialize(&self.bytes).expect("Can't deserialize the info")
}
/// Converts the `CompiledFunctionFrameInfo` to a `UnprocessedFunctionFrameInfo`
pub fn serialize(processed: &CompiledFunctionFrameInfo) -> Self {
// let mut s = flexbuffers::FlexbufferSerializer::new();
// processed
// .serialize(&mut s)
// .expect("Can't serialize the info");
// let bytes = s.take_buffer();
let bytes = bincode::serialize(&processed).expect("Can't serialize the info");
Self { bytes }
}
}
/// We hold the frame info in two states, mainly because we want to
/// process it lazily to speed up execution.
///
/// When a Trap occurs, we process the frame info lazily for each
/// function in the frame. That way we minimize as much as we can
/// the upfront effort.
///
/// The data can also be processed upfront. This will happen in the case
/// of compiling at the same time that emiting the JIT.
/// In that case, we don't need to deserialize/process anything
/// as the data is already in memory.
#[derive(Clone)]
pub enum SerializableFunctionFrameInfo {
/// The unprocessed frame info (binary)
Unprocessed(UnprocessedFunctionFrameInfo),
/// The processed frame info (memory struct)
Processed(CompiledFunctionFrameInfo),
}
impl SerializableFunctionFrameInfo {
/// Returns true if the extra function info is not yet
/// processed
pub fn is_unprocessed(&self) -> bool {
match self {
Self::Unprocessed(_) => true,
_ => false,
}
}
}
// Below:
// The custom ser/de for `SerializableFunctionFrameInfo`.
impl Serialize for SerializableFunctionFrameInfo {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let unprocessed = match self {
Self::Processed(processed) => UnprocessedFunctionFrameInfo::serialize(processed),
Self::Unprocessed(unprocessed) => unprocessed.clone(),
};
s.serialize_bytes(&unprocessed.bytes)
}
}
struct FunctionFrameInfoVisitor;
impl<'de> Visitor<'de> for FunctionFrameInfoVisitor {
type Value = UnprocessedFunctionFrameInfo;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("bytes")
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Ok(UnprocessedFunctionFrameInfo { bytes: v })
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> {
Ok(UnprocessedFunctionFrameInfo { bytes: v.to_vec() })
}
}
impl<'de> Deserialize<'de> for SerializableFunctionFrameInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(SerializableFunctionFrameInfo::Unprocessed(
deserializer.deserialize_byte_buf(FunctionFrameInfoVisitor)?,
))
}
}

View File

@@ -3,6 +3,7 @@ use backtrace::Backtrace;
use std::error::Error;
use std::fmt;
use std::sync::Arc;
use std::sync::RwLockReadGuard;
use wasmer_runtime::{raise_user_trap, Trap, TrapCode};
/// A struct representing an aborted instruction execution, with a message
@@ -26,12 +27,12 @@ impl RuntimeError {
/// Creates a new `Trap` with `message`.
/// # Example
/// ```
/// let trap = wasmer_jit::RuntimeError::new("unexpected error");
/// let trap = wasmer_engine::RuntimeError::new("unexpected error");
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Self {
let info = FRAME_INFO.read().unwrap();
Self::new_with_trace(&info, None, message.into(), Backtrace::new_unresolved())
Self::new_with_trace(info, None, message.into(), Backtrace::new_unresolved())
}
/// Create a new RuntimeError from a Trap.
@@ -47,17 +48,26 @@ impl RuntimeError {
*error.downcast().expect("only `Trap` errors are supported")
}
Trap::Jit { pc, backtrace } => {
let info = if info.should_process_frame(pc).unwrap_or(false) {
drop(info);
let mut info = FRAME_INFO.write().unwrap();
info.maybe_process_frame(pc).unwrap();
drop(info);
FRAME_INFO.read().unwrap()
} else {
info
};
let code = info
.lookup_trap_info(pc)
.map_or(TrapCode::StackOverflow, |info| info.trap_code);
Self::new_wasm(&info, Some(pc), code, backtrace)
Self::new_wasm(info, Some(pc), code, backtrace)
}
Trap::Wasm {
trap_code,
backtrace,
} => Self::new_wasm(&info, None, trap_code, backtrace),
} => Self::new_wasm(info, None, trap_code, backtrace),
Trap::OOM { backtrace } => {
Self::new_with_trace(&info, None, "out of memory".to_string(), backtrace)
Self::new_with_trace(info, None, "out of memory".to_string(), backtrace)
}
}
}
@@ -68,60 +78,69 @@ impl RuntimeError {
}
fn new_wasm(
info: &GlobalFrameInfo,
info: RwLockReadGuard<GlobalFrameInfo>,
trap_pc: Option<usize>,
code: TrapCode,
backtrace: Backtrace,
) -> Self {
let desc = match code {
TrapCode::StackOverflow => "call stack exhausted",
TrapCode::HeapSetterOutOfBounds => "memory out of bounds: data segment does not fit",
TrapCode::HeapAccessOutOfBounds => "out of bounds memory access",
TrapCode::TableSetterOutOfBounds => {
"table out of bounds: elements segment does not fit"
}
TrapCode::TableAccessOutOfBounds => "undefined element: out of bounds table access",
TrapCode::OutOfBounds => "out of bounds",
TrapCode::IndirectCallToNull => "uninitialized element",
TrapCode::BadSignature => "indirect call type mismatch",
TrapCode::IntegerOverflow => "integer overflow",
TrapCode::IntegerDivisionByZero => "integer divide by zero",
TrapCode::BadConversionToInteger => "invalid conversion to integer",
TrapCode::UnreachableCodeReached => "unreachable",
TrapCode::Interrupt => "interrupt",
TrapCode::User(_) => unreachable!(),
};
let msg = format!("{}", desc);
let msg = code.message().to_string();
Self::new_with_trace(info, trap_pc, msg, backtrace)
}
fn new_with_trace(
info: &GlobalFrameInfo,
info: RwLockReadGuard<GlobalFrameInfo>,
trap_pc: Option<usize>,
message: String,
native_trace: Backtrace,
) -> Self {
let mut wasm_trace = Vec::new();
for frame in native_trace.frames() {
let pc = frame.ip() as usize;
if pc == 0 {
continue;
let frames: Vec<usize> = native_trace
.frames()
.iter()
.filter_map(|frame| {
let pc = frame.ip() as usize;
if pc == 0 {
None
} else {
// Note that we need to be careful about the pc we pass in here to
// lookup frame information. This program counter is used to
// translate back to an original source location in the origin wasm
// module. If this pc is the exact pc that the trap happened at,
// then we look up that pc precisely. Otherwise backtrace
// information typically points at the pc *after* the call
// instruction (because otherwise it's likely a call instruction on
// the stack). In that case we want to lookup information for the
// previous instruction (the call instruction) so we subtract one as
// the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
Some(pc_to_lookup)
}
})
.collect();
// If any of the frames is not processed, we adquire the lock to
// modify the GlobalFrameInfo module.
let info = if frames
.iter()
.any(|pc| info.should_process_frame(*pc).unwrap_or(false))
{
// We drop the read lock, to get a write one.
drop(info);
let mut info = FRAME_INFO.write().unwrap();
for pc in frames.iter() {
drop(info.maybe_process_frame(*pc));
}
// Note that we need to be careful about the pc we pass in here to
// lookup frame information. This program counter is used to
// translate back to an original source location in the origin wasm
// module. If this pc is the exact pc that the trap happened at,
// then we look up that pc precisely. Otherwise backtrace
// information typically points at the pc *after* the call
// instruction (because otherwise it's likely a call instruction on
// the stack). In that case we want to lookup information for the
// previous instruction (the call instruction) so we subtract one as
// the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
if let Some(info) = info.lookup_frame_info(pc_to_lookup) {
wasm_trace.push(info);
}
}
drop(info);
FRAME_INFO.read().unwrap()
} else {
info
};
// Let's construct the trace
let wasm_trace = frames
.into_iter()
.filter_map(|pc| info.lookup_frame_info(pc))
.collect::<Vec<_>>();
Self {
inner: Arc::new(RuntimeErrorInner {
message,

View File

@@ -10,14 +10,14 @@
//! let module: Module = ...;
//! FRAME_INFO.register(module, compiled_functions);
//! ```
use crate::CompiledModule;
use crate::serialize::SerializableFunctionFrameInfo;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use wasm_common::entity::EntityRef;
use wasm_common::FuncIndex;
use wasmer_compiler::{FunctionAddressMap, SourceLoc, TrapInformation};
use wasmer_runtime::Module;
use wasm_common::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasm_common::LocalFunctionIndex;
use wasmer_compiler::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation};
use wasmer_runtime::{Module, VMFunctionBody};
lazy_static::lazy_static! {
/// This is a global cache of backtrace frame information for all active
@@ -54,13 +54,52 @@ struct ModuleFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<Module>,
frame_infos: PrimaryMap<LocalFunctionIndex, SerializableFunctionFrameInfo>,
}
impl ModuleFrameInfo {
fn function_debug_info(
&self,
local_index: LocalFunctionIndex,
) -> &SerializableFunctionFrameInfo {
&self.frame_infos.get(local_index).unwrap()
}
fn process_function_debug_info(&mut self, local_index: LocalFunctionIndex) {
let mut func = self.frame_infos.get_mut(local_index).unwrap();
let processed: CompiledFunctionFrameInfo = match func {
SerializableFunctionFrameInfo::Processed(_) => {
// This should be a no-op on processed info
return;
}
SerializableFunctionFrameInfo::Unprocessed(unprocessed) => unprocessed.deserialize(),
};
*func = SerializableFunctionFrameInfo::Processed(processed)
}
fn processed_function_frame_info(
&self,
local_index: LocalFunctionIndex,
) -> &CompiledFunctionFrameInfo {
match self.function_debug_info(local_index) {
SerializableFunctionFrameInfo::Processed(di) => &di,
_ => unreachable!("frame info should already be processed"),
}
}
/// Gets a function given a pc
fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
let (end, func) = self.functions.range(pc..).next()?;
if pc < func.start || *end < pc {
return None;
}
Some(func)
}
}
struct FunctionInfo {
start: usize,
index: FuncIndex,
traps: Vec<TrapInformation>,
instr_map: FunctionAddressMap,
local_index: LocalFunctionIndex,
}
impl GlobalFrameInfo {
@@ -69,14 +108,17 @@ impl GlobalFrameInfo {
/// Returns an object if this `pc` is known to some previously registered
/// module, or returns `None` if no information can be found.
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let (module, func) = self.func(pc)?;
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
// map that to a wasm original source location.
let rel_pos = pc - func.start;
let pos = match func
.instr_map
let instr_map = &module
.processed_function_frame_info(func.local_index)
.address_map;
let pos = match instr_map
.instructions
.binary_search_by_key(&rel_pos, |map| map.code_offset)
{
@@ -93,7 +135,7 @@ impl GlobalFrameInfo {
// always get called with a `pc` that's an exact instruction
// boundary.
Err(n) => {
let instr = &func.instr_map.instructions[n - 1];
let instr = &instr_map.instructions[n - 1];
if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
Some(n - 1)
} else {
@@ -109,38 +151,62 @@ impl GlobalFrameInfo {
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
let instr = match pos {
Some(pos) => func.instr_map.instructions[pos].srcloc,
None => func.instr_map.start_srcloc,
Some(pos) => instr_map.instructions[pos].srcloc,
None => instr_map.start_srcloc,
};
let func_index = module.module.func_index(func.local_index);
Some(FrameInfo {
module_name: module.module.name(),
func_index: func.index.index() as u32,
func_name: module.module.func_names.get(&func.index).cloned(),
func_index: func_index.index() as u32,
func_name: module.module.func_names.get(&func_index).cloned(),
instr,
func_start: func.instr_map.start_srcloc,
func_start: instr_map.start_srcloc,
})
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let (_module, func) = self.func(pc)?;
let idx = func
.traps
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let traps = &module.processed_function_frame_info(func.local_index).traps;
let idx = traps
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
.ok()?;
Some(&func.traps[idx])
Some(&traps[idx])
}
fn func(&self, pc: usize) -> Option<(&ModuleFrameInfo, &FunctionInfo)> {
let (end, info) = self.ranges.range(pc..).next()?;
if pc < info.start || *end < pc {
/// Should process the frame before anything?
pub fn should_process_frame(&self, pc: usize) -> Option<bool> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let extra_func_info = module.function_debug_info(func.local_index);
Some(extra_func_info.is_unprocessed())
}
/// Process the frame info in case is not yet processed
pub fn maybe_process_frame(&mut self, pc: usize) -> Option<()> {
let module = self.module_info_mut(pc)?;
let func = module.function_info(pc)?;
module.process_function_debug_info(func.local_index);
Some(())
}
/// Gets a module given a pc
fn module_info(&self, pc: usize) -> Option<&ModuleFrameInfo> {
let (end, module_info) = self.ranges.range(pc..).next()?;
if pc < module_info.start || *end < pc {
return None;
}
let (end, func) = info.functions.range(pc..).next()?;
if pc < func.start || *end < pc {
Some(module_info)
}
/// Gets a module given a pc
fn module_info_mut(&mut self, pc: usize) -> Option<&mut ModuleFrameInfo> {
let (end, module_info) = self.ranges.range_mut(pc..).next()?;
if pc < module_info.start || *end < pc {
return None;
}
Some((info, func))
Some(module_info)
}
}
@@ -158,16 +224,15 @@ impl Drop for GlobalFrameInfoRegistration {
/// compiled functions within `module`. If the `module` has no functions
/// then `None` will be returned. Otherwise the returned object, when
/// dropped, will be used to unregister all name information from this map.
pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration> {
pub fn register(
module: Arc<Module>,
finished_functions: &BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
frame_infos: PrimaryMap<LocalFunctionIndex, SerializableFunctionFrameInfo>,
) -> Option<GlobalFrameInfoRegistration> {
let mut min = usize::max_value();
let mut max = 0;
let mut functions = BTreeMap::new();
for (((i, allocated), traps), instrs) in module
.finished_functions()
.iter()
.zip(module.traps().values())
.zip(module.address_transform().values())
{
for (i, allocated) in finished_functions.iter() {
let (start, end) = unsafe {
let ptr = (**allocated).as_ptr();
let len = (**allocated).len();
@@ -177,9 +242,7 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
max = cmp::max(max, end);
let func = FunctionInfo {
start,
index: module.module().func_index(i),
traps: traps.to_vec(),
instr_map: (*instrs).clone(),
local_index: i,
};
assert!(functions.insert(end, func).is_none());
}
@@ -203,7 +266,8 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
ModuleFrameInfo {
start: min,
functions,
module: module.module().clone(),
module: module.clone(),
frame_infos,
},
);
assert!(prev.is_none());

View File

@@ -0,0 +1,6 @@
mod error;
mod frame_info;
pub use error::RuntimeError;
pub use frame_info::{
register as register_frame_info, FrameInfo, GlobalFrameInfoRegistration, FRAME_INFO,
};

View File

@@ -0,0 +1,71 @@
use crate::error::LinkError;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType,
TableIndex, TableType,
};
use wasmer_runtime::{LinearMemory, Module, Table, VMGlobalDefinition};
use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunables for an engine
pub trait Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType`
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan;
/// Get a `TablePlan` for the provided `TableType`
fn table_plan(&self, table: TableType) -> TablePlan;
/// Create a memory given a memory type
fn create_memory(&self, memory_type: MemoryPlan) -> Result<LinearMemory, String>;
/// Create a memory given a memory type
fn create_table(&self, table_type: TablePlan) -> Result<Table, String>;
/// Allocate memory for just the memories of the current module.
fn create_memories(
&self,
module: &Module,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
) -> Result<PrimaryMap<LocalMemoryIndex, LinearMemory>, LinkError> {
let num_imports = module.num_imported_memories;
let mut memories: PrimaryMap<LocalMemoryIndex, _> =
PrimaryMap::with_capacity(module.memories.len() - num_imports);
for index in num_imports..module.memories.len() {
let plan = memory_plans[MemoryIndex::new(index)].clone();
memories.push(self.create_memory(plan).map_err(LinkError::Resource)?);
}
Ok(memories)
}
/// Allocate memory for just the tables of the current module.
fn create_tables(
&self,
module: &Module,
table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> Result<PrimaryMap<LocalTableIndex, Table>, LinkError> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<LocalTableIndex, _> =
PrimaryMap::with_capacity(module.tables.len() - num_imports);
for index in num_imports..module.tables.len() {
let plan = table_plans[TableIndex::new(index)].clone();
tables.push(self.create_table(plan).map_err(LinkError::Resource)?);
}
Ok(tables)
}
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(
&self,
module: &Module,
) -> Result<PrimaryMap<LocalGlobalIndex, VMGlobalDefinition>, LinkError> {
let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for _ in &module.globals.values().as_slice()[num_imports..] {
vmctx_globals.push(VMGlobalDefinition::new());
}
Ok(vmctx_globals)
}
}

View File

@@ -1,9 +0,0 @@
# Wasmer JIT
The Wasmer JIT is usable with any compiler implementation
based on `wasmer-compiler`.
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: this project started as a subfork of [this crate](https://crates.io/crates/wasmtime-jit).

View File

@@ -1,279 +0,0 @@
//! JIT compilation.
use crate::error::InstantiationError;
use crate::resolver::Resolver;
use crate::tunables::Tunables;
use crate::CodeMemory;
use crate::{CompiledModule, DeserializeError, SerializeError};
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{FuncType, LocalFuncIndex, MemoryIndex, MemoryType, TableIndex, TableType};
use wasmer_compiler::{
Compilation, CompileError, Compiler as BaseCompiler, CompilerConfig, FunctionAddressMap,
FunctionBodyData, JumpTableOffsets, ModuleTranslationState, Relocations, TrapInformation,
};
use wasmer_runtime::{
InstanceHandle, LinearMemory, MemoryPlan, Module, SignatureRegistry, Table, TablePlan,
VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
/// A WebAssembly `JIT` Engine.
pub struct JITEngine {
inner: Arc<RefCell<JITEngineInner>>,
}
impl JITEngine {
/// Create a new JIT Engine given config
pub fn new<T: CompilerConfig>(config: &T) -> Self
where
T: ?Sized,
{
let compiler = config.compiler();
let tunables = Tunables::for_target(compiler.target().triple());
Self {
inner: Arc::new(RefCell::new(JITEngineInner {
compiler,
tunables,
trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
})),
}
}
fn compiler(&self) -> std::cell::Ref<'_, JITEngineInner> {
self.inner.borrow()
}
fn compiler_mut(&self) -> std::cell::RefMut<'_, JITEngineInner> {
self.inner.borrow_mut()
}
/// Creates a memory
pub fn create_memory(&self, memory_type: &MemoryType) -> Result<LinearMemory, String> {
self.compiler().create_memory(memory_type)
}
/// Creates a table
pub fn create_table(&self, table_type: &TableType) -> Table {
self.compiler().create_table(table_type)
}
/// Register a signature
pub fn register_signature(&self, func_type: &FuncType) -> VMSharedSignatureIndex {
let compiler = self.compiler();
compiler.signatures().register(func_type)
}
/// Lookup a signature
pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FuncType> {
let compiler = self.compiler();
compiler.signatures().lookup(sig)
}
/// Retrieves a trampoline given a signature
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
self.compiler().trampoline(sig)
}
/// Validates a WebAssembly module
pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
self.compiler().validate(binary)
}
/// Compile a WebAssembly binary
pub fn compile(&self, binary: &[u8]) -> Result<CompiledModule, CompileError> {
CompiledModule::new(&mut self.compiler_mut(), binary)
}
/// Instantiates a WebAssembly module
pub fn instantiate(
&self,
compiled_module: &CompiledModule,
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
unsafe { compiled_module.instantiate(&self.compiler(), resolver, Box::new(())) }
}
/// Serializes a WebAssembly module
pub fn serialize(&self, compiled_module: &CompiledModule) -> Result<Vec<u8>, SerializeError> {
compiled_module.serialize()
}
/// Deserializes a WebAssembly module
pub fn deserialize(&self, bytes: &[u8]) -> Result<CompiledModule, DeserializeError> {
CompiledModule::deserialize(&mut self.compiler_mut(), bytes)
}
}
impl Clone for JITEngine {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
/// The inner contents of `JITEngine`
pub struct JITEngineInner {
/// The compiler
compiler: Box<dyn BaseCompiler>,
/// The tunable values
tunables: Tunables,
/// Pointers to trampoline functions used to enter particular signatures
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The code memory is responsible of publishing the compiled
/// functions to memory.
code_memory: CodeMemory,
/// The signature registry is used mainly to operate with trampolines
/// performantly.
signatures: SignatureRegistry,
}
impl JITEngineInner {
/// Return the tunables in use by this engine.
pub fn tunables(&self) -> &Tunables {
&self.tunables
}
/// Gets the compiler associated to this JIT
pub fn compiler(&self) -> &dyn BaseCompiler {
&*self.compiler
}
/// Validate the module
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.compiler().validate_module(data)
}
/// Compile the given function bodies.
pub(crate) fn compile_module<'data>(
&mut self,
module: &Module,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
self.compiler.compile_module(
module,
module_translation,
function_body_inputs,
memory_plans,
table_plans,
)
}
/// Compile the given function bodies.
pub(crate) fn compile<'data>(
&mut self,
module: &Module,
compilation: &Compilation,
) -> Result<
(
PrimaryMap<LocalFuncIndex, *mut [VMFunctionBody]>,
PrimaryMap<LocalFuncIndex, JumpTableOffsets>,
Relocations,
PrimaryMap<LocalFuncIndex, Vec<TrapInformation>>,
PrimaryMap<LocalFuncIndex, FunctionAddressMap>,
),
CompileError,
> {
let relocations = compilation.get_relocations();
let traps = compilation.get_traps();
// Allocate all of the compiled functions into executable memory,
// copying over their contents.
let allocated_functions =
self.code_memory
.allocate_functions(&compilation)
.map_err(|message| {
CompileError::Resource(format!(
"failed to allocate memory for functions: {}",
message
))
})?;
// Trampoline generation.
// We do it in two steps:
// 1. Generate only the trampolines for the signatures that are unique
// 2. Push the compiled code to memory
let mut unique_signatures: HashMap<VMSharedSignatureIndex, FuncType> = HashMap::new();
// for sig in module.exported_signatures() {
for sig in module.signatures.values() {
let index = self.signatures.register(&sig);
if unique_signatures.contains_key(&index) {
continue;
}
unique_signatures.insert(index, sig.clone());
}
let compiled_trampolines = self
.compiler
.compile_wasm_trampolines(&unique_signatures.values().cloned().collect::<Vec<_>>())?;
for ((index, _), compiled_function) in
unique_signatures.iter().zip(compiled_trampolines.iter())
{
let ptr = self
.code_memory
.allocate_for_function(compiled_function)
.map_err(|message| CompileError::Resource(message))?
.as_ptr();
let trampoline =
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
self.trampolines.insert(*index, trampoline);
}
let jt_offsets = compilation.get_jt_offsets();
let address_maps = compilation.get_address_maps();
Ok((
allocated_functions,
jt_offsets,
relocations,
traps,
address_maps,
))
}
/// Make a memory plan given a memory type
pub(crate) fn make_memory_plan(&self, memory_type: &MemoryType) -> MemoryPlan {
self.tunables().memory_plan(memory_type.clone())
}
/// Make a memory plan given a memory type
pub(crate) fn make_table_plan(&self, table_type: &TableType) -> TablePlan {
self.tunables().table_plan(table_type.clone())
}
/// Create a memory given a memory type
pub fn create_memory(&self, memory_type: &MemoryType) -> Result<LinearMemory, String> {
let plan = self.make_memory_plan(memory_type);
LinearMemory::new(&plan)
}
/// Create a memory given a memory type
pub fn create_table(&self, table_type: &TableType) -> Table {
let plan = self.make_table_plan(table_type);
Table::new(&plan)
}
/// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.publish();
}
/// Shared signature registry.
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
}
/// Gets the trampoline pre-registered for a particular signature
pub fn trampoline(&self, sig: VMSharedSignatureIndex) -> Option<VMTrampoline> {
self.trampolines.get(&sig).cloned()
}
}

View File

@@ -1,415 +0,0 @@
//! Define the `instantiate` function, which takes a byte array containing an
//! encoded wasm module and returns a live wasm instance. Also, define
//! `CompiledModule` to allow compiling and instantiating to be done as separate
//! steps.
use crate::engine::JITEngineInner;
use crate::error::{DeserializeError, SerializeError};
use crate::error::{InstantiationError, LinkError};
use crate::link::link_module;
use crate::resolver::{resolve_imports, Resolver};
use crate::trap::register as register_frame_info;
use crate::trap::GlobalFrameInfoRegistration;
use crate::trap::RuntimeError;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::sync::{Arc, Mutex};
use wasm_common::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasm_common::{
DataInitializer, DataInitializerLocation, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex,
LocalTableIndex, MemoryIndex, SignatureIndex, TableIndex,
};
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{Compilation, CompileError, FunctionAddressMap, TrapInformation};
use wasmer_runtime::{
InstanceHandle, LinearMemory, Module, SignatureRegistry, Table, VMFunctionBody,
VMGlobalDefinition, VMSharedSignatureIndex,
};
use wasmer_runtime::{MemoryPlan, TablePlan};
/// Structure to cache the content ot the compilation
#[derive(Serialize, Deserialize)]
struct CacheRawCompiledModule {
compilation: Arc<Compilation>,
module: Arc<Module>,
data_initializers: Arc<Box<[OwnedDataInitializer]>>,
// Plans for that module
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
}
/// This is similar to `CompiledModule`, but references the data initializers
/// from the wasm buffer rather than holding its own copy.
struct RawCompiledModule {
compilation: Compilation,
module: Module,
finished_functions: BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
traps: BoxedSlice<LocalFuncIndex, Vec<TrapInformation>>,
address_transform: BoxedSlice<LocalFuncIndex, FunctionAddressMap>,
// Plans
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
}
impl RawCompiledModule {
/// Create a new `RawCompiledModule` by compiling the wasm module in `data` and instatiating it.
fn new(jit: &mut JITEngineInner, data: &[u8]) -> Result<Self, CompileError> {
let environ = ModuleEnvironment::new();
let translation = environ
.translate(data)
.map_err(|error| CompileError::Wasm(error))?;
let memory_plans: PrimaryMap<MemoryIndex, MemoryPlan> = translation
.module
.memories
.iter()
.map(|(_index, memory_type)| jit.make_memory_plan(memory_type))
.collect();
let table_plans: PrimaryMap<TableIndex, TablePlan> = translation
.module
.tables
.iter()
.map(|(_index, table_type)| jit.make_table_plan(table_type))
.collect();
let compilation = jit.compile_module(
&translation.module,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?;
let data_initializers = translation
.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
RawCompiledModule::from_parts(
jit,
compilation,
translation.module,
data_initializers,
memory_plans,
table_plans,
)
}
/// Construct a `RawCompiledModule` from the most basic component parts.
fn from_parts(
jit: &mut JITEngineInner,
compilation: Compilation,
module: Module,
data_initializers: Box<[OwnedDataInitializer]>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Self, CompileError> {
let (finished_functions, jt_offsets, relocations, traps, address_transforms) =
jit.compile(&module, &compilation)?;
link_module(&module, &finished_functions, &jt_offsets, relocations);
// Compute indices into the shared signature table.
let signatures = {
let signature_registry = jit.signatures();
module
.signatures
.values()
.map(|sig| signature_registry.register(sig))
.collect::<PrimaryMap<_, _>>()
};
// Make all code compiled thus far executable.
jit.publish_compiled_code();
Ok(Self {
module: module,
finished_functions: finished_functions.into_boxed_slice(),
compilation,
data_initializers: data_initializers,
signatures: signatures.into_boxed_slice(),
traps: traps.into_boxed_slice(),
address_transform: address_transforms.into_boxed_slice(),
memory_plans,
table_plans,
})
}
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
compilation: Arc<Compilation>,
module: Arc<Module>,
finished_functions: BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]>,
data_initializers: Arc<Box<[OwnedDataInitializer]>>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
traps: BoxedSlice<LocalFuncIndex, Vec<TrapInformation>>,
address_transform: BoxedSlice<LocalFuncIndex, FunctionAddressMap>,
frame_info_registration: Mutex<Option<Option<GlobalFrameInfoRegistration>>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
}
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
pub fn new(jit: &mut JITEngineInner, data: &[u8]) -> Result<Self, CompileError> {
let raw = RawCompiledModule::new(jit, data)?;
Ok(Self::from_parts(
raw.compilation,
raw.module,
raw.finished_functions,
raw.data_initializers,
raw.signatures.clone(),
raw.traps,
raw.address_transform,
raw.memory_plans,
raw.table_plans,
))
}
/// Serialize a CompiledModule
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
let cached = CacheRawCompiledModule {
compilation: self.compilation.clone(),
module: self.module.clone(),
data_initializers: self.data_initializers.clone(),
memory_plans: self.memory_plans.clone(),
table_plans: self.table_plans.clone(),
};
bincode::serialize(&cached).map_err(|e| SerializeError::Generic(format!("{:?}", e)))
}
/// Deserialize a CompiledModule
pub fn deserialize(
jit: &mut JITEngineInner,
bytes: &[u8],
) -> Result<CompiledModule, DeserializeError> {
let cached: CacheRawCompiledModule = bincode::deserialize(bytes)
.map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
let compilation = Arc::try_unwrap(cached.compilation);
let module = Arc::try_unwrap(cached.module);
let data_initializers = Arc::try_unwrap(cached.data_initializers);
let memory_plans = cached.memory_plans;
let table_plans = cached.table_plans;
if let (Ok(c), Ok(m), Ok(d)) = (compilation, module, data_initializers) {
let raw = RawCompiledModule::from_parts(jit, c, m, d, memory_plans, table_plans)
.map_err(|e| DeserializeError::Compiler(e))?;
return Ok(Self::from_parts(
raw.compilation,
raw.module,
raw.finished_functions,
raw.data_initializers,
raw.signatures.clone(),
raw.traps,
raw.address_transform,
raw.memory_plans,
raw.table_plans,
));
}
Err(DeserializeError::Generic(
"Can't deserialize the data".to_string(),
))
}
/// Construct a `CompiledModule` from component parts.
pub fn from_parts(
compilation: Compilation,
module: Module,
finished_functions: BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
traps: BoxedSlice<LocalFuncIndex, Vec<TrapInformation>>,
address_transform: BoxedSlice<LocalFuncIndex, FunctionAddressMap>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Self {
Self {
compilation: Arc::new(compilation),
module: Arc::new(module),
finished_functions,
data_initializers: Arc::new(data_initializers),
signatures,
traps,
address_transform,
frame_info_registration: Mutex::new(None),
memory_plans,
table_plans,
}
}
fn memory_plans(&self) -> &PrimaryMap<MemoryIndex, MemoryPlan> {
&self.memory_plans
}
fn table_plans(&self) -> &PrimaryMap<TableIndex, TablePlan> {
&self.table_plans
}
/// Crate an `Instance` from this `CompiledModule`.
///
/// Note that if only one instance of this module is needed, it may be more
/// efficient to call the top-level `instantiate`, since that avoids copying
/// the data initializers.
///
/// # Unsafety
///
/// See `InstanceHandle::new`
pub unsafe fn instantiate(
&self,
jit: &JITEngineInner,
resolver: &dyn Resolver,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
let is_bulk_memory: bool = jit.compiler().features().bulk_memory;
let sig_registry: &SignatureRegistry = jit.signatures();
let data_initializers = self
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>();
let imports = resolve_imports(
&self.module,
&sig_registry,
resolver,
self.memory_plans(),
self.table_plans(),
)
.map_err(InstantiationError::Link)?;
let finished_memories =
create_memories(&self.module, self.memory_plans()).map_err(InstantiationError::Link)?;
let finished_tables = create_tables(&self.module, self.table_plans());
let finished_globals = create_globals(&self.module);
// Register the frame info for the module
self.register_frame_info();
InstanceHandle::new(
Arc::clone(&self.module),
self.finished_functions.clone(),
finished_memories,
finished_tables,
finished_globals,
imports,
&data_initializers,
self.signatures.clone(),
is_bulk_memory,
host_state,
)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
/// Return a reference-counting pointer to a module.
pub fn module(&self) -> &Arc<Module> {
&self.module
}
/// Return a reference-counting pointer to a module.
pub fn module_mut(&mut self) -> &mut Arc<Module> {
&mut self.module
}
/// Return a reference to a module.
pub fn module_ref(&self) -> &Module {
&self.module
}
/// Returns the map of all finished JIT functions compiled for this module
pub fn finished_functions(&self) -> &BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]> {
&self.finished_functions
}
/// Register this module's stack frame information into the global scope.
///
/// This is required to ensure that any traps can be properly symbolicated.
fn register_frame_info(&self) {
let mut info = self.frame_info_registration.lock().unwrap();
if info.is_some() {
return;
}
*info = Some(register_frame_info(&self));
}
/// Returns the a map for all traps in this module.
pub fn traps(&self) -> &BoxedSlice<LocalFuncIndex, Vec<TrapInformation>> {
&self.traps
}
/// Returns a map of compiled addresses back to original bytecode offsets.
pub fn address_transform(&self) -> &BoxedSlice<LocalFuncIndex, FunctionAddressMap> {
&self.address_transform
}
}
/// Allocate memory for just the memories of the current module.
fn create_memories(
module: &Module,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
) -> Result<BoxedSlice<LocalMemoryIndex, LinearMemory>, LinkError> {
let num_imports = module.num_imported_memories;
let mut memories: PrimaryMap<LocalMemoryIndex, _> =
PrimaryMap::with_capacity(module.memories.len() - num_imports);
for index in num_imports..module.memories.len() {
let plan = memory_plans[MemoryIndex::new(index)].clone();
memories.push(LinearMemory::new(&plan).map_err(LinkError::Resource)?);
}
Ok(memories.into_boxed_slice())
}
/// Allocate memory for just the tables of the current module.
fn create_tables(
module: &Module,
table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> BoxedSlice<LocalTableIndex, Table> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<LocalTableIndex, _> =
PrimaryMap::with_capacity(module.tables.len() - num_imports);
for index in num_imports..module.tables.len() {
let plan = table_plans[TableIndex::new(index)].clone();
tables.push(Table::new(&plan));
}
tables.into_boxed_slice()
}
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(module: &Module) -> BoxedSlice<LocalGlobalIndex, VMGlobalDefinition> {
let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for _ in &module.globals.values().as_slice()[num_imports..] {
vmctx_globals.push(VMGlobalDefinition::new());
}
vmctx_globals.into_boxed_slice()
}
/// Similar to `DataInitializer`, but owns its own copy of the data rather
/// than holding a slice of the original module.
#[derive(Clone, Serialize, Deserialize)]
pub struct OwnedDataInitializer {
/// The location where the initialization is to be performed.
location: DataInitializerLocation,
/// The initialization data.
data: Box<[u8]>,
}
impl OwnedDataInitializer {
fn new(borrowed: &DataInitializer<'_>) -> Self {
Self {
location: borrowed.location.clone(),
data: borrowed.data.to_vec().into_boxed_slice(),
}
}
}

View File

@@ -1,4 +0,0 @@
mod error;
mod frame_info;
pub use error::RuntimeError;
pub use frame_info::{register, FrameInfo, GlobalFrameInfoRegistration, FRAME_INFO};

View File

@@ -1,10 +1,12 @@
# Wasmer Runtime
This is the `wasmer-runtime` crate, which contains wasm runtime library
support, supporting the wasm ABI used by any [`wasmer-compiler`] implementation,
and [`wasmer-jit`].
support, supporting the wasm ABI used by any [`wasmer-engine`] implementation.
> Note: this project started as a subfork of [this crate](https://crates.io/crates/wasmtime-runtime).
[`wasmer-engine`]: https://crates.io/crates/wasmer-engine
[`wasmer-compiler`]: https://crates.io/crates/wasmer-compiler
[`wasmer-jit`]: https://crates.io/crates/wasmer-jit
### Acknowledgments
This project borrowed some of the code for the VM structure and trapping from the [wasmtime-runtime](https://crates.io/crates/wasmtime-runtime).
Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project.

View File

@@ -1,17 +1,13 @@
use crate::instance::InstanceHandle;
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
use std::collections::HashSet;
use wasm_common::entity::{BoxedSlice, PrimaryMap};
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
use wasm_common::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex};
/// Resolved import pointers.
#[derive(Clone)]
pub struct Imports {
/// The set of instances that the imports depend on.
pub dependencies: HashSet<InstanceHandle>,
/// Resolved addresses for imported functions.
pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,
pub functions: BoxedSlice<FunctionIndex, VMFunctionImport>,
/// Resolved addresses for imported tables.
pub tables: BoxedSlice<TableIndex, VMTableImport>,
@@ -26,14 +22,12 @@ pub struct Imports {
impl Imports {
/// Construct a new `Imports` instance.
pub fn new(
dependencies: HashSet<InstanceHandle>,
function_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
function_imports: PrimaryMap<FunctionIndex, VMFunctionImport>,
table_imports: PrimaryMap<TableIndex, VMTableImport>,
memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
) -> Self {
Self {
dependencies,
functions: function_imports.into_boxed_slice(),
tables: table_imports.into_boxed_slice(),
memories: memory_imports.into_boxed_slice(),
@@ -44,7 +38,6 @@ impl Imports {
/// Construct a new `Imports` instance with no imports.
pub fn none() -> Self {
Self {
dependencies: HashSet::new(),
functions: PrimaryMap::new().into_boxed_slice(),
tables: PrimaryMap::new().into_boxed_slice(),
memories: PrimaryMap::new().into_boxed_slice(),

View File

@@ -5,7 +5,7 @@ use crate::export::Export;
use crate::imports::Imports;
use crate::memory::LinearMemory;
use crate::table::Table;
use crate::trap::{catch_traps, init_traphandlers, Trap, TrapCode};
use crate::trap::{catch_traps, init_traps, Trap, TrapCode};
use crate::vmcontext::{
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport,
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
@@ -24,8 +24,8 @@ use std::sync::Arc;
use std::{mem, ptr, slice};
use wasm_common::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
use wasm_common::{
DataIndex, DataInitializer, ElemIndex, ExportIndex, FuncIndex, GlobalIndex, GlobalInit,
LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, Pages,
DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit,
LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, Pages,
SignatureIndex, TableIndex,
};
@@ -35,7 +35,7 @@ cfg_if::cfg_if! {
impl InstanceHandle {
/// Set a custom signal handler
pub fn set_signal_handler<H>(&mut self, handler: H)
pub fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
{
@@ -47,7 +47,7 @@ cfg_if::cfg_if! {
impl InstanceHandle {
/// Set a custom signal handler
pub fn set_signal_handler<H>(&mut self, handler: H)
pub fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool,
{
@@ -62,14 +62,6 @@ cfg_if::cfg_if! {
/// This is repr(C) to ensure that the vmctx field is last.
#[repr(C)]
pub(crate) struct Instance {
/// The number of references to this `Instance`.
refcount: Cell<usize>,
/// `Instance`s from which this `Instance` imports. These won't
/// create reference cycles because wasm instances can't cyclically
/// import from each other.
dependencies: HashSet<InstanceHandle>,
/// The `Module` this `Instance` was instantiated from.
module: Arc<Module>,
@@ -92,7 +84,7 @@ pub(crate) struct Instance {
passive_data: RefCell<HashMap<DataIndex, Arc<[u8]>>>,
/// Pointers to functions in executable memory.
finished_functions: BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]>,
finished_functions: BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
/// Hosts can store arbitrary per-instance information here.
host_state: Box<dyn Any>,
@@ -136,7 +128,7 @@ impl Instance {
}
/// Return the indexed `VMFunctionImport`.
fn imported_function(&self, index: FuncIndex) -> &VMFunctionImport {
fn imported_function(&self, index: FunctionIndex) -> &VMFunctionImport {
let index = usize::try_from(index.as_u32()).unwrap();
unsafe { &*self.imported_functions_ptr().add(index) }
}
@@ -347,7 +339,7 @@ impl Instance {
/// Return an iterator over the exports of this instance.
///
/// Specifically, it provides access to the key-value pairs, where they keys
/// Specifically, it provides access to the key-value pairs, where the keys
/// are export names, and the values are export declarations which can be
/// resolved `lookup_by_declaration`.
pub fn exports(&self) -> indexmap::map::Iter<String, ExportIndex> {
@@ -544,9 +536,9 @@ impl Instance {
Layout::from_size_align(size, align).unwrap()
}
/// Get a `VMCallerCheckedAnyfunc` for the given `FuncIndex`.
fn get_caller_checked_anyfunc(&self, index: FuncIndex) -> VMCallerCheckedAnyfunc {
if index == FuncIndex::reserved_value() {
/// Get a `VMCallerCheckedAnyfunc` for the given `FunctionIndex`.
fn get_caller_checked_anyfunc(&self, index: FunctionIndex) -> VMCallerCheckedAnyfunc {
if index == FunctionIndex::reserved_value() {
return VMCallerCheckedAnyfunc::default();
}
@@ -783,14 +775,12 @@ impl InstanceHandle {
/// safety.
pub unsafe fn new(
module: Arc<Module>,
finished_functions: BoxedSlice<LocalFuncIndex, *mut [VMFunctionBody]>,
finished_functions: BoxedSlice<LocalFunctionIndex, *mut [VMFunctionBody]>,
finished_memories: BoxedSlice<LocalMemoryIndex, LinearMemory>,
finished_tables: BoxedSlice<LocalTableIndex, Table>,
finished_globals: BoxedSlice<LocalGlobalIndex, VMGlobalDefinition>,
imports: Imports,
data_initializers: &[DataInitializer<'_>],
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
is_bulk_memory: bool,
host_state: Box<dyn Any>,
) -> Result<Self, Trap> {
let vmctx_tables = finished_tables
@@ -813,8 +803,6 @@ impl InstanceHandle {
let handle = {
let instance = Instance {
refcount: Cell::new(1),
dependencies: imports.dependencies,
module,
offsets,
memories: finished_memories,
@@ -883,30 +871,42 @@ impl InstanceHandle {
VMBuiltinFunctionsArray::initialized(),
);
// Ensure that our signal handlers are ready for action.
init_traps();
// Perform infallible initialization in this constructor, while fallible
// initialization is deferred to the `initialize` method.
initialize_passive_elements(instance);
initialize_globals(instance);
Ok(handle)
}
/// Finishes the instantiation process started by `Instance::new`.
///
/// Only safe to call immediately after instantiation.
pub unsafe fn finish_instantiation(
&self,
is_bulk_memory: bool,
data_initializers: &[DataInitializer<'_>],
) -> Result<(), Trap> {
// Check initializer bounds before initializing anything. Only do this
// when bulk memory is disabled, since the bulk memory proposal changes
// instantiation such that the intermediate results of failed
// initializations are visible.
if !is_bulk_memory {
check_table_init_bounds(instance)?;
check_memory_init_bounds(instance, data_initializers)?;
check_table_init_bounds(self.instance())?;
check_memory_init_bounds(self.instance(), data_initializers)?;
}
// Apply the initializers.
initialize_tables(instance)?;
initialize_passive_elements(instance);
initialize_memories(instance, data_initializers)?;
initialize_globals(instance);
// Ensure that our signal handlers are ready for action.
// TODO: Move these calls out of `InstanceHandle`.
init_traphandlers();
initialize_tables(self.instance())?;
initialize_memories(self.instance(), data_initializers)?;
// The WebAssembly spec specifies that the start function is
// invoked automatically at instantiation time.
instance.invoke_start_function()?;
Ok(handle)
self.instance().invoke_start_function()?;
Ok(())
}
/// Create a new `InstanceHandle` pointing at the instance
@@ -917,7 +917,6 @@ impl InstanceHandle {
/// be a `VMContext` allocated as part of an `Instance`.
pub unsafe fn from_vmctx(vmctx: *mut VMContext) -> Self {
let instance = (&mut *vmctx).instance();
instance.refcount.set(instance.refcount.get() + 1);
Self {
instance: instance as *const Instance as *mut Instance,
}
@@ -1032,32 +1031,36 @@ impl InstanceHandle {
pub(crate) fn instance(&self) -> &Instance {
unsafe { &*(self.instance as *const Instance) }
}
/// Deallocates memory associated with this instance.
///
/// Note that this is unsafe because there might be other handles to this
/// `InstanceHandle` elsewhere, and there's nothing preventing usage of
/// this handle after this function is called.
pub unsafe fn dealloc(&self) {
let instance = self.instance();
let layout = instance.alloc_layout();
ptr::drop_in_place(self.instance);
alloc::dealloc(self.instance.cast(), layout);
}
}
impl Clone for InstanceHandle {
fn clone(&self) -> Self {
let instance = self.instance();
instance.refcount.set(instance.refcount.get() + 1);
Self {
instance: self.instance,
}
}
}
impl Drop for InstanceHandle {
fn drop(&mut self) {
let instance = self.instance();
let count = instance.refcount.get();
instance.refcount.set(count - 1);
if count == 1 {
let layout = instance.alloc_layout();
unsafe {
ptr::drop_in_place(self.instance);
alloc::dealloc(self.instance.cast(), layout);
}
}
}
}
// TODO: uncomment this, as we need to store the handles
// in the store, and once the store is dropped, then the instances
// will too.
// impl Drop for InstanceHandle {
// fn drop(&mut self) {
// unsafe { self.dealloc() }
// }
// }
fn check_table_init_bounds(instance: &Instance) -> Result<(), Trap> {
let module = Arc::clone(&instance.module);
@@ -1170,7 +1173,7 @@ fn initialize_tables(instance: &Instance) -> Result<(), Trap> {
}
/// Initialize the `Instance::passive_elements` map by resolving the
/// `Module::passive_elements`'s `FuncIndex`s into `VMCallerCheckedAnyfunc`s for
/// `Module::passive_elements`'s `FunctionIndex`s into `VMCallerCheckedAnyfunc`s for
/// this instance.
fn initialize_passive_elements(instance: &Instance) {
let mut passive_elements = instance.passive_elements.borrow_mut();
@@ -1226,26 +1229,23 @@ fn initialize_memories(
fn initialize_globals(instance: &Instance) {
let module = Arc::clone(&instance.module);
let num_imports = module.num_imported_globals;
for (index, global) in module.globals.iter().skip(num_imports) {
let def_index = module.local_global_index(index).unwrap();
for (index, initializer) in module.global_initializers.iter() {
unsafe {
let to = instance.global_ptr(def_index);
match global.initializer {
GlobalInit::I32Const(x) => *(*to).as_i32_mut() = x,
GlobalInit::I64Const(x) => *(*to).as_i64_mut() = x,
GlobalInit::F32Const(x) => *(*to).as_f32_mut() = x,
GlobalInit::F64Const(x) => *(*to).as_f64_mut() = x,
let to = instance.global_ptr(index);
match initializer {
GlobalInit::I32Const(x) => *(*to).as_i32_mut() = *x,
GlobalInit::I64Const(x) => *(*to).as_i64_mut() = *x,
GlobalInit::F32Const(x) => *(*to).as_f32_mut() = *x,
GlobalInit::F64Const(x) => *(*to).as_f64_mut() = *x,
GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = *x.bytes(),
GlobalInit::GetGlobal(x) => {
let from = if let Some(def_x) = module.local_global_index(x) {
let from = if let Some(def_x) = module.local_global_index(*x) {
instance.global(def_x)
} else {
*instance.imported_global(x).definition
*instance.imported_global(*x).definition
};
*to = from;
}
GlobalInit::Import => panic!("locally-defined global initialized as import"),
GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(),
}
}

View File

@@ -41,7 +41,10 @@ pub use crate::imports::Imports;
pub use crate::instance::InstanceHandle;
pub use crate::memory::LinearMemory;
pub use crate::mmap::Mmap;
pub use crate::module::{MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle};
pub use crate::module::{
ExportsIterator, ImportsIterator, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan,
TableStyle,
};
pub use crate::probestack::PROBESTACK;
pub use crate::sig_registry::SignatureRegistry;
pub use crate::table::Table;

View File

@@ -34,7 +34,7 @@
use crate::probestack::PROBESTACK;
use crate::table::Table;
use crate::trap::raise_lib_trap;
use crate::trap::{raise_lib_trap, Trap, TrapCode};
use crate::vmcontext::VMContext;
use serde::{Deserialize, Serialize};
use std::fmt;
@@ -319,13 +319,14 @@ pub unsafe extern "C" fn wasmer_data_drop(vmctx: *mut VMContext, data_index: u32
instance.data_drop(data_index)
}
/// Implementation for raising a trap
pub unsafe extern "C" fn wasmer_raise_trap(trap_code: TrapCode) -> ! {
let trap = Trap::wasm(trap_code);
raise_lib_trap(trap)
}
/// The name of a runtime library routine.
///
/// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
/// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
/// the runtime library routine. This way, Cranelift doesn't have to know about the naming
/// convention in the embedding VM's runtime library.
///
/// This list is likely to grow over time.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LibCall {
@@ -348,6 +349,8 @@ pub enum LibCall {
NearestF32,
/// nearest.f64
NearestF64,
/// A custom trap
RaiseTrap,
// /// libc.memcpy
// Memcpy,
// /// libc.memset
@@ -371,6 +374,7 @@ impl LibCall {
Self::FloorF64 => wasmer_f64_floor as usize,
Self::TruncF64 => wasmer_f64_trunc as usize,
Self::NearestF64 => wasmer_f64_nearest as usize,
Self::RaiseTrap => wasmer_raise_trap as usize,
Self::Probestack => PROBESTACK as usize,
// other => panic!("unexpected libcall: {}", other),
}

View File

@@ -5,13 +5,15 @@ use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::iter::ExactSizeIterator;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
DataIndex, ElemIndex, ExportIndex, ExportType, ExternType, FuncIndex, FuncType, GlobalIndex,
GlobalType, ImportIndex, ImportType, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex,
LocalTableIndex, MemoryIndex, MemoryType, Pages, SignatureIndex, TableIndex, TableType,
DataIndex, ElemIndex, ExportIndex, ExportType, ExternType, FunctionIndex, FunctionType,
GlobalIndex, GlobalInit, GlobalType, ImportIndex, ImportType, LocalFunctionIndex,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, Pages,
SignatureIndex, TableIndex, TableType,
};
/// A WebAssembly table initializer.
@@ -24,7 +26,7 @@ pub struct TableElements {
/// The offset to add to the base.
pub offset: usize,
/// The values to write into the table elements.
pub elements: Box<[FuncIndex]>,
pub elements: Box<[FunctionIndex]>,
}
/// Implemenation styles for WebAssembly linear memory.
@@ -113,25 +115,28 @@ pub struct Module {
pub exports: IndexMap<String, ExportIndex>,
/// The module "start" function, if present.
pub start_func: Option<FuncIndex>,
pub start_func: Option<FunctionIndex>,
/// WebAssembly table initializers.
pub table_elements: Vec<TableElements>,
/// WebAssembly passive elements.
pub passive_elements: HashMap<ElemIndex, Box<[FuncIndex]>>,
pub passive_elements: HashMap<ElemIndex, Box<[FunctionIndex]>>,
/// WebAssembly passive data segments.
pub passive_data: HashMap<DataIndex, Arc<[u8]>>,
/// WebAssembly global initializers.
pub global_initializers: PrimaryMap<LocalGlobalIndex, GlobalInit>,
/// WebAssembly function names.
pub func_names: HashMap<FuncIndex, String>,
pub func_names: HashMap<FunctionIndex, String>,
/// WebAssembly function signatures.
pub signatures: PrimaryMap<SignatureIndex, FuncType>,
pub signatures: PrimaryMap<SignatureIndex, FunctionType>,
/// Types of functions (imported and local).
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
/// WebAssembly functions (imported and local).
pub functions: PrimaryMap<FunctionIndex, SignatureIndex>,
/// WebAssembly tables (imported and local).
pub tables: PrimaryMap<TableIndex, TableType>,
@@ -167,6 +172,7 @@ impl Module {
table_elements: Vec::new(),
passive_elements: HashMap::new(),
passive_data: HashMap::new(),
global_initializers: PrimaryMap::new(),
func_names: HashMap::new(),
signatures: PrimaryMap::new(),
functions: PrimaryMap::new(),
@@ -181,12 +187,12 @@ impl Module {
}
/// Get the given passive element, if it exists.
pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FuncIndex]> {
pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FunctionIndex]> {
self.passive_elements.get(&index).map(|es| &**es)
}
/// Get the exported signatures of the module
pub fn exported_signatures(&self) -> Vec<FuncType> {
pub fn exported_signatures(&self) -> Vec<FunctionType> {
self.exports
.iter()
.filter_map(|(_name, export_index)| match export_index {
@@ -197,17 +203,17 @@ impl Module {
}
_ => None,
})
.collect::<Vec<FuncType>>()
.collect::<Vec<FunctionType>>()
}
/// Get the export types of the module
pub fn exports<'a>(&'a self) -> impl Iterator<Item = ExportType> + 'a {
self.exports.iter().map(move |(name, export_index)| {
pub fn exports<'a>(&'a self) -> ExportsIterator<impl Iterator<Item = ExportType> + 'a> {
let iter = self.exports.iter().map(move |(name, export_index)| {
let extern_type = match export_index {
ExportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap();
let func_type = self.signatures.get(signature.clone()).unwrap();
ExternType::Func(func_type.clone())
ExternType::Function(func_type.clone())
}
ExportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap();
@@ -223,19 +229,24 @@ impl Module {
}
};
ExportType::new(name, extern_type)
})
});
ExportsIterator {
iter,
size: self.exports.len(),
}
}
/// Get the export types of the module
pub fn imports<'a>(&'a self) -> impl Iterator<Item = ImportType> + 'a {
self.imports
pub fn imports<'a>(&'a self) -> ImportsIterator<impl Iterator<Item = ImportType> + 'a> {
let iter = self
.imports
.iter()
.map(move |((module, field, _), import_index)| {
let extern_type = match import_index {
ImportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap();
let func_type = self.signatures.get(signature.clone()).unwrap();
ExternType::Func(func_type.clone())
ExternType::Function(func_type.clone())
}
ImportIndex::Table(i) => {
let table_type = self.tables.get(i.clone()).unwrap();
@@ -251,24 +262,28 @@ impl Module {
}
};
ImportType::new(module, field, extern_type)
})
});
ImportsIterator {
iter,
size: self.imports.len(),
}
}
/// Convert a `LocalFuncIndex` into a `FuncIndex`.
pub fn func_index(&self, local_func: LocalFuncIndex) -> FuncIndex {
FuncIndex::new(self.num_imported_funcs + local_func.index())
/// Convert a `LocalFunctionIndex` into a `FunctionIndex`.
pub fn func_index(&self, local_func: LocalFunctionIndex) -> FunctionIndex {
FunctionIndex::new(self.num_imported_funcs + local_func.index())
}
/// Convert a `FuncIndex` into a `LocalFuncIndex`. Returns None if the
/// Convert a `FunctionIndex` into a `LocalFunctionIndex`. Returns None if the
/// index is an imported function.
pub fn local_func_index(&self, func: FuncIndex) -> Option<LocalFuncIndex> {
pub fn local_func_index(&self, func: FunctionIndex) -> Option<LocalFunctionIndex> {
func.index()
.checked_sub(self.num_imported_funcs)
.map(LocalFuncIndex::new)
.map(LocalFunctionIndex::new)
}
/// Test whether the given function index is for an imported function.
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
pub fn is_imported_function(&self, index: FunctionIndex) -> bool {
index.index() < self.num_imported_funcs
}
@@ -343,3 +358,126 @@ impl fmt::Display for Module {
write!(f, "{}", self.name())
}
}
// Code inspired from
// https://www.reddit.com/r/rust/comments/9vspv4/extending_iterators_ergonomically/
/// This iterator allows us to iterate over the exports
/// and offer nice API ergonomics over it.
pub struct ExportsIterator<I: Iterator<Item = ExportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ExportType> + Sized> ExactSizeIterator for ExportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ExportType> + Sized> ExportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ExportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ExportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ExportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ExportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
}
impl<I: Iterator<Item = ExportType> + Sized> Iterator for ExportsIterator<I> {
type Item = ExportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
/// This iterator allows us to iterate over the imports
/// and offer nice API ergonomics over it.
pub struct ImportsIterator<I: Iterator<Item = ImportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ImportType> + Sized> ExactSizeIterator for ImportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ImportType> + Sized> ImportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ImportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ImportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ImportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ImportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
}
impl<I: Iterator<Item = ImportType> + Sized> Iterator for ImportsIterator<I> {
type Item = ImportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}

View File

@@ -6,7 +6,7 @@ use more_asserts::{assert_lt, debug_assert_lt};
use std::collections::{hash_map, HashMap};
use std::convert::TryFrom;
use std::sync::RwLock;
use wasm_common::FuncType;
use wasm_common::FunctionType;
/// WebAssembly requires that the caller and callee signatures in an indirect
/// call must match. To implement this efficiently, keep a registry of all
@@ -24,8 +24,8 @@ pub struct SignatureRegistry {
#[derive(Debug, Default)]
struct Inner {
signature2index: HashMap<FuncType, VMSharedSignatureIndex>,
index2signature: HashMap<VMSharedSignatureIndex, FuncType>,
signature2index: HashMap<FunctionType, VMSharedSignatureIndex>,
index2signature: HashMap<VMSharedSignatureIndex, FunctionType>,
}
impl SignatureRegistry {
@@ -37,7 +37,7 @@ impl SignatureRegistry {
}
/// Register a signature and return its unique index.
pub fn register(&self, sig: &FuncType) -> VMSharedSignatureIndex {
pub fn register(&self, sig: &FunctionType) -> VMSharedSignatureIndex {
let mut inner = self.inner.write().unwrap();
let len = inner.signature2index.len();
match inner.signature2index.entry(sig.clone()) {
@@ -62,7 +62,7 @@ impl SignatureRegistry {
///
/// Note that for this operation to be semantically correct the `idx` must
/// have previously come from a call to `register` of this same object.
pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<FuncType> {
pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<FunctionType> {
self.inner
.read()
.unwrap()

View File

@@ -19,20 +19,22 @@ pub struct Table {
impl Table {
/// Create a new table instance with specified minimum and maximum number of elements.
pub fn new(plan: &TablePlan) -> Self {
pub fn new(plan: &TablePlan) -> Result<Self, String> {
match plan.table.ty {
Type::FuncRef => (),
ty => unimplemented!("tables of types other than anyfunc ({})", ty),
ty => return Err(format!("tables of types other than anyfunc ({})", ty)),
};
match plan.style {
TableStyle::CallerChecksSignature => Self {
TableStyle::CallerChecksSignature => Ok(Self {
vec: RefCell::new(vec![
VMCallerCheckedAnyfunc::default();
usize::try_from(plan.table.minimum).unwrap()
usize::try_from(plan.table.minimum).map_err(|_| {
"Table minimum is bigger than usize".to_string()
})?
]),
maximum: plan.table.maximum,
plan: plan.clone(),
},
}),
}
}

View File

@@ -7,4 +7,4 @@ pub use trapcode::TrapCode;
pub use traphandlers::{
catch_traps, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, Trap,
};
pub use traphandlers::{init as init_traphandlers, resume_panic};
pub use traphandlers::{init_traps, resume_panic};

View File

@@ -8,60 +8,82 @@ use serde::{Deserialize, Serialize};
///
/// All trap instructions have an explicit trap code.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum TrapCode {
/// The current stack space was exhausted.
///
/// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
/// stack guard page.
StackOverflow,
StackOverflow = 0,
/// Memory data doesn't fit the memory size.
///
/// This only can happen during instantiation.
HeapSetterOutOfBounds,
HeapSetterOutOfBounds = 1,
/// A `heap_addr` instruction detected an out-of-bounds error.
///
/// Note that not all out-of-bounds heap accesses are reported this way;
/// some are detected by a segmentation fault on the heap unmapped or
/// offset-guard pages.
HeapAccessOutOfBounds,
HeapAccessOutOfBounds = 2,
/// Table Elements doesn't fit the table size.
///
/// This only can happen during instantiation.
TableSetterOutOfBounds,
TableSetterOutOfBounds = 3,
/// A `table_addr` instruction detected an out-of-bounds error.
TableAccessOutOfBounds,
TableAccessOutOfBounds = 4,
/// Other bounds checking error.
OutOfBounds,
OutOfBounds = 5,
/// Indirect call to a null table entry.
IndirectCallToNull,
IndirectCallToNull = 6,
/// Signature mismatch on indirect call.
BadSignature,
BadSignature = 7,
/// An integer arithmetic operation caused an overflow.
IntegerOverflow,
IntegerOverflow = 8,
/// An integer division by zero.
IntegerDivisionByZero,
IntegerDivisionByZero = 9,
/// Failed float-to-int conversion.
BadConversionToInteger,
BadConversionToInteger = 10,
/// Code that was supposed to have been unreachable was reached.
UnreachableCodeReached,
UnreachableCodeReached = 11,
/// Execution has potentially run too long and may be interrupted.
/// This trap is resumable.
Interrupt,
Interrupt = 12,
// /// A user-defined trap code.
// User(u16),
}
/// A user-defined trap code.
User(u16),
impl TrapCode {
/// Gets the message for this trap code
pub fn message(&self) -> &str {
match self {
Self::StackOverflow => "call stack exhausted",
Self::HeapSetterOutOfBounds => "memory out of bounds: data segment does not fit",
Self::HeapAccessOutOfBounds => "out of bounds memory access",
Self::TableSetterOutOfBounds => "table out of bounds: elements segment does not fit",
Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
Self::OutOfBounds => "out of bounds",
Self::IndirectCallToNull => "uninitialized element",
Self::BadSignature => "indirect call type mismatch",
Self::IntegerOverflow => "integer overflow",
Self::IntegerDivisionByZero => "integer divide by zero",
Self::BadConversionToInteger => "invalid conversion to integer",
Self::UnreachableCodeReached => "unreachable",
Self::Interrupt => "interrupt",
// Self::User(_) => unreachable!(),
}
}
}
impl Display for TrapCode {
@@ -81,7 +103,7 @@ impl Display for TrapCode {
BadConversionToInteger => "bad_toint",
UnreachableCodeReached => "unreachable",
Interrupt => "interrupt",
User(x) => return write!(f, "user{}", x),
// User(x) => return write!(f, "user{}", x),
};
f.write_str(identifier)
}
@@ -106,7 +128,7 @@ impl FromStr for TrapCode {
"bad_toint" => Ok(BadConversionToInteger),
"unreachable" => Ok(UnreachableCodeReached),
"interrupt" => Ok(Interrupt),
_ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
// _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
_ => Err(()),
}
}
@@ -141,8 +163,8 @@ mod tests {
}
assert_eq!("bogus".parse::<TrapCode>(), Err(()));
assert_eq!(TrapCode::User(17).to_string(), "user17");
assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
// assert_eq!(TrapCode::User(17).to_string(), "user17");
// assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
assert_eq!("user".parse::<TrapCode>(), Err(()));
assert_eq!("user-1".parse::<TrapCode>(), Err(()));
assert_eq!("users".parse::<TrapCode>(), Err(()));

View File

@@ -254,7 +254,7 @@ cfg_if::cfg_if! {
/// function needs to be called at the end of the startup process, after other
/// handlers have been installed. This function can thus be called multiple
/// times, having no effect after the first call.
pub fn init() {
pub fn init_traps() {
static INIT: Once = Once::new();
INIT.call_once(real_init);
}
@@ -409,7 +409,7 @@ where
{
// Ensure that we have our sigaltstack installed.
#[cfg(unix)]
setup_unix_sigaltstack()?;
setup_unix_signalstack()?;
return CallThreadState::new(vmctx).with(|cx| {
RegisterSetjmp(
@@ -628,7 +628,7 @@ mod tls {
/// and registering our own alternate stack that is large enough and has a guard
/// page.
#[cfg(unix)]
fn setup_unix_sigaltstack() -> Result<(), Trap> {
fn setup_unix_signalstack() -> Result<(), Trap> {
use std::cell::RefCell;
use std::convert::TryInto;
use std::ptr::null_mut;

View File

@@ -644,9 +644,13 @@ impl VMBuiltinFunctionIndex {
pub const fn get_data_drop_index() -> Self {
Self(12)
}
/// Returns an index for wasm's `raise_trap` instruction.
pub const fn get_raise_trap_index() -> Self {
Self(13)
}
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
13
14
}
/// Return the index as an u32 number.
@@ -702,6 +706,8 @@ impl VMBuiltinFunctionsArray {
wasmer_memory_init as usize;
ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] =
wasmer_data_drop as usize;
ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] =
wasmer_raise_trap as usize;
debug_assert!(ptrs.iter().cloned().all(|p| p != 0));
@@ -709,7 +715,7 @@ impl VMBuiltinFunctionsArray {
}
}
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
/// The VM "context", which is pointed to by the `vmctx` arg in the compiler.
/// This has information about globals, memories, tables, and other runtime
/// state associated with the current instance.
///

View File

@@ -6,7 +6,7 @@ use crate::VMBuiltinFunctionIndex;
use more_asserts::assert_lt;
use std::convert::TryFrom;
use wasm_common::{
FuncIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
SignatureIndex, TableIndex,
};
@@ -364,7 +364,7 @@ impl VMOffsets {
}
/// Return the offset to `VMFunctionImport` index `index`.
pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
pub fn vmctx_vmfunction_import(&self, index: FunctionIndex) -> u32 {
assert_lt!(index.as_u32(), self.num_imported_functions);
self.vmctx_imported_functions_begin()
.checked_add(
@@ -455,14 +455,14 @@ impl VMOffsets {
}
/// Return the offset to the `body` field in `*const VMFunctionBody` index `index`.
pub fn vmctx_vmfunction_import_body(&self, index: FuncIndex) -> u32 {
pub fn vmctx_vmfunction_import_body(&self, index: FunctionIndex) -> u32 {
self.vmctx_vmfunction_import(index)
.checked_add(u32::from(self.vmfunction_import_body()))
.unwrap()
}
/// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
pub fn vmctx_vmfunction_import_vmctx(&self, index: FunctionIndex) -> u32 {
self.vmctx_vmfunction_import(index)
.checked_add(u32::from(self.vmfunction_import_vmctx()))
.unwrap()

View File

@@ -1,7 +1,7 @@
[package]
name = "wasmer-wasi-experimental-io-devices"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
publish = true
@@ -12,7 +12,7 @@ license = "MIT"
maintenance = { status = "experimental" }
[dependencies]
log = "0.4"
tracing = "0.1"
minifb = "0.13"
wasmer-wasi = { version = "0.16.2", path = "../wasi" }
ref_thread_local = "0.0"

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