mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-12 05:18:43 +00:00
Merge branch 'master' into add-c-api
This commit is contained in:
@@ -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
175
Cargo.lock
generated
@@ -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"
|
||||
|
||||
65
Cargo.toml
65
Cargo.toml
@@ -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"
|
||||
|
||||
10
build.rs
10
build.rs
@@ -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| {
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -1,5 +1,72 @@
|
||||
## Wasmer API
|
||||
# Wasmer [](https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master) [](https://slack.wasmer.io) [](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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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, )* })
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
4
lib/cache/Cargo.toml
vendored
4
lib/cache/Cargo.toml
vendored
@@ -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
4
lib/cache/README.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Wasmer Cache
|
||||
|
||||
The `wasmer-cache` crate allows easily caching WebAssembly
|
||||
modules in your system.
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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");
|
||||
|
||||
142
lib/compiler-cranelift/src/sink.rs
Normal file
142
lib/compiler-cranelift/src/sink.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)?;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
12
lib/compiler-singlepass/README.md
Normal file
12
lib/compiler-singlepass/README.md
Normal 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.
|
||||
@@ -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(),
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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, _>>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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>>;
|
||||
|
||||
71
lib/compiler/src/section.rs
Normal file
71
lib/compiler/src/section.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
39
lib/engine-jit/Cargo.toml
Normal 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
13
lib/engine-jit/README.md
Normal 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.
|
||||
@@ -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;
|
||||
256
lib/engine-jit/src/engine.rs
Normal file
256
lib/engine-jit/src/engine.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
296
lib/engine-jit/src/module.rs
Normal file
296
lib/engine-jit/src/module.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
37
lib/engine-jit/src/serialize.rs
Normal file
37
lib/engine-jit/src/serialize.rs
Normal 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>,
|
||||
}
|
||||
@@ -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
12
lib/engine/README.md
Normal 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
46
lib/engine/src/engine.rs
Normal 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>;
|
||||
}
|
||||
@@ -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
43
lib/engine/src/lib.rs
Normal 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
28
lib/engine/src/module.rs
Normal 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.
|
||||
@@ -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
107
lib/engine/src/serialize.rs
Normal 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)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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());
|
||||
6
lib/engine/src/trap/mod.rs
Normal file
6
lib/engine/src/trap/mod.rs
Normal 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,
|
||||
};
|
||||
71
lib/engine/src/tunables.rs
Normal file
71
lib/engine/src/tunables.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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).
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
mod error;
|
||||
mod frame_info;
|
||||
pub use error::RuntimeError;
|
||||
pub use frame_info::{register, FrameInfo, GlobalFrameInfoRegistration, FRAME_INFO};
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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(()));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user