1
0
mirror of https://github.com/fluencelabs/wasmer synced 2025-04-23 17:32:14 +00:00
This commit is contained in:
Lachlan Sneff 2019-02-19 15:36:22 -08:00
parent 3c7dc200fa
commit 82eea00a02
20 changed files with 267 additions and 257 deletions

@ -23,24 +23,16 @@ libc = "0.2.48"
# Dependencies for caching. # Dependencies for caching.
[dependencies.serde] [dependencies.serde]
version = "1.0" version = "1.0"
optional = true
[dependencies.serde_derive] [dependencies.serde_derive]
version = "1.0" version = "1.0"
optional = true
[dependencies.serde_bytes] [dependencies.serde_bytes]
version = "0.10" version = "0.10"
optional = true
# [dependencies.bincode]
# version = "1.0.1"
# optional = true
[dependencies.serde-bench] [dependencies.serde-bench]
version = "0.0.7" version = "0.0.7"
optional = true
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.0.1" } wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.0.1" }
[features] [features]
cache = ["serde", "serde_derive", "serde_bytes", "serde-bench", "wasmer-runtime-core/cache"]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]

@ -32,8 +32,7 @@ impl CacheGenerator {
impl CacheGen for CacheGenerator { impl CacheGen for CacheGenerator {
fn generate_cache(&self, module: &ModuleInner) -> Result<(Box<ModuleInfo>, Box<[u8]>, Arc<Memory>), Error> { fn generate_cache(&self, module: &ModuleInner) -> Result<(Box<ModuleInfo>, Box<[u8]>, Arc<Memory>), Error> {
let info = Box::new(module.info.clone()); let info = Box::new(module.info.clone());
Ok((info, self.backend_cache.into_backend_data()?.into_boxed_slice(), Arc::clone(&self.memory)))
Err(Error::Unknown("".to_string()))
} }
} }
@ -54,18 +53,26 @@ pub struct BackendCache {
impl BackendCache { impl BackendCache {
pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> { pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> {
let (info, backend_data, compiled_code) = cache.consume(); let (info, backend_data, compiled_code_arc) = cache.consume();
let backend_cache = deserialize(backend_data.as_slice()) // If this is the only references to this arc, move the memory out.
// else, clone the memory to a new location. This could take a long time,
// depending on the throughput of your memcpy implementation.
let compiled_code = match Arc::try_unwrap(compiled_code_arc) {
Ok(code) => code,
Err(arc) => (*arc).clone(),
};
let backend_cache = deserialize(&backend_data)
.map_err(|e| Error::DeserializeError(e.to_string()))?; .map_err(|e| Error::DeserializeError(e.to_string()))?;
Ok((info, compiled_code, backend_cache)) Ok((info, compiled_code, backend_cache))
} }
pub fn into_backend_data(self) -> Result<Vec<u8>, Error> { pub fn into_backend_data(&self) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?; serialize(&mut buffer, self).map_err(|e| Error::SerializeError(e.to_string()))?;
Ok(buffer) Ok(buffer)
} }

@ -1,4 +1,4 @@
#[cfg(feature = "cache")]
mod cache; mod cache;
mod func_env; mod func_env;
mod libcalls; mod libcalls;
@ -14,7 +14,7 @@ use cranelift_codegen::{
settings::{self, Configurable}, settings::{self, Configurable},
}; };
use target_lexicon::Triple; use target_lexicon::Triple;
#[cfg(feature = "cache")]
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::Memory, backend::sys::Memory,
cache::{Cache, Error as CacheError}, cache::{Cache, Error as CacheError},
@ -25,10 +25,10 @@ use wasmer_runtime_core::{
error::{CompileError, CompileResult}, error::{CompileError, CompileResult},
module::ModuleInner, module::ModuleInner,
}; };
#[cfg(feature = "cache")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[cfg(feature = "cache")]
extern crate serde; extern crate serde;
use wasmparser::{self, WasmDecoder}; use wasmparser::{self, WasmDecoder};
@ -57,12 +57,12 @@ impl Compiler for CraneliftCompiler {
} }
/// Create a wasmer Module from an already-compiled cache. /// Create a wasmer Module from an already-compiled cache.
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> { unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> {
module::Module::from_cache(cache) module::Module::from_cache(cache)
} }
// #[cfg(feature = "cache")] //
// fn compile_to_backend_cache_data( // fn compile_to_backend_cache_data(
// &self, // &self,
// wasm: &[u8], // wasm: &[u8],

@ -1,4 +1,4 @@
#[cfg(feature = "cache")]
use crate::cache::{BackendCache, CacheGenerator}; use crate::cache::{BackendCache, CacheGenerator};
use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines}; use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines};
@ -8,7 +8,7 @@ use cranelift_wasm;
use hashbrown::HashMap; use hashbrown::HashMap;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "cache")]
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::sys::Memory, backend::sys::Memory,
cache::{Cache, Error as CacheError}, cache::{Cache, Error as CacheError},
@ -17,13 +17,15 @@ use wasmer_runtime_core::{
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::Backend, backend::Backend,
error::CompileResult, error::CompileResult,
module::{ModuleInfo, ModuleInner, StringTable, WasmHash}, module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex}, structures::{Map, TypedIndex},
types::{ types::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type, FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
}, },
}; };
use wasmer_runtime_core::module::WasmHash;
/// This contains all of the items in a `ModuleInner` except the `func_resolver`. /// This contains all of the items in a `ModuleInner` except the `func_resolver`.
pub struct Module { pub struct Module {
pub info: ModuleInfo, pub info: ModuleInfo,
@ -56,6 +58,7 @@ impl Module {
namespace_table: StringTable::new(), namespace_table: StringTable::new(),
name_table: StringTable::new(), name_table: StringTable::new(),
wasm_hash: WasmHash::generate(wasm), wasm_hash: WasmHash::generate(wasm),
}, },
} }
@ -77,6 +80,7 @@ impl Module {
let protected_caller = let protected_caller =
Caller::new(&self.info, handler_data, trampolines); Caller::new(&self.info, handler_data, trampolines);
let cache_gen = Box::new(CacheGenerator::new(backend_cache, Arc::clone(&func_resolver.memory))); let cache_gen = Box::new(CacheGenerator::new(backend_cache, Arc::clone(&func_resolver.memory)));
Ok(ModuleInner { Ok(ModuleInner {
@ -88,26 +92,7 @@ impl 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.info)?;
// let trampolines = Trampolines::new(isa, &self.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.info, backend_cache, compiled_code))
// }
#[cfg(feature = "cache")]
pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> { pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> {
let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?; let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?;
@ -120,6 +105,8 @@ impl Module {
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?; .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?;
let protected_caller = Caller::new(&info, handler_data, trampolines); let protected_caller = Caller::new(&info, handler_data, trampolines);
let cache_gen = Box::new(CacheGenerator::new(backend_cache, Arc::clone(&func_resolver.memory))); let cache_gen = Box::new(CacheGenerator::new(backend_cache, Arc::clone(&func_resolver.memory)));
Ok(ModuleInner { Ok(ModuleInner {

@ -22,7 +22,7 @@ pub mod call_names {
pub const DYNAMIC_MEM_SIZE: u32 = 5; pub const DYNAMIC_MEM_SIZE: u32 = 5;
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Reloc { pub enum Reloc {
Abs8, Abs8,
@ -30,7 +30,7 @@ pub enum Reloc {
X86CallPCRel4, X86CallPCRel4,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum LibCall { pub enum LibCall {
Probestack, Probestack,
@ -44,7 +44,7 @@ pub enum LibCall {
NearestF64, NearestF64,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExternalRelocation { pub struct ExternalRelocation {
/// The relocation code. /// The relocation code.
@ -66,7 +66,7 @@ pub struct LocalRelocation {
pub target: FuncIndex, pub target: FuncIndex,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum VmCallKind { pub enum VmCallKind {
StaticMemoryGrow, StaticMemoryGrow,
@ -79,7 +79,7 @@ pub enum VmCallKind {
DynamicMemorySize, DynamicMemorySize,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum VmCall { pub enum VmCall {
Local(VmCallKind), Local(VmCallKind),
@ -87,7 +87,7 @@ pub enum VmCall {
} }
/// Specify the type of relocation /// Specify the type of relocation
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum RelocationType { pub enum RelocationType {
Intrinsic(String), Intrinsic(String),
@ -218,7 +218,7 @@ impl binemit::RelocSink for RelocSink {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum TrapCode { pub enum TrapCode {
StackOverflow, StackOverflow,
@ -244,7 +244,7 @@ impl RelocSink {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct TrapData { pub struct TrapData {
pub trapcode: TrapCode, pub trapcode: TrapCode,
@ -253,7 +253,7 @@ pub struct TrapData {
/// Simple implementation of a TrapSink /// Simple implementation of a TrapSink
/// that saves the info for later. /// that saves the info for later.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
pub struct TrapSink { pub struct TrapSink {
trap_datas: Vec<(usize, TrapData)>, trap_datas: Vec<(usize, TrapData)>,
} }

@ -1,4 +1,4 @@
#[cfg(feature = "cache")]
use crate::{ use crate::{
cache::{BackendCache, TrampolineCache}, cache::{BackendCache, TrampolineCache},
trampoline::Trampolines, trampoline::Trampolines,
@ -20,7 +20,7 @@ use std::{
cell::UnsafeCell, cell::UnsafeCell,
sync::Arc, sync::Arc,
}; };
#[cfg(feature = "cache")]
use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
self, self,
@ -43,16 +43,24 @@ extern "C" {
pub fn __chkstk(); pub fn __chkstk();
} }
fn lookup_func(map: &SliceMap<LocalFuncIndex, usize>, memory: &Memory, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *map.get(local_func_index)?;
let ptr = unsafe { memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
#[allow(dead_code)] #[allow(dead_code)]
pub struct FuncResolverBuilder { pub struct FuncResolverBuilder {
resolver: FuncResolver, map: Map<LocalFuncIndex, usize>,
memory: Memory,
local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>, local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>,
external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>, external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
import_len: usize, import_len: usize,
} }
impl FuncResolverBuilder { impl FuncResolverBuilder {
#[cfg(feature = "cache")]
pub fn new_from_backend_cache( pub fn new_from_backend_cache(
backend_cache: BackendCache, backend_cache: BackendCache,
mut code: Memory, mut code: Memory,
@ -68,10 +76,8 @@ impl FuncResolverBuilder {
Ok(( Ok((
Self { Self {
resolver: FuncResolver {
map: backend_cache.offsets, map: backend_cache.offsets,
memory: Arc::new(UnsafeCell::new(code)), memory: code,
},
local_relocs: Map::new(), local_relocs: Map::new(),
external_relocs: backend_cache.external_relocs, external_relocs: backend_cache.external_relocs,
import_len: info.imported_functions.len(), import_len: info.imported_functions.len(),
@ -155,7 +161,8 @@ impl FuncResolverBuilder {
let handler_data = HandlerData::new(Arc::new(trap_sink), memory.as_ptr() as _, memory.size()); let handler_data = HandlerData::new(Arc::new(trap_sink), memory.as_ptr() as _, memory.size());
let mut func_resolver_builder = Self { let mut func_resolver_builder = Self {
resolver: FuncResolver { map, memory: Arc::new(UnsafeCell::new(memory)) }, map,
memory,
local_relocs, local_relocs,
external_relocs, external_relocs,
import_len: info.imported_functions.len(), import_len: info.imported_functions.len(),
@ -171,11 +178,11 @@ impl FuncResolverBuilder {
for ref reloc in relocs.iter() { for ref reloc in relocs.iter() {
let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len); let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len);
let target_func_address = let target_func_address =
self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize; lookup_func(&self.map, &self.memory, local_func_index).unwrap().as_ptr() as usize;
// We need the address of the current function // We need the address of the current function
// because these calls are relative. // because these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize; let func_addr = lookup_func(&self.map, &self.memory, index).unwrap().as_ptr() as usize;
unsafe { unsafe {
let reloc_address = func_addr + reloc.offset as usize; let reloc_address = func_addr + reloc.offset as usize;
@ -190,14 +197,11 @@ impl FuncResolverBuilder {
} }
pub fn finalize( pub fn finalize(
self, mut self,
signatures: &SliceMap<SigIndex, Arc<FuncSig>>, signatures: &SliceMap<SigIndex, Arc<FuncSig>>,
trampolines: Arc<Trampolines>, trampolines: Arc<Trampolines>,
handler_data: HandlerData, handler_data: HandlerData,
) -> CompileResult<(FuncResolver, BackendCache)> { ) -> CompileResult<(FuncResolver, BackendCache)> {
{
let mut memory = unsafe { (*self.resolver.memory.get()) };
for (index, relocs) in self.external_relocs.iter() { for (index, relocs) in self.external_relocs.iter() {
for ref reloc in relocs.iter() { for ref reloc in relocs.iter() {
let target_func_address: isize = match reloc.target { let target_func_address: isize = match reloc.target {
@ -269,7 +273,7 @@ impl FuncResolverBuilder {
// We need the address of the current function // We need the address of the current function
// because some of these calls are relative. // because some of these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr(); let func_addr = lookup_func(&self.map, &self.memory, index).unwrap().as_ptr() as usize;
// Determine relocation type and apply relocation. // Determine relocation type and apply relocation.
match reloc.reloc { match reloc.reloc {
@ -277,9 +281,9 @@ impl FuncResolverBuilder {
let ptr_to_write = (target_func_address as u64) let ptr_to_write = (target_func_address as u64)
.checked_add(reloc.addend as u64) .checked_add(reloc.addend as u64)
.unwrap(); .unwrap();
let empty_space_offset = self.resolver.map[index] + reloc.offset as usize; let empty_space_offset = self.map[index] + reloc.offset as usize;
let ptr_slice = unsafe { let ptr_slice = unsafe {
&mut memory.as_slice_mut() &mut self.memory.as_slice_mut()
[empty_space_offset..empty_space_offset + 8] [empty_space_offset..empty_space_offset + 8]
}; };
LittleEndian::write_u64(ptr_slice, ptr_to_write); LittleEndian::write_u64(ptr_slice, ptr_to_write);
@ -297,20 +301,22 @@ impl FuncResolverBuilder {
} }
unsafe { unsafe {
memory self.memory
.protect(.., Protect::ReadExec) .protect(.., Protect::ReadExec)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?; .map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
} }
}
let backend_cache = BackendCache { let backend_cache = BackendCache {
external_relocs: self.external_relocs.clone(), external_relocs: self.external_relocs.clone(),
offsets: self.resolver.map.clone(), offsets: self.map.clone(),
trap_sink: handler_data.trap_data, trap_sink: handler_data.trap_data,
trampolines: trampolines.to_trampoline_cache(), trampolines: trampolines.to_trampoline_cache(),
}; };
Ok((self.resolver, backend_cache)) Ok((FuncResolver {
map: self.map,
memory: Arc::new(self.memory),
}, backend_cache))
} }
} }
@ -320,16 +326,7 @@ unsafe impl Send for FuncResolver {}
/// Resolves a function index to a function address. /// Resolves a function index to a function address.
pub struct FuncResolver { pub struct FuncResolver {
map: Map<LocalFuncIndex, usize>, map: Map<LocalFuncIndex, usize>,
pub(crate) memory: Arc<UnsafeCell<Memory>>, pub(crate) memory: Arc<Memory>,
}
impl FuncResolver {
fn lookup(&self, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self.map.get(local_func_index)?;
let ptr = unsafe { (*self.memory.get()).as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
} }
// Implements FuncResolver trait. // Implements FuncResolver trait.
@ -339,7 +336,7 @@ impl backend::FuncResolver for FuncResolver {
_module: &wasmer_runtime_core::module::ModuleInner, _module: &wasmer_runtime_core::module::ModuleInner,
index: LocalFuncIndex, index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> { ) -> Option<NonNull<vm::Func>> {
self.lookup(index) lookup_func(&self.map, &self.memory, index)
} }
} }

@ -1,4 +1,4 @@
#[cfg(feature = "cache")]
use crate::cache::TrampolineCache; use crate::cache::TrampolineCache;
use cranelift_codegen::{ use cranelift_codegen::{
binemit::{NullTrapSink, Reloc, RelocSink}, binemit::{NullTrapSink, Reloc, RelocSink},
@ -33,7 +33,7 @@ pub struct Trampolines {
} }
impl Trampolines { impl Trampolines {
#[cfg(feature = "cache")]
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self { pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
// pub struct TrampolineCache { // pub struct TrampolineCache {
// #[serde(with = "serde_bytes")] // #[serde(with = "serde_bytes")]
@ -57,7 +57,7 @@ impl Trampolines {
} }
} }
#[cfg(feature = "cache")]
pub fn to_trampoline_cache(&self) -> TrampolineCache { pub fn to_trampoline_cache(&self) -> TrampolineCache {
let mut code = vec![0; self.memory.size()]; let mut code = vec![0; self.memory.size()];

@ -8,7 +8,6 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
hashbrown = "0.1"
nix = "0.12.0" nix = "0.12.0"
page_size = "0.4.1" page_size = "0.4.1"
wasmparser = "0.23.0" wasmparser = "0.23.0"
@ -21,22 +20,20 @@ libc = "0.2.48"
# Dependencies for caching. # Dependencies for caching.
[dependencies.serde] [dependencies.serde]
version = "1.0" version = "1.0"
optional = true features = ["rc"]
[dependencies.serde_derive] [dependencies.serde_derive]
version = "1.0" version = "1.0"
optional = true
[dependencies.serde_bytes] [dependencies.serde_bytes]
version = "0.10" version = "0.10"
optional = true
[dependencies.serde-bench] [dependencies.serde-bench]
version = "0.0.7" version = "0.0.7"
optional = true
[dependencies.memmap] [dependencies.memmap]
version = "0.7.0" version = "0.7.0"
optional = true
[dependencies.sha2] [dependencies.sha2]
version = "0.8.0" version = "0.8.0"
optional = true [dependencies.hashbrown]
version = "0.1"
features = ["serde"]
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["memoryapi"] } winapi = { version = "0.3", features = ["memoryapi"] }
@ -47,5 +44,4 @@ field-offset = "0.1.1"
[features] [features]
debug = [] debug = []
cache = ["serde/rc", "serde_derive", "serde_bytes", "hashbrown/serde", "serde-bench", "memmap", "sha2"]

@ -2,11 +2,11 @@ use crate::{
backing::ImportBacking, backing::ImportBacking,
error::CompileResult, error::CompileResult,
error::RuntimeResult, error::RuntimeResult,
module::ModuleInner, module::{ModuleInner},
types::{FuncIndex, LocalFuncIndex, Value}, types::{FuncIndex, LocalFuncIndex, Value},
vm, vm,
}; };
#[cfg(feature = "cache")]
use crate::{ use crate::{
cache::{Cache, Error as CacheError}, cache::{Cache, Error as CacheError},
module::ModuleInfo, module::ModuleInfo,
@ -14,12 +14,14 @@ use crate::{
}; };
use std::ptr::NonNull; use std::ptr::NonNull;
use std::sync::Arc;
pub mod sys { pub mod sys {
pub use crate::sys::*; pub use crate::sys::*;
} }
pub use crate::sig_registry::SigRegistry; pub use crate::sig_registry::SigRegistry;
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Backend { pub enum Backend {
Cranelift, Cranelift,
@ -43,7 +45,7 @@ pub trait Compiler {
/// be called from inside the runtime. /// be called from inside the runtime.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>; fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError>; unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError>;
} }
@ -93,6 +95,7 @@ pub trait FuncResolver: Send + Sync {
) -> Option<NonNull<vm::Func>>; ) -> Option<NonNull<vm::Func>>;
} }
pub trait CacheGen: Send + Sync { pub trait CacheGen: Send + Sync {
fn generate_cache(&self, module: &ModuleInner) -> Result<(Box<ModuleInfo>, Box<[u8]>, Arc<Memory>), CacheError>; fn generate_cache(&self, module: &ModuleInner) -> Result<(Box<ModuleInfo>, Box<[u8]>, Arc<Memory>), CacheError>;
} }

@ -6,6 +6,7 @@ use std::{
fs::File, fs::File,
io::{self, Seek, SeekFrom, Write}, io::{self, Seek, SeekFrom, Write},
mem, mem,
sync::Arc,
path::Path, path::Path,
slice, slice,
}; };
@ -67,31 +68,26 @@ impl CacheHeader {
struct CacheInner { struct CacheInner {
info: Box<ModuleInfo>, info: Box<ModuleInfo>,
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
backend_metadata: Vec<u8>, backend_metadata: Box<[u8]>,
compiled_code: Memory, compiled_code: Arc<Memory>,
} }
pub struct Cache { pub struct Cache {
inner: CacheInner, inner: CacheInner,
wasm_hash: Box<[u8; 32]>,
} }
impl Cache { impl Cache {
pub(crate) fn new( pub(crate) fn from_parts(
wasm: &[u8],
info: Box<ModuleInfo>, info: Box<ModuleInfo>,
backend_metadata: Vec<u8>, backend_metadata: Box<[u8]>,
compiled_code: Memory, compiled_code: Arc<Memory>,
) -> Self { ) -> Self {
let wasm_hash = hash_data(wasm);
Self { Self {
inner: CacheInner { inner: CacheInner {
info, info,
backend_metadata, backend_metadata,
compiled_code, compiled_code,
}, },
wasm_hash: Box::new(wasm_hash),
} }
} }
@ -110,7 +106,6 @@ impl Cache {
Ok(Cache { Ok(Cache {
inner, inner,
wasm_hash: Box::new(header.wasm_hash),
}) })
} }
@ -118,12 +113,8 @@ impl Cache {
&self.inner.info &self.inner.info
} }
pub fn wasm_hash(&self) -> &[u8; 32] {
&self.wasm_hash
}
#[doc(hidden)] #[doc(hidden)]
pub fn consume(self) -> (ModuleInfo, Vec<u8>, Memory) { pub fn consume(self) -> (ModuleInfo, Box<[u8]>, Arc<Memory>) {
( (
*self.inner.info, *self.inner.info,
self.inner.backend_metadata, self.inner.backend_metadata,
@ -152,11 +143,7 @@ impl Cache {
file.seek(SeekFrom::Start(0)) file.seek(SeekFrom::Start(0))
.map_err(|e| Error::Unknown(e.to_string()))?; .map_err(|e| Error::Unknown(e.to_string()))?;
let wasm_hash = { let wasm_hash = self.inner.info.wasm_hash.into_array();
let mut array = [0u8; 32];
array.copy_from_slice(&*self.wasm_hash);
array
};
let cache_header = CacheHeader { let cache_header = CacheHeader {
magic: [ magic: [

@ -2,7 +2,7 @@
#[macro_use] #[macro_use]
extern crate field_offset; extern crate field_offset;
#[cfg(feature = "cache")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@ -11,7 +11,7 @@ mod macros;
#[doc(hidden)] #[doc(hidden)]
pub mod backend; pub mod backend;
mod backing; mod backing;
#[cfg(feature = "cache")]
pub mod cache; pub mod cache;
pub mod error; pub mod error;
pub mod export; pub mod export;
@ -44,7 +44,7 @@ pub use self::module::Module;
pub use self::typed_func::Func; pub use self::typed_func::Func;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "cache")]
use self::cache::{Cache, Error as CacheError}; use self::cache::{Cache, Error as CacheError};
pub mod prelude { pub mod prelude {
@ -90,7 +90,7 @@ pub fn validate(wasm: &[u8]) -> bool {
} }
} }
// #[cfg(feature = "cache")] //
// pub fn compile_to_cache_with( // pub fn compile_to_cache_with(
// wasm: &[u8], // wasm: &[u8],
// compiler: &dyn backend::Compiler, // compiler: &dyn backend::Compiler,
@ -102,7 +102,7 @@ pub fn validate(wasm: &[u8]) -> bool {
// Ok(Cache::new(wasm, info, backend_metadata, compiled_code)) // Ok(Cache::new(wasm, info, backend_metadata, compiled_code))
// } // }
#[cfg(feature = "cache")]
pub unsafe fn load_cache_with( pub unsafe fn load_cache_with(
cache: Cache, cache: Cache,
compiler: &dyn backend::Compiler, compiler: &dyn backend::Compiler,

@ -1,6 +1,6 @@
use crate::{ use crate::{
backend::{Backend, FuncResolver, ProtectedCaller, CacheGen}, backend::{Backend, FuncResolver, ProtectedCaller},
error::Result, error,
import::ImportObject, import::ImportObject,
structures::{Map, TypedIndex}, structures::{Map, TypedIndex},
typed_func::EARLY_TRAPPER, typed_func::EARLY_TRAPPER,
@ -10,8 +10,11 @@ use crate::{
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
SigIndex, TableDescriptor, TableIndex, SigIndex, TableDescriptor, TableIndex,
}, },
cache::{Cache, Error as CacheError},
Instance, Instance,
}; };
use crate::backend::CacheGen;
use hashbrown::HashMap; use hashbrown::HashMap;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::sync::Arc; use std::sync::Arc;
@ -21,13 +24,15 @@ use std::sync::Arc;
pub struct ModuleInner { pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>, pub func_resolver: Box<dyn FuncResolver>,
pub protected_caller: Box<dyn ProtectedCaller>, pub protected_caller: Box<dyn ProtectedCaller>,
pub cache_gen: Box<dyn CacheGen>, pub cache_gen: Box<dyn CacheGen>,
pub info: ModuleInfo, pub info: ModuleInfo,
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
pub struct ModuleInfo { pub struct ModuleInfo {
// This are strictly local and the typsystem ensures that. // This are strictly local and the typsystem ensures that.
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>, pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
@ -57,13 +62,19 @@ pub struct ModuleInfo {
pub wasm_hash: WasmHash, pub wasm_hash: WasmHash,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
pub struct WasmHash([u8; 32]); pub struct WasmHash([u8; 32]);
impl WasmHash { impl WasmHash {
pub fn generate(wasm: &[u8]) -> Self { pub fn generate(wasm: &[u8]) -> Self {
WasmHash(super::cache::hash_data(wasm)) WasmHash(crate::cache::hash_data(wasm))
}
pub(crate) fn into_array(self) -> [u8; 32] {
self.0
} }
} }
@ -109,22 +120,27 @@ impl Module {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn instantiate(&self, import_object: &ImportObject) -> Result<Instance> { pub fn instantiate(&self, import_object: &ImportObject) -> error::Result<Instance> {
Instance::new(Arc::clone(&self.inner), import_object) Instance::new(Arc::clone(&self.inner), import_object)
} }
pub fn cache(&self) -> Result<Cache, CacheError> {
let (info, backend_cache, code) = self.inner.cache_gen.generate_cache(&self.inner)?;
}
} }
impl ModuleInner {} impl ModuleInner {}
#[doc(hidden)] #[doc(hidden)]
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ImportName { pub struct ImportName {
pub namespace_index: NamespaceIndex, pub namespace_index: NamespaceIndex,
pub name_index: NameIndex, pub name_index: NameIndex,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportIndex { pub enum ExportIndex {
Func(FuncIndex), Func(FuncIndex),
@ -134,7 +150,7 @@ pub enum ExportIndex {
} }
/// A data initializer for linear memory. /// A data initializer for linear memory.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DataInitializer { pub struct DataInitializer {
/// The index of the memory to initialize. /// The index of the memory to initialize.
@ -147,7 +163,7 @@ pub struct DataInitializer {
} }
/// A WebAssembly table initializer. /// A WebAssembly table initializer.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TableInitializer { pub struct TableInitializer {
/// The index of a table to initialize. /// The index of a table to initialize.
@ -209,7 +225,7 @@ impl<K: TypedIndex> StringTableBuilder<K> {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StringTable<K: TypedIndex> { pub struct StringTable<K: TypedIndex> {
table: Map<K, (u32, u32)>, table: Map<K, (u32, u32)>,
@ -233,7 +249,7 @@ impl<K: TypedIndex> StringTable<K> {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct NamespaceIndex(u32); pub struct NamespaceIndex(u32);
@ -249,7 +265,7 @@ impl TypedIndex for NamespaceIndex {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct NameIndex(u32); pub struct NameIndex(u32);

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

@ -10,18 +10,18 @@ pub use self::unix::*;
#[cfg(windows)] #[cfg(windows)]
pub use self::windows::*; pub use self::windows::*;
#[cfg(feature = "cache")]
use serde::{ use serde::{
de::{self, SeqAccess, Visitor}, de::{self, SeqAccess, Visitor},
ser::SerializeStruct, ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer, Deserialize, Deserializer, Serialize, Serializer,
}; };
#[cfg(feature = "cache")]
use serde_bytes::Bytes; use serde_bytes::Bytes;
#[cfg(feature = "cache")]
use std::fmt; use std::fmt;
#[cfg(feature = "cache")]
impl Serialize for Memory { impl Serialize for Memory {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
@ -36,7 +36,7 @@ impl Serialize for Memory {
} }
} }
#[cfg(feature = "cache")]
impl<'de> Deserialize<'de> for Memory { impl<'de> Deserialize<'de> for Memory {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where

@ -205,7 +205,17 @@ impl Drop for Memory {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] impl Clone for Memory {
fn clone(&self) -> Self {
let mut new = Memory::with_size_protect(self.size, self.protection).unwrap();
unsafe {
new.as_slice_mut().copy_from_slice(self.as_slice());
}
new
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum Protect { pub enum Protect {

@ -156,7 +156,17 @@ impl Drop for Memory {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] impl Clone for Memory {
fn clone(&self) -> Self {
let mut new = Memory::with_size_protect(self.size, self.protection).unwrap();
unsafe {
new.as_slice_mut().copy_from_slice(self.as_slice());
}
new
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum Protect { pub enum Protect {

@ -2,7 +2,7 @@ use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, unit
use std::{borrow::Cow, mem}; use std::{borrow::Cow, mem};
/// Represents a WebAssembly type. /// Represents a WebAssembly type.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type { pub enum Type {
/// The `i32` type. /// The `i32` type.
@ -25,7 +25,7 @@ impl std::fmt::Display for Type {
/// ///
/// As the number of types in WebAssembly expand, /// As the number of types in WebAssembly expand,
/// this structure will expand as well. /// this structure will expand as well.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Value { pub enum Value {
/// The `i32` type. /// The `i32` type.
@ -171,14 +171,14 @@ impl ValueType for f64 {
} }
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElementType { pub enum ElementType {
/// Any wasm function. /// Any wasm function.
Anyfunc, Anyfunc,
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct TableDescriptor { pub struct TableDescriptor {
/// Type of data stored in this table. /// Type of data stored in this table.
@ -203,7 +203,7 @@ impl TableDescriptor {
/// A const value initializer. /// A const value initializer.
/// Over time, this will be able to represent more and more /// Over time, this will be able to represent more and more
/// complex expressions. /// complex expressions.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Initializer { pub enum Initializer {
/// Corresponds to a `const.*` instruction. /// Corresponds to a `const.*` instruction.
@ -212,7 +212,7 @@ pub enum Initializer {
GetGlobal(ImportedGlobalIndex), GetGlobal(ImportedGlobalIndex),
} }
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlobalDescriptor { pub struct GlobalDescriptor {
pub mutable: bool, pub mutable: bool,
@ -220,7 +220,7 @@ pub struct GlobalDescriptor {
} }
/// A wasm global. /// A wasm global.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GlobalInit { pub struct GlobalInit {
pub desc: GlobalDescriptor, pub desc: GlobalDescriptor,
@ -228,7 +228,7 @@ pub struct GlobalInit {
} }
/// A wasm memory. /// A wasm memory.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryDescriptor { pub struct MemoryDescriptor {
/// The minimum number of allowed pages. /// The minimum number of allowed pages.
@ -261,7 +261,7 @@ impl MemoryDescriptor {
/// The signature of a function that is either implemented /// The signature of a function that is either implemented
/// in a wasm module or exposed to wasm by the host. /// in a wasm module or exposed to wasm by the host.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FuncSig { pub struct FuncSig {
params: Cow<'static, [Type]>, params: Cow<'static, [Type]>,
@ -324,7 +324,7 @@ pub trait LocalImport {
#[rustfmt::skip] #[rustfmt::skip]
macro_rules! define_map_index { macro_rules! define_map_index {
($ty:ident) => { ($ty:ident) => {
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct $ty (u32); pub struct $ty (u32);
impl TypedIndex for $ty { impl TypedIndex for $ty {
@ -400,7 +400,7 @@ define_local_or_import![
(GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals), (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
]; ];
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] #[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SigIndex(u32); pub struct SigIndex(u32);
impl TypedIndex for SigIndex { impl TypedIndex for SigIndex {

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

@ -9,10 +9,15 @@ edition = "2018"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" }
lazy_static = "1.2.0" lazy_static = "1.2.0"
[dependencies.wasmer-runtime-core]
path = "../runtime-core"
version = "0.1.2"
[dependencies.wasmer-clif-backend]
path = "../clif-backend"
version = "0.1.2"
[features] [features]
default = ["wasmer-clif-backend/cache", "wasmer-runtime-core/cache"]
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]

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