diff --git a/.gitignore b/.gitignore index 3768a739734f..8861fc3b5949 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ capi/ api-docs/ api-docs-repo/ tags +.ycm* diff --git a/Cargo.lock b/Cargo.lock index f6c7da54dc62..95225f5d180e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.3", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.13" @@ -86,7 +97,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if", + "cfg-if 0.1.10", "constant_time_eq", "crypto-mac", "digest", @@ -104,6 +115,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "314889ea31cda264cb7c3d6e6e5c9415a987ecb0e72c17c00d36fbb881d34abe" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a2b3b92c135dae665a6f760205b89187638e83bed17ef3e44e83c712cf30600" +dependencies = [ + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -148,6 +180,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "chrono" version = "0.4.15" @@ -341,7 +379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg 1.0.1", - "cfg-if", + "cfg-if 0.1.10", "crossbeam-utils", "lazy_static", "maybe-uninit", @@ -356,7 +394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg 1.0.1", - "cfg-if", + "cfg-if 0.1.10", "lazy_static", ] @@ -524,7 +562,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "serde", ] @@ -543,11 +581,22 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + [[package]] name = "ghost" version = "0.1.2" @@ -609,9 +658,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -639,9 +691,9 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "indexmap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg 1.0.1", "hashbrown", @@ -733,7 +785,7 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" dependencies = [ "arrayvec", "bitflags", - "cfg-if", + "cfg-if 0.1.10", "ryu", "static_assertions", ] @@ -772,7 +824,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] @@ -840,7 +892,7 @@ checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "void", ] @@ -909,9 +961,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "orbclient" @@ -967,7 +1019,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "cloudabi", "libc", "redox_syscall", @@ -1039,6 +1091,26 @@ dependencies = [ "unicode-xid 0.2.1", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", +] + [[package]] name = "quote" version = "0.6.13" @@ -1082,7 +1154,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.14", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", @@ -1130,7 +1202,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.14", ] [[package]] @@ -1307,6 +1379,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "rend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1033f6fe7ce48c8333e5412891b933e85d6a3a09728c4883240edf64e7a6f11a" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bf572c17c77322f4d858c214def56b13a3c32b8d833cd6d28a92de8325ac5f" +dependencies = [ + "bytecheck", + "hashbrown", + "indexmap", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3eca50f172b8e59e2080810fb41b65f047960c197149564d4bd0680af1888e" +dependencies = [ + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", +] + [[package]] name = "rustc_version" version = "0.2.3" @@ -1398,10 +1505,16 @@ version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34e71125077d297d57e4c1acfe8981b5bdfbf5a20e7b589abfdcb33bf1127f86" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "semver" version = "0.9.0" @@ -1579,7 +1692,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "rand 0.7.3", "redox_syscall", @@ -1883,7 +1996,7 @@ name = "wasmer-emscripten" version = "0.15.0" dependencies = [ "byteorder", - "getrandom", + "getrandom 0.1.14", "lazy_static", "libc", "log", @@ -1995,6 +2108,7 @@ version = "0.15.0" dependencies = [ "cbindgen", "libc", + "rkyv", "wasmer-clif-backend", "wasmer-emscripten", "wasmer-llvm-backend", @@ -2021,6 +2135,7 @@ dependencies = [ "nix", "page_size", "parking_lot", + "rkyv", "rustc_version", "serde", "serde-bench", @@ -2055,6 +2170,7 @@ dependencies = [ "lazy_static", "libc", "nix", + "rkyv", "serde", "serde_derive", "smallvec 0.6.13", @@ -2081,7 +2197,7 @@ dependencies = [ "bincode", "byteorder", "generational-arena", - "getrandom", + "getrandom 0.1.14", "libc", "log", "serde", diff --git a/lib/llvm-backend/src/stackmap.rs b/lib/llvm-backend/src/stackmap.rs index d2f1eef89111..50b7fbd07ba0 100644 --- a/lib/llvm-backend/src/stackmap.rs +++ b/lib/llvm-backend/src/stackmap.rs @@ -253,8 +253,8 @@ impl StackmapEntry { machine_stack_layout.push(major.clone()); } else { machine_stack_layout.push(MachineValue::TwoHalves(Box::new(( - major.clone(), - minor.clone(), + major.clone().into(), + minor.clone().into(), )))); } } diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 4596912c69de..efce4f34e0aa 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -17,6 +17,10 @@ crate-type = ["cdylib", "rlib", "staticlib"] [dependencies] libc = "0.2.60" +[dependencies.rkyv] +version = "0.7.26" +features = ["indexmap"] + [dependencies.wasmer-runtime] default-features = false path = "../runtime" diff --git a/lib/runtime-c-api/src/instance_cache.rs b/lib/runtime-c-api/src/instance_cache.rs index 18952fc18174..58f1e074dd59 100644 --- a/lib/runtime-c-api/src/instance_cache.rs +++ b/lib/runtime-c-api/src/instance_cache.rs @@ -3,13 +3,48 @@ use crate::{ instance::{wasmer_instance_t, wasmer_compilation_options_t, CompilationOptions, prepare_middleware_chain_generator, get_compiler}, wasmer_result_t, }; -use wasmer_runtime_core::{cache::Artifact, import::ImportObject}; + +use rkyv::{ + Deserialize as RkyvDeserialize, + ser::Serializer, + ser::serializers::AllocSerializer, +}; + +use wasmer_runtime_core::{ + cache::{Artifact, Error as CacheError}, + import::ImportObject, +}; use std::slice; use crate::import::GLOBAL_IMPORT_OBJECT; #[cfg(not(feature = "cranelift-backend"))] use wasmer_middleware_common::metering; +#[cfg(feature = "singlepass-backend")] +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_enable_rkyv() { + wasmer_singlepass_backend::USE_RKYV_SERIALIZATION = true; +} + +#[cfg(feature = "singlepass-backend")] +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_instance_disable_rkyv() { + wasmer_singlepass_backend::USE_RKYV_SERIALIZATION = false; +} + +#[cfg(feature = "singlepass-backend")] +pub unsafe fn is_rkyv_enabled() -> bool { + wasmer_singlepass_backend::USE_RKYV_SERIALIZATION +} + +#[cfg(not(feature = "singlepass-backend"))] +pub unsafe fn is_rkyv_enabled() -> bool { + false +} + + #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub unsafe extern "C" fn wasmer_instance_cache( @@ -26,28 +61,26 @@ pub unsafe extern "C" fn wasmer_instance_cache( let instance = &mut *(instance as *mut wasmer_runtime::Instance); let module = instance.module(); + match module.cache() { Err(error) => { update_last_error(CApiError { - msg: format!("{:?}", error), + msg: format!("wasmer_instance_cache: artifact creation failed: {:?}", error), }); return wasmer_result_t::WASMER_ERROR; } Ok(artifact) => { - match artifact.serialize() { + match serialize_artifact(artifact) { Err(error) => { update_last_error(CApiError { - msg: format!("{:?}", error), + msg: format!("wasmer_instance_cache: artifact serialization failed: {:?}", error), }); return wasmer_result_t::WASMER_ERROR; } - Ok(bytes_vec) => { - if !bytes_vec.is_empty() { - let buf = bytes_vec.into_boxed_slice(); - *cache_bytes = buf.as_ptr(); - *cache_len = buf.len() as u32; - std::mem::forget(buf); - } + Ok(bytes) => { + *cache_bytes = bytes.as_ptr(); + *cache_len = bytes.len() as u32; + std::mem::forget(bytes); } } } @@ -75,22 +108,24 @@ pub unsafe extern "C" fn wasmer_instance_from_cache( let options: &CompilationOptions = &*(options as *const CompilationOptions); let compiler_chain_generator = prepare_middleware_chain_generator(&options); let compiler = get_compiler(compiler_chain_generator); - let new_module = match Artifact::deserialize(bytes) { - Ok(serialized_cache) => match wasmer_runtime_core::load_cache_with(serialized_cache, &compiler) { - Ok(deserialized_module) => { - deserialized_module - } - Err(_) => { - update_last_error(CApiError { - msg: "Failed to compile the serialized module".to_string(), - }); - return wasmer_result_t::WASMER_ERROR; - } - }, - Err(err) => { - println!("{:?}", err); + + let artifact = match deserialize_artifact(bytes) { + Ok(deserialized_artifact) => deserialized_artifact, + Err(_) => { update_last_error(CApiError { - msg: "Failed to deserialize the module".to_string(), + msg: "wasmer_instance_from_cache: artifact deserialization failed".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + } + }; + + let new_module = match wasmer_runtime_core::load_cache_with(artifact, &compiler) { + Ok(deserialized_module) => { + deserialized_module + } + Err(_) => { + update_last_error(CApiError { + msg: "wasmer_instance_from_cache: artifact instantiation into module failed".to_string(), }); return wasmer_result_t::WASMER_ERROR; } @@ -109,3 +144,63 @@ pub unsafe extern "C" fn wasmer_instance_from_cache( *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t; wasmer_result_t::WASMER_OK } + +#[cfg(feature = "singlepass-backend")] +fn serialize_artifact(artifact: Artifact) -> Result, CacheError> { + let serializer = match unsafe { is_rkyv_enabled() } { + true => serialize_artifact_with_rkyv, + false => serialize_artifact_with_serde, + }; + + serializer(artifact).into() +} + +#[cfg(not(feature = "singlepass-backend"))] +fn serialize_artifact(artifact: Artifact) -> Result, CacheError> { + serialize_artifact_with_serde(artifact).into() +} + +#[cfg(feature = "singlepass-backend")] +fn serialize_artifact_with_rkyv(artifact: Artifact) -> Result, CacheError> { + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&artifact).unwrap(); + let serialized = serializer.into_serializer().into_inner().into_boxed_slice(); + if serialized.is_empty() { + return Err(CacheError::SerializeError("rkyv serialization failed".to_string())); + } + + Ok(serialized) +} + +fn serialize_artifact_with_serde(artifact: Artifact) -> Result, CacheError> { + match artifact.serialize() { + Ok(serialized) => Ok(serialized.into_boxed_slice()), + Err(error) => Err(error), + } +} + +#[cfg(feature = "singlepass-backend")] +fn deserialize_artifact(bytes: &[u8]) -> Result { + let deserializer = match unsafe { is_rkyv_enabled() } { + true => deserialize_artifact_with_rkyv, + false => deserialize_artifact_with_serde, + }; + + deserializer(bytes) +} + +#[cfg(not(feature = "singlepass-backend"))] +fn deserialize_artifact(bytes: &[u8]) -> Result { + deserialize_artifact_with_serde(bytes) +} + +#[cfg(feature = "singlepass-backend")] +fn deserialize_artifact_with_rkyv(bytes: &[u8]) -> Result { + let archived = unsafe { rkyv::archived_root::(&bytes[..]) }; + let artifact: Artifact = RkyvDeserialize::::deserialize(archived, &mut rkyv::Infallible).unwrap(); + Ok(artifact) +} + +fn deserialize_artifact_with_serde(bytes: &[u8]) -> Result { + Artifact::deserialize(bytes) +} diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 455dbaaa58aa..cccdcb148516 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -1029,6 +1029,10 @@ const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_cont */ void wasmer_instance_destroy(wasmer_instance_t *instance); +void wasmer_instance_disable_rkyv(void); + +void wasmer_instance_enable_rkyv(void); + /** * Gets all the exports of the given WebAssembly instance. * diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 888cb07b3a25..98426f852e71 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -832,6 +832,10 @@ const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_cont /// ``` void wasmer_instance_destroy(wasmer_instance_t *instance); +void wasmer_instance_disable_rkyv(); + +void wasmer_instance_enable_rkyv(); + /// Gets all the exports of the given WebAssembly instance. /// /// This function stores a Rust vector of exports into `exports` as an diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index f7278a1cdadc..6bb6477eb122 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -23,8 +23,12 @@ bincode = "1.1" wasm-debug = { optional = true, version = "0.1.0" } target-lexicon = "0.9" +[dependencies.rkyv] +version = "0.7.26" +features = ["indexmap"] + [dependencies.indexmap] -version = "1.2" +version = "1.7" features = ["serde-1"] # Dependencies for caching. diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 4aca2d2a71c2..173c0ae36d01 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -18,6 +18,12 @@ use std::{any::Any, ptr::NonNull}; use std::collections::HashMap; +use rkyv::{ + Archive, + Serialize as RkyvSerialize, + Deserialize as RkyvDeserialize, +}; + pub mod sys { pub use crate::sys::*; } @@ -154,7 +160,7 @@ impl CompilerConfig { } /// An exception table for a `RunnableModule`. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct ExceptionTable { /// Mappings from offsets in generated machine code to the corresponding exception code. pub offset_to_code: HashMap, @@ -167,7 +173,7 @@ impl ExceptionTable { } /// The code of an exception. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub enum ExceptionCode { /// An `unreachable` opcode was executed. Unreachable = 0, diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index 0e74ee4dc51d..3f8820788341 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -2,7 +2,8 @@ //! serializing compiled wasm code to a binary format. The binary format can be persisted, //! and loaded to allow skipping compilation and fast startup. -use crate::{module::ModuleInfo, sys::Memory}; +use crate::{module::ModuleInfo, sys::Memory, sys::ArchivableMemory}; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; use std::{io, mem, slice}; /// Indicates the invalid type of invalid cache file @@ -152,16 +153,20 @@ impl ArtifactHeader { } -#[derive(Serialize, Deserialize)] -struct ArtifactInner { +/// Inner information of an Artifact. +#[derive(Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] +pub struct ArtifactInner { info: Box, #[serde(with = "serde_bytes")] backend_metadata: Box<[u8]>, + + #[with(ArchivableMemory)] compiled_code: Memory, } /// Artifact are produced by caching, are serialized/deserialized to binaries, and contain /// module info, backend metadata, and compiled code. +#[derive(Archive, RkyvSerialize, RkyvDeserialize)] pub struct Artifact { inner: ArtifactInner, } @@ -229,3 +234,113 @@ impl Artifact { /// A unique ID generated from the version of Wasmer for use with cache versioning pub const WASMER_VERSION_HASH: &'static str = include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt")); + +#[cfg(test)] +mod tests { + use super::Artifact; + use super::ArtifactInner; + use super::Memory; + use super::ModuleInfo; + use std::collections::HashMap; + use crate::structures::Map; + use crate::module::StringTable; + use rkyv::ser::serializers::AllocSerializer; + use rkyv::ser::Serializer as RkyvSerializer; + use rkyv::Deserialize; + use rkyv::Archived; + + #[test] + fn test_rkyv_artifact() { + let bytes = make_test_bytes(); + let memory = make_test_memory(&bytes); + + let module_info = make_empty_module_info(); + let artifact = Artifact::from_parts( + Box::new(module_info), + b"test_backend".to_vec().into_boxed_slice(), + memory, + ); + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&artifact).unwrap(); + let serialized = serializer.into_serializer().into_inner(); + assert!(serialized.len() > 0); + print!("{:?}", serialized); + + let archived: &Archived + = unsafe { rkyv::archived_root::(&serialized[..]) }; + + let deserialized_artifact = Deserialize::::deserialize(archived, &mut rkyv::Infallible).unwrap(); + unsafe { assert_eq!(deserialized_artifact.inner.compiled_code.as_slice(), artifact.inner.compiled_code.as_slice()) }; + assert_eq!(deserialized_artifact.inner.compiled_code.protection(), artifact.inner.compiled_code.protection()); + } + + #[test] + fn test_rkyv_artifact_inner() { + let bytes = make_test_bytes(); + let memory = make_test_memory(&bytes); + + let module_info = make_empty_module_info(); + let artifact_inner = ArtifactInner { + info: Box::new(module_info), + backend_metadata: b"test_backend".to_vec().into_boxed_slice(), + compiled_code: memory, + }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&artifact_inner).unwrap(); + let serialized = serializer.into_serializer().into_inner(); + assert!(serialized.len() > 0); + print!("{:?}", serialized); + + let archived: &Archived + = unsafe { rkyv::archived_root::(&serialized[..]) }; + + let deserialized_artifact_inner = Deserialize::::deserialize(archived, &mut rkyv::Infallible).unwrap(); + unsafe { assert_eq!(deserialized_artifact_inner.compiled_code.as_slice(), artifact_inner.compiled_code.as_slice()) }; + assert_eq!(deserialized_artifact_inner.compiled_code.protection(), artifact_inner.compiled_code.protection()); + } + + fn make_empty_module_info() -> ModuleInfo { + ModuleInfo { + memories: Map::new(), + globals: Map::new(), + tables: Map::new(), + imported_functions: Map::new(), + imported_memories: Map::new(), + imported_tables: Map::new(), + imported_globals: Map::new(), + exports: Default::default(), + data_initializers: Vec::new(), + elem_initializers: Vec::new(), + start_func: None, + func_assoc: Map::new(), + signatures: Map::new(), + backend: "test".to_string(), + namespace_table: StringTable::new(), + name_table: StringTable::new(), + em_symbol_map: None, + custom_sections: HashMap::new(), + generate_debug_info: false, + #[cfg(feature = "generate-debug-information")] + debug_info_manager: crate::jit_debug::JitCodeDebugInfoManager::new(), + } + } + + fn make_test_memory(bytes: &Vec) -> Memory { + let mut memory = Memory::with_size_protect(1000, crate::sys::Protect::ReadWrite) + .expect("Could not create memory"); + unsafe { + memory.as_slice_mut().copy_from_slice(&bytes[..]); + } + memory + } + + fn make_test_bytes() -> Vec { + let page_size = page_size::get(); + let mut bytes = b"abcdefghijkl".to_vec(); + let padding_zeros = [0 as u8; 1].repeat(page_size - bytes.len()); + bytes.extend(padding_zeros); + bytes + } +} diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 46818f7c3b35..83797d65266b 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -14,6 +14,8 @@ use std::{cell::Cell, fmt, mem, sync::Arc}; use std::sync::Mutex as StdMutex; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; + pub use self::dynamic::DynamicMemory; pub use self::static_::StaticMemory; pub use self::view::{Atomically, MemoryView}; @@ -173,7 +175,7 @@ impl fmt::Debug for Memory { } /// A kind a memory. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub enum MemoryType { /// A dynamic memory. Dynamic, diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 47a38ae8babf..41c280d99477 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -15,10 +15,13 @@ use crate::{ Instance, }; +use indexmap::IndexMap; + +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; + use crate::backend::CacheGen; #[cfg(feature = "generate-debug-information")] use crate::jit_debug; -use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; @@ -31,7 +34,7 @@ pub struct ModuleInner { } /// Container for module data including memories, globals, tables, imports, and exports. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct ModuleInfo { /// Map of memory index to memory descriptors. // This are strictly local and the typesystem ensures that. @@ -176,7 +179,7 @@ impl Clone for Module { impl ModuleInner {} #[doc(hidden)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct ImportName { pub namespace_index: NamespaceIndex, pub name_index: NameIndex, @@ -188,7 +191,7 @@ pub struct ImportName { /// Used in [`ModuleInfo`] to access function signatures ([`SigIndex`]s, /// [`FuncSig`]), [`GlobalInit`]s, [`MemoryDescriptor`]s, and /// [`TableDescriptor`]s. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)] pub enum ExportIndex { /// Function export index. [`FuncIndex`] is a type-safe handle referring to /// a Wasm function. @@ -205,7 +208,7 @@ pub enum ExportIndex { } /// A data initializer for linear memory. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct DataInitializer { /// The index of the memory to initialize. pub memory_index: MemoryIndex, @@ -217,7 +220,7 @@ pub struct DataInitializer { } /// A WebAssembly table initializer. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct TableInitializer { /// The index of a table to initialize. pub table_index: TableIndex, @@ -228,6 +231,7 @@ pub struct TableInitializer { } /// String table builder. +#[derive(Archive, RkyvSerialize, RkyvDeserialize)] pub struct StringTableBuilder { map: IndexMap, buffer: String, @@ -283,7 +287,7 @@ impl StringTableBuilder { } /// A map of index to string. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct StringTable { table: Map, buffer: String, @@ -318,7 +322,7 @@ impl StringTable { } /// A type-safe handle referring to a module namespace. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub struct NamespaceIndex(u32); impl TypedIndex for NamespaceIndex { @@ -334,7 +338,7 @@ impl TypedIndex for NamespaceIndex { } /// A type-safe handle referring to a name in a module namespace. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub struct NameIndex(u32); impl TypedIndex for NameIndex { diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 1dfcae8135e3..0041eb91983f 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -7,12 +7,18 @@ use std::collections::BTreeMap; use std::ops::Bound::{Included, Unbounded}; use std::sync::Arc; +use rkyv::{ + Archive, + Serialize as RkyvSerialize, + Deserialize as RkyvDeserialize, +}; + /// An index to a register -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct RegisterIndex(pub usize); /// A kind of wasm or constant value -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub enum WasmAbstractValue { /// A wasm runtime value Runtime, @@ -21,7 +27,7 @@ pub enum WasmAbstractValue { } /// A container for the state of a running wasm instance. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct MachineState { /// Stack values. pub stack_values: Vec, @@ -38,7 +44,7 @@ pub struct MachineState { } /// A diff of two `MachineState`s. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct MachineStateDiff { /// Last. pub last: Option, @@ -64,7 +70,7 @@ pub struct MachineStateDiff { } /// A kind of machine value. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub enum MachineValue { /// Undefined. Undefined, @@ -83,11 +89,38 @@ pub enum MachineValue { /// Wasm Local. WasmLocal(usize), /// Two Halves. - TwoHalves(Box<(MachineValue, MachineValue)>), // 32-bit values. TODO: optimize: add another type for inner "half" value to avoid boxing? + TwoHalves(Box<(MachineSubvalue, MachineSubvalue)>), // 32-bit values. TODO: optimize: add another type for inner "half" value to avoid boxing? +} + +/// A kind of machine value used in MachineValue::TwoHalves. Created so that MachineValue does not +/// reference two more MachineValues as part of TwoHalves. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] +pub enum MachineSubvalue { + /// Undefined. + Undefined, + /// Vmctx Deref. + VmctxDeref(Vec), + /// Wasm Stack. + WasmStack(usize), + /// Wasm Local. + WasmLocal(usize), +} + +impl From for MachineSubvalue { + #[inline] + fn from(subvalue: MachineValue) -> MachineSubvalue { + match subvalue { + MachineValue::Undefined => MachineSubvalue::Undefined, + MachineValue::VmctxDeref(v) => MachineSubvalue::VmctxDeref(v), + MachineValue::WasmStack(i) => MachineSubvalue::WasmStack(i), + MachineValue::WasmLocal(i) => MachineSubvalue::WasmLocal(i), + _ => unreachable!("invalid kind of MachineValue to convert to MachineSubvalue"), + } + } } /// A map of function states. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct FunctionStateMap { /// Initial. pub initial: MachineState, @@ -112,7 +145,7 @@ pub struct FunctionStateMap { } /// A kind of suspend offset. -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub enum SuspendOffset { /// A loop. Loop(usize), @@ -123,7 +156,7 @@ pub enum SuspendOffset { } /// Info for an offset. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct OffsetInfo { /// End offset. pub end_offset: usize, // excluded bound @@ -134,7 +167,7 @@ pub struct OffsetInfo { } /// A map of module state. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct ModuleStateMap { /// Local functions. pub local_functions: BTreeMap, @@ -753,7 +786,7 @@ pub mod x64 { stack_offset -= 1; // TODO: Cleanup match inner.0 { - MachineValue::WasmStack(x) => match state.wasm_stack[x] { + MachineSubvalue::WasmStack(x) => match state.wasm_stack[x] { WasmAbstractValue::Const(x) => { assert!(x <= std::u32::MAX as u64); stack[stack_offset] |= x; @@ -764,7 +797,7 @@ pub mod x64 { stack[stack_offset] |= v; } }, - MachineValue::WasmLocal(x) => match fsm.locals[x] { + MachineSubvalue::WasmLocal(x) => match fsm.locals[x] { WasmAbstractValue::Const(x) => { assert!(x <= std::u32::MAX as u64); stack[stack_offset] |= x; @@ -775,16 +808,15 @@ pub mod x64 { stack[stack_offset] |= v; } }, - MachineValue::VmctxDeref(ref seq) => { + MachineSubvalue::VmctxDeref(ref seq) => { stack[stack_offset] |= compute_vmctx_deref(vmctx as *const Ctx, seq) & (std::u32::MAX as u64); } - MachineValue::Undefined => {} - _ => unimplemented!("TwoHalves.0"), + MachineSubvalue::Undefined => {} } match inner.1 { - MachineValue::WasmStack(x) => match state.wasm_stack[x] { + MachineSubvalue::WasmStack(x) => match state.wasm_stack[x] { WasmAbstractValue::Const(x) => { assert!(x <= std::u32::MAX as u64); stack[stack_offset] |= x << 32; @@ -795,7 +827,7 @@ pub mod x64 { stack[stack_offset] |= v << 32; } }, - MachineValue::WasmLocal(x) => match fsm.locals[x] { + MachineSubvalue::WasmLocal(x) => match fsm.locals[x] { WasmAbstractValue::Const(x) => { assert!(x <= std::u32::MAX as u64); stack[stack_offset] |= x << 32; @@ -806,14 +838,13 @@ pub mod x64 { stack[stack_offset] |= v << 32; } }, - MachineValue::VmctxDeref(ref seq) => { + MachineSubvalue::VmctxDeref(ref seq) => { stack[stack_offset] |= (compute_vmctx_deref(vmctx as *const Ctx, seq) & (std::u32::MAX as u64)) << 32; } - MachineValue::Undefined => {} - _ => unimplemented!("TwoHalves.1"), + MachineSubvalue::Undefined => {} } } } @@ -1198,26 +1229,24 @@ pub mod x64 { let v = *stack; stack = stack.offset(1); match inner.0 { - MachineValue::WasmStack(idx) => { + MachineSubvalue::WasmStack(idx) => { wasm_stack[idx] = Some(v & 0xffffffffu64); } - MachineValue::WasmLocal(idx) => { + MachineSubvalue::WasmLocal(idx) => { wasm_locals[idx] = Some(v & 0xffffffffu64); } - MachineValue::VmctxDeref(_) => {} - MachineValue::Undefined => {} - _ => unimplemented!("TwoHalves.0 (read)"), + MachineSubvalue::VmctxDeref(_) => {} + MachineSubvalue::Undefined => {} } match inner.1 { - MachineValue::WasmStack(idx) => { + MachineSubvalue::WasmStack(idx) => { wasm_stack[idx] = Some(v >> 32); } - MachineValue::WasmLocal(idx) => { + MachineSubvalue::WasmLocal(idx) => { wasm_locals[idx] = Some(v >> 32); } - MachineValue::VmctxDeref(_) => {} - MachineValue::Undefined => {} - _ => unimplemented!("TwoHalves.1 (read)"), + MachineSubvalue::VmctxDeref(_) => {} + MachineSubvalue::Undefined => {} } } } diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 0ea4beb4ff92..9e41fd75a4e0 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -7,8 +7,10 @@ use std::{ slice, vec, }; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; + /// Dense item map -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct Map where K: TypedIndex, diff --git a/lib/runtime-core/src/sys/memory_rkyv.rs b/lib/runtime-core/src/sys/memory_rkyv.rs new file mode 100644 index 000000000000..c7954eb77358 --- /dev/null +++ b/lib/runtime-core/src/sys/memory_rkyv.rs @@ -0,0 +1,149 @@ +#[cfg(unix)] +use crate::sys::unix::{Memory, Protect}; + +#[cfg(windows)] +use crate::sys::windows::{Memory, Protect}; + +use rkyv::{ + Archive, + Archived, + Fallible, + Serialize as RkyvSerialize, + Deserialize as RkyvDeserialize, + ser::{Serializer, ScratchSpace}, + with::{ArchiveWith, SerializeWith, DeserializeWith}, +}; + +/// A serializable wrapper for Memory. +pub struct ArchivableMemory; + +/// The archived contents of a Memory. +#[derive(Archive, RkyvSerialize, RkyvDeserialize, Debug, PartialEq)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug))] +#[archive_attr(derive(PartialEq))] +pub struct CompactMemory { + contents: Vec, + content_size: u32, + protection: Protect, +} + +impl CompactMemory { + /// Construct a CompactMemory from a Memory. + pub unsafe fn from_memory(memory: &Memory) -> Self { + CompactMemory { + contents: memory.as_slice().to_vec(), + content_size: memory.content_size(), + protection: memory.protection(), + } + } + + /// Construct a Memory from a CompactMemory. + pub unsafe fn into_memory(&self) -> Memory { + let bytes = self.contents.as_slice(); + + let mut memory = Memory::with_size_protect(bytes.len(), Protect::ReadWrite) + .expect("Could not create a memory"); + + memory.as_slice_mut().copy_from_slice(&*bytes); + + if memory.protection() != self.protection { + memory + .protect(.., self.protection) + .expect("Could not protect memory as its original protection"); + } + + memory.set_content_size(self.content_size); + + memory + } +} + +impl ArchiveWith for ArchivableMemory { + type Archived = ::Archived; + type Resolver = ::Resolver; + + unsafe fn resolve_with(memory: &Memory, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let archived_memory = CompactMemory::from_memory(memory); + archived_memory.resolve(pos, resolver, out); + } +} + +impl SerializeWith for ArchivableMemory +where + S: Serializer + ScratchSpace +{ + fn serialize_with(memory: &Memory, serializer: &mut S) -> Result { + unsafe { + let archived_memory = CompactMemory::from_memory(memory); + archived_memory.serialize(serializer) + } + } +} + +impl DeserializeWith, Memory, D> for ArchivableMemory +{ + fn deserialize_with(archived_memory: &Archived, deserializer: &mut D) -> Result { + let compact_memory: CompactMemory = archived_memory.deserialize(deserializer)?; + let memory = unsafe { compact_memory.into_memory() }; + + Ok(memory) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rkyv::ser::serializers::AllocSerializer; + use crate::sys::unix::*; + + #[test] + fn test_new_memory() { + let bytes = make_test_bytes(); + let memory = make_test_memory(&bytes); + unsafe { + assert_eq!(memory.as_slice(), bytes); + } + } + + #[test] + fn test_rkyv_compact_memory() { + let bytes = make_test_bytes(); + let memory = make_test_memory(&bytes); + + let compact_memory = unsafe { CompactMemory::from_memory(&memory) }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&compact_memory).unwrap(); + let serialized = serializer.into_serializer().into_inner(); + assert!(serialized.len() > 0); + + let archived = unsafe { rkyv::archived_root::(&serialized[..]) }; + + let deserialized: CompactMemory = archived.deserialize(&mut rkyv::Infallible).unwrap(); + assert_eq!(deserialized, compact_memory); + + let deserialized_memory = unsafe { deserialized.into_memory() }; + assert_eq!(deserialized_memory.protection(), memory.protection()); + unsafe { + assert_eq!(deserialized_memory.as_slice(), memory.as_slice()); + }; + } + + fn make_test_memory(bytes: &Vec) -> Memory { + let mut memory = Memory::with_size_protect(1000, Protect::ReadWrite) + .expect("Could not create memory"); + unsafe { + memory.as_slice_mut().copy_from_slice(&bytes[..]); + } + memory + } + + fn make_test_bytes() -> Vec { + let page_size = page_size::get(); + let mut bytes = b"abcdefghijkl".to_vec(); + let padding_zeros = [0 as u8; 1].repeat(page_size - bytes.len()); + bytes.extend(padding_zeros); + bytes + } +} diff --git a/lib/runtime-core/src/sys/mod.rs b/lib/runtime-core/src/sys/mod.rs index 02b4fd25669b..19a041f5ee26 100644 --- a/lib/runtime-core/src/sys/mod.rs +++ b/lib/runtime-core/src/sys/mod.rs @@ -1,3 +1,5 @@ +mod memory_rkyv; + #[cfg(unix)] mod unix; @@ -10,6 +12,8 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; +pub use self::memory_rkyv::*; + use serde::{ de::{self, SeqAccess, Visitor}, ser::SerializeStruct, diff --git a/lib/runtime-core/src/sys/unix/memory.rs b/lib/runtime-core/src/sys/unix/memory.rs index 45a58a3e860e..8b56ac6b78eb 100644 --- a/lib/runtime-core/src/sys/unix/memory.rs +++ b/lib/runtime-core/src/sys/unix/memory.rs @@ -6,6 +6,7 @@ use nix::libc; use page_size; use std::ops::{Bound, RangeBounds}; use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, slice, sync::Arc}; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; unsafe impl Send for Memory {} unsafe impl Sync for Memory {} @@ -17,6 +18,7 @@ pub struct Memory { size: usize, protection: Protect, fd: Option>, + content_size: u32, } impl Memory { @@ -53,6 +55,7 @@ impl Memory { size: file_len as usize, protection, fd: Some(Arc::new(raw_fd)), + content_size: 0, }) } } @@ -65,6 +68,7 @@ impl Memory { size: 0, protection, fd: None, + content_size: 0, }); } @@ -89,10 +93,19 @@ impl Memory { size, protection, fd: None, + content_size: 0, }) } } + /// Create a new memory with the given contents size and protection. + /// Used when the size of the contents must be tracked (e.g. for rkyv deserialization). + pub fn with_content_size_protect(content_size: u32, protection: Protect) -> Result { + let mut memory = Self::with_size_protect(content_size as usize, protection)?; + memory.set_content_size(content_size); + Ok(memory) + } + /// Create a new memory with the given size. pub fn with_size(size: usize) -> Result { if size == 0 { @@ -101,6 +114,7 @@ impl Memory { size: 0, protection: Protect::None, fd: None, + content_size: 0, }); } @@ -128,6 +142,7 @@ impl Memory { size, protection: Protect::None, fd: None, + content_size: 0, }) } } @@ -172,6 +187,12 @@ impl Memory { } } + /// Set the content size of this memory. Must be set manually, as this is different in each + /// case. + pub fn set_content_size(&mut self, size: u32) { + self.content_size = size; + } + /// Split this memory into multiple memories by the given offset. pub fn split_at(mut self, offset: usize) -> (Memory, Memory) { let page_size = page_size::get(); @@ -186,6 +207,7 @@ impl Memory { size: second_size, protection: self.protection, fd: self.fd.clone(), + content_size: 0, }; (self, second) @@ -199,11 +221,21 @@ impl Memory { self.size } + /// Gets the size of the actual contents of this memory. + pub fn content_size(&self) -> u32 { + self.content_size + } + /// Gets a slice for this memory. pub unsafe fn as_slice(&self) -> &[u8] { slice::from_raw_parts(self.ptr, self.size) } + /// Gets a slice for this memory, bounded by content_size. + pub unsafe fn as_slice_contents(&self) -> &[u8] { + slice::from_raw_parts(self.ptr, self.content_size as usize) + } + /// Gets a mutable slice for this memory. pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] { slice::from_raw_parts_mut(self.ptr, self.size) @@ -251,8 +283,11 @@ impl Clone for Memory { } /// Kinds of memory protection. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)] #[allow(dead_code)] +#[archive(compare(PartialEq))] +#[archive_attr(derive(Debug))] +#[archive_attr(derive(PartialEq))] pub enum Protect { /// Read/write/exec allowed. None, @@ -294,8 +329,8 @@ impl Protect { } } -#[derive(Debug)] -struct RawFd(i32); +#[derive(Debug, Archive, RkyvSerialize, RkyvDeserialize)] +pub struct RawFd(i32); impl RawFd { fn from_file(f: File) -> Self { diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 122aa544a8b8..712806112cec 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -3,9 +3,10 @@ use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages}; use std::{borrow::Cow, convert::TryFrom}; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize, with::AsOwned}; /// Represents a WebAssembly type. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub enum Type { /// The `i32` type. I32, @@ -29,7 +30,7 @@ impl std::fmt::Display for Type { /// /// As the number of types in WebAssembly expand, /// this structure will expand as well. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Archive, RkyvSerialize, RkyvDeserialize)] pub enum Value { /// The `i32` type. I32(i32), @@ -246,7 +247,7 @@ macro_rules! convert_value_impl { convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); /// Kinds of element types. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)] pub enum ElementType { /// Any wasm function. Anyfunc, @@ -254,7 +255,7 @@ pub enum ElementType { /// Describes the properties of a table including the element types, minimum and optional maximum, /// number of elements in the table. -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Archive, RkyvSerialize, RkyvDeserialize)] pub struct TableDescriptor { /// Type of data stored in this table. pub element: ElementType, @@ -278,7 +279,7 @@ impl TableDescriptor { /// A const value initializer. /// Over time, this will be able to represent more and more /// complex expressions. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Archive, RkyvSerialize, RkyvDeserialize)] pub enum Initializer { /// Corresponds to a `const.*` instruction. Const(Value), @@ -287,7 +288,7 @@ pub enum Initializer { } /// Describes the mutability and type of a Global -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)] pub struct GlobalDescriptor { /// Mutable flag. pub mutable: bool, @@ -296,7 +297,7 @@ pub struct GlobalDescriptor { } /// A wasm global. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)] pub struct GlobalInit { /// Global descriptor. pub desc: GlobalDescriptor, @@ -305,7 +306,7 @@ pub struct GlobalInit { } /// A wasm memory descriptor. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)] pub struct MemoryDescriptor { /// The minimum number of allowed pages. pub minimum: Pages, @@ -353,9 +354,11 @@ impl MemoryDescriptor { /// The signature of a function that is either implemented /// in a wasm module or exposed to wasm by the host. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub struct FuncSig { + #[with(AsOwned)] params: Cow<'static, [Type]>, + #[with(AsOwned)] returns: Cow<'static, [Type]>, } @@ -423,7 +426,7 @@ pub trait LocalImport { macro_rules! define_map_index { ($ty:ident) => { /// Typed Index - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); impl TypedIndex for $ty { @@ -503,7 +506,7 @@ define_local_or_import![ ]; /// Index for signature. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, RkyvSerialize, RkyvDeserialize)] pub struct SigIndex(u32); impl TypedIndex for SigIndex { #[doc(hidden)] diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index 780ceb034dda..0df5bdec2c07 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -6,6 +6,8 @@ use std::{ ops::{Add, Sub}, }; +use rkyv::{Archive, Serialize as RkyvSerialize, Deserialize as RkyvDeserialize}; + /// The page size in bytes of a wasm page. pub const WASM_PAGE_SIZE: usize = 65_536; /// The max number of wasm pages allowed. @@ -15,7 +17,7 @@ pub const WASM_MAX_PAGES: usize = 65_536; pub const WASM_MIN_PAGES: usize = 256; /// Units of WebAssembly pages (as specified to be 65,536 bytes). -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Archive, RkyvSerialize, RkyvDeserialize)] pub struct Pages(pub u32); impl Pages { diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index 0478b5ba7e53..dc5ee96aecb2 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -24,6 +24,9 @@ bincode = "1.2" serde = "1.0" serde_derive = "1.0" +[dependencies.rkyv] +version = "0.7.26" + [features] default = ["deterministic-execution"] deterministic-execution = ["wasmparser/deterministic", "wasmer-runtime-core/deterministic-execution"] diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index ec44ddb1efa3..116e0deb0ee1 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -18,6 +18,16 @@ use std::{ slice, sync::{Arc, RwLock}, usize, + convert::TryInto, +}; + +use rkyv::{ + Archive, + Archived, + Serialize as RkyvSerialize, + Deserialize as RkyvDeserialize, + ser::Serializer, + ser::serializers::AllocSerializer, }; use bincode; @@ -61,6 +71,8 @@ pub const INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS: usize = 7; /// Inline breakpoint size for aarch64. pub const INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS: usize = 12; +pub static mut USE_RKYV_SERIALIZATION: bool = false; + static BACKEND_ID: &str = "singlepass"; #[cfg(target_arch = "x86_64")] @@ -253,7 +265,7 @@ pub struct X64ExecutionContext { /// On-disk cache format. /// Offsets are relative to the start of the executable image. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)] pub struct CacheImage { /// The executable image. code: Vec, @@ -300,7 +312,8 @@ pub struct SinglepassCache { impl CacheGen for SinglepassCache { fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { - let mut memory = Memory::with_size_protect(self.buffer.len(), Protect::ReadWrite) + let content_size: u32 = self.buffer.len().try_into().unwrap(); + let mut memory = Memory::with_content_size_protect(content_size, Protect::ReadWrite) .map_err(CacheError::SerializeError)?; let buffer = &*self.buffer; @@ -842,8 +855,19 @@ impl ModuleCodeGenerator exception_table: exception_table.clone(), }; - let cache = SinglepassCache { - buffer: Arc::from(bincode::serialize(&cache_image).unwrap().into_boxed_slice()), + let cache = if unsafe { USE_RKYV_SERIALIZATION } { + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&cache_image).unwrap(); + let archived_cache_image = serializer.into_serializer().into_inner(); + + SinglepassCache { + buffer: Arc::from(archived_cache_image.as_slice()), + } + } else { + let serialized_cache_image = bincode::serialize(&cache_image).unwrap().into_boxed_slice(); + SinglepassCache { + buffer: Arc::from(serialized_cache_image), + } }; Ok(( @@ -934,8 +958,15 @@ impl ModuleCodeGenerator unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { let (info, _, memory) = artifact.consume(); - let cache_image: CacheImage = bincode::deserialize(memory.as_slice()) - .map_err(|x| CacheError::DeserializeError(format!("{:?}", x)))?; + let cache_image: CacheImage = if USE_RKYV_SERIALIZATION { + let memory_contents = memory.as_slice_contents(); + let archived_cache_image: &Archived + = rkyv::archived_root::(memory_contents); + RkyvDeserialize::::deserialize(archived_cache_image, &mut rkyv::Infallible).unwrap() + } else { + bincode::deserialize(memory.as_slice()) + .map_err(|x| CacheError::DeserializeError(format!("{:?}", x)))? + }; let mut code_mem = CodeMemory::new(cache_image.code.len()); code_mem[0..cache_image.code.len()].copy_from_slice(&cache_image.code); diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs index 0704cd8eba60..701c4c47e835 100644 --- a/lib/singlepass-backend/src/lib.rs +++ b/lib/singlepass-backend/src/lib.rs @@ -41,6 +41,8 @@ mod machine; #[cfg(target_arch = "aarch64")] mod translator_aarch64; +pub use codegen_x64::USE_RKYV_SERIALIZATION; + pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator; pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator; diff --git a/src/webassembly.rs b/src/webassembly.rs index 0df8b59e8b18..0fd75b20ab41 100644 --- a/src/webassembly.rs +++ b/src/webassembly.rs @@ -1,4 +1,3 @@ -use std::panic; pub use wasmer_runtime::compile_with_config_with; use wasmer_runtime::{self as runtime, error::Result, ImportObject, Instance, Module};