From 4ca1d965ce7f9a635fb90c05e1133e476818e59a Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Mon, 18 Dec 2023 17:30:35 -0600 Subject: [PATCH 01/24] chore: set resolver to v2 --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 862e7fee..e6fe5777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ [workspace] members = ["crates/host", "crates/common"] exclude = ["test"] + +resolver = "2" From 74e689b917eb53ad84a325af4580db35f53c6753 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Mon, 18 Dec 2023 17:32:30 -0600 Subject: [PATCH 02/24] refactor: reinstate module cache --- crates/host/src/module.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 9889a82c..93567e33 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -286,6 +286,40 @@ impl SerializedModuleCache { } } +/// Caches deserialized wasm modules. Deserialization of cached modules from +/// the cache to create callable instances is slow. Therefore deserialized +/// modules are cached. +#[derive(Default, Debug)] +pub struct ModuleCache { + plru: MicroCache, + key_map: PlruKeyMap, + cache: BTreeMap>, +} + +impl PlruCache for ModuleCache { + type Item = ModuleWithStore; + + fn plru_mut(&mut self) -> &mut MicroCache { + &mut self.plru + } + + fn key_map_mut(&mut self) -> &mut PlruKeyMap { + &mut self.key_map + } + + fn key_map(&self) -> &PlruKeyMap { + &self.key_map + } + + fn cache(&self) -> &BTreeMap> { + &self.cache + } + + fn cache_mut(&mut self) -> &mut BTreeMap> { + &mut self.cache + } +} + /// Caches wasm instances. Reusing wasm instances allows maximum speed in function /// calls but also introduces the possibility of memory corruption or other bad /// state that is inappropriate to persist/reuse/access across calls. It is the From f64a9282582c9f44f71cb4a2cdf241ff4aea44fb Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Fri, 5 Jan 2024 12:02:24 -0600 Subject: [PATCH 03/24] test: fix module caching --- crates/host/src/module.rs | 101 ++++++++++++++++++++------------------ test/src/test.rs | 48 +++++++++++++++++- test/src/wasms.rs | 96 +++++++++++++++++------------------- 3 files changed, 143 insertions(+), 102 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 93567e33..3296041a 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -12,12 +12,9 @@ use std::fs::OpenOptions; use std::io::Write; use std::path::PathBuf; use std::sync::Arc; -use wasmer::BaseTunables; -use wasmer::Cranelift; use wasmer::Engine; use wasmer::Instance; use wasmer::Module; -use wasmer::NativeEngineExt; use wasmer::Store; /// We expect cache keys to be produced via hashing so 32 bytes is enough for all @@ -128,7 +125,8 @@ pub struct SerializedModuleCache { pub plru: MicroCache, pub key_map: PlruKeyMap, pub cache: BTreeMap>, - pub cranelift: fn() -> Cranelift, + pub compiling_engine: fn() -> Engine, + pub runtime_engine: Engine, pub maybe_fs_dir: Option, } @@ -159,13 +157,18 @@ impl PlruCache for SerializedModuleCache { impl SerializedModuleCache { /// Build a default `SerializedModuleCache` with a `Cranelift` that will be used /// to compile modules for serialization as needed. - pub fn default_with_cranelift(cranelift: fn() -> Cranelift) -> Self { + pub fn default_with_cranelift( + make_compiling_engine: fn() -> Engine, + maybe_fs_dir: Option, + ) -> Self { + let compiling_engine = make_compiling_engine; Self { - cranelift, + compiling_engine, + runtime_engine: Engine::default(), plru: MicroCache::default(), key_map: PlruKeyMap::default(), cache: BTreeMap::default(), - maybe_fs_dir: None, + maybe_fs_dir, } } @@ -175,27 +178,13 @@ impl SerializedModuleCache { .map(|dir_path| dir_path.clone().join(hex::encode(key))) } - fn store(&self) -> Store { - let mut engine: Engine = (self.cranelift)().into(); - // Workaround for invalid memory access on iOS. - // https://github.com/holochain/holochain/issues/3096 - engine.set_tunables(BaseTunables { - static_memory_bound: 0x4000.into(), - static_memory_offset_guard_size: 0x1_0000, - dynamic_memory_offset_guard_size: 0x1_0000, - }); - Store::new(engine) - } - /// Given a wasm, compiles with cranelift, serializes the result, adds it to /// the cache and returns that. fn get_with_build_cache( &mut self, key: CacheKey, wasm: &[u8], - ) -> Result, wasmer::RuntimeError> { - let store = self.store(); - + ) -> Result, wasmer::RuntimeError> { let maybe_module_path = self.module_path(key); let (module, serialized_module) = match maybe_module_path.as_ref().map(|module_path| { // We do this the long way to get `Bytes` instead of `Vec` so @@ -218,13 +207,20 @@ impl SerializedModuleCache { })?; Ok::(bytes_mut.into_inner().freeze()) }) { - Some(Ok(serialized_module)) => ( - unsafe { Module::deserialize(&store, serialized_module.clone()) } - .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?, - serialized_module, - ), + Some(Ok(serialized_module)) => { + let deserialized_module = + unsafe { Module::deserialize(&self.runtime_engine, serialized_module.clone()) } + .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; + (deserialized_module, serialized_module) + } + // no serialized module found on the file system, so serialize the + // wasm binary and write it to the file system _fs_miss => { - let module = Module::from_binary(&store, wasm) + // Each module needs to be compiled with a new engine because + // of middleware like metering. Middleware is compiled into the + // module once and available in all instances created from it. + let compiling_engine = (self.compiling_engine)(); + let module = Module::from_binary(&compiling_engine, wasm) .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; let serialized_module = module .serialize() @@ -252,34 +248,41 @@ impl SerializedModuleCache { } } + // deserialize the module to be returned for instantiation + // + // A new middleware per module is required, hence a new engine + // per module is needed too. Serialization allows for uncoupling + // the module from the engine that was used for compilation. + // After that a new engine can be created to deserialize the + // module again. + // This procedure facilitates caching of modules that can be + // instatiated with fresh stores free from state. Instance + // creation is highly performant which makes caching of instances + // and stores unnecessary. + let module = unsafe { + Module::deserialize(&self.runtime_engine, serialized_module.clone()) + .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))? + }; + (module, serialized_module) } }; self.put_item(key, Arc::new(serialized_module.clone())); - Ok(Arc::new(ModuleWithStore { - store: Arc::new(Mutex::new(store)), - module: Arc::new(module), - })) + Ok(Arc::new(module)) } /// Given a wasm, attempts to get the serialized module for it from the cache. - /// If the cache misses a new serialized module, will be built from the wasm. - pub fn get( - &mut self, - key: CacheKey, - wasm: &[u8], - ) -> Result, wasmer::RuntimeError> { + /// If the cache misses, a new serialized module will be built from the wasm. + pub fn get(&mut self, key: CacheKey, wasm: &[u8]) -> Result, wasmer::RuntimeError> { match self.cache.get(&key) { Some(serialized_module) => { - let store = self.store(); - let module = unsafe { Module::deserialize(&store, (**serialized_module).clone()) } - .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; + let module = unsafe { + Module::deserialize(&self.runtime_engine, (**serialized_module).clone()) + } + .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; self.touch(&key); - Ok(Arc::new(ModuleWithStore { - store: Arc::new(Mutex::new(store)), - module: Arc::new(module), - })) + Ok(Arc::new(module)) } None => self.get_with_build_cache(key, wasm), } @@ -287,17 +290,17 @@ impl SerializedModuleCache { } /// Caches deserialized wasm modules. Deserialization of cached modules from -/// the cache to create callable instances is slow. Therefore deserialized -/// modules are cached. +/// the cache to create callable instances is slow. Therefore modules are +/// cached in memory after deserialization. #[derive(Default, Debug)] pub struct ModuleCache { plru: MicroCache, key_map: PlruKeyMap, - cache: BTreeMap>, + cache: BTreeMap>, } impl PlruCache for ModuleCache { - type Item = ModuleWithStore; + type Item = Module; fn plru_mut(&mut self) -> &mut MicroCache { &mut self.plru diff --git a/test/src/test.rs b/test/src/test.rs index b5505d4a..9ba3adb3 100644 --- a/test/src/test.rs +++ b/test/src/test.rs @@ -98,7 +98,7 @@ pub mod tests { use super::*; use crate::wasms; use holochain_wasmer_host::module::InstanceWithStore; - use holochain_wasmer_host::module::ModuleWithStore; + use std::thread; use test_common::StringType; use wasmer::AsStoreMut; use wasms::TestWasm; @@ -110,7 +110,7 @@ pub mod tests { #[test] fn host_externs_toolable() { - let ModuleWithStore { module, .. } = (*TestWasm::Test.module(false)).clone(); + let module = (*TestWasm::Test.module(false)).clone(); // Imports will be the minimal set of functions actually used by the wasm // NOT the complete list defined by `host_externs!`. assert_eq!( @@ -248,6 +248,50 @@ pub mod tests { assert_eq!(&String::from(result), &expected_string,); } + #[test] + fn concurrent_calls() { + let some_inner = "foo"; + let some_struct = SomeStruct::new(some_inner.into()); + + let InstanceWithStore { + store: store_1, + instance: instance_1, + } = TestWasm::Test.instance(); + let InstanceWithStore { + store: store_2, + instance: instance_2, + } = TestWasm::Test.instance(); + + let call_1 = thread::spawn({ + let some_struct = some_struct.clone(); + move || { + let result: SomeStruct = guest::call( + &mut store_1.lock().as_store_mut(), + instance_1, + "native_type", + some_struct.clone(), + ) + .expect("native type handling"); + assert_eq!(some_struct, result); + } + }); + let call_2 = thread::spawn({ + let some_struct = some_struct.clone(); + move || { + let result: SomeStruct = guest::call( + &mut store_2.lock().as_store_mut(), + instance_2, + "native_type", + some_struct.clone(), + ) + .expect("native type handling"); + assert_eq!(some_struct, result); + } + }); + assert!(matches!(call_1.join(), Ok(()))); + assert!(matches!(call_2.join(), Ok(()))); + } + #[test] fn native_test() { let some_inner = "foo"; diff --git a/test/src/wasms.rs b/test/src/wasms.rs index 33cb3b5e..09c6f48b 100644 --- a/test/src/wasms.rs +++ b/test/src/wasms.rs @@ -1,19 +1,20 @@ use crate::import::imports; -use holochain_wasmer_host::module::InstanceCache; use holochain_wasmer_host::module::InstanceWithStore; -use holochain_wasmer_host::module::ModuleWithStore; use holochain_wasmer_host::module::SerializedModuleCache; use holochain_wasmer_host::prelude::*; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; use parking_lot::RwLock; use std::sync::Arc; use wasmer::wasmparser::Operator; use wasmer::AsStoreMut; use wasmer::CompilerConfig; use wasmer::Cranelift; +use wasmer::Engine; use wasmer::FunctionEnv; use wasmer::Imports; use wasmer::Instance; +use wasmer::Module; +use wasmer::Store; use wasmer_middlewares::Metering; pub enum TestWasm { @@ -26,8 +27,6 @@ pub enum TestWasm { pub static SERIALIZED_MODULE_CACHE: OnceCell> = OnceCell::new(); pub static SERIALIZED_MODULE_CACHE_UNMETERED: OnceCell> = OnceCell::new(); -pub static INSTANCE_CACHE: Lazy> = - Lazy::new(|| RwLock::new(InstanceCache::default())); impl TestWasm { pub fn bytes(&self) -> &[u8] { @@ -81,7 +80,7 @@ impl TestWasm { } } - pub fn module(&self, metered: bool) -> Arc { + pub fn module(&self, metered: bool) -> Arc { match self.module_cache(metered).get() { Some(cache) => cache.write().get(self.key(metered), self.bytes()).unwrap(), None => { @@ -90,24 +89,27 @@ impl TestWasm { let metering = Arc::new(Metering::new(10_000_000_000, cost_function)); let mut cranelift = Cranelift::default(); cranelift.canonicalize_nans(true).push_middleware(metering); - cranelift + Engine::from(cranelift) }; let cranelift_fn_unmetered = || { let mut cranelift = Cranelift::default(); cranelift.canonicalize_nans(true); - cranelift + Engine::from(cranelift) }; // This will error if the cache is already initialized // which could happen if two tests are running in parallel. // It doesn't matter which one wins, so we just ignore the error. let _did_init_ok = self.module_cache(metered).set(parking_lot::RwLock::new( - SerializedModuleCache::default_with_cranelift(if metered { - cranelift_fn - } else { - cranelift_fn_unmetered - }), + SerializedModuleCache::default_with_cranelift( + if metered { + cranelift_fn + } else { + cranelift_fn_unmetered + }, + None, + ), )); // Just recurse now that the cache is initialized. @@ -117,55 +119,47 @@ impl TestWasm { } pub fn _instance(&self, metered: bool) -> InstanceWithStore { - let module_with_store = self.module(metered); + let module = self.module(metered); let function_env; - let instance; - { - let mut store_lock = module_with_store.store.lock(); - let mut store_mut = store_lock.as_store_mut(); - function_env = FunctionEnv::new(&mut store_mut, Env::default()); - let built_imports: Imports = imports(&mut store_mut, &function_env); - instance = - Instance::new(&mut store_mut, &module_with_store.module, &built_imports).unwrap(); - } + let mut store = Store::default(); + function_env = FunctionEnv::new(&mut store, Env::default()); + let built_imports: Imports = imports(&mut store.as_store_mut(), &function_env); + let instance = Instance::new(&mut store, &module, &built_imports).unwrap(); - { - let mut store_lock = module_with_store.store.lock(); - let mut function_env_mut = function_env.into_mut(&mut store_lock); - let (data_mut, store_mut) = function_env_mut.data_and_store_mut(); - data_mut.memory = Some(instance.exports.get_memory("memory").unwrap().clone()); - data_mut.deallocate = Some( + let mut function_env_mut = function_env.into_mut(&mut store); + let (data_mut, store_mut) = function_env_mut.data_and_store_mut(); + data_mut.memory = Some(instance.exports.get_memory("memory").unwrap().clone()); + data_mut.deallocate = Some( + instance + .exports + .get_typed_function(&store_mut, "__hc__deallocate_1") + .unwrap(), + ); + data_mut.allocate = Some( + instance + .exports + .get_typed_function(&store_mut, "__hc__allocate_1") + .unwrap(), + ); + if metered { + data_mut.wasmer_metering_points_exhausted = Some( instance .exports - .get_typed_function(&store_mut, "__hc__deallocate_1") - .unwrap(), + .get_global("wasmer_metering_points_exhausted") + .unwrap() + .clone(), ); - data_mut.allocate = Some( + data_mut.wasmer_metering_remaining_points = Some( instance .exports - .get_typed_function(&store_mut, "__hc__allocate_1") - .unwrap(), + .get_global("wasmer_metering_remaining_points") + .unwrap() + .clone(), ); - if metered { - data_mut.wasmer_metering_points_exhausted = Some( - instance - .exports - .get_global("wasmer_metering_points_exhausted") - .unwrap() - .clone(), - ); - data_mut.wasmer_metering_remaining_points = Some( - instance - .exports - .get_global("wasmer_metering_remaining_points") - .unwrap() - .clone(), - ); - } } InstanceWithStore { - store: module_with_store.store.clone(), + store: Arc::new(Mutex::new(store)), instance: Arc::new(instance), } } From 55f7b996c611cd6ea8981780484f853902aa5b09 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Sat, 6 Jan 2024 08:06:13 -0600 Subject: [PATCH 04/24] chore: update host package version to 0.0.91 --- crates/host/Cargo.toml | 2 +- test/Cargo.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/host/Cargo.toml b/crates/host/Cargo.toml index bc437e17..d3b10814 100644 --- a/crates/host/Cargo.toml +++ b/crates/host/Cargo.toml @@ -2,7 +2,7 @@ name = "holochain_wasmer_host" description = "wasm host code" license = "Apache-2.0" -version = "0.0.90" +version = "0.0.91" authors = [ "thedavidmeister", "thedavidmeister@gmail.com" ] edition = "2021" diff --git a/test/Cargo.lock b/test/Cargo.lock index b3c91cb5..f7bc7276 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -792,7 +792,7 @@ dependencies = [ [[package]] name = "holochain_wasmer_host" -version = "0.0.90" +version = "0.0.91" dependencies = [ "bimap", "bytes", From e6679d149c86fc0a4ed652b74336118f7436f607 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 14:14:41 -0600 Subject: [PATCH 05/24] test(host): add test for cache --- crates/host/src/module.rs | 119 +++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 3296041a..51021274 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -6,6 +6,7 @@ use bytes::Bytes; use bytes::BytesMut; use holochain_wasmer_common::WasmError; use parking_lot::Mutex; +use parking_lot::RwLock; use std::collections::BTreeMap; use std::fs::File; use std::fs::OpenOptions; @@ -125,7 +126,7 @@ pub struct SerializedModuleCache { pub plru: MicroCache, pub key_map: PlruKeyMap, pub cache: BTreeMap>, - pub compiling_engine: fn() -> Engine, + pub make_compiling_engine: fn() -> Engine, pub runtime_engine: Engine, pub maybe_fs_dir: Option, } @@ -161,9 +162,8 @@ impl SerializedModuleCache { make_compiling_engine: fn() -> Engine, maybe_fs_dir: Option, ) -> Self { - let compiling_engine = make_compiling_engine; Self { - compiling_engine, + make_compiling_engine, runtime_engine: Engine::default(), plru: MicroCache::default(), key_map: PlruKeyMap::default(), @@ -209,7 +209,7 @@ impl SerializedModuleCache { }) { Some(Ok(serialized_module)) => { let deserialized_module = - unsafe { Module::deserialize(&self.runtime_engine, serialized_module.clone()) } + unsafe { Module::deserialize(&Engine::default(), serialized_module.clone()) } .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; (deserialized_module, serialized_module) } @@ -219,8 +219,8 @@ impl SerializedModuleCache { // Each module needs to be compiled with a new engine because // of middleware like metering. Middleware is compiled into the // module once and available in all instances created from it. - let compiling_engine = (self.compiling_engine)(); - let module = Module::from_binary(&compiling_engine, wasm) + // let wasm = include_bytes!("/Users/jost/Desktop/holochain/holochain-wasmer/test/test_wasm/target/wasm32-unknown-unknown/release/test_wasm.wasm"); + let module = Module::from_binary(&(self.make_compiling_engine)(), wasm) .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; let serialized_module = module .serialize() @@ -293,13 +293,13 @@ impl SerializedModuleCache { /// the cache to create callable instances is slow. Therefore modules are /// cached in memory after deserialization. #[derive(Default, Debug)] -pub struct ModuleCache { +pub struct DeserializedModuleCache { plru: MicroCache, key_map: PlruKeyMap, cache: BTreeMap>, } -impl PlruCache for ModuleCache { +impl PlruCache for DeserializedModuleCache { type Item = Module; fn plru_mut(&mut self) -> &mut MicroCache { @@ -323,37 +323,90 @@ impl PlruCache for ModuleCache { } } -/// Caches wasm instances. Reusing wasm instances allows maximum speed in function -/// calls but also introduces the possibility of memory corruption or other bad -/// state that is inappropriate to persist/reuse/access across calls. It is the -/// responsibility of the host to discard instances that are not eligible for reuse. -#[derive(Default, Debug)] -pub struct InstanceCache { - plru: MicroCache, - key_map: PlruKeyMap, - cache: BTreeMap>, +#[derive(Debug)] +pub struct ModuleCache { + serialized_module_cache: Arc>, + deserialized_module_cache: Arc>, } -impl PlruCache for InstanceCache { - type Item = InstanceWithStore; - - fn plru_mut(&mut self) -> &mut MicroCache { - &mut self.plru - } - - fn key_map_mut(&mut self) -> &mut PlruKeyMap { - &mut self.key_map +impl ModuleCache { + pub fn new(make_compiling_engine: fn() -> Engine, maybe_fs_dir: Option) -> Self { + let serialized_module_cache = Arc::new(RwLock::new( + SerializedModuleCache::default_with_cranelift(make_compiling_engine, maybe_fs_dir), + )); + let deserialized_module_cache = Arc::new(RwLock::new(DeserializedModuleCache::default())); + ModuleCache { + serialized_module_cache, + deserialized_module_cache, + } } - fn key_map(&self) -> &PlruKeyMap { - &self.key_map + pub fn get(&self, key: CacheKey, wasm: &[u8]) -> Result, wasmer::RuntimeError> { + // check deserialized module cache first for module + { + let mut deserialized_cache = self.deserialized_module_cache.write(); + if let Some(module) = deserialized_cache.get_item(&key) { + return Ok(module); + } + } + // get module from serialized cache otherwise + // if cache does not contain module, it will be built from wasm bytes + // and then cached in serialized cache + let module; + { + let mut serialized_cache = self.serialized_module_cache.write(); + module = serialized_cache.get(key, wasm)?; + } + // cache in deserialized module cache too + { + let mut deserialized_cache = self.deserialized_module_cache.write(); + deserialized_cache.put_item(key, module.clone()); + } + Ok(module) } +} - fn cache(&self) -> &BTreeMap> { - &self.cache +#[test] +fn cache_test() { + // simple example wasm taken from wasmer docs + // https://docs.rs/wasmer/latest/wasmer/struct.Module.html#example + let wasm: Vec = vec![ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, 0x01, + 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, + 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, + 0x1a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, + 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, + ]; + let make_compiling_engine = || Engine::default(); + let module_cache = ModuleCache::new(make_compiling_engine, None); + assert_eq!( + module_cache.serialized_module_cache.read().cache.is_empty(), + true + ); + assert_eq!( + module_cache + .deserialized_module_cache + .read() + .cache + .is_empty(), + true + ); + + let key: CacheKey = [0u8; 32].into(); + let module = module_cache.get(key.clone(), &wasm).unwrap(); + + // make sure module has been stored in serialized cache under key + { + let serialized_cached_module = module_cache.serialized_module_cache.write().get_item(&key); + assert_eq!(matches!(serialized_cached_module, Some(_)), true); } - - fn cache_mut(&mut self) -> &mut BTreeMap> { - &mut self.cache + // make sure module has been stored in deserialized cache under key + { + let deserialized_cached_module = module_cache + .deserialized_module_cache + .write() + .get_item(&key) + .unwrap(); + assert_eq!(*deserialized_cached_module, *module); } } From 5eb7ce487976ff7bb4d55ab737fd087fb56431c9 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 16:07:31 -0600 Subject: [PATCH 06/24] refactor: move all wasmer-types related items to holochain-wasmer --- crates/host/src/module.rs | 64 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 51021274..107c5264 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -12,11 +12,21 @@ use std::fs::File; use std::fs::OpenOptions; use std::io::Write; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; +use tracing::info; +use wasmer::wasmparser; +use wasmer::CompileError; +use wasmer::CompilerConfig; +use wasmer::CpuFeature; +use wasmer::Cranelift; use wasmer::Engine; use wasmer::Instance; use wasmer::Module; use wasmer::Store; +use wasmer::Target; +use wasmer::Triple; +use wasmer_middlewares::Metering; /// We expect cache keys to be produced via hashing so 32 bytes is enough for all /// purposes. @@ -117,6 +127,53 @@ pub trait PlruCache { } } +#[cfg(not(test))] +/// one hundred giga ops +pub const WASM_METERING_LIMIT: u64 = 100_000_000_000; + +#[cfg(test)] +/// ten mega ops. +/// We don't want tests to run forever, and it can take several minutes for 100 giga ops to run. +pub const WASM_METERING_LIMIT: u64 = 10_000_000; + +/// Generate an engine with a wasm compiler +/// and Metering (use limits) in place. +pub fn cranelift() -> Engine { + let cost_function = |_operator: &wasmparser::Operator| -> u64 { 1 }; + // @todo 100 giga-ops is totally arbitrary cutoff so we probably + // want to make the limit configurable somehow. + let metering = Arc::new(Metering::new(WASM_METERING_LIMIT, cost_function)); + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true).push_middleware(metering); + Engine::from(compiler) +} + +/// Take WASM binary and prepare a wasmer Module suitable for iOS +pub fn build_ios_module(wasm: &[u8]) -> Result { + info!( + "Found wasm and was instructed to serialize it for ios in wasmer format, doing so now..." + ); + let compiler_config = cranelift(); + let store = Store::new(compiler_config); + Module::from_binary(&store, wasm) +} + +/// Configuration of a Target for wasmer for iOS +pub fn wasmer_ios_target() -> Target { + // use what I see in + // platform ios headless example + // https://github.com/wasmerio/wasmer/blob/447c2e3a152438db67be9ef649327fabcad6f5b8/examples/platform_ios_headless.rs#L38-L53 + let triple = Triple::from_str("aarch64-apple-ios").unwrap(); + let cpu_feature = CpuFeature::set(); + Target::new(triple, cpu_feature) +} + +/// Generate a Dylib Engine suitable for iOS. +/// Useful for re-building an iOS Module from a preserialized WASM Module. +pub fn ios_dylib_headless_engine() -> Engine { + Engine::default() +} + /// Cache for serialized modules. These are fully compiled wasm modules that are /// then serialized by wasmer and can be cached. A serialized wasm module must still /// be deserialized before it can be used to build instances. The deserialization @@ -330,9 +387,9 @@ pub struct ModuleCache { } impl ModuleCache { - pub fn new(make_compiling_engine: fn() -> Engine, maybe_fs_dir: Option) -> Self { + pub fn new(maybe_fs_dir: Option) -> Self { let serialized_module_cache = Arc::new(RwLock::new( - SerializedModuleCache::default_with_cranelift(make_compiling_engine, maybe_fs_dir), + SerializedModuleCache::default_with_cranelift(cranelift, maybe_fs_dir), )); let deserialized_module_cache = Arc::new(RwLock::new(DeserializedModuleCache::default())); ModuleCache { @@ -377,8 +434,7 @@ fn cache_test() { 0x1a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, ]; - let make_compiling_engine = || Engine::default(); - let module_cache = ModuleCache::new(make_compiling_engine, None); + let module_cache = ModuleCache::new(None); assert_eq!( module_cache.serialized_module_cache.read().cache.is_empty(), true From 7f4ce38c17af394f0a89f68b27fbc2b2b45af664 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 16:22:03 -0600 Subject: [PATCH 07/24] refactor(host): change identifier names according to refactors --- crates/host/src/module.rs | 44 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 107c5264..50782f84 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -23,6 +23,7 @@ use wasmer::Cranelift; use wasmer::Engine; use wasmer::Instance; use wasmer::Module; +use wasmer::NativeEngineExt; use wasmer::Store; use wasmer::Target; use wasmer::Triple; @@ -138,11 +139,12 @@ pub const WASM_METERING_LIMIT: u64 = 10_000_000; /// Generate an engine with a wasm compiler /// and Metering (use limits) in place. -pub fn cranelift() -> Engine { +pub fn make_compiler_engine() -> Engine { let cost_function = |_operator: &wasmparser::Operator| -> u64 { 1 }; // @todo 100 giga-ops is totally arbitrary cutoff so we probably // want to make the limit configurable somehow. let metering = Arc::new(Metering::new(WASM_METERING_LIMIT, cost_function)); + // the only place where the wasm compiler engine is set let mut compiler = Cranelift::default(); compiler.canonicalize_nans(true).push_middleware(metering); Engine::from(compiler) @@ -153,8 +155,8 @@ pub fn build_ios_module(wasm: &[u8]) -> Result { info!( "Found wasm and was instructed to serialize it for ios in wasmer format, doing so now..." ); - let compiler_config = cranelift(); - let store = Store::new(compiler_config); + let compiler_engine = make_compiler_engine(); + let store = Store::new(compiler_engine); Module::from_binary(&store, wasm) } @@ -168,10 +170,10 @@ pub fn wasmer_ios_target() -> Target { Target::new(triple, cpu_feature) } -/// Generate a Dylib Engine suitable for iOS. +/// Generate a runtime `Engine` without compiler suitable for iOS. /// Useful for re-building an iOS Module from a preserialized WASM Module. -pub fn ios_dylib_headless_engine() -> Engine { - Engine::default() +pub fn make_ios_runtime_engine() -> Engine { + Engine::headless() } /// Cache for serialized modules. These are fully compiled wasm modules that are @@ -183,7 +185,11 @@ pub struct SerializedModuleCache { pub plru: MicroCache, pub key_map: PlruKeyMap, pub cache: BTreeMap>, - pub make_compiling_engine: fn() -> Engine, + // a function to create a new compiler engine for every module + pub make_compiler_engine: fn() -> Engine, + // the runtime engine has to live as long as the module; + // keeping it in the cache and using it for all modules + // make sure of that pub runtime_engine: Engine, pub maybe_fs_dir: Option, } @@ -213,14 +219,14 @@ impl PlruCache for SerializedModuleCache { } impl SerializedModuleCache { - /// Build a default `SerializedModuleCache` with a `Cranelift` that will be used - /// to compile modules for serialization as needed. - pub fn default_with_cranelift( - make_compiling_engine: fn() -> Engine, + /// Build a default `SerializedModuleCache` with a fn to create an `Engine` + /// that will be used to compile modules from wasms as needed. + pub fn default_with_engine( + make_compiler_engine: fn() -> Engine, maybe_fs_dir: Option, ) -> Self { Self { - make_compiling_engine, + make_compiler_engine, runtime_engine: Engine::default(), plru: MicroCache::default(), key_map: PlruKeyMap::default(), @@ -235,7 +241,7 @@ impl SerializedModuleCache { .map(|dir_path| dir_path.clone().join(hex::encode(key))) } - /// Given a wasm, compiles with cranelift, serializes the result, adds it to + /// Given a wasm, compiles with compiler engine, serializes the result, adds it to /// the cache and returns that. fn get_with_build_cache( &mut self, @@ -276,8 +282,8 @@ impl SerializedModuleCache { // Each module needs to be compiled with a new engine because // of middleware like metering. Middleware is compiled into the // module once and available in all instances created from it. - // let wasm = include_bytes!("/Users/jost/Desktop/holochain/holochain-wasmer/test/test_wasm/target/wasm32-unknown-unknown/release/test_wasm.wasm"); - let module = Module::from_binary(&(self.make_compiling_engine)(), wasm) + let compiler_engine = (self.make_compiler_engine)(); + let module = Module::from_binary(&compiler_engine, wasm) .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; let serialized_module = module .serialize() @@ -310,8 +316,10 @@ impl SerializedModuleCache { // A new middleware per module is required, hence a new engine // per module is needed too. Serialization allows for uncoupling // the module from the engine that was used for compilation. - // After that a new engine can be created to deserialize the - // module again. + // After that another engine can be used to deserialize the + // module again. The engine has to live as long as the module to + // prevent memory access out of bounds errors. + // // This procedure facilitates caching of modules that can be // instatiated with fresh stores free from state. Instance // creation is highly performant which makes caching of instances @@ -389,7 +397,7 @@ pub struct ModuleCache { impl ModuleCache { pub fn new(maybe_fs_dir: Option) -> Self { let serialized_module_cache = Arc::new(RwLock::new( - SerializedModuleCache::default_with_cranelift(cranelift, maybe_fs_dir), + SerializedModuleCache::default_with_engine(make_compiler_engine, maybe_fs_dir), )); let deserialized_module_cache = Arc::new(RwLock::new(DeserializedModuleCache::default())); ModuleCache { From d663a565ce4d481515bbb46792811618785cb49e Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 17:05:48 -0600 Subject: [PATCH 08/24] docs: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea417d1..ff4116f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +- Refactor: Instance cache in host crate has been removed in favor of a deserialized module cache `DeserializedModuleCache`. An abstraction for caching (serialized & deserialized modules) called `ModuleCache` was added. +- Refactor: All logic related to modules and wasmer caching from `holochain` has been moved to the host crate. Consequently functions for wasmer development under iOS need to be imported from there. + ## [0.0.90] - Bump wasmer to 4.2.4 From 29409f729f584dab70558215cee667213d4efc17 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 17:08:45 -0600 Subject: [PATCH 09/24] test: fix module creation --- test/src/wasms.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/wasms.rs b/test/src/wasms.rs index 09c6f48b..9811e9bc 100644 --- a/test/src/wasms.rs +++ b/test/src/wasms.rs @@ -102,7 +102,7 @@ impl TestWasm { // which could happen if two tests are running in parallel. // It doesn't matter which one wins, so we just ignore the error. let _did_init_ok = self.module_cache(metered).set(parking_lot::RwLock::new( - SerializedModuleCache::default_with_cranelift( + SerializedModuleCache::default_with_engine( if metered { cranelift_fn } else { From febc87cd651e43711ed5f349c8945b6dae936e73 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 9 Jan 2024 17:59:20 -0600 Subject: [PATCH 10/24] test: comment out bench wasm_instance due to upstream memory leak --- test/benches/bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/benches/bench.rs b/test/benches/bench.rs index c0ed6b5a..cde14f47 100644 --- a/test/benches/bench.rs +++ b/test/benches/bench.rs @@ -277,7 +277,7 @@ criterion_group!( wasm_module_compile, wasm_module_deserialize_from_file, wasm_module, - wasm_instance, + // wasm_instance, wasm_call, wasm_call_n, test_process_string, From 7c39692e1f2fa84c40514d24c618e923f8bc30b0 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 06:45:24 -0600 Subject: [PATCH 11/24] test: comment out bench wasm_module due to upstream memory leak --- test/benches/bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/benches/bench.rs b/test/benches/bench.rs index cde14f47..467996bf 100644 --- a/test/benches/bench.rs +++ b/test/benches/bench.rs @@ -276,7 +276,7 @@ criterion_group!( benches, wasm_module_compile, wasm_module_deserialize_from_file, - wasm_module, + // wasm_module, // wasm_instance, wasm_call, wasm_call_n, From 3aa836d3e40551b3f695a232dbf0c4a84fdec074 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 10:28:31 -0600 Subject: [PATCH 12/24] refactor: move ios precompiled module getter to holochain-wasmer --- crates/host/src/module.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 50782f84..36ce9627 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -20,6 +20,7 @@ use wasmer::CompileError; use wasmer::CompilerConfig; use wasmer::CpuFeature; use wasmer::Cranelift; +use wasmer::DeserializeError; use wasmer::Engine; use wasmer::Instance; use wasmer::Module; @@ -150,6 +151,12 @@ pub fn make_compiler_engine() -> Engine { Engine::from(compiler) } +/// Generate a runtime `Engine` without compiler suitable for iOS. +/// Useful for re-building an iOS Module from a preserialized WASM Module. +pub fn make_ios_runtime_engine() -> Engine { + Engine::headless() +} + /// Take WASM binary and prepare a wasmer Module suitable for iOS pub fn build_ios_module(wasm: &[u8]) -> Result { info!( @@ -160,6 +167,13 @@ pub fn build_ios_module(wasm: &[u8]) -> Result { Module::from_binary(&store, wasm) } +/// Take a previously compiled module for iOS, stored in a file, +/// and deserialize it. +pub fn precompiled_module(dylib_path: &PathBuf) -> Result { + let engine = make_ios_runtime_engine(); + unsafe { Module::deserialize_from_file(&engine, dylib_path) } +} + /// Configuration of a Target for wasmer for iOS pub fn wasmer_ios_target() -> Target { // use what I see in @@ -170,12 +184,6 @@ pub fn wasmer_ios_target() -> Target { Target::new(triple, cpu_feature) } -/// Generate a runtime `Engine` without compiler suitable for iOS. -/// Useful for re-building an iOS Module from a preserialized WASM Module. -pub fn make_ios_runtime_engine() -> Engine { - Engine::headless() -} - /// Cache for serialized modules. These are fully compiled wasm modules that are /// then serialized by wasmer and can be cached. A serialized wasm module must still /// be deserialized before it can be used to build instances. The deserialization From 6351ca2f111ac69de59429c2954a3c14b3077d76 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 11:35:25 -0600 Subject: [PATCH 13/24] refactor: rename precompiled module fn --- crates/host/src/module.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 36ce9627..5619b692 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -167,11 +167,10 @@ pub fn build_ios_module(wasm: &[u8]) -> Result { Module::from_binary(&store, wasm) } -/// Take a previously compiled module for iOS, stored in a file, -/// and deserialize it. -pub fn precompiled_module(dylib_path: &PathBuf) -> Result { +/// Deserialize a previously compiled module for iOS from a file. +pub fn get_ios_module_from_file(path: &PathBuf) -> Result { let engine = make_ios_runtime_engine(); - unsafe { Module::deserialize_from_file(&engine, dylib_path) } + unsafe { Module::deserialize_from_file(&engine, path) } } /// Configuration of a Target for wasmer for iOS @@ -235,7 +234,9 @@ impl SerializedModuleCache { ) -> Self { Self { make_compiler_engine, - runtime_engine: Engine::default(), + // the engine to execute function calls on instances does not + // require a compiler + runtime_engine: Engine::headless(), plru: MicroCache::default(), key_map: PlruKeyMap::default(), cache: BTreeMap::default(), From 1624030bfc43401e7dff2f4183e22688daf8e91b Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 13:26:07 -0600 Subject: [PATCH 14/24] build: switch to llvm compiler --- crates/host/Cargo.toml | 1 + crates/host/src/module.rs | 8 ++-- flake.lock | 40 +++++++++---------- flake.nix | 24 +++++++---- test/Cargo.lock | 83 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 124 insertions(+), 32 deletions(-) diff --git a/crates/host/Cargo.toml b/crates/host/Cargo.toml index d3b10814..06008398 100644 --- a/crates/host/Cargo.toml +++ b/crates/host/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] wasmer = "=4.2.4" wasmer-middlewares = "=4.2.4" +wasmer-compiler-llvm = "=4.2.4" holochain_wasmer_common = { version = "=0.0.90", path = "../common" } holochain_serialized_bytes = "=0.0.53" serde = "1" diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 5619b692..121647c6 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -28,6 +28,7 @@ use wasmer::NativeEngineExt; use wasmer::Store; use wasmer::Target; use wasmer::Triple; +use wasmer_compiler_llvm::LLVM; use wasmer_middlewares::Metering; /// We expect cache keys to be produced via hashing so 32 bytes is enough for all @@ -146,8 +147,9 @@ pub fn make_compiler_engine() -> Engine { // want to make the limit configurable somehow. let metering = Arc::new(Metering::new(WASM_METERING_LIMIT, cost_function)); // the only place where the wasm compiler engine is set - let mut compiler = Cranelift::default(); - compiler.canonicalize_nans(true).push_middleware(metering); + let mut compiler = LLVM::default(); + compiler.canonicalize_nans(true); + compiler.push_middleware(metering); Engine::from(compiler) } @@ -281,7 +283,7 @@ impl SerializedModuleCache { }) { Some(Ok(serialized_module)) => { let deserialized_module = - unsafe { Module::deserialize(&Engine::default(), serialized_module.clone()) } + unsafe { Module::deserialize(&self.runtime_engine, serialized_module.clone()) } .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; (deserialized_module, serialized_module) } diff --git a/flake.lock b/flake.lock index fe873382..f003581c 100644 --- a/flake.lock +++ b/flake.lock @@ -175,16 +175,16 @@ "holochain": { "flake": false, "locked": { - "lastModified": 1696380813, - "narHash": "sha256-C8lw4tShiaAQ00dAtP2t/m6NxQtYtE1J4giFP3/F0tA=", + "lastModified": 1703255365, + "narHash": "sha256-Rhez/oZISXBUt2nBdjFUv1c7435GEY5AyDQx5Kn47hk=", "owner": "holochain", "repo": "holochain", - "rev": "a9df0d68a0ddc85822e54368575bc0397ec7049a", + "rev": "ecb2576372a2bbc05985725e7f1558029871cc72", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.3.0-beta-dev.20", + "ref": "holochain-0.3.0-beta-dev.29", "repo": "holochain", "type": "github" } @@ -224,11 +224,11 @@ ] }, "locked": { - "lastModified": 1696728050, - "narHash": "sha256-xZURYSNnZ4AIVBH4c1qQ4uyNC+r7LF9fP8/Y0akNq8s=", + "lastModified": 1704891846, + "narHash": "sha256-kwI3iOEMzYez/W2CZ4Eu9nKMuqvk7sOPPcF9nNPADZI=", "owner": "holochain", "repo": "holochain", - "rev": "4993778514717dc4dcdf86d42c409935b7c0cdaf", + "rev": "f1b05c392dbe5e4195396eba384977fa9af26de8", "type": "github" }, "original": { @@ -257,16 +257,16 @@ "launcher": { "flake": false, "locked": { - "lastModified": 1684183666, - "narHash": "sha256-rOE/W/BBYyZKOyypKb8X9Vpc4ty1TNRoI/fV5+01JPw=", + "lastModified": 1701093498, + "narHash": "sha256-M9uaBXzHGxPFqmJjZKWpWmGCZdn02o0OHxZA32UBrJo=", "owner": "holochain", "repo": "launcher", - "rev": "75ecdd0aa191ed830cc209a984a6030e656042ff", + "rev": "ba2c48eb881368746048f392184c6b3e18ea4feb", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.2", + "ref": "holochain-weekly", "repo": "launcher", "type": "github" } @@ -288,11 +288,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1696604326, - "narHash": "sha256-YXUNI0kLEcI5g8lqGMb0nh67fY9f2YoJsILafh6zlMo=", + "lastModified": 1704538339, + "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "87828a0e03d1418e848d3dd3f3014a632e4a4f64", + "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", "type": "github" }, "original": { @@ -393,11 +393,11 @@ ] }, "locked": { - "lastModified": 1696644659, - "narHash": "sha256-l/DgT519At8HhXDQHz3+H8AjaEbrsb7Xkqgj+JNHV6k=", + "lastModified": 1704766659, + "narHash": "sha256-Q2wQ9jzp4j96HokmhUQey+pyZMp4Fye/ZPSLtBBV1J8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "126829788e99c188be4eeb805f144d73d8a00f2c", + "rev": "afa87241c19241aca9b7a9103635b82db2b147bb", "type": "github" }, "original": { @@ -447,11 +447,11 @@ }, "locked": { "dir": "versions/weekly", - "lastModified": 1696728050, - "narHash": "sha256-xZURYSNnZ4AIVBH4c1qQ4uyNC+r7LF9fP8/Y0akNq8s=", + "lastModified": 1704891846, + "narHash": "sha256-kwI3iOEMzYez/W2CZ4Eu9nKMuqvk7sOPPcF9nNPADZI=", "owner": "holochain", "repo": "holochain", - "rev": "4993778514717dc4dcdf86d42c409935b7c0cdaf", + "rev": "f1b05c392dbe5e4195396eba384977fa9af26de8", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index bd3b3f0f..e7c41b0e 100644 --- a/flake.nix +++ b/flake.nix @@ -13,16 +13,24 @@ outputs = inputs @ { ... }: inputs.holochain-flake.inputs.flake-parts.lib.mkFlake { inherit inputs; } - { + { systems = builtins.attrNames inputs.holochain-flake.devShells; perSystem = { config, pkgs, system, ... }: { - devShells.default = pkgs.mkShell { - inputsFrom = [ - inputs.holochain-flake.devShells.${system}.rustDev - ]; - packages = [ - ]; + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.libffi + pkgs.libxml2 + pkgs.llvmPackages_15.llvm + pkgs.ncurses + ]; + inputsFrom = [ + inputs.holochain-flake.devShells.${system}.rustDev + ]; + packages = [ ]; + env = pkgs.lib.optionalAttrs pkgs.stdenv.cc.isClang { + NIX_LDFLAGS = "-l${pkgs.stdenv.cc.libcxx.cxxabi.libName}"; }; + }; }; - }; + }; } diff --git a/test/Cargo.lock b/test/Cargo.lock index f7bc7276..44547d74 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.1", "rustc-demangle", ] @@ -804,6 +804,7 @@ dependencies = [ "serde", "tracing", "wasmer", + "wasmer-compiler-llvm", "wasmer-middlewares", ] @@ -864,6 +865,31 @@ dependencies = [ "hashbrown 0.14.1", ] +[[package]] +name = "inkwell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbac11e485159a525867fb7e6aa61981453e6a72f625fde6a4ab3047b0c6dec9" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "inkwell_internals" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "itertools" version = "0.10.5" @@ -912,6 +938,19 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +[[package]] +name = "llvm-sys" +version = "150.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd60e740af945d99c2446a52e3ab8cdba2f740a40a16c51f6871bdea2abc687" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver 1.0.19", +] + [[package]] name = "lock_api" version = "0.4.10" @@ -994,6 +1033,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.32.1" @@ -1356,6 +1404,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.19", +] + [[package]] name = "rustix" version = "0.38.21" @@ -2049,6 +2106,30 @@ dependencies = [ "wasmer-types", ] +[[package]] +name = "wasmer-compiler-llvm" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be3781663b4208c0b1abae4282f32bd1d355495e72e11989852ac70047552a31" +dependencies = [ + "byteorder", + "cc", + "inkwell", + "itertools", + "lazy_static", + "libc", + "object 0.28.4", + "rayon", + "regex", + "rustc_version", + "semver 1.0.19", + "smallvec", + "target-lexicon", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + [[package]] name = "wasmer-derive" version = "4.2.4" From 033be0337e520d05edd3b944d748bb4b5c0bb55f Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 13:56:15 -0600 Subject: [PATCH 15/24] revert switch to llvm --- crates/host/Cargo.toml | 1 - crates/host/src/module.rs | 8 +++----- flake.lock | 40 +++++++++++++++++++-------------------- flake.nix | 24 ++++++++--------------- test/src/wasms.rs | 3 +-- 5 files changed, 32 insertions(+), 44 deletions(-) diff --git a/crates/host/Cargo.toml b/crates/host/Cargo.toml index 06008398..d3b10814 100644 --- a/crates/host/Cargo.toml +++ b/crates/host/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" [dependencies] wasmer = "=4.2.4" wasmer-middlewares = "=4.2.4" -wasmer-compiler-llvm = "=4.2.4" holochain_wasmer_common = { version = "=0.0.90", path = "../common" } holochain_serialized_bytes = "=0.0.53" serde = "1" diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 121647c6..5619b692 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -28,7 +28,6 @@ use wasmer::NativeEngineExt; use wasmer::Store; use wasmer::Target; use wasmer::Triple; -use wasmer_compiler_llvm::LLVM; use wasmer_middlewares::Metering; /// We expect cache keys to be produced via hashing so 32 bytes is enough for all @@ -147,9 +146,8 @@ pub fn make_compiler_engine() -> Engine { // want to make the limit configurable somehow. let metering = Arc::new(Metering::new(WASM_METERING_LIMIT, cost_function)); // the only place where the wasm compiler engine is set - let mut compiler = LLVM::default(); - compiler.canonicalize_nans(true); - compiler.push_middleware(metering); + let mut compiler = Cranelift::default(); + compiler.canonicalize_nans(true).push_middleware(metering); Engine::from(compiler) } @@ -283,7 +281,7 @@ impl SerializedModuleCache { }) { Some(Ok(serialized_module)) => { let deserialized_module = - unsafe { Module::deserialize(&self.runtime_engine, serialized_module.clone()) } + unsafe { Module::deserialize(&Engine::default(), serialized_module.clone()) } .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; (deserialized_module, serialized_module) } diff --git a/flake.lock b/flake.lock index f003581c..fe873382 100644 --- a/flake.lock +++ b/flake.lock @@ -175,16 +175,16 @@ "holochain": { "flake": false, "locked": { - "lastModified": 1703255365, - "narHash": "sha256-Rhez/oZISXBUt2nBdjFUv1c7435GEY5AyDQx5Kn47hk=", + "lastModified": 1696380813, + "narHash": "sha256-C8lw4tShiaAQ00dAtP2t/m6NxQtYtE1J4giFP3/F0tA=", "owner": "holochain", "repo": "holochain", - "rev": "ecb2576372a2bbc05985725e7f1558029871cc72", + "rev": "a9df0d68a0ddc85822e54368575bc0397ec7049a", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.3.0-beta-dev.29", + "ref": "holochain-0.3.0-beta-dev.20", "repo": "holochain", "type": "github" } @@ -224,11 +224,11 @@ ] }, "locked": { - "lastModified": 1704891846, - "narHash": "sha256-kwI3iOEMzYez/W2CZ4Eu9nKMuqvk7sOPPcF9nNPADZI=", + "lastModified": 1696728050, + "narHash": "sha256-xZURYSNnZ4AIVBH4c1qQ4uyNC+r7LF9fP8/Y0akNq8s=", "owner": "holochain", "repo": "holochain", - "rev": "f1b05c392dbe5e4195396eba384977fa9af26de8", + "rev": "4993778514717dc4dcdf86d42c409935b7c0cdaf", "type": "github" }, "original": { @@ -257,16 +257,16 @@ "launcher": { "flake": false, "locked": { - "lastModified": 1701093498, - "narHash": "sha256-M9uaBXzHGxPFqmJjZKWpWmGCZdn02o0OHxZA32UBrJo=", + "lastModified": 1684183666, + "narHash": "sha256-rOE/W/BBYyZKOyypKb8X9Vpc4ty1TNRoI/fV5+01JPw=", "owner": "holochain", "repo": "launcher", - "rev": "ba2c48eb881368746048f392184c6b3e18ea4feb", + "rev": "75ecdd0aa191ed830cc209a984a6030e656042ff", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-weekly", + "ref": "holochain-0.2", "repo": "launcher", "type": "github" } @@ -288,11 +288,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704538339, - "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", + "lastModified": 1696604326, + "narHash": "sha256-YXUNI0kLEcI5g8lqGMb0nh67fY9f2YoJsILafh6zlMo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", + "rev": "87828a0e03d1418e848d3dd3f3014a632e4a4f64", "type": "github" }, "original": { @@ -393,11 +393,11 @@ ] }, "locked": { - "lastModified": 1704766659, - "narHash": "sha256-Q2wQ9jzp4j96HokmhUQey+pyZMp4Fye/ZPSLtBBV1J8=", + "lastModified": 1696644659, + "narHash": "sha256-l/DgT519At8HhXDQHz3+H8AjaEbrsb7Xkqgj+JNHV6k=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "afa87241c19241aca9b7a9103635b82db2b147bb", + "rev": "126829788e99c188be4eeb805f144d73d8a00f2c", "type": "github" }, "original": { @@ -447,11 +447,11 @@ }, "locked": { "dir": "versions/weekly", - "lastModified": 1704891846, - "narHash": "sha256-kwI3iOEMzYez/W2CZ4Eu9nKMuqvk7sOPPcF9nNPADZI=", + "lastModified": 1696728050, + "narHash": "sha256-xZURYSNnZ4AIVBH4c1qQ4uyNC+r7LF9fP8/Y0akNq8s=", "owner": "holochain", "repo": "holochain", - "rev": "f1b05c392dbe5e4195396eba384977fa9af26de8", + "rev": "4993778514717dc4dcdf86d42c409935b7c0cdaf", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e7c41b0e..bd3b3f0f 100644 --- a/flake.nix +++ b/flake.nix @@ -13,24 +13,16 @@ outputs = inputs @ { ... }: inputs.holochain-flake.inputs.flake-parts.lib.mkFlake { inherit inputs; } - { + { systems = builtins.attrNames inputs.holochain-flake.devShells; perSystem = { config, pkgs, system, ... }: { - devShells.default = pkgs.mkShell { - buildInputs = [ - pkgs.libffi - pkgs.libxml2 - pkgs.llvmPackages_15.llvm - pkgs.ncurses - ]; - inputsFrom = [ - inputs.holochain-flake.devShells.${system}.rustDev - ]; - packages = [ ]; - env = pkgs.lib.optionalAttrs pkgs.stdenv.cc.isClang { - NIX_LDFLAGS = "-l${pkgs.stdenv.cc.libcxx.cxxabi.libName}"; + devShells.default = pkgs.mkShell { + inputsFrom = [ + inputs.holochain-flake.devShells.${system}.rustDev + ]; + packages = [ + ]; }; - }; }; - }; + }; } diff --git a/test/src/wasms.rs b/test/src/wasms.rs index 9811e9bc..c431331a 100644 --- a/test/src/wasms.rs +++ b/test/src/wasms.rs @@ -120,9 +120,8 @@ impl TestWasm { pub fn _instance(&self, metered: bool) -> InstanceWithStore { let module = self.module(metered); - let function_env; let mut store = Store::default(); - function_env = FunctionEnv::new(&mut store, Env::default()); + let function_env = FunctionEnv::new(&mut store, Env::default()); let built_imports: Imports = imports(&mut store.as_store_mut(), &function_env); let instance = Instance::new(&mut store, &module, &built_imports).unwrap(); From b1e5afa31c2047041c131a8d4c2a412681bad516 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 15:48:04 -0600 Subject: [PATCH 16/24] chore: revert host crate version bump --- crates/host/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/host/Cargo.toml b/crates/host/Cargo.toml index d3b10814..bc437e17 100644 --- a/crates/host/Cargo.toml +++ b/crates/host/Cargo.toml @@ -2,7 +2,7 @@ name = "holochain_wasmer_host" description = "wasm host code" license = "Apache-2.0" -version = "0.0.91" +version = "0.0.90" authors = [ "thedavidmeister", "thedavidmeister@gmail.com" ] edition = "2021" From fb5444557e14873fa1a3a8e3352fbfa1164e63ad Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 15:51:16 -0600 Subject: [PATCH 17/24] test: fix concurrent calls --- test/src/test.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/src/test.rs b/test/src/test.rs index 9ba3adb3..158e9868 100644 --- a/test/src/test.rs +++ b/test/src/test.rs @@ -265,31 +265,27 @@ pub mod tests { let call_1 = thread::spawn({ let some_struct = some_struct.clone(); move || { - let result: SomeStruct = guest::call( + guest::call::<_, SomeStruct>( &mut store_1.lock().as_store_mut(), instance_1, "native_type", some_struct.clone(), ) - .expect("native type handling"); - assert_eq!(some_struct, result); } }); let call_2 = thread::spawn({ let some_struct = some_struct.clone(); move || { - let result: SomeStruct = guest::call( + guest::call::<_, SomeStruct>( &mut store_2.lock().as_store_mut(), instance_2, "native_type", some_struct.clone(), ) - .expect("native type handling"); - assert_eq!(some_struct, result); } }); - assert!(matches!(call_1.join(), Ok(()))); - assert!(matches!(call_2.join(), Ok(()))); + assert!(matches!(call_1.join(), Ok(SomeStruct))); + assert!(matches!(call_2.join(), Ok(SomeStruct))); } #[test] From 830dee7f4f235ceaf7f537cd6997ebbbf2a3be9d Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Wed, 10 Jan 2024 15:52:01 -0600 Subject: [PATCH 18/24] chore: add cargo lock --- test/Cargo.lock | 85 ++----------------------------------------------- 1 file changed, 2 insertions(+), 83 deletions(-) diff --git a/test/Cargo.lock b/test/Cargo.lock index 44547d74..b3c91cb5 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.32.1", + "object", "rustc-demangle", ] @@ -792,7 +792,7 @@ dependencies = [ [[package]] name = "holochain_wasmer_host" -version = "0.0.91" +version = "0.0.90" dependencies = [ "bimap", "bytes", @@ -804,7 +804,6 @@ dependencies = [ "serde", "tracing", "wasmer", - "wasmer-compiler-llvm", "wasmer-middlewares", ] @@ -865,31 +864,6 @@ dependencies = [ "hashbrown 0.14.1", ] -[[package]] -name = "inkwell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbac11e485159a525867fb7e6aa61981453e6a72f625fde6a4ab3047b0c6dec9" -dependencies = [ - "either", - "inkwell_internals", - "libc", - "llvm-sys", - "once_cell", - "parking_lot", -] - -[[package]] -name = "inkwell_internals" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "itertools" version = "0.10.5" @@ -938,19 +912,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" -[[package]] -name = "llvm-sys" -version = "150.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd60e740af945d99c2446a52e3ab8cdba2f740a40a16c51f6871bdea2abc687" -dependencies = [ - "cc", - "lazy_static", - "libc", - "regex", - "semver 1.0.19", -] - [[package]] name = "lock_api" version = "0.4.10" @@ -1033,15 +994,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.32.1" @@ -1404,15 +1356,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.19", -] - [[package]] name = "rustix" version = "0.38.21" @@ -2106,30 +2049,6 @@ dependencies = [ "wasmer-types", ] -[[package]] -name = "wasmer-compiler-llvm" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be3781663b4208c0b1abae4282f32bd1d355495e72e11989852ac70047552a31" -dependencies = [ - "byteorder", - "cc", - "inkwell", - "itertools", - "lazy_static", - "libc", - "object 0.28.4", - "rayon", - "regex", - "rustc_version", - "semver 1.0.19", - "smallvec", - "target-lexicon", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - [[package]] name = "wasmer-derive" version = "4.2.4" From 00ac4ab8f1d5e4a7dea73a341064f86b2d956487 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Thu, 11 Jan 2024 10:48:14 -0600 Subject: [PATCH 19/24] revert some formatting --- test/src/wasms.rs | 82 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/test/src/wasms.rs b/test/src/wasms.rs index c431331a..4605dc79 100644 --- a/test/src/wasms.rs +++ b/test/src/wasms.rs @@ -4,17 +4,17 @@ use holochain_wasmer_host::module::SerializedModuleCache; use holochain_wasmer_host::prelude::*; use once_cell::sync::OnceCell; use parking_lot::RwLock; +use wasmer::Engine; +use wasmer::Module; +use wasmer::Store; use std::sync::Arc; use wasmer::wasmparser::Operator; use wasmer::AsStoreMut; use wasmer::CompilerConfig; use wasmer::Cranelift; -use wasmer::Engine; use wasmer::FunctionEnv; use wasmer::Imports; use wasmer::Instance; -use wasmer::Module; -use wasmer::Store; use wasmer_middlewares::Metering; pub enum TestWasm { @@ -102,14 +102,12 @@ impl TestWasm { // which could happen if two tests are running in parallel. // It doesn't matter which one wins, so we just ignore the error. let _did_init_ok = self.module_cache(metered).set(parking_lot::RwLock::new( - SerializedModuleCache::default_with_engine( - if metered { - cranelift_fn - } else { - cranelift_fn_unmetered - }, - None, - ), + SerializedModuleCache::default_with_engine(if metered { + cranelift_fn + } else { + cranelift_fn_unmetered + }, + None), )); // Just recurse now that the cache is initialized. @@ -121,40 +119,48 @@ impl TestWasm { pub fn _instance(&self, metered: bool) -> InstanceWithStore { let module = self.module(metered); let mut store = Store::default(); - let function_env = FunctionEnv::new(&mut store, Env::default()); - let built_imports: Imports = imports(&mut store.as_store_mut(), &function_env); - let instance = Instance::new(&mut store, &module, &built_imports).unwrap(); + let function_env; + let instance; + { + let mut store_mut = store.as_store_mut(); + function_env = FunctionEnv::new(&mut store_mut, Env::default()); + let built_imports: Imports = imports(&mut store_mut, &function_env); + instance = + Instance::new(&mut store_mut, &module, &built_imports).unwrap(); + } - let mut function_env_mut = function_env.into_mut(&mut store); - let (data_mut, store_mut) = function_env_mut.data_and_store_mut(); - data_mut.memory = Some(instance.exports.get_memory("memory").unwrap().clone()); - data_mut.deallocate = Some( - instance - .exports - .get_typed_function(&store_mut, "__hc__deallocate_1") - .unwrap(), - ); - data_mut.allocate = Some( - instance - .exports - .get_typed_function(&store_mut, "__hc__allocate_1") - .unwrap(), - ); - if metered { - data_mut.wasmer_metering_points_exhausted = Some( + { + let mut function_env_mut = function_env.into_mut(&mut store); + let (data_mut, store_mut) = function_env_mut.data_and_store_mut(); + data_mut.memory = Some(instance.exports.get_memory("memory").unwrap().clone()); + data_mut.deallocate = Some( instance .exports - .get_global("wasmer_metering_points_exhausted") - .unwrap() - .clone(), + .get_typed_function(&store_mut, "__hc__deallocate_1") + .unwrap(), ); - data_mut.wasmer_metering_remaining_points = Some( + data_mut.allocate = Some( instance .exports - .get_global("wasmer_metering_remaining_points") - .unwrap() - .clone(), + .get_typed_function(&store_mut, "__hc__allocate_1") + .unwrap(), ); + if metered { + data_mut.wasmer_metering_points_exhausted = Some( + instance + .exports + .get_global("wasmer_metering_points_exhausted") + .unwrap() + .clone(), + ); + data_mut.wasmer_metering_remaining_points = Some( + instance + .exports + .get_global("wasmer_metering_remaining_points") + .unwrap() + .clone(), + ); + } } InstanceWithStore { From 0047757939beb4d34372ecc7b8ab4d2737f885a2 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Thu, 11 Jan 2024 10:50:21 -0600 Subject: [PATCH 20/24] comment bench --- test/benches/bench.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/benches/bench.rs b/test/benches/bench.rs index 467996bf..9a76390e 100644 --- a/test/benches/bench.rs +++ b/test/benches/bench.rs @@ -276,6 +276,11 @@ criterion_group!( benches, wasm_module_compile, wasm_module_deserialize_from_file, + // currently the bench fails because such numerous deserialization of modules causes memory leaks + // because of an upstream issue where the memory for deserialization is kept as long as the engine lives + // https://github.com/wasmerio/wasmer/issues/4377#issuecomment-1879386384 + // this shouldn't affect Holochain in practice because we're only deserializing every module once. + // wasm_module, // wasm_instance, wasm_call, From 307fe6ae7a3df61bc50c3dc16b5889a62dd7ba99 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Thu, 11 Jan 2024 10:58:21 -0600 Subject: [PATCH 21/24] update docs --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4116f5..bfc558f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] -- Refactor: Instance cache in host crate has been removed in favor of a deserialized module cache `DeserializedModuleCache`. An abstraction for caching (serialized & deserialized modules) called `ModuleCache` was added. +- **BREAKING CHANGE:** Instance cache in host crate has been removed in favor of a deserialized module cache `DeserializedModuleCache`. An abstraction for caching (serialized & deserialized modules) called `ModuleCache` was added. - Refactor: All logic related to modules and wasmer caching from `holochain` has been moved to the host crate. Consequently functions for wasmer development under iOS need to be imported from there. ## [0.0.90] From 5183d212c1d8ceb649f11ef24e227fc18898d4a8 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Thu, 11 Jan 2024 11:53:45 -0600 Subject: [PATCH 22/24] fix: use cache's runtime engine --- crates/host/src/module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index 5619b692..d8f2f929 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -281,7 +281,7 @@ impl SerializedModuleCache { }) { Some(Ok(serialized_module)) => { let deserialized_module = - unsafe { Module::deserialize(&Engine::default(), serialized_module.clone()) } + unsafe { Module::deserialize(&self.runtime_engine, serialized_module.clone()) } .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; (deserialized_module, serialized_module) } From e22e5620b0b230d9328279c49ef057bc57fa4202 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Thu, 11 Jan 2024 11:56:42 -0600 Subject: [PATCH 23/24] bring back ios workaround --- crates/host/src/module.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index d8f2f929..ce7eb451 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -16,6 +16,7 @@ use std::str::FromStr; use std::sync::Arc; use tracing::info; use wasmer::wasmparser; +use wasmer::BaseTunables; use wasmer::CompileError; use wasmer::CompilerConfig; use wasmer::CpuFeature; @@ -291,7 +292,14 @@ impl SerializedModuleCache { // Each module needs to be compiled with a new engine because // of middleware like metering. Middleware is compiled into the // module once and available in all instances created from it. - let compiler_engine = (self.make_compiler_engine)(); + let mut compiler_engine = (self.make_compiler_engine)(); + // Workaround for invalid memory access on iOS. + // https://github.com/holochain/holochain/issues/3096 + compiler_engine.set_tunables(BaseTunables { + static_memory_bound: 0x4000.into(), + static_memory_offset_guard_size: 0x1_0000, + dynamic_memory_offset_guard_size: 0x1_0000, + }); let module = Module::from_binary(&compiler_engine, wasm) .map_err(|e| wasm_error!(WasmErrorInner::Compile(e.to_string())))?; let serialized_module = module From 80c7c241d8915a67e1fccd715e0fd6c879d92d54 Mon Sep 17 00:00:00 2001 From: Jost Schulte Date: Tue, 16 Jan 2024 10:30:39 -0600 Subject: [PATCH 24/24] test: put module test behind test cfg --- crates/host/src/module.rs | 89 +++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/crates/host/src/module.rs b/crates/host/src/module.rs index ce7eb451..40964d26 100644 --- a/crates/host/src/module.rs +++ b/crates/host/src/module.rs @@ -448,46 +448,53 @@ impl ModuleCache { } } -#[test] -fn cache_test() { - // simple example wasm taken from wasmer docs - // https://docs.rs/wasmer/latest/wasmer/struct.Module.html#example - let wasm: Vec = vec![ - 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, 0x01, - 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, - 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, - 0x1a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, - 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, - ]; - let module_cache = ModuleCache::new(None); - assert_eq!( - module_cache.serialized_module_cache.read().cache.is_empty(), - true - ); - assert_eq!( - module_cache - .deserialized_module_cache - .read() - .cache - .is_empty(), - true - ); - - let key: CacheKey = [0u8; 32].into(); - let module = module_cache.get(key.clone(), &wasm).unwrap(); - - // make sure module has been stored in serialized cache under key - { - let serialized_cached_module = module_cache.serialized_module_cache.write().get_item(&key); - assert_eq!(matches!(serialized_cached_module, Some(_)), true); - } - // make sure module has been stored in deserialized cache under key - { - let deserialized_cached_module = module_cache - .deserialized_module_cache - .write() - .get_item(&key) - .unwrap(); - assert_eq!(*deserialized_cached_module, *module); +#[cfg(test)] +pub mod tests { + use crate::module::{CacheKey, ModuleCache, PlruCache}; + + #[test] + fn cache_test() { + // simple example wasm taken from wasmer docs + // https://docs.rs/wasmer/latest/wasmer/struct.Module.html#example + let wasm: Vec = vec![ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, + 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, 0x61, 0x64, 0x64, 0x5f, + 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, + 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, + 0x70, 0x30, + ]; + let module_cache = ModuleCache::new(None); + assert_eq!( + module_cache.serialized_module_cache.read().cache.is_empty(), + true + ); + assert_eq!( + module_cache + .deserialized_module_cache + .read() + .cache + .is_empty(), + true + ); + + let key: CacheKey = [0u8; 32].into(); + let module = module_cache.get(key.clone(), &wasm).unwrap(); + + // make sure module has been stored in serialized cache under key + { + let serialized_cached_module = + module_cache.serialized_module_cache.write().get_item(&key); + assert_eq!(matches!(serialized_cached_module, Some(_)), true); + } + // make sure module has been stored in deserialized cache under key + { + let deserialized_cached_module = module_cache + .deserialized_module_cache + .write() + .get_item(&key) + .unwrap(); + assert_eq!(*deserialized_cached_module, *module); + } } }