Add caching. (#134)

* Allow a module to have a different signature registry than the process-specific

* Add core ability to build compiled code caches

* Remove timing printouts

* Serialize/Deserialize memories to reduce copies

* Work more on api

* Relocate local functions relatively before external functions

* Fix incorrect definition in test

* merge errors caused by merge

* Fix emscripten compile

* Fix review comments
This commit is contained in:
Lachlan Sneff
2019-02-06 16:26:45 -08:00
committed by GitHub
parent 2f2f86a4de
commit 8fe9b7eac2
34 changed files with 1768 additions and 291 deletions

124
Cargo.lock generated
View File

@ -26,6 +26,30 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "block-buffer"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-padding"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.1"
@ -146,6 +170,14 @@ dependencies = [
"wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno"
version = "0.2.4"
@ -184,6 +216,11 @@ dependencies = [
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "field-offset"
version = "0.1.1"
@ -208,6 +245,14 @@ name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.2.11"
@ -220,6 +265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -230,6 +276,11 @@ dependencies = [
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indexmap"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.3"
@ -276,6 +327,15 @@ dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.12.0"
@ -288,6 +348,11 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "opaque-debug"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "owning_ref"
version = "0.4.0"
@ -506,6 +571,23 @@ name = "serde"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde-bench"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_bytes"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.85"
@ -526,6 +608,17 @@ dependencies = [
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sha2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "smallvec"
version = "0.6.8"
@ -623,6 +716,11 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-segmentation"
version = "1.2.1"
@ -701,6 +799,10 @@ dependencies = [
"cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.1.2",
"wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -723,6 +825,7 @@ dependencies = [
name = "wasmer-runtime"
version = "0.1.4"
dependencies = [
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.1.2",
"wasmer-runtime-core 0.1.2",
]
@ -734,11 +837,17 @@ dependencies = [
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.1.2",
"wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -797,6 +906,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum block-buffer 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "509de513cca6d92b6aacf9c61acfe7eaa160837323a81068d690cc1f8e5740da"
"checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
@ -811,17 +923,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "789907218eeebebcea8122c2053d71affac91c96ce72cea35ebfdbbf547e82af"
"checksum cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "474bee81d620a473bf43411a3d6f10ffbf7965141dc5e5b76d8d2151dde3285d"
"checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a"
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e"
"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
@ -829,7 +945,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242"
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
@ -856,8 +974,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752"
"checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd"
"checksum serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3"
"checksum serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "a915306b0f1ac5607797697148c223bedeaa36bcc2e28a01441cd638cc6567b4"
"checksum serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "4b90a9fbe1211e57d3e1c15670f1cb00802988fb23a1a4aad7a2b63544f1920e"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
"checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
@ -869,6 +990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"

View File

@ -19,5 +19,23 @@ wasmparser = "0.23.0"
byteorder = "1"
nix = "0.12.0"
# Dependencies for caching.
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dependencies.serde_bytes]
version = "0.10"
optional = true
# [dependencies.bincode]
# version = "1.0.1"
# optional = true
[dependencies.serde-bench]
version = "0.0.7"
optional = true
[features]
cache = ["serde", "serde_derive", "serde_bytes", "serde-bench", "wasmer-runtime-core/cache"]
debug = ["wasmer-runtime-core/debug"]

View File

@ -0,0 +1,46 @@
use crate::relocation::{ExternalRelocation, TrapSink};
use hashbrown::HashMap;
use wasmer_runtime_core::{
backend::sys::Memory,
cache::{Cache, Error},
module::ModuleInfo,
structures::Map,
types::{LocalFuncIndex, SigIndex},
};
use serde_bench::{deserialize, serialize};
#[derive(Serialize, Deserialize)]
pub struct TrampolineCache {
#[serde(with = "serde_bytes")]
pub code: Vec<u8>,
pub offsets: HashMap<SigIndex, usize>,
}
#[derive(Serialize, Deserialize)]
pub struct BackendCache {
pub external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
pub offsets: Map<LocalFuncIndex, usize>,
pub trap_sink: TrapSink,
pub trampolines: TrampolineCache,
}
impl BackendCache {
pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> {
let (info, backend_data, compiled_code) = cache.consume();
let backend_cache = deserialize(backend_data.as_slice())
.map_err(|e| Error::DeserializeError(e.to_string()))?;
Ok((info, compiled_code, backend_cache))
}
pub fn into_backend_data(self) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new();
serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?;
Ok(buffer)
}
}

View File

@ -11,7 +11,7 @@ use wasmer_runtime_core::{
backend::{ProtectedCaller, Token},
error::RuntimeResult,
export::Context,
module::{ExportIndex, ModuleInner},
module::{ExportIndex, ModuleInfo, ModuleInner},
types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value},
vm::{self, ImportBacking},
};
@ -23,7 +23,7 @@ pub struct Caller {
}
impl Caller {
pub fn new(module: &ModuleInner, handler_data: HandlerData, trampolines: Trampolines) -> Self {
pub fn new(module: &ModuleInfo, handler_data: HandlerData, trampolines: Trampolines) -> Self {
let mut func_export_set = HashSet::new();
for export_index in module.exports.values() {
if let ExportIndex::Func(func_index) = export_index {
@ -118,6 +118,7 @@ fn get_func_from_index(
func_index: FuncIndex,
) -> (*const vm::Func, Context, Arc<FuncSig>, SigIndex) {
let sig_index = *module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
@ -141,7 +142,7 @@ fn get_func_from_index(
}
};
let signature = module.sig_registry.lookup_signature(sig_index);
let signature = Arc::clone(&module.info.signatures[sig_index]);
(func_ptr, ctx, signature, sig_index)
}

View File

@ -5,8 +5,7 @@
//! unless you have memory unsafety elsewhere in your code.
use crate::call::sighandler::install_sighandler;
use crate::relocation::{TrapData, TrapSink};
use cranelift_codegen::ir::TrapCode;
use crate::relocation::{TrapCode, TrapData, TrapSink};
use nix::libc::{c_void, siginfo_t};
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
use std::cell::{Cell, UnsafeCell};
@ -36,7 +35,7 @@ unsafe impl Send for HandlerData {}
unsafe impl Sync for HandlerData {}
pub struct HandlerData {
trap_data: TrapSink,
pub trap_data: TrapSink,
buffer_ptr: *const c_void,
buffer_size: usize,
}

View File

@ -4,6 +4,7 @@ use cranelift_codegen::{
ir::{self, InstBuilder},
isa,
};
use cranelift_entity::EntityRef;
use cranelift_wasm::{self, FuncEnvironment, ModuleEnvironment};
use std::mem;
use wasmer_runtime_core::{
@ -162,7 +163,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
offset: (local_memory_ptr_offset as i64).into(),
global_type: ptr_type,
}),
self.env.module.memories[local_mem_index],
self.env.module.info.memories[local_mem_index],
)
}
LocalOrImport::Import(import_mem_index) => {
@ -182,7 +183,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
offset: (local_memory_ptr_offset as i64).into(),
global_type: ptr_type,
}),
self.env.module.imported_memories[import_mem_index].1,
self.env.module.info.imported_memories[import_mem_index].1,
)
}
};
@ -273,7 +274,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
(
table_struct_ptr_ptr,
self.env.module.tables[local_table_index],
self.env.module.info.tables[local_table_index],
)
}
LocalOrImport::Import(import_table_index) => {
@ -295,7 +296,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
(
table_struct_ptr_ptr,
self.env.module.imported_tables[import_table_index].1,
self.env.module.info.imported_tables[import_table_index].1,
)
}
};
@ -367,7 +368,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
// Make this colocated so all calls between local functions are relative.
colocated: true,
})
}
@ -428,9 +430,24 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
pos.ins().trapz(func_ptr, ir::TrapCode::IndirectCallToNull);
let sig_index = self.env.deduplicated[clif_sig_index];
let expected_sig = {
let sig_index_global = pos.func.create_global_value(ir::GlobalValueData::Symbol {
// The index of the `ExternalName` is the undeduplicated, signature index.
name: ir::ExternalName::user(
call_names::SIG_NAMESPACE,
clif_sig_index.index() as u32,
),
offset: 0.into(),
colocated: false,
});
pos.ins().symbol_value(ir::types::I64, sig_index_global)
// let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
// self.env.deduplicated[clif_sig_index]
};
let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
let not_equal_flags = pos.ins().ifcmp(found_sig, expected_sig);
pos.ins().trapif(
@ -555,12 +572,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.memories[local_mem_index],
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.imported_memories[import_mem_index].1,
self.env.module.info.imported_memories[import_mem_index].1,
),
};
@ -618,12 +635,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.memories[local_mem_index],
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.imported_memories[import_mem_index].1,
self.env.module.info.imported_memories[import_mem_index].1,
),
};

View File

@ -1,3 +1,5 @@
#[cfg(feature = "cache")]
mod cache;
mod call;
mod func_env;
mod libcalls;
@ -12,11 +14,23 @@ use cranelift_codegen::{
settings::{self, Configurable},
};
use target_lexicon::Triple;
#[cfg(feature = "cache")]
use wasmer_runtime_core::{
backend::sys::Memory,
cache::{Cache, Error as CacheError},
module::ModuleInfo,
};
use wasmer_runtime_core::{
backend::{Compiler, Token},
error::{CompileError, CompileResult},
module::ModuleInner,
};
#[cfg(feature = "cache")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "cache")]
extern crate serde;
use wasmparser::{self, WasmDecoder};
pub struct CraneliftCompiler {}
@ -28,7 +42,7 @@ impl CraneliftCompiler {
}
impl Compiler for CraneliftCompiler {
// Compiles wasm binary to a wasmer module.
/// Compiles wasm binary to a wasmer module.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner> {
validate(wasm)?;
@ -36,10 +50,48 @@ impl Compiler for CraneliftCompiler {
let mut module = module::Module::empty();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
module.compile(&*isa, func_bodies)
}
/// Create a wasmer Module from an already-compiled cache.
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> {
module::Module::from_cache(cache)
}
#[cfg(feature = "cache")]
fn compile_to_backend_cache_data(
&self,
wasm: &[u8],
_: Token,
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> {
validate(wasm)?;
let isa = get_isa();
let mut module = module::Module::empty();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
let (info, backend_cache, compiled_code) = module
.compile_to_backend_cache(&*isa, func_bodies)
.map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e),
})?;
let buffer =
backend_cache
.into_backend_data()
.map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e),
})?;
Ok((Box::new(info), buffer, compiled_code))
}
}
fn get_isa() -> Box<isa::TargetIsa> {

View File

@ -1,4 +1,7 @@
#[cfg(feature = "cache")]
use crate::cache::BackendCache;
use crate::{call::Caller, resolver::FuncResolverBuilder, trampoline::Trampolines};
use cranelift_codegen::{ir, isa};
use cranelift_entity::EntityRef;
use cranelift_wasm;
@ -7,11 +10,15 @@ use std::{
ops::{Deref, DerefMut},
ptr::NonNull,
};
#[cfg(feature = "cache")]
use wasmer_runtime_core::{
backend::SigRegistry,
backend::{FuncResolver, ProtectedCaller, Token},
backend::sys::Memory,
cache::{Cache, Error as CacheError},
};
use wasmer_runtime_core::{
backend::{Backend, FuncResolver, ProtectedCaller, Token},
error::{CompileResult, RuntimeResult},
module::ModuleInner,
module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex},
types::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
@ -59,6 +66,7 @@ impl Module {
func_resolver: Box::new(Placeholder),
protected_caller: Box::new(Placeholder),
info: ModuleInfo {
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
@ -76,7 +84,12 @@ impl Module {
start_func: None,
func_assoc: Map::new(),
sig_registry: SigRegistry,
signatures: Map::new(),
backend: Backend::Cranelift,
namespace_table: StringTable::new(),
name_table: StringTable::new(),
},
},
}
}
@ -86,19 +99,60 @@ impl Module {
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<ModuleInner> {
let imported_functions_len = self.module.imported_functions.len();
let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, imported_functions_len)?;
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
self.module.func_resolver = Box::new(func_resolver_builder.finalize()?);
self.module.func_resolver =
Box::new(func_resolver_builder.finalize(&self.module.info.signatures)?);
let trampolines = Trampolines::new(isa, &self.module);
let trampolines = Trampolines::new(isa, &self.module.info);
self.module.protected_caller =
Box::new(Caller::new(&self.module, handler_data, trampolines));
Box::new(Caller::new(&self.module.info, handler_data, trampolines));
Ok(self.module)
}
#[cfg(feature = "cache")]
pub fn compile_to_backend_cache(
self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<(ModuleInfo, BackendCache, Memory)> {
let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
let trampolines = Trampolines::new(isa, &self.module.info);
let trampoline_cache = trampolines.to_trampoline_cache();
let (backend_cache, compiled_code) =
func_resolver_builder.to_backend_cache(trampoline_cache, handler_data);
Ok((self.module.info, backend_cache, compiled_code))
}
#[cfg(feature = "cache")]
pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> {
let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?;
let (func_resolver_builder, trampolines, handler_data) =
FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?;
let func_resolver = Box::new(
func_resolver_builder
.finalize(&info.signatures)
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?,
);
let protected_caller = Box::new(Caller::new(&info, handler_data, trampolines));
Ok(ModuleInner {
func_resolver,
protected_caller,
info,
})
}
}
impl Deref for Module {

View File

@ -3,16 +3,18 @@ use crate::{
module::{Converter, Module},
};
use cranelift_codegen::{ir, isa};
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
use hashbrown::HashMap;
use std::sync::Arc;
use wasmer_runtime_core::{
error::{CompileError, CompileResult},
module::{DataInitializer, ExportIndex, ImportName, TableInitializer},
module::{
DataInitializer, ExportIndex, ImportName, NameIndex, NamespaceIndex, StringTableBuilder,
TableInitializer,
},
structures::{Map, TypedIndex},
types::{
ElementType, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, Initializer,
LocalFuncIndex, LocalOrImport, MemoryDescriptor, SigIndex, TableDescriptor, Value,
ElementType, GlobalDescriptor, GlobalIndex, GlobalInit, Initializer, LocalFuncIndex,
LocalOrImport, MemoryDescriptor, SigIndex, TableDescriptor, Value,
},
units::Pages,
};
@ -23,8 +25,8 @@ pub struct ModuleEnv<'module, 'isa> {
pub signatures: Map<SigIndex, ir::Signature>,
globals: Map<GlobalIndex, cranelift_wasm::Global>,
func_bodies: Map<LocalFuncIndex, ir::Function>,
pub deduplicated: PrimaryMap<cranelift_wasm::SignatureIndex, SigIndex>,
duplicated: HashMap<SigIndex, cranelift_wasm::SignatureIndex>,
namespace_table_builder: StringTableBuilder<NamespaceIndex>,
name_table_builder: StringTableBuilder<NameIndex>,
}
impl<'module, 'isa> ModuleEnv<'module, 'isa> {
@ -35,14 +37,18 @@ impl<'module, 'isa> ModuleEnv<'module, 'isa> {
signatures: Map::new(),
globals: Map::new(),
func_bodies: Map::new(),
deduplicated: PrimaryMap::new(),
duplicated: HashMap::new(),
namespace_table_builder: StringTableBuilder::new(),
name_table_builder: StringTableBuilder::new(),
}
}
pub fn translate(mut self, wasm: &[u8]) -> CompileResult<Map<LocalFuncIndex, ir::Function>> {
translate_module(wasm, &mut self)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
self.module.info.namespace_table = self.namespace_table_builder.finish();
self.module.info.name_table = self.name_table_builder.finish();
Ok(self.func_bodies)
}
}
@ -55,12 +61,11 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: &ir::Signature) {
let clif_sig_index = self.signatures.push(sig.clone());
let func_sig: FuncSig = Converter(sig).into();
let sig_index = self.module.sig_registry.lookup_sig_index(func_sig);
self.deduplicated.push(sig_index);
self.duplicated
.insert(sig_index, Converter(clif_sig_index).into());
self.signatures.push(sig.clone());
self.module
.info
.signatures
.push(Arc::new(Converter(sig).into()));
}
/// Return the signature with the given index.
@ -75,25 +80,34 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
namespace: &'data str,
name: &'data str,
) {
let sig_index = self.deduplicated[clif_sig_index];
self.module.func_assoc.push(sig_index);
// We convert the cranelift signature index to
// a wasmer signature index without deduplicating
// because we'll deduplicate later.
let sig_index = Converter(clif_sig_index).into();
self.module.info.func_assoc.push(sig_index);
let namespace_index = self.namespace_table_builder.register(namespace);
let name_index = self.name_table_builder.register(name);
// Add import names to list of imported functions
self.module.imported_functions.push(ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
self.module.info.imported_functions.push(ImportName {
namespace_index,
name_index,
});
}
/// Return the number of imported funcs.
fn get_num_func_imports(&self) -> usize {
self.module.imported_functions.len()
self.module.info.imported_functions.len()
}
/// Declares the type (signature) of a local function in the module.
fn declare_func_type(&mut self, clif_sig_index: cranelift_wasm::SignatureIndex) {
let sig_index = self.deduplicated[clif_sig_index];
self.module.func_assoc.push(sig_index);
// We convert the cranelift signature index to
// a wasmer signature index without deduplicating
// because we'll deduplicate later.
let sig_index = Converter(clif_sig_index).into();
self.module.info.func_assoc.push(sig_index);
}
/// Return the signature index for the given function index.
@ -101,8 +115,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
&self,
func_index: cranelift_wasm::FuncIndex,
) -> cranelift_wasm::SignatureIndex {
let sig_index: SigIndex = self.module.func_assoc[Converter(func_index).into()];
self.duplicated[&sig_index]
let sig_index: SigIndex = self.module.info.func_assoc[Converter(func_index).into()];
Converter(sig_index).into()
}
/// Declares a global to the environment.
@ -134,7 +148,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
};
// Add global ir to the list of globals
self.module.globals.push(GlobalInit { desc, init });
self.module.info.globals.push(GlobalInit { desc, init });
self.globals.push(global);
}
@ -151,9 +165,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
_ => false,
});
let namespace_index = self.namespace_table_builder.register(namespace);
let name_index = self.name_table_builder.register(name);
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
namespace_index,
name_index,
};
let desc = GlobalDescriptor {
@ -162,7 +179,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
};
// Add global ir to the list of globals
self.module.imported_globals.push((import_name, desc));
self.module.info.imported_globals.push((import_name, desc));
self.globals.push(global);
}
@ -176,7 +193,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
fn declare_table(&mut self, table: cranelift_wasm::Table) {
use cranelift_wasm::TableElementType;
// Add table ir to the list of tables
self.module.tables.push(TableDescriptor {
self.module.info.tables.push(TableDescriptor {
element: match table.ty {
TableElementType::Func => ElementType::Anyfunc,
_ => unimplemented!(),
@ -195,9 +212,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
) {
use cranelift_wasm::TableElementType;
let namespace_index = self.namespace_table_builder.register(namespace);
let name_index = self.name_table_builder.register(name);
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
namespace_index,
name_index,
};
let imported_table = TableDescriptor {
@ -211,6 +231,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
// Add import names to list of imported tables
self.module
.info
.imported_tables
.push((import_name, imported_table));
}
@ -239,7 +260,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
};
// Add table initializer to list of table initializers
self.module.elem_initializers.push(TableInitializer {
self.module.info.elem_initializers.push(TableInitializer {
table_index: Converter(table_index).into(),
base,
elements: elements
@ -251,7 +272,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a memory to the environment
fn declare_memory(&mut self, memory: cranelift_wasm::Memory) {
self.module.memories.push(MemoryDescriptor {
self.module.info.memories.push(MemoryDescriptor {
minimum: Pages(memory.minimum),
maximum: memory.maximum.map(|max| Pages(max)),
shared: memory.shared,
@ -265,9 +286,12 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
namespace: &'data str,
name: &'data str,
) {
let namespace_index = self.namespace_table_builder.register(namespace);
let name_index = self.name_table_builder.register(name);
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
namespace_index,
name_index,
};
let memory = MemoryDescriptor {
@ -277,7 +301,10 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
};
// Add import names to list of imported memories
self.module.imported_memories.push((import_name, memory));
self.module
.info
.imported_memories
.push((import_name, memory));
}
/// Fills a declared memory with bytes at module instantiation.
@ -303,7 +330,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
};
// Add data initializer to list of data initializers
self.module.data_initializers.push(DataInitializer {
self.module.info.data_initializers.push(DataInitializer {
memory_index: Converter(memory_index).into(),
base,
data: data.to_vec(),
@ -312,14 +339,14 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a function export to the environment.
fn declare_func_export(&mut self, func_index: cranelift_wasm::FuncIndex, name: &'data str) {
self.module.exports.insert(
self.module.info.exports.insert(
name.to_string(),
ExportIndex::Func(Converter(func_index).into()),
);
}
/// Declares a table export to the environment.
fn declare_table_export(&mut self, table_index: cranelift_wasm::TableIndex, name: &'data str) {
self.module.exports.insert(
self.module.info.exports.insert(
name.to_string(),
ExportIndex::Table(Converter(table_index).into()),
);
@ -330,7 +357,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
memory_index: cranelift_wasm::MemoryIndex,
name: &'data str,
) {
self.module.exports.insert(
self.module.info.exports.insert(
name.to_string(),
ExportIndex::Memory(Converter(memory_index).into()),
);
@ -341,7 +368,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
global_index: cranelift_wasm::GlobalIndex,
name: &'data str,
) {
self.module.exports.insert(
self.module.info.exports.insert(
name.to_string(),
ExportIndex::Global(Converter(global_index).into()),
);
@ -349,7 +376,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a start function.
fn declare_start_func(&mut self, func_index: cranelift_wasm::FuncIndex) {
self.module.start_func = Some(Converter(func_index).into());
self.module.info.start_func = Some(Converter(func_index).into());
}
/// Provides the contents of a function body.

View File

@ -3,14 +3,16 @@
//! any other calls that this function is doing, so we can "patch" the
//! function addrs in runtime with the functions we need.
use cranelift_codegen::binemit;
pub use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode};
use hashbrown::HashMap;
use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex};
use cranelift_codegen::ir::{self, ExternalName, SourceLoc};
use wasmer_runtime_core::{
structures::TypedIndex,
types::{FuncIndex, SigIndex},
};
pub mod call_names {
pub const LOCAL_NAMESPACE: u32 = 1;
pub const IMPORT_NAMESPACE: u32 = 2;
pub const SIG_NAMESPACE: u32 = 3;
pub const STATIC_MEM_GROW: u32 = 0;
pub const STATIC_MEM_SIZE: u32 = 1;
@ -20,10 +22,33 @@ pub mod call_names {
pub const DYNAMIC_MEM_SIZE: u32 = 5;
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Reloc {
Abs8,
X86PCRel4,
X86CallPCRel4,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone)]
pub enum LibCall {
Probestack,
CeilF32,
CeilF64,
FloorF32,
FloorF64,
TruncF32,
TruncF64,
NearestF32,
NearestF64,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Relocation {
pub struct ExternalRelocation {
/// The relocation code.
pub reloc: binemit::Reloc,
pub reloc: Reloc,
/// The offset where to apply the relocation.
pub offset: binemit::CodeOffset,
/// The addend to add to the relocation value.
@ -32,6 +57,16 @@ pub struct Relocation {
pub target: RelocationType,
}
pub struct LocalRelocation {
/// The offset where to apply the relocation.
pub offset: binemit::CodeOffset,
/// The addend to add to the relocation value.
pub addend: binemit::Addend,
/// Relocation type.
pub target: FuncIndex,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub enum VmCallKind {
StaticMemoryGrow,
@ -44,6 +79,7 @@ pub enum VmCallKind {
DynamicMemorySize,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub enum VmCall {
Local(VmCallKind),
@ -51,18 +87,20 @@ pub enum VmCall {
}
/// Specify the type of relocation
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub enum RelocationType {
Normal(LocalFuncIndex),
Intrinsic(String),
LibCall(LibCall),
VmCall(VmCall),
Signature(SigIndex),
}
/// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink {
/// Relocations recorded for the function.
pub func_relocs: Vec<Relocation>,
pub external_relocs: Vec<ExternalRelocation>,
pub local_relocs: Vec<LocalRelocation>,
}
impl binemit::RelocSink for RelocSink {
@ -82,22 +120,30 @@ impl binemit::RelocSink for RelocSink {
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc = match reloc {
binemit::Reloc::Abs8 => Reloc::Abs8,
binemit::Reloc::X86PCRel4 => Reloc::X86PCRel4,
binemit::Reloc::X86CallPCRel4 => Reloc::X86CallPCRel4,
_ => unimplemented!("unimplented reloc type: {}", reloc),
};
match *name {
ExternalName::User {
namespace: 0,
index,
} => {
self.func_relocs.push(Relocation {
reloc,
assert_eq!(reloc, Reloc::X86CallPCRel4);
self.local_relocs.push(LocalRelocation {
offset,
addend,
target: RelocationType::Normal(LocalFuncIndex::new(index as usize)),
target: FuncIndex::new(index as usize),
});
}
ExternalName::User { namespace, index } => {
use self::call_names::*;
let target = RelocationType::VmCall(match namespace {
LOCAL_NAMESPACE => VmCall::Local(match index {
let target = match namespace {
LOCAL_NAMESPACE => RelocationType::VmCall(VmCall::Local(match index {
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
STATIC_MEM_SIZE => VmCallKind::StaticMemorySize,
@ -107,8 +153,8 @@ impl binemit::RelocSink for RelocSink {
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
_ => unimplemented!(),
}),
IMPORT_NAMESPACE => VmCall::Import(match index {
})),
IMPORT_NAMESPACE => RelocationType::VmCall(VmCall::Import(match index {
STATIC_MEM_GROW => VmCallKind::StaticMemoryGrow,
STATIC_MEM_SIZE => VmCallKind::StaticMemorySize,
@ -118,10 +164,11 @@ impl binemit::RelocSink for RelocSink {
DYNAMIC_MEM_GROW => VmCallKind::DynamicMemoryGrow,
DYNAMIC_MEM_SIZE => VmCallKind::DynamicMemorySize,
_ => unimplemented!(),
}),
})),
SIG_NAMESPACE => RelocationType::Signature(SigIndex::new(index as usize)),
_ => unimplemented!(),
});
self.func_relocs.push(Relocation {
};
self.external_relocs.push(ExternalRelocation {
reloc,
offset,
addend,
@ -131,7 +178,7 @@ impl binemit::RelocSink for RelocSink {
ExternalName::TestCase { length, ascii } => {
let (slice, _) = ascii.split_at(length as usize);
let name = String::from_utf8(slice.to_vec()).unwrap();
self.func_relocs.push(Relocation {
self.external_relocs.push(ExternalRelocation {
reloc,
offset,
addend,
@ -139,8 +186,20 @@ impl binemit::RelocSink for RelocSink {
});
}
ExternalName::LibCall(libcall) => {
let libcall = match libcall {
ir::LibCall::CeilF32 => LibCall::CeilF32,
ir::LibCall::FloorF32 => LibCall::FloorF32,
ir::LibCall::TruncF32 => LibCall::TruncF32,
ir::LibCall::NearestF32 => LibCall::NearestF32,
ir::LibCall::CeilF64 => LibCall::CeilF64,
ir::LibCall::FloorF64 => LibCall::FloorF64,
ir::LibCall::TruncF64 => LibCall::TruncF64,
ir::LibCall::NearestF64 => LibCall::NearestF64,
ir::LibCall::Probestack => LibCall::Probestack,
_ => unimplemented!("unimplemented libcall: {}", libcall),
};
let relocation_type = RelocationType::LibCall(libcall);
self.func_relocs.push(Relocation {
self.external_relocs.push(ExternalRelocation {
reloc,
offset,
addend,
@ -159,43 +218,64 @@ impl binemit::RelocSink for RelocSink {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub enum TrapCode {
StackOverflow,
HeapOutOfBounds,
TableOutOfBounds,
OutOfBounds,
IndirectCallToNull,
BadSignature,
IntegerOverflow,
IntegerDivisionByZero,
BadConversionToInteger,
Interrupt,
User(u16),
}
/// Implementation of a relocation sink that just saves all the information for later
impl RelocSink {
pub fn new() -> RelocSink {
RelocSink {
func_relocs: Vec::new(),
pub fn new() -> Self {
Self {
external_relocs: Vec::new(),
local_relocs: Vec::new(),
}
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub struct TrapData {
pub trapcode: TrapCode,
pub srcloc: SourceLoc,
pub srcloc: u32,
}
/// Simple implementation of a TrapSink
/// that saves the info for later.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
pub struct TrapSink {
trap_datas: HashMap<usize, TrapData>,
trap_datas: Vec<(usize, TrapData)>,
}
impl TrapSink {
pub fn new() -> TrapSink {
TrapSink {
trap_datas: HashMap::new(),
trap_datas: Vec::new(),
}
}
pub fn lookup(&self, offset: usize) -> Option<TrapData> {
self.trap_datas.get(&offset).cloned()
self.trap_datas.get(offset).map(|(_, trap_data)| *trap_data)
}
pub fn drain_local(&mut self, current_func_offset: usize, local: &mut LocalTrapSink) {
local.trap_datas.drain(..).for_each(|(offset, trap_data)| {
self.trap_datas
.insert(current_func_offset + offset, trap_data);
});
self.trap_datas.extend(
local
.trap_datas
.drain(..)
.map(|(offset, trap_data)| (current_func_offset + offset, trap_data)),
);
}
}
@ -210,8 +290,27 @@ impl LocalTrapSink {
}
impl binemit::TrapSink for LocalTrapSink {
fn trap(&mut self, offset: u32, srcloc: SourceLoc, trapcode: TrapCode) {
self.trap_datas
.push((offset as usize, TrapData { trapcode, srcloc }));
fn trap(&mut self, offset: u32, srcloc: SourceLoc, trapcode: ir::TrapCode) {
let trapcode = match trapcode {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapOutOfBounds,
ir::TrapCode::TableOutOfBounds => TrapCode::TableOutOfBounds,
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::Interrupt => TrapCode::Interrupt,
ir::TrapCode::User(x) => TrapCode::User(x),
};
self.trap_datas.push((
offset as usize,
TrapData {
trapcode,
srcloc: srcloc.bits(),
},
));
}
}

View File

@ -1,39 +1,104 @@
use crate::call::HandlerData;
use crate::libcalls;
use crate::relocation::{
LocalTrapSink, Reloc, RelocSink, Relocation, RelocationType, TrapSink, VmCall, VmCallKind,
#[cfg(feature = "cache")]
use crate::{
cache::{BackendCache, TrampolineCache},
trampoline::Trampolines,
};
use crate::{
call::HandlerData,
libcalls,
relocation::{
ExternalRelocation, LibCall, LocalRelocation, LocalTrapSink, Reloc, RelocSink,
RelocationType, TrapSink, VmCall, VmCallKind,
},
};
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context};
use std::mem;
use std::ptr::{write_unaligned, NonNull};
use std::{
mem,
ptr::{write_unaligned, NonNull},
sync::Arc,
};
#[cfg(feature = "cache")]
use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::{
self,
backend::{
self,
sys::{Memory, Protect},
SigRegistry,
},
error::{CompileError, CompileResult},
structures::{Map, TypedIndex},
types::LocalFuncIndex,
module::ModuleInfo,
structures::{Map, SliceMap, TypedIndex},
types::{FuncSig, LocalFuncIndex, SigIndex},
vm, vmcalls,
};
#[allow(dead_code)]
pub struct FuncResolverBuilder {
resolver: FuncResolver,
relocations: Map<LocalFuncIndex, Vec<Relocation>>,
local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>,
external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
import_len: usize,
}
impl FuncResolverBuilder {
#[cfg(feature = "cache")]
pub fn new_from_backend_cache(
backend_cache: BackendCache,
mut code: Memory,
info: &ModuleInfo,
) -> Result<(Self, Trampolines, HandlerData), CacheError> {
unsafe {
code.protect(.., Protect::ReadWrite)
.map_err(|e| CacheError::Unknown(e.to_string()))?;
}
let handler_data =
HandlerData::new(backend_cache.trap_sink, code.as_ptr() as _, code.size());
Ok((
Self {
resolver: FuncResolver {
map: backend_cache.offsets,
memory: code,
},
local_relocs: Map::new(),
external_relocs: backend_cache.external_relocs,
import_len: info.imported_functions.len(),
},
Trampolines::from_trampoline_cache(backend_cache.trampolines),
handler_data,
))
}
#[cfg(feature = "cache")]
pub fn to_backend_cache(
mut self,
trampolines: TrampolineCache,
handler_data: HandlerData,
) -> (BackendCache, Memory) {
self.relocate_locals();
(
BackendCache {
external_relocs: self.external_relocs,
offsets: self.resolver.map,
trap_sink: handler_data.trap_data,
trampolines,
},
self.resolver.memory,
)
}
pub fn new(
isa: &isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>,
import_len: usize,
info: &ModuleInfo,
) -> CompileResult<(Self, HandlerData)> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
let mut relocations = Map::with_capacity(function_bodies.len());
let mut local_relocs = Map::with_capacity(function_bodies.len());
let mut external_relocs = Map::new();
let mut trap_sink = TrapSink::new();
let mut local_trap_sink = LocalTrapSink::new();
@ -58,7 +123,8 @@ impl FuncResolverBuilder {
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
compiled_functions.push(code_buf);
relocations.push(reloc_sink.func_relocs);
local_relocs.push(reloc_sink.local_relocs.into_boxed_slice());
external_relocs.push(reloc_sink.external_relocs.into_boxed_slice());
}
let mut memory = Memory::with_size(total_size)
@ -98,43 +164,58 @@ impl FuncResolverBuilder {
let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size());
Ok((
Self {
let mut func_resolver_builder = Self {
resolver: FuncResolver { map, memory },
relocations,
import_len,
},
handler_data,
))
local_relocs,
external_relocs,
import_len: info.imported_functions.len(),
};
func_resolver_builder.relocate_locals();
Ok((func_resolver_builder, handler_data))
}
pub fn finalize(mut self) -> CompileResult<FuncResolver> {
for (index, relocs) in self.relocations.iter() {
for ref reloc in relocs {
fn relocate_locals(&mut self) {
for (index, relocs) in self.local_relocs.iter() {
for ref reloc in relocs.iter() {
let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len);
let target_func_address =
self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize;
// We need the address of the current function
// because these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize;
unsafe {
let reloc_address = func_addr + reloc.offset as usize;
let reloc_delta = target_func_address
.wrapping_sub(reloc_address)
.wrapping_add(reloc.addend as usize);
write_unaligned(reloc_address as *mut u32, reloc_delta as u32);
}
}
}
}
pub fn finalize(
mut self,
signatures: &SliceMap<SigIndex, Arc<FuncSig>>,
) -> CompileResult<FuncResolver> {
for (index, relocs) in self.external_relocs.iter() {
for ref reloc in relocs.iter() {
let target_func_address: isize = match reloc.target {
RelocationType::Normal(local_func_index) => {
// This will always be an internal function
// because imported functions are not
// called in this way.
// Adjust from wasm-wide function index to index of locally-defined functions only.
let local_func_index =
LocalFuncIndex::new(local_func_index.index() - self.import_len);
self.resolver.lookup(local_func_index).unwrap().as_ptr() as isize
}
RelocationType::LibCall(libcall) => match libcall {
ir::LibCall::CeilF32 => libcalls::ceilf32 as isize,
ir::LibCall::FloorF32 => libcalls::floorf32 as isize,
ir::LibCall::TruncF32 => libcalls::truncf32 as isize,
ir::LibCall::NearestF32 => libcalls::nearbyintf32 as isize,
ir::LibCall::CeilF64 => libcalls::ceilf64 as isize,
ir::LibCall::FloorF64 => libcalls::floorf64 as isize,
ir::LibCall::TruncF64 => libcalls::truncf64 as isize,
ir::LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
ir::LibCall::Probestack => libcalls::__rust_probestack as isize,
_ => Err(CompileError::InternalError {
msg: format!("unexpected libcall: {}", libcall),
})?,
LibCall::CeilF32 => libcalls::ceilf32 as isize,
LibCall::FloorF32 => libcalls::floorf32 as isize,
LibCall::TruncF32 => libcalls::truncf32 as isize,
LibCall::NearestF32 => libcalls::nearbyintf32 as isize,
LibCall::CeilF64 => libcalls::ceilf64 as isize,
LibCall::FloorF64 => libcalls::floorf64 as isize,
LibCall::TruncF64 => libcalls::truncf64 as isize,
LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
LibCall::Probestack => libcalls::__rust_probestack as isize,
},
RelocationType::Intrinsic(ref name) => match name.as_str() {
"i32print" => i32_print as isize,
@ -181,10 +262,15 @@ impl FuncResolverBuilder {
}
},
},
RelocationType::Signature(sig_index) => {
let sig_index =
SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index]));
sig_index.index() as _
}
};
// We need the address of the current function
// because these calls are relative.
// because some of these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr();
// Determine relocation type and apply relocation.
@ -200,17 +286,14 @@ impl FuncResolverBuilder {
};
LittleEndian::write_u64(ptr_slice, ptr_to_write);
}
Reloc::X86PCRel4 => unsafe {
let reloc_address = func_addr.offset(reloc.offset as isize) as isize;
let reloc_addend = reloc.addend as isize;
// TODO: Handle overflow.
let reloc_delta_i32 =
(target_func_address - reloc_address + reloc_addend) as i32;
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => unsafe {
let reloc_address = (func_addr as usize) + reloc.offset as usize;
let reloc_delta = target_func_address
.wrapping_sub(reloc_address as isize)
.wrapping_add(reloc.addend as isize);
write_unaligned(reloc_address as *mut u32, reloc_delta as u32);
},
_ => Err(CompileError::InternalError {
msg: format!("unsupported reloc kind: {}", reloc.reloc),
})?,
}
}
}

View File

@ -1,3 +1,5 @@
#[cfg(feature = "cache")]
use crate::cache::TrampolineCache;
use cranelift_codegen::{
binemit::{NullTrapSink, Reloc, RelocSink},
cursor::{Cursor, FuncCursor},
@ -8,7 +10,7 @@ use hashbrown::HashMap;
use std::{iter, mem};
use wasmer_runtime_core::{
backend::sys::{Memory, Protect},
module::{ExportIndex, ModuleInner},
module::{ExportIndex, ModuleInfo},
types::{FuncSig, SigIndex, Type},
vm,
};
@ -27,7 +29,45 @@ pub struct Trampolines {
}
impl Trampolines {
pub fn new(isa: &isa::TargetIsa, module: &ModuleInner) -> Self {
#[cfg(feature = "cache")]
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
// pub struct TrampolineCache {
// #[serde(with = "serde_bytes")]
// code: Vec<u8>,
// offsets: HashMap<SigIndex, usize>,
// }
let mut memory = Memory::with_size(cache.code.len()).unwrap();
unsafe {
memory.protect(.., Protect::ReadWrite).unwrap();
// Copy over the compiled code.
memory.as_slice_mut()[..cache.code.len()].copy_from_slice(cache.code.as_slice());
memory.protect(.., Protect::ReadExec).unwrap();
}
Self {
memory,
offsets: cache.offsets,
}
}
#[cfg(feature = "cache")]
pub fn to_trampoline_cache(self) -> TrampolineCache {
let mut code = vec![0; self.memory.size()];
unsafe {
code.copy_from_slice(self.memory.as_slice());
}
TrampolineCache {
code,
offsets: self.offsets,
}
}
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
let func_index_iter = module
.exports
.values()
@ -43,7 +83,7 @@ impl Trampolines {
for exported_func_index in func_index_iter {
let sig_index = module.func_assoc[*exported_func_index];
let func_sig = module.sig_registry.lookup_signature(sig_index);
let func_sig = &module.signatures[sig_index];
let trampoline_func = generate_func(&func_sig);

View File

@ -15,8 +15,14 @@ use wasmer_runtime_core::{
/// We check if a provided module is an Emscripten generated one
pub fn is_emscripten_module(module: &Module) -> bool {
for (_, import_name) in &module.0.imported_functions {
if import_name.name == "_emscripten_memcpy_big" && import_name.namespace == "env" {
for (_, import_name) in &module.0.info.imported_functions {
let namespace = module
.0
.info
.namespace_table
.get(import_name.namespace_index);
let field = module.0.info.name_table.get(import_name.name_index);
if field == "_emscripten_memcpy_big" && namespace == "env" {
return true;
}
}
@ -24,12 +30,12 @@ pub fn is_emscripten_module(module: &Module) -> bool {
}
pub fn get_emscripten_table_size(module: &Module) -> (u32, Option<u32>) {
let (_, table) = &module.0.imported_tables[ImportedTableIndex::new(0)];
let (_, table) = &module.0.info.imported_tables[ImportedTableIndex::new(0)];
(table.minimum, table.maximum)
}
pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option<Pages>) {
let (_, memory) = &module.0.imported_memories[ImportedMemoryIndex::new(0)];
let (_, memory) = &module.0.info.imported_memories[ImportedMemoryIndex::new(0)];
(memory.minimum, memory.maximum)
}

View File

@ -14,6 +14,28 @@ page_size = "0.4.1"
wasmparser = "0.23.0"
parking_lot = "0.7.1"
lazy_static = "1.2.0"
indexmap = "1.0.2"
# Dependencies for caching.
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dependencies.serde_bytes]
version = "0.10"
optional = true
[dependencies.serde-bench]
version = "0.0.7"
optional = true
[dependencies.memmap]
version = "0.7.0"
optional = true
[dependencies.sha2]
version = "0.8.0"
optional = true
libc = "0.2.48"
errno = "0.2.4"
@ -29,4 +51,5 @@ field-offset = "0.1.1"
[features]
debug = []
cache = ["serde/rc", "serde_derive", "serde_bytes", "hashbrown/serde", "serde-bench", "memmap", "sha2"]

View File

@ -6,6 +6,12 @@ use crate::{
types::{FuncIndex, LocalFuncIndex, Value},
vm,
};
#[cfg(feature = "cache")]
use crate::{
cache::{Cache, Error as CacheError},
module::ModuleInfo,
sys::Memory,
};
use std::ptr::NonNull;
pub mod sys {
@ -13,6 +19,12 @@ pub mod sys {
}
pub use crate::sig_registry::SigRegistry;
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Backend {
Cranelift,
}
/// This type cannot be constructed from
/// outside the runtime crate.
pub struct Token {
@ -30,6 +42,16 @@ pub trait Compiler {
/// The `CompileToken` parameter ensures that this can only
/// be called from inside the runtime.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError>;
#[cfg(feature = "cache")]
fn compile_to_backend_cache_data(
&self,
wasm: &[u8],
_: Token,
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)>;
}
/// The functionality exposed by this trait is expected to be used

View File

@ -5,6 +5,7 @@ use crate::{
import::ImportObject,
memory::Memory,
module::{ImportName, ModuleInner},
sig_registry::SigRegistry,
structures::{BoxedMap, Map, SliceMap, TypedIndex},
table::Table,
types::{
@ -13,7 +14,7 @@ use crate::{
},
vm,
};
use std::slice;
use std::{slice, sync::Arc};
#[derive(Debug)]
pub struct LocalBacking {
@ -58,9 +59,8 @@ impl LocalBacking {
}
fn generate_memories(module: &ModuleInner) -> BoxedMap<LocalMemoryIndex, Memory> {
let mut memories = Map::with_capacity(module.memories.len());
for (_, &desc) in &module.memories {
let mut memories = Map::with_capacity(module.info.memories.len());
for (_, &desc) in &module.info.memories {
memories.push(Memory::new(desc).expect("unable to create memory"));
}
@ -74,6 +74,7 @@ impl LocalBacking {
) -> BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory> {
// For each init that has some data...
for init in module
.info
.data_initializers
.iter()
.filter(|init| init.data.len() > 0)
@ -92,7 +93,7 @@ impl LocalBacking {
match init.memory_index.local_or_import(module) {
LocalOrImport::Local(local_memory_index) => {
let memory_desc = module.memories[local_memory_index];
let memory_desc = module.info.memories[local_memory_index];
let data_top = init_base + init.data.len();
assert!(memory_desc.minimum.bytes().0 >= data_top);
@ -128,9 +129,9 @@ impl LocalBacking {
}
fn generate_tables(module: &ModuleInner) -> BoxedMap<LocalTableIndex, Table> {
let mut tables = Map::with_capacity(module.tables.len());
let mut tables = Map::with_capacity(module.info.tables.len());
for (_, &table_desc) in module.tables.iter() {
for (_, &table_desc) in module.info.tables.iter() {
let table = Table::new(table_desc).unwrap();
tables.push(table);
}
@ -145,7 +146,7 @@ impl LocalBacking {
tables: &mut SliceMap<LocalTableIndex, Table>,
vmctx: *mut vm::Ctx,
) -> BoxedMap<LocalTableIndex, *mut vm::LocalTable> {
for init in &module.elem_initializers {
for init in &module.info.elem_initializers {
let init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
@ -170,8 +171,11 @@ impl LocalBacking {
table.anyfunc_direct_access_mut(|elements| {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
let sig_index = module.info.func_assoc[func_index];
let signature = &module.info.signatures[sig_index];
let sig_id = vm::SigId(
SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32,
);
let (func, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => (
@ -205,8 +209,11 @@ impl LocalBacking {
table.anyfunc_direct_access_mut(|elements| {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
let sig_index = module.info.func_assoc[func_index];
let signature = &module.info.signatures[sig_index];
let sig_id = vm::SigId(
SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32,
);
let (func, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => (
@ -243,9 +250,9 @@ impl LocalBacking {
module: &ModuleInner,
imports: &ImportBacking,
) -> BoxedMap<LocalGlobalIndex, Global> {
let mut globals = Map::with_capacity(module.globals.len());
let mut globals = Map::with_capacity(module.info.globals.len());
for (_, global_init) in module.globals.iter() {
for (_, global_init) in module.info.globals.iter() {
let value = match &global_init.init {
Initializer::Const(value) => value.clone(),
Initializer::GetGlobal(import_global_index) => {
@ -348,10 +355,21 @@ fn import_functions(
vmctx: *mut vm::Ctx,
) -> LinkResult<BoxedMap<ImportedFuncIndex, vm::ImportedFunc>> {
let mut link_errors = vec![];
let mut functions = Map::with_capacity(module.imported_functions.len());
for (index, ImportName { namespace, name }) in &module.imported_functions {
let sig_index = module.func_assoc[index.convert_up(module)];
let expected_sig = module.sig_registry.lookup_signature(sig_index);
let mut functions = Map::with_capacity(module.info.imported_functions.len());
for (
index,
ImportName {
namespace_index,
name_index,
},
) in &module.info.imported_functions
{
let sig_index = module.info.func_assoc[index.convert_up(module)];
let expected_sig = &module.info.signatures[sig_index];
let namespace = module.info.namespace_table.get(*namespace_index);
let name = module.info.name_table.get(*name_index);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
@ -361,7 +379,7 @@ fn import_functions(
ctx,
signature,
}) => {
if expected_sig == signature {
if *expected_sig == signature {
functions.push(vm::ImportedFunc {
func: func.inner(),
vmctx: match ctx {
@ -371,8 +389,8 @@ fn import_functions(
});
} else {
link_errors.push(LinkError::IncorrectImportSignature {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: expected_sig.clone(),
found: signature.clone(),
});
@ -387,16 +405,16 @@ fn import_functions(
}
.to_string();
link_errors.push(LinkError::IncorrectImportType {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: "function".to_string(),
found: export_type_name,
});
}
None => {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
});
}
}
@ -417,11 +435,22 @@ fn import_memories(
BoxedMap<ImportedMemoryIndex, *mut vm::LocalMemory>,
)> {
let mut link_errors = vec![];
let mut memories = Map::with_capacity(module.imported_memories.len());
let mut vm_memories = Map::with_capacity(module.imported_memories.len());
for (_index, (ImportName { namespace, name }, expected_memory_desc)) in
&module.imported_memories
let mut memories = Map::with_capacity(module.info.imported_memories.len());
let mut vm_memories = Map::with_capacity(module.info.imported_memories.len());
for (
_index,
(
ImportName {
namespace_index,
name_index,
},
expected_memory_desc,
),
) in &module.info.imported_memories
{
let namespace = module.info.namespace_table.get(*namespace_index);
let name = module.info.name_table.get(*name_index);
let memory_import = imports
.get_namespace(&namespace)
.and_then(|namespace| namespace.get_export(&name));
@ -432,8 +461,8 @@ fn import_memories(
vm_memories.push(memory.vm_local_memory());
} else {
link_errors.push(LinkError::IncorrectMemoryDescriptor {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: *expected_memory_desc,
found: memory.descriptor(),
});
@ -448,16 +477,16 @@ fn import_memories(
}
.to_string();
link_errors.push(LinkError::IncorrectImportType {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: "memory".to_string(),
found: export_type_name,
});
}
None => {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
});
}
}
@ -478,9 +507,22 @@ fn import_tables(
BoxedMap<ImportedTableIndex, *mut vm::LocalTable>,
)> {
let mut link_errors = vec![];
let mut tables = Map::with_capacity(module.imported_tables.len());
let mut vm_tables = Map::with_capacity(module.imported_tables.len());
for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables {
let mut tables = Map::with_capacity(module.info.imported_tables.len());
let mut vm_tables = Map::with_capacity(module.info.imported_tables.len());
for (
_index,
(
ImportName {
namespace_index,
name_index,
},
expected_table_desc,
),
) in &module.info.imported_tables
{
let namespace = module.info.namespace_table.get(*namespace_index);
let name = module.info.name_table.get(*name_index);
let table_import = imports
.get_namespace(&namespace)
.and_then(|namespace| namespace.get_export(&name));
@ -491,8 +533,8 @@ fn import_tables(
tables.push(table);
} else {
link_errors.push(LinkError::IncorrectTableDescriptor {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: *expected_table_desc,
found: table.descriptor(),
});
@ -507,16 +549,16 @@ fn import_tables(
}
.to_string();
link_errors.push(LinkError::IncorrectImportType {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: "table".to_string(),
found: export_type_name,
});
}
None => {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
});
}
}
@ -537,9 +579,21 @@ fn import_globals(
BoxedMap<ImportedGlobalIndex, *mut vm::LocalGlobal>,
)> {
let mut link_errors = vec![];
let mut globals = Map::with_capacity(module.imported_globals.len());
let mut vm_globals = Map::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, imported_global_desc)) in &module.imported_globals {
let mut globals = Map::with_capacity(module.info.imported_globals.len());
let mut vm_globals = Map::with_capacity(module.info.imported_globals.len());
for (
_,
(
ImportName {
namespace_index,
name_index,
},
imported_global_desc,
),
) in &module.info.imported_globals
{
let namespace = module.info.namespace_table.get(*namespace_index);
let name = module.info.name_table.get(*name_index);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
@ -550,8 +604,8 @@ fn import_globals(
globals.push(global);
} else {
link_errors.push(LinkError::IncorrectGlobalDescriptor {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: *imported_global_desc,
found: global.descriptor(),
});
@ -566,16 +620,16 @@ fn import_globals(
}
.to_string();
link_errors.push(LinkError::IncorrectImportType {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
expected: "global".to_string(),
found: export_type_name,
});
}
None => {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.clone(),
name: name.clone(),
namespace: namespace.to_string(),
name: name.to_string(),
});
}
}

View File

@ -0,0 +1,181 @@
use crate::{module::ModuleInfo, sys::Memory};
use memmap::Mmap;
use serde_bench::{deserialize, serialize};
use sha2::{Digest, Sha256};
use std::{
fs::File,
io::{self, Seek, SeekFrom, Write},
mem,
path::Path,
slice,
};
#[derive(Debug)]
pub enum InvalidFileType {
InvalidSize,
InvalidMagic,
}
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
DeserializeError(String),
SerializeError(String),
Unknown(String),
InvalidFile(InvalidFileType),
InvalidatedCache,
}
const CURRENT_CACHE_VERSION: u64 = 0;
/// The header of a cache file.
#[repr(C, packed)]
struct CacheHeader {
magic: [u8; 8], // [W, A, S, M, E, R, \0, \0]
version: u64,
data_len: u64,
wasm_hash: [u8; 32], // Sha256 of the wasm in binary format.
}
impl CacheHeader {
pub fn read_from_slice(buffer: &[u8]) -> Result<(&CacheHeader, &[u8]), Error> {
if buffer.len() >= mem::size_of::<CacheHeader>() {
if &buffer[..8] == "WASMER\0\0".as_bytes() {
let (header_slice, body_slice) = buffer.split_at(mem::size_of::<CacheHeader>());
let header = unsafe { &*(header_slice.as_ptr() as *const CacheHeader) };
if header.version == CURRENT_CACHE_VERSION {
Ok((header, body_slice))
} else {
Err(Error::InvalidatedCache)
}
} else {
Err(Error::InvalidFile(InvalidFileType::InvalidMagic))
}
} else {
Err(Error::InvalidFile(InvalidFileType::InvalidSize))
}
}
pub fn as_slice(&self) -> &[u8] {
let ptr = self as *const CacheHeader as *const u8;
unsafe { slice::from_raw_parts(ptr, mem::size_of::<CacheHeader>()) }
}
}
#[derive(Serialize, Deserialize)]
struct CacheInner {
info: Box<ModuleInfo>,
#[serde(with = "serde_bytes")]
backend_metadata: Vec<u8>,
compiled_code: Memory,
}
pub struct Cache {
inner: CacheInner,
wasm_hash: Box<[u8; 32]>,
}
impl Cache {
pub(crate) fn new(
wasm: &[u8],
info: Box<ModuleInfo>,
backend_metadata: Vec<u8>,
compiled_code: Memory,
) -> Self {
let wasm_hash = hash_data(wasm);
Self {
inner: CacheInner {
info,
backend_metadata,
compiled_code,
},
wasm_hash: Box::new(wasm_hash),
}
}
pub fn open<P>(path: P) -> Result<Cache, Error>
where
P: AsRef<Path>,
{
let file = File::open(path).map_err(|e| Error::IoError(e))?;
let mmap = unsafe { Mmap::map(&file).map_err(|e| Error::IoError(e))? };
let (header, body_slice) = CacheHeader::read_from_slice(&mmap[..])?;
let inner =
deserialize(body_slice).map_err(|e| Error::DeserializeError(format!("{:#?}", e)))?;
Ok(Cache {
inner,
wasm_hash: Box::new(header.wasm_hash),
})
}
pub fn info(&self) -> &ModuleInfo {
&self.inner.info
}
pub fn wasm_hash(&self) -> &[u8; 32] {
&self.wasm_hash
}
#[doc(hidden)]
pub fn consume(self) -> (ModuleInfo, Vec<u8>, Memory) {
(
*self.inner.info,
self.inner.backend_metadata,
self.inner.compiled_code,
)
}
pub fn store<P>(&self, path: P) -> Result<(), Error>
where
P: AsRef<Path>,
{
let mut file = File::create(path).map_err(|e| Error::IoError(e))?;
let mut buffer = Vec::new();
serialize(&mut buffer, &self.inner).map_err(|e| Error::SerializeError(e.to_string()))?;
let data_len = buffer.len() as u64;
file.seek(SeekFrom::Start(mem::size_of::<CacheHeader>() as u64))
.map_err(|e| Error::IoError(e))?;
file.write(buffer.as_slice())
.map_err(|e| Error::IoError(e))?;
file.seek(SeekFrom::Start(0))
.map_err(|e| Error::Unknown(e.to_string()))?;
let wasm_hash = {
let mut array = [0u8; 32];
array.copy_from_slice(&*self.wasm_hash);
array
};
let cache_header = CacheHeader {
magic: [
'W' as u8, 'A' as u8, 'S' as u8, 'M' as u8, 'E' as u8, 'R' as u8, 0, 0,
],
version: CURRENT_CACHE_VERSION,
data_len,
wasm_hash,
};
file.write(cache_header.as_slice())
.map_err(|e| Error::IoError(e))?;
Ok(())
}
}
pub fn hash_data(data: &[u8]) -> [u8; 32] {
let mut array = [0u8; 32];
array.copy_from_slice(Sha256::digest(data).as_slice());
array
}

View File

@ -49,7 +49,7 @@ impl<'a> ExportIter<'a> {
pub(crate) fn new(module: &'a ModuleInner, inner: &'a mut InstanceInner) -> Self {
Self {
inner,
iter: module.exports.iter(),
iter: module.info.exports.iter(),
module,
}
}

View File

@ -7,6 +7,7 @@ use crate::{
import::{ImportObject, LikeNamespace},
memory::Memory,
module::{ExportIndex, Module, ModuleInner},
sig_registry::SigRegistry,
table::Table,
typed_func::{Func, Safe, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value},
@ -65,7 +66,7 @@ impl Instance {
let instance = Instance { module, inner };
if let Some(start_index) = instance.module.start_func {
if let Some(start_index) = instance.module.info.start_func {
instance.call_with_index(start_index, &[])?;
}
@ -98,6 +99,7 @@ impl Instance {
{
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
@ -107,10 +109,11 @@ impl Instance {
if let ExportIndex::Func(func_index) = export_index {
let sig_index = *self
.module
.info
.func_assoc
.get(*func_index)
.expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_signature(sig_index);
let signature = SigRegistry.lookup_signature(sig_index);
if signature.params() != Args::types() || signature.returns() != Rets::types() {
Err(ResolveError::Signature {
@ -167,6 +170,7 @@ impl Instance {
pub fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc> {
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
@ -176,10 +180,11 @@ impl Instance {
if let ExportIndex::Func(func_index) = export_index {
let sig_index = *self
.module
.info
.func_assoc
.get(*func_index)
.expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_signature(sig_index);
let signature = Arc::clone(&self.module.info.signatures[sig_index]);
Ok(DynFunc {
signature,
@ -220,6 +225,7 @@ impl Instance {
pub fn call(&self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
@ -270,10 +276,11 @@ impl Instance {
fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult<Vec<Value>> {
let sig_index = *self
.module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_signature(sig_index);
let signature = &self.module.info.signatures[sig_index];
if !signature.check_param_value_types(args) {
Err(ResolveError::Signature {
@ -344,6 +351,7 @@ impl InstanceInner {
func_index: FuncIndex,
) -> (FuncPointer, Context, Arc<FuncSig>) {
let sig_index = *module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
@ -367,9 +375,13 @@ impl InstanceInner {
}
};
let signature = module.sig_registry.lookup_signature(sig_index);
let signature = &module.info.signatures[sig_index];
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
(
unsafe { FuncPointer::new(func_ptr) },
ctx,
Arc::clone(signature),
)
}
fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory {
@ -406,7 +418,7 @@ impl InstanceInner {
impl LikeNamespace for Instance {
fn get_export(&self, name: &str) -> Option<Export> {
let export_index = self.module.exports.get(name)?;
let export_index = self.module.info.exports.get(name)?;
Some(self.inner.get_export_from_index(&self.module, export_index))
}

View File

@ -2,11 +2,17 @@
#[macro_use]
extern crate field_offset;
#[cfg(feature = "cache")]
#[macro_use]
extern crate serde_derive;
#[macro_use]
mod macros;
#[doc(hidden)]
pub mod backend;
mod backing;
#[cfg(feature = "cache")]
pub mod cache;
pub mod error;
pub mod export;
pub mod global;
@ -36,6 +42,9 @@ pub use self::module::Module;
pub use self::typed_func::Func;
use std::sync::Arc;
#[cfg(feature = "cache")]
use self::cache::{Cache, Error as CacheError};
pub mod prelude {
pub use crate::import::{ImportObject, Namespace};
pub use crate::types::{
@ -79,5 +88,28 @@ pub fn validate(wasm: &[u8]) -> bool {
}
}
#[cfg(feature = "cache")]
pub fn compile_to_cache_with(
wasm: &[u8],
compiler: &dyn backend::Compiler,
) -> CompileResult<Cache> {
let token = backend::Token::generate();
let (info, backend_metadata, compiled_code) =
compiler.compile_to_backend_cache_data(wasm, token)?;
Ok(Cache::new(wasm, info, backend_metadata, compiled_code))
}
#[cfg(feature = "cache")]
pub unsafe fn load_cache_with(
cache: Cache,
compiler: &dyn backend::Compiler,
) -> std::result::Result<module::Module, CacheError> {
let token = backend::Token::generate();
compiler
.from_cache(cache, token)
.map(|inner| module::Module::new(Arc::new(inner)))
}
/// The current version of this crate
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@ -1,11 +1,10 @@
use crate::{
backend::{FuncResolver, ProtectedCaller},
backend::{Backend, FuncResolver, ProtectedCaller},
error::Result,
import::ImportObject,
sig_registry::SigRegistry,
structures::Map,
structures::{Map, TypedIndex},
types::{
FuncIndex, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
SigIndex, TableDescriptor, TableIndex,
@ -13,6 +12,7 @@ use crate::{
Instance,
};
use hashbrown::HashMap;
use indexmap::IndexMap;
use std::sync::Arc;
/// This is used to instantiate a new WebAssembly module.
@ -21,6 +21,11 @@ pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>,
pub protected_caller: Box<dyn ProtectedCaller>,
pub info: ModuleInfo,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
pub struct ModuleInfo {
// This are strictly local and the typsystem ensures that.
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
pub globals: Map<LocalGlobalIndex, GlobalInit>,
@ -40,7 +45,11 @@ pub struct ModuleInner {
pub start_func: Option<FuncIndex>,
pub func_assoc: Map<FuncIndex, SigIndex>,
pub sig_registry: SigRegistry,
pub signatures: Map<SigIndex, Arc<FuncSig>>,
pub backend: Backend,
pub namespace_table: StringTable<NamespaceIndex>,
pub name_table: StringTable<NameIndex>,
}
/// A compiled WebAssembly module.
@ -87,21 +96,14 @@ impl Module {
impl ModuleInner {}
#[doc(hidden)]
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct ImportName {
pub namespace: String,
pub name: String,
}
impl From<(String, String)> for ImportName {
fn from(n: (String, String)) -> Self {
ImportName {
namespace: n.0,
name: n.1,
}
}
pub namespace_index: NamespaceIndex,
pub name_index: NameIndex,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportIndex {
Func(FuncIndex),
@ -111,6 +113,7 @@ pub enum ExportIndex {
}
/// A data initializer for linear memory.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct DataInitializer {
/// The index of the memory to initialize.
@ -118,10 +121,12 @@ pub struct DataInitializer {
/// Either a constant offset or a `get_global`
pub base: Initializer,
/// The initialization data.
#[cfg_attr(feature = "cache", serde(with = "serde_bytes"))]
pub data: Vec<u8>,
}
/// A WebAssembly table initializer.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct TableInitializer {
/// The index of a table to initialize.
@ -131,3 +136,110 @@ pub struct TableInitializer {
/// The values to write into the table elements.
pub elements: Vec<FuncIndex>,
}
pub struct StringTableBuilder<K: TypedIndex> {
map: IndexMap<String, (K, u32, u32)>,
buffer: String,
count: u32,
}
impl<K: TypedIndex> StringTableBuilder<K> {
pub fn new() -> Self {
Self {
map: IndexMap::new(),
buffer: String::new(),
count: 0,
}
}
pub fn register<S>(&mut self, s: S) -> K
where
S: Into<String> + AsRef<str>,
{
let s_str = s.as_ref();
if self.map.contains_key(s_str) {
self.map[s_str].0
} else {
let offset = self.buffer.len();
let length = s_str.len();
let index = TypedIndex::new(self.count as _);
self.buffer.push_str(s_str);
self.map
.insert(s.into(), (index, offset as u32, length as u32));
self.count += 1;
index
}
}
pub fn finish(self) -> StringTable<K> {
let table = self
.map
.values()
.map(|(_, offset, length)| (*offset, *length))
.collect();
StringTable {
table,
buffer: self.buffer,
}
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct StringTable<K: TypedIndex> {
table: Map<K, (u32, u32)>,
buffer: String,
}
impl<K: TypedIndex> StringTable<K> {
pub fn new() -> Self {
Self {
table: Map::new(),
buffer: String::new(),
}
}
pub fn get(&self, index: K) -> &str {
let (offset, length) = self.table[index];
let offset = offset as usize;
let length = length as usize;
&self.buffer[offset..offset + length]
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct NamespaceIndex(u32);
impl TypedIndex for NamespaceIndex {
#[doc(hidden)]
fn new(index: usize) -> Self {
NamespaceIndex(index as _)
}
#[doc(hidden)]
fn index(&self) -> usize {
self.0 as usize
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct NameIndex(u32);
impl TypedIndex for NameIndex {
#[doc(hidden)]
fn new(index: usize) -> Self {
NameIndex(index as _)
}
#[doc(hidden)]
fn index(&self) -> usize {
self.0 as usize
}
}

View File

@ -8,6 +8,7 @@ use std::{
};
/// Dense item map
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Map<K, V>
where

View File

@ -6,7 +6,7 @@ pub use self::boxed::BoxedMap;
pub use self::map::{Iter, IterMut, Map};
pub use self::slice::SliceMap;
pub trait TypedIndex {
pub trait TypedIndex: Copy + Clone {
#[doc(hidden)]
fn new(index: usize) -> Self;
#[doc(hidden)]

View File

@ -9,3 +9,77 @@ pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
#[cfg(feature = "cache")]
use serde::{
de::{self, SeqAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
#[cfg(feature = "cache")]
use serde_bytes::Bytes;
#[cfg(feature = "cache")]
use std::fmt;
#[cfg(feature = "cache")]
impl Serialize for Memory {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
assert!(self.protection().is_readable());
let mut state = serializer.serialize_struct("Memory", 2)?;
state.serialize_field("protection", &self.protection())?;
state.serialize_field("data", &Bytes::new(unsafe { self.as_slice() }))?;
state.end()
}
}
#[cfg(feature = "cache")]
impl<'de> Deserialize<'de> for Memory {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MemoryVisitor;
impl<'de> Visitor<'de> for MemoryVisitor {
type Value = Memory;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "struct Memory")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Memory, V::Error>
where
V: SeqAccess<'de>,
{
let original_protection = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let bytes: Bytes = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let mut memory = Memory::with_size_protect(bytes.len(), Protect::ReadWrite)
.expect("Could not create a memory");
unsafe {
memory.as_slice_mut().copy_from_slice(&*bytes);
if memory.protection() != original_protection {
memory
.protect(.., original_protection)
.expect("Could not protect memory as its original protection");
}
}
Ok(memory)
}
}
deserializer.deserialize_struct("Memory", &["protection", "data"], MemoryVisitor)
}
}

View File

@ -2,7 +2,7 @@ use errno;
use nix::libc;
use page_size;
use std::ops::{Bound, RangeBounds};
use std::{ptr, slice};
use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, rc::Rc, slice};
unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
@ -11,14 +11,86 @@ unsafe impl Sync for Memory {}
pub struct Memory {
ptr: *mut u8,
size: usize,
protection: Protect,
fd: Option<Rc<RawFd>>,
}
impl Memory {
pub fn from_file_path<P>(path: P, protection: Protect) -> Result<Self, String>
where
P: AsRef<Path>,
{
let file = File::open(path).map_err(|e| e.to_string())?;
let file_len = file.metadata().map_err(|e| e.to_string())?.len();
let raw_fd = RawFd::from_file(file);
let ptr = unsafe {
libc::mmap(
ptr::null_mut(),
file_len as usize,
protection.to_protect_const() as i32,
libc::MAP_PRIVATE,
raw_fd.0,
0,
)
};
if ptr == -1 as _ {
Err(errno::errno().to_string())
} else {
Ok(Self {
ptr: ptr as *mut u8,
size: file_len as usize,
protection,
fd: Some(Rc::new(raw_fd)),
})
}
}
pub fn with_size_protect(size: usize, protection: Protect) -> Result<Self, String> {
if size == 0 {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection,
fd: None,
});
}
let size = round_up_to_page_size(size, page_size::get());
let ptr = unsafe {
libc::mmap(
ptr::null_mut(),
size,
protection.to_protect_const() as i32,
libc::MAP_PRIVATE | libc::MAP_ANON,
-1,
0,
)
};
if ptr == -1 as _ {
Err(errno::errno().to_string())
} else {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection,
fd: None,
})
}
}
pub fn with_size(size: usize) -> Result<Self, String> {
if size == 0 {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection: Protect::None,
fd: None,
});
}
@ -41,6 +113,8 @@ impl Memory {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection: Protect::None,
fd: None,
})
}
}
@ -48,9 +122,9 @@ impl Memory {
pub unsafe fn protect(
&mut self,
range: impl RangeBounds<usize>,
protect: Protect,
protection: Protect,
) -> Result<(), String> {
let protect = protect.to_protect_const();
let protect = protection.to_protect_const();
let range_start = match range.start_bound() {
Bound::Included(start) => *start,
@ -75,10 +149,32 @@ impl Memory {
if success == -1 {
Err(errno::errno().to_string())
} else {
self.protection = protection;
Ok(())
}
}
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
let page_size = page_size::get();
if offset % page_size == 0 {
let second_ptr = unsafe { self.ptr.add(offset) };
let second_size = self.size - offset;
self.size = offset;
let second = Memory {
ptr: second_ptr,
size: second_size,
protection: self.protection,
fd: self.fd.clone(),
};
(self, second)
} else {
panic!("offset must be multiple of page size: {}", offset)
}
}
pub fn size(&self) -> usize {
self.size
}
@ -91,6 +187,10 @@ impl Memory {
slice::from_raw_parts_mut(self.ptr, self.size)
}
pub fn protection(&self) -> Protect {
self.protection
}
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
@ -105,6 +205,7 @@ impl Drop for Memory {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Protect {
@ -123,6 +224,41 @@ impl Protect {
Protect::ReadExec => 1 | 4,
}
}
pub fn is_readable(self) -> bool {
match self {
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
_ => false,
}
}
pub fn is_writable(self) -> bool {
match self {
Protect::ReadWrite => true,
_ => false,
}
}
}
#[derive(Debug)]
struct RawFd(i32);
impl RawFd {
fn from_file(f: File) -> Self {
RawFd(f.into_raw_fd())
}
}
impl Drop for RawFd {
fn drop(&mut self) {
let success = unsafe { libc::close(self.0) };
assert_eq!(
success,
0,
"failed to close mmapped file descriptor: {}",
errno::errno()
);
}
}
/// Round `size` up to the nearest multiple of `page_size`.

View File

@ -14,6 +14,7 @@ unsafe impl Sync for Memory {}
pub struct Memory {
ptr: *mut u8,
size: usize,
protection: Protect,
}
impl Memory {
@ -22,6 +23,7 @@ impl Memory {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection: Protect::None,
});
}
@ -35,6 +37,7 @@ impl Memory {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection: Protect::None,
})
}
}
@ -71,10 +74,31 @@ impl Memory {
if ptr.is_null() {
Err("unable to protect memory".to_string())
} else {
self.protection = protection;
Ok(())
}
}
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
let page_size = page_size::get();
if offset % page_size == 0 {
let second_ptr = unsafe { self.ptr.add(offset) };
let second_size = self.size - offset;
self.size = offset;
let second = Memory {
ptr: second_ptr,
size: second_size,
protection: self.protection,
};
(self, second)
} else {
panic!("offset must be multiple of page size: {}", offset)
}
}
pub fn size(&self) -> usize {
self.size
}
@ -87,6 +111,10 @@ impl Memory {
slice::from_raw_parts_mut(self.ptr, self.size)
}
pub fn protection(&self) -> Protect {
self.protection
}
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
@ -101,6 +129,7 @@ impl Drop for Memory {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Protect {
@ -119,6 +148,20 @@ impl Protect {
Protect::ReadExec => PAGE_EXECUTE_READ,
}
}
pub fn is_readable(self) -> bool {
match self {
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
_ => false,
}
}
pub fn is_writable(self) -> bool {
match self {
Protect::ReadWrite => true,
_ => false,
}
}
}
/// Round `size` up to the nearest multiple of `page_size`.

View File

@ -2,6 +2,7 @@ use crate::{memory::MemoryType, module::ModuleInner, structures::TypedIndex, uni
use std::{borrow::Cow, mem};
/// Represents a WebAssembly type.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
/// The `i32` type.
@ -18,6 +19,7 @@ pub enum Type {
///
/// As the number of types in WebAssembly expand,
/// this structure will expand as well.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
/// The `i32` type.
@ -145,12 +147,32 @@ macro_rules! convert_value_impl {
convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64);
impl ValueType for f32 {
fn into_le(self, buffer: &mut [u8]) {
self.to_bits().into_le(buffer);
}
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
Ok(f32::from_bits(<u32 as ValueType>::from_le(buffer)?))
}
}
impl ValueType for f64 {
fn into_le(self, buffer: &mut [u8]) {
self.to_bits().into_le(buffer);
}
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
Ok(f64::from_bits(<u64 as ValueType>::from_le(buffer)?))
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElementType {
/// Any wasm function.
Anyfunc,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub struct TableDescriptor {
/// Type of data stored in this table.
@ -175,6 +197,7 @@ impl TableDescriptor {
/// A const value initializer.
/// Over time, this will be able to represent more and more
/// complex expressions.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum Initializer {
/// Corresponds to a `const.*` instruction.
@ -183,6 +206,7 @@ pub enum Initializer {
GetGlobal(ImportedGlobalIndex),
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlobalDescriptor {
pub mutable: bool,
@ -190,6 +214,7 @@ pub struct GlobalDescriptor {
}
/// A wasm global.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct GlobalInit {
pub desc: GlobalDescriptor,
@ -197,6 +222,7 @@ pub struct GlobalInit {
}
/// A wasm memory.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryDescriptor {
/// The minimum number of allowed pages.
@ -229,6 +255,7 @@ impl MemoryDescriptor {
/// The signature of a function that is either implemented
/// in a wasm module or exposed to wasm by the host.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FuncSig {
params: Cow<'static, [Type]>,
@ -273,6 +300,7 @@ pub trait LocalImport {
#[rustfmt::skip]
macro_rules! define_map_index {
($ty:ident) => {
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct $ty (u32);
impl TypedIndex for $ty {
@ -313,17 +341,17 @@ macro_rules! define_local_or_import {
($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => {
impl $ty {
pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> {
if self.index() < module.$imports.len() {
if self.index() < module.info.$imports.len() {
LocalOrImport::Import(<Self as LocalImport>::Import::new(self.index()))
} else {
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.$imports.len()))
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.info.$imports.len()))
}
}
}
impl $local_ty {
pub fn convert_up(self, module: &ModuleInner) -> $ty {
$ty ((self.index() + module.$imports.len()) as u32)
$ty ((self.index() + module.info.$imports.len()) as u32)
}
}
@ -348,6 +376,7 @@ define_local_or_import![
(GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
];
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SigIndex(u32);
impl TypedIndex for SigIndex {

View File

@ -7,6 +7,7 @@ const WASM_PAGE_SIZE: usize = 65_536;
const WASM_MAX_PAGES: usize = 65_536;
/// Units of WebAssembly pages (as specified to be 65,536 bytes).
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Pages(pub u32);
@ -32,6 +33,7 @@ impl fmt::Debug for Pages {
}
/// Units of WebAssembly memory in terms of 8-bit bytes.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Bytes(pub usize);

View File

@ -423,7 +423,7 @@ mod vm_offset_tests {
#[cfg(test)]
mod vm_ctx_tests {
use super::{Ctx, ImportBacking, LocalBacking};
use crate::module::ModuleInner;
use crate::module::{ModuleInfo, ModuleInner, StringTable};
use crate::structures::Map;
use std::ffi::c_void;
@ -493,7 +493,7 @@ mod vm_ctx_tests {
fn generate_module() -> ModuleInner {
use super::Func;
use crate::backend::{FuncResolver, ProtectedCaller, SigRegistry, Token};
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token};
use crate::error::RuntimeResult;
use crate::types::{FuncIndex, LocalFuncIndex, Value};
use hashbrown::HashMap;
@ -525,6 +525,7 @@ mod vm_ctx_tests {
ModuleInner {
func_resolver: Box::new(Placeholder),
protected_caller: Box::new(Placeholder),
info: ModuleInfo {
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
@ -543,7 +544,12 @@ mod vm_ctx_tests {
start_func: None,
func_assoc: Map::new(),
sig_registry: SigRegistry,
signatures: Map::new(),
backend: Backend::Cranelift,
namespace_table: StringTable::new(),
name_table: StringTable::new(),
},
}
}
}

View File

@ -11,7 +11,10 @@ readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2", optional = true }
lazy_static = "1.2.0"
[features]
default = ["wasmer-clif-backend"]
default = ["default-compiler", "cache"]
default-compiler = ["wasmer-clif-backend/cache", "wasmer-runtime-core/cache"]
cache = ["default-compiler"]
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]

128
lib/runtime/src/cache.rs Normal file
View File

@ -0,0 +1,128 @@
use crate::Module;
use std::path::Path;
use wasmer_runtime_core::cache::{hash_data, Cache as CoreCache};
pub use wasmer_runtime_core::cache::Error;
/// On-disk storage of compiled WebAssembly.
///
/// A `Cache` can be used to quickly reload already
/// compiled WebAssembly from a previous execution
/// during which the wasm was explicitly compiled
/// as a `Cache`.
///
/// # Usage:
///
/// ```
/// use wasmer_runtime::{compile_cache, Cache};
///
/// # use wasmer_runtime::error::{CompileResult, CacheError};
/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> {
/// // Make a cache.
/// let cache = compile_cache(wasm)?;
///
/// # Ok(())
/// # }
/// # fn usage_cache(cache: Cache) -> Result<(), CacheError> {
/// // Store the cache in a file.
/// cache.store("some_cache_file")?;
///
/// // Load the cache.
/// let cache = Cache::load("some_cache_file")?;
/// let module = unsafe { cache.into_module()? };
/// # Ok(())
/// # }
/// ```
///
/// # Performance Characteristics:
///
/// Loading caches from files has been optimized for latency.
/// There is still more work to do that will reduce
/// loading time, especially for very large modules,
/// but it will require signifigant internal work.
///
/// # Drawbacks:
///
/// Due to internal shortcomings, you cannot convert
/// a module into a `Cache`. This means that compiling
/// into a `Cache` and then converting into a module
/// has more overhead than directly compiling
/// into a [`Module`].
///
/// [`Module`]: struct.Module.html
pub struct Cache(pub(crate) CoreCache);
impl Cache {
/// Load a `Cache` from the file specified by `path`.
///
/// # Usage:
///
/// ```
/// use wasmer_runtime::Cache;
/// # use wasmer_runtime::error::CacheError;
///
/// # fn load_cache() -> Result<(), CacheError> {
/// let cache = Cache::load("some_file.cache")?;
/// # Ok(())
/// # }
/// ```
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
CoreCache::open(path).map(|core_cache| Cache(core_cache))
}
/// Convert a `Cache` into a [`Module`].
///
/// [`Module`]: struct.Module.html
///
/// # Usage:
///
/// ```
/// use wasmer_runtime::Cache;
///
/// # use wasmer_runtime::error::CacheError;
/// # fn cache2module(cache: Cache) -> Result<(), CacheError> {
/// let module = unsafe { cache.into_module()? };
/// # Ok(())
/// # }
/// ```
///
/// # Notes:
///
/// This method is unsafe because the runtime cannot confirm
/// that this cache was not tampered with or corrupted.
pub unsafe fn into_module(self) -> Result<Module, Error> {
let default_compiler = super::default_compiler();
wasmer_runtime_core::load_cache_with(self.0, default_compiler)
}
/// Compare the Sha256 hash of the wasm this cache was build
/// from with some other WebAssembly.
///
/// The main use-case for this is invalidating old caches.
pub fn compare_wasm(&self, wasm: &[u8]) -> bool {
let param_wasm_hash = hash_data(wasm);
self.0.wasm_hash() as &[u8] == &param_wasm_hash as &[u8]
}
/// Store this cache in a file.
///
/// # Notes:
///
/// If a file exists at the specified path, it will be overwritten.
///
/// # Usage:
///
/// ```
/// use wasmer_runtime::Cache;
///
/// # use wasmer_runtime::error::CacheError;
/// # fn store_cache(cache: Cache) -> Result<(), CacheError> {
/// cache.store("some_file.cache")?;
/// # Ok(())
/// # }
/// ```
pub fn store<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
self.0.store(path)
}
}

View File

@ -83,10 +83,8 @@ pub use wasmer_runtime_core::table::Table;
pub use wasmer_runtime_core::types::Value;
pub use wasmer_runtime_core::vm::Ctx;
pub use wasmer_runtime_core::{compile_with, validate};
pub use wasmer_runtime_core::error;
pub use wasmer_runtime_core::Func;
pub use wasmer_runtime_core::{compile_with, validate};
pub use wasmer_runtime_core::{func, imports};
pub mod memory {
@ -100,12 +98,26 @@ pub mod wasm {
pub use wasmer_runtime_core::types::{FuncSig, MemoryDescriptor, TableDescriptor, Type, Value};
}
pub mod error {
#[cfg(feature = "cache")]
pub use super::cache::Error as CacheError;
pub use wasmer_runtime_core::error::*;
}
pub mod units {
//! Various unit types.
pub use wasmer_runtime_core::units::{Bytes, Pages};
}
#[cfg(feature = "cache")]
mod cache;
#[cfg(feature = "default-compiler")]
use wasmer_runtime_core::backend::Compiler;
#[cfg(feature = "cache")]
pub use self::cache::Cache;
/// Compile WebAssembly binary code into a [`Module`].
/// This function is useful if it is necessary to
/// compile a module before it can be instantiated
@ -119,10 +131,9 @@ pub mod units {
/// binary code of the wasm module you want to compile.
/// # Errors:
/// If the operation fails, the function returns `Err(error::CompileError::...)`.
#[cfg(feature = "wasmer-clif-backend")]
#[cfg(feature = "default-compiler")]
pub fn compile(wasm: &[u8]) -> error::CompileResult<Module> {
use wasmer_clif_backend::CraneliftCompiler;
wasmer_runtime_core::compile_with(&wasm[..], &CraneliftCompiler::new())
wasmer_runtime_core::compile_with(&wasm[..], default_compiler())
}
/// Compile and instantiate WebAssembly code without
@ -143,11 +154,48 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult<Module> {
/// `error::CompileError`, `error::LinkError`, or
/// `error::RuntimeError` (all combined into an `error::Error`),
/// depending on the cause of the failure.
#[cfg(feature = "wasmer-clif-backend")]
#[cfg(feature = "default-compiler")]
pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result<Instance> {
let module = compile(wasm)?;
module.instantiate(import_object)
}
/// Compile wasm into a [`Cache`] that can be stored to a file or
/// converted into [`Module`].
///
/// [`Cache`]: struct.Cache.html
/// [`Module`]: struct.Module.html
///
/// # Usage:
///
/// ```
/// # use wasmer_runtime::error::CompileResult;
/// use wasmer_runtime::compile_cache;
///
/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> {
/// let cache = compile_cache(wasm)?;
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "cache")]
pub fn compile_cache(wasm: &[u8]) -> error::CompileResult<Cache> {
let default_compiler = default_compiler();
wasmer_runtime_core::compile_to_cache_with(wasm, default_compiler)
.map(|core_cache| Cache(core_cache))
}
#[cfg(feature = "default-compiler")]
fn default_compiler() -> &'static dyn Compiler {
use lazy_static::lazy_static;
use wasmer_clif_backend::CraneliftCompiler;
lazy_static! {
static ref DEFAULT_COMPILER: CraneliftCompiler = { CraneliftCompiler::new() };
}
&*DEFAULT_COMPILER as &dyn Compiler
}
/// The current version of this crate
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@ -19,5 +19,5 @@ wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" }
wabt = "0.7.2"
[features]
default = ["fast-tests"]
default = ["fast-tests", "wasmer-runtime-core/cache", "wasmer-clif-backend/cache"]
fast-tests = []

View File

@ -1,6 +1,8 @@
use std::fs::remove_file;
use wabt::wat2wasm;
use wasmer_clif_backend::CraneliftCompiler;
use wasmer_runtime_core::{
cache::Cache,
error::Result,
global::Global,
memory::Memory,
@ -13,7 +15,9 @@ use wasmer_runtime_core::{
static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm");
fn main() -> Result<()> {
let compiler = CraneliftCompiler::new();
let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed");
let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new())?;
let memory = Memory::new(MemoryDescriptor {
@ -74,7 +78,10 @@ static IMPORT_MODULE: &str = r#"
(import "env" "table" (table 10 anyfunc))
(import "env" "global" (global i32))
(import "env" "print_i32" (func $print_i32 (type $t0)))
(func $identity (type $t0) (param $p0 i32) (result i32)
get_local $p0)
(func $print_num (export "print_num") (type $t0) (param $p0 i32) (result i32)
get_global 0
call $identity
call $print_i32))
"#;