mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-24 06:01:33 +00:00
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:
124
Cargo.lock
generated
124
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"]
|
46
lib/clif-backend/src/cache.rs
Normal file
46
lib/clif-backend/src/cache.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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(),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
})?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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"]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
181
lib/runtime-core/src/cache.rs
Normal file
181
lib/runtime-core/src/cache.rs
Normal 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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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`.
|
||||
|
@ -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`.
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
128
lib/runtime/src/cache.rs
Normal 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] == ¶m_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)
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
@ -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 = []
|
@ -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))
|
||||
"#;
|
||||
|
Reference in New Issue
Block a user