diff --git a/Cargo.lock b/Cargo.lock index 0b8216ff97..dbec032ef7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1976,6 +1976,7 @@ dependencies = [ "region", "rustc-demangle", "target-lexicon", + "tempfile", "wasi-common", "wasmparser 0.51.2", "wasmtime-environ", diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index fa7f29d996..ece18d5837 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -34,6 +34,7 @@ pretty_env_logger = "0.3.0" rayon = "1.2.1" file-per-thread-logger = "0.1.1" wat = "1.0" +tempfile = "3.1" [badges] maintenance = { status = "actively-developed" } diff --git a/crates/api/src/frame_info.rs b/crates/api/src/frame_info.rs index 45b050e7ca..5e0ea13331 100644 --- a/crates/api/src/frame_info.rs +++ b/crates/api/src/frame_info.rs @@ -69,7 +69,7 @@ impl GlobalFrameInfo { if end > max { max = end; } - let func_index = module.module().func_index(i); + let func_index = module.module().local.func_index(i); assert!(functions.insert(end, (start, func_index)).is_none()); } if functions.len() == 0 { diff --git a/crates/api/src/runtime.rs b/crates/api/src/runtime.rs index f367328fa3..25aa3f6a58 100644 --- a/crates/api/src/runtime.rs +++ b/crates/api/src/runtime.rs @@ -507,3 +507,71 @@ fn _assert_send_sync() { _assert::(); _assert::(); } + +#[cfg(test)] +mod tests { + use super::*; + use crate::Module; + use tempfile::TempDir; + + #[test] + fn cache_accounts_for_opt_level() -> Result<()> { + let td = TempDir::new()?; + let config_path = td.path().join("config.toml"); + std::fs::write( + &config_path, + &format!( + " + [cache] + enabled = true + directory = '{}' + ", + td.path().join("cache").display() + ), + )?; + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::None) + .cache_config_load(&config_path)?; + let store = Store::new(&Engine::new(&cfg)); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 0); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 1); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::Speed) + .cache_config_load(&config_path)?; + let store = Store::new(&Engine::new(&cfg)); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 0); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 1); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::SpeedAndSize) + .cache_config_load(&config_path)?; + let store = Store::new(&Engine::new(&cfg)); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 0); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 1); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.debug_info(true).cache_config_load(&config_path)?; + let store = Store::new(&Engine::new(&cfg)); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 0); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + Module::new(&store, "(module (func))")?; + assert_eq!(store.engine().config.cache_config.cache_hits(), 1); + assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + + Ok(()) + } +} diff --git a/crates/api/src/trampoline/create_handle.rs b/crates/api/src/trampoline/create_handle.rs index 86bca33876..ea87ee394d 100644 --- a/crates/api/src/trampoline/create_handle.rs +++ b/crates/api/src/trampoline/create_handle.rs @@ -27,6 +27,7 @@ pub(crate) fn create_handle( // Compute indices into the shared signature table. let signatures = module + .local .signatures .values() .map(|sig| store.compiler().signatures().register(sig)) diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs index c76dbc49b7..83753be403 100644 --- a/crates/api/src/trampoline/func.rs +++ b/crates/api/src/trampoline/func.rs @@ -81,7 +81,8 @@ unsafe extern "C" fn stub_fn( let (args, returns_len) = { let module = instance.module_ref(); - let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]]; + let signature = + &module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]]; let mut args = Vec::new(); for i in 2..signature.params.len() { @@ -101,7 +102,8 @@ unsafe extern "C" fn stub_fn( state.func.call(&args, &mut returns)?; let module = instance.module_ref(); - let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]]; + let signature = + &module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]]; for (i, ret) in returns.iter_mut().enumerate() { if ret.ty().get_wasmtime_type() != Some(signature.returns[i].value_type) { return Err(Trap::new( @@ -266,8 +268,8 @@ pub fn create_handle_with_function( PrimaryMap::new(); let mut code_memory = CodeMemory::new(); - let sig_id = module.signatures.push(sig.clone()); - let func_id = module.functions.push(sig_id); + let sig_id = module.local.signatures.push(sig.clone()); + let func_id = module.local.functions.push(sig_id); module .exports .insert("trampoline".to_string(), Export::Function(func_id)); @@ -313,8 +315,8 @@ pub unsafe fn create_handle_with_raw_function( let mut module = Module::new(); let mut finished_functions = PrimaryMap::new(); - let sig_id = module.signatures.push(sig.clone()); - let func_id = module.functions.push(sig_id); + let sig_id = module.local.signatures.push(sig.clone()); + let func_id = module.local.functions.push(sig_id); module .exports .insert("trampoline".to_string(), Export::Function(func_id)); diff --git a/crates/api/src/trampoline/global.rs b/crates/api/src/trampoline/global.rs index 8820749a2e..ad87386a3e 100644 --- a/crates/api/src/trampoline/global.rs +++ b/crates/api/src/trampoline/global.rs @@ -25,7 +25,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result Result Result String::from_utf8(output.stdout).unwrap(), - Err(_) => String::from("git-not-found"), + Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(), + Err(_) => env!("CARGO_PKG_VERSION").to_string(), }; println!("cargo:rustc-env=GIT_REV={}", git_rev); } diff --git a/crates/environ/src/cache.rs b/crates/environ/src/cache.rs index a8258054d7..290a54033f 100644 --- a/crates/environ/src/cache.rs +++ b/crates/environ/src/cache.rs @@ -1,14 +1,13 @@ use crate::address_map::{ModuleAddressMap, ValueLabelsRanges}; use crate::compilation::{Compilation, Relocations, Traps}; -use crate::module::Module; -use crate::module_environ::FunctionBodyData; -use cranelift_codegen::{ir, isa}; +use cranelift_codegen::ir; use cranelift_entity::PrimaryMap; use cranelift_wasm::DefinedFuncIndex; use log::{debug, trace, warn}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::fs; +use std::hash::Hash; use std::hash::Hasher; use std::io::Write; use std::path::{Path, PathBuf}; @@ -23,7 +22,7 @@ use worker::Worker; pub struct ModuleCacheEntry<'config>(Option>); struct ModuleCacheEntryInner<'config> { - mod_cache_path: PathBuf, + root_path: PathBuf, cache_config: &'config CacheConfig, } @@ -51,21 +50,10 @@ pub type ModuleCacheDataTupleType = ( struct Sha256Hasher(Sha256); impl<'config> ModuleCacheEntry<'config> { - pub fn new<'data>( - module: &Module, - function_body_inputs: &PrimaryMap>, - isa: &dyn isa::TargetIsa, - compiler_name: &str, - generate_debug_info: bool, - cache_config: &'config CacheConfig, - ) -> Self { + pub fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { if cache_config.enabled() { Self(Some(ModuleCacheEntryInner::new( - module, - function_body_inputs, - isa, compiler_name, - generate_debug_info, cache_config, ))) } else { @@ -78,42 +66,47 @@ impl<'config> ModuleCacheEntry<'config> { Self(Some(inner)) } - pub fn get_data(&self) -> Option { - if let Some(inner) = &self.0 { - inner.get_data().map(|val| { - inner - .cache_config - .worker() - .on_cache_get_async(&inner.mod_cache_path); // call on success - val - }) - } else { - None - } - } + pub fn get_data( + &self, + state: T, + compute: fn(T) -> Result, + ) -> Result { + let mut hasher = Sha256Hasher(Sha256::new()); + state.hash(&mut hasher); + let hash: [u8; 32] = hasher.0.result().into(); + // standard encoding uses '/' which can't be used for filename + let hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD); - pub fn update_data(&self, data: &ModuleCacheData) { - if let Some(inner) = &self.0 { - if inner.update_data(data).is_some() { - inner - .cache_config - .worker() - .on_cache_update_async(&inner.mod_cache_path); // call on success - } + let inner = match &self.0 { + Some(inner) => inner, + None => return compute(state).map(ModuleCacheData::from_tuple), + }; + + if let Some(cached_val) = inner.get_data(&hash) { + let mod_cache_path = inner.root_path.join(&hash); + inner.cache_config.on_cache_get_async(&mod_cache_path); // call on success + return Ok(cached_val); } + let val_to_cache = ModuleCacheData::from_tuple(compute(state)?); + if inner.update_data(&hash, &val_to_cache).is_some() { + let mod_cache_path = inner.root_path.join(&hash); + inner.cache_config.on_cache_update_async(&mod_cache_path); // call on success + } + Ok(val_to_cache) } } impl<'config> ModuleCacheEntryInner<'config> { - fn new<'data>( - module: &Module, - function_body_inputs: &PrimaryMap>, - isa: &dyn isa::TargetIsa, - compiler_name: &str, - generate_debug_info: bool, - cache_config: &'config CacheConfig, - ) -> Self { - let hash = Sha256Hasher::digest(module, function_body_inputs); + fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { + // If debug assertions are enabled then assume that we're some sort of + // local build. We don't want local builds to stomp over caches between + // builds, so just use a separate cache directory based on the mtime of + // our executable, which should roughly correlate with "you changed the + // source code so you get a different directory". + // + // Otherwise if this is a release build we use the `GIT_REV` env var + // which is either the git rev if installed from git or the crate + // version if installed from crates.io. let compiler_dir = if cfg!(debug_assertions) { fn self_mtime() -> Option { let path = std::env::current_exe().ok()?; @@ -138,26 +131,18 @@ impl<'config> ModuleCacheEntryInner<'config> { comp_ver = env!("GIT_REV"), ) }; - let mod_filename = format!( - "mod-{mod_hash}{mod_dbg}", - mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename - mod_dbg = if generate_debug_info { ".d" } else { "" }, - ); - let mod_cache_path = cache_config - .directory() - .join(isa.triple().to_string()) - .join(compiler_dir) - .join(mod_filename); + let root_path = cache_config.directory().join("modules").join(compiler_dir); Self { - mod_cache_path, + root_path, cache_config, } } - fn get_data(&self) -> Option { - trace!("get_data() for path: {}", self.mod_cache_path.display()); - let compressed_cache_bytes = fs::read(&self.mod_cache_path).ok()?; + fn get_data(&self, hash: &str) -> Option { + let mod_cache_path = self.root_path.join(hash); + trace!("get_data() for path: {}", mod_cache_path.display()); + let compressed_cache_bytes = fs::read(&mod_cache_path).ok()?; let cache_bytes = zstd::decode_all(&compressed_cache_bytes[..]) .map_err(|err| warn!("Failed to decompress cached code: {}", err)) .ok()?; @@ -166,8 +151,9 @@ impl<'config> ModuleCacheEntryInner<'config> { .ok() } - fn update_data(&self, data: &ModuleCacheData) -> Option<()> { - trace!("update_data() for path: {}", self.mod_cache_path.display()); + fn update_data(&self, hash: &str, data: &ModuleCacheData) -> Option<()> { + let mod_cache_path = self.root_path.join(hash); + trace!("update_data() for path: {}", mod_cache_path.display()); let serialized_data = bincode::serialize(&data) .map_err(|err| warn!("Failed to serialize cached code: {}", err)) .ok()?; @@ -180,17 +166,17 @@ impl<'config> ModuleCacheEntryInner<'config> { // Optimize syscalls: first, try writing to disk. It should succeed in most cases. // Otherwise, try creating the cache directory and retry writing to the file. - if fs_write_atomic(&self.mod_cache_path, "mod", &compressed_data) { + if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) { return Some(()); } debug!( "Attempting to create the cache directory, because \ failed to write cached code to disk, path: {}", - self.mod_cache_path.display(), + mod_cache_path.display(), ); - let cache_dir = self.mod_cache_path.parent().unwrap(); + let cache_dir = mod_cache_path.parent().unwrap(); fs::create_dir_all(cache_dir) .map_err(|err| { warn!( @@ -201,7 +187,7 @@ impl<'config> ModuleCacheEntryInner<'config> { }) .ok()?; - if fs_write_atomic(&self.mod_cache_path, "mod", &compressed_data) { + if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) { Some(()) } else { None @@ -233,17 +219,6 @@ impl ModuleCacheData { } } -impl Sha256Hasher { - pub fn digest<'data>( - module: &Module, - function_body_inputs: &PrimaryMap>, - ) -> [u8; 32] { - let mut hasher = Self(Sha256::new()); - module.hash_for_cache(function_body_inputs, &mut hasher); - hasher.0.result().into() - } -} - impl Hasher for Sha256Hasher { fn finish(&self) -> u64 { panic!("Sha256Hasher doesn't support finish!"); diff --git a/crates/environ/src/cache/config.rs b/crates/environ/src/cache/config.rs index 7686d187cd..67fd00f6d8 100644 --- a/crates/environ/src/cache/config.rs +++ b/crates/environ/src/cache/config.rs @@ -11,6 +11,8 @@ use serde::{ use std::fmt::Debug; use std::fs; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; use std::time::Duration; // wrapped, so we have named section in config, @@ -88,6 +90,14 @@ pub struct CacheConfig { #[serde(skip)] worker: Option, + #[serde(skip)] + state: Arc, +} + +#[derive(Default, Debug)] +struct CacheState { + hits: AtomicUsize, + misses: AtomicUsize, } /// Creates a new configuration file at specified path, or default path if None is passed. @@ -324,6 +334,7 @@ impl CacheConfig { file_count_limit_percent_if_deleting: None, files_total_size_limit_percent_if_deleting: None, worker: None, + state: Arc::new(CacheState::default()), } } @@ -366,6 +377,26 @@ impl CacheConfig { self.worker.as_ref().unwrap() } + /// Returns the number of cache hits seen so far + pub fn cache_hits(&self) -> usize { + self.state.hits.load(SeqCst) + } + + /// Returns the number of cache misses seen so far + pub fn cache_misses(&self) -> usize { + self.state.misses.load(SeqCst) + } + + pub(crate) fn on_cache_get_async(&self, path: impl AsRef) { + self.state.hits.fetch_add(1, SeqCst); + self.worker().on_cache_get_async(path) + } + + pub(crate) fn on_cache_update_async(&self, path: impl AsRef) { + self.state.misses.fetch_add(1, SeqCst); + self.worker().on_cache_update_async(path) + } + fn load_and_parse_file(config_file: Option<&Path>) -> Result { // get config file path let (config_file, user_custom_file) = match config_file { diff --git a/crates/environ/src/cache/tests.rs b/crates/environ/src/cache/tests.rs index 1d80415514..d454371178 100644 --- a/crates/environ/src/cache/tests.rs +++ b/crates/environ/src/cache/tests.rs @@ -1,20 +1,7 @@ use super::config::tests::test_prolog; use super::*; -use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; -use crate::compilation::{ - CompiledFunction, CompiledFunctionUnwindInfo, Relocation, RelocationTarget, TrapInformation, -}; -use crate::module::{MemoryPlan, MemoryStyle, Module}; -use cranelift_codegen::{binemit, ir, isa, settings, ValueLocRange}; -use cranelift_entity::EntityRef; -use cranelift_entity::{PrimaryMap, SecondaryMap}; -use cranelift_wasm::{DefinedFuncIndex, FuncIndex, Global, GlobalInit, Memory, SignatureIndex}; -use rand::rngs::SmallRng; -use rand::{Rng, SeedableRng}; -use std::cmp::min; +use cranelift_entity::PrimaryMap; use std::fs; -use std::str::FromStr; -use target_lexicon::triple; // Since cache system is a global thing, each test needs to be run in seperate process. // So, init() tests are run as integration tests. @@ -73,272 +60,45 @@ fn test_write_read_cache() { fs::canonicalize(cache_dir).unwrap() ); - let mut rng = SmallRng::from_seed([ - 0x42, 0x04, 0xF3, 0x44, 0x11, 0x22, 0x33, 0x44, 0x67, 0x68, 0xFF, 0x00, 0x44, 0x23, 0x7F, - 0x96, - ]); - - let mut code_container = Vec::new(); - code_container.resize(0x4000, 0); - rng.fill(&mut code_container[..]); - - let isa1 = new_isa("riscv64-unknown-unknown"); - let isa2 = new_isa("i386"); - let module1 = new_module(&mut rng); - let module2 = new_module(&mut rng); - let function_body_inputs1 = new_function_body_inputs(&mut rng, &code_container); - let function_body_inputs2 = new_function_body_inputs(&mut rng, &code_container); let compiler1 = "test-1"; let compiler2 = "test-2"; - let entry1 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new( - &module1, - &function_body_inputs1, - &*isa1, - compiler1, - false, - &cache_config, - )); - assert!(entry1.0.is_some()); - assert!(entry1.get_data().is_none()); - let data1 = new_module_cache_data(&mut rng); - entry1.update_data(&data1); - assert_eq!(entry1.get_data().expect("Cache should be available"), data1); + let entry1 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler1, &cache_config)); + let entry2 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler2, &cache_config)); - let entry2 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new( - &module2, - &function_body_inputs1, - &*isa1, - compiler1, - false, - &cache_config, - )); - let data2 = new_module_cache_data(&mut rng); - entry2.update_data(&data2); - assert_eq!(entry1.get_data().expect("Cache should be available"), data1); - assert_eq!(entry2.get_data().expect("Cache should be available"), data2); + entry1.get_data(1, |_| new_module_cache_data()).unwrap(); + entry1.get_data::<_, i32>(1, |_| panic!()).unwrap(); - let entry3 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new( - &module1, - &function_body_inputs2, - &*isa1, - compiler1, - false, - &cache_config, - )); - let data3 = new_module_cache_data(&mut rng); - entry3.update_data(&data3); - assert_eq!(entry1.get_data().expect("Cache should be available"), data1); - assert_eq!(entry2.get_data().expect("Cache should be available"), data2); - assert_eq!(entry3.get_data().expect("Cache should be available"), data3); + entry1.get_data(2, |_| new_module_cache_data()).unwrap(); + entry1.get_data::<_, i32>(1, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(2, |_| panic!()).unwrap(); - let entry4 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new( - &module1, - &function_body_inputs1, - &*isa2, - compiler1, - false, - &cache_config, - )); - let data4 = new_module_cache_data(&mut rng); - entry4.update_data(&data4); - assert_eq!(entry1.get_data().expect("Cache should be available"), data1); - assert_eq!(entry2.get_data().expect("Cache should be available"), data2); - assert_eq!(entry3.get_data().expect("Cache should be available"), data3); - assert_eq!(entry4.get_data().expect("Cache should be available"), data4); + entry1.get_data(3, |_| new_module_cache_data()).unwrap(); + entry1.get_data::<_, i32>(1, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(2, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(3, |_| panic!()).unwrap(); - let entry5 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new( - &module1, - &function_body_inputs1, - &*isa1, - compiler2, - false, - &cache_config, - )); - let data5 = new_module_cache_data(&mut rng); - entry5.update_data(&data5); - assert_eq!(entry1.get_data().expect("Cache should be available"), data1); - assert_eq!(entry2.get_data().expect("Cache should be available"), data2); - assert_eq!(entry3.get_data().expect("Cache should be available"), data3); - assert_eq!(entry4.get_data().expect("Cache should be available"), data4); - assert_eq!(entry5.get_data().expect("Cache should be available"), data5); + entry1.get_data(4, |_| new_module_cache_data()).unwrap(); + entry1.get_data::<_, i32>(1, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(2, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(3, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(4, |_| panic!()).unwrap(); - let data6 = new_module_cache_data(&mut rng); - entry1.update_data(&data6); - assert_eq!(entry1.get_data().expect("Cache should be available"), data6); - assert_eq!(entry2.get_data().expect("Cache should be available"), data2); - assert_eq!(entry3.get_data().expect("Cache should be available"), data3); - assert_eq!(entry4.get_data().expect("Cache should be available"), data4); - assert_eq!(entry5.get_data().expect("Cache should be available"), data5); - - assert!(data1 != data2 && data1 != data3 && data1 != data4 && data1 != data5 && data1 != data6); + entry2.get_data(1, |_| new_module_cache_data()).unwrap(); + entry1.get_data::<_, i32>(1, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(2, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(3, |_| panic!()).unwrap(); + entry1.get_data::<_, i32>(4, |_| panic!()).unwrap(); + entry2.get_data::<_, i32>(1, |_| panic!()).unwrap(); } -fn new_isa(name: &str) -> Box { - let shared_builder = settings::builder(); - let shared_flags = settings::Flags::new(shared_builder); - isa::lookup(triple!(name)) - .expect("can't find specified isa") - .finish(shared_flags) -} - -fn new_module(rng: &mut impl Rng) -> Module { - // There are way too many fields. Just fill in some of them. - let mut m = Module::new(); - - if rng.gen_bool(0.5) { - m.signatures.push(ir::Signature { - params: vec![], - returns: vec![], - call_conv: isa::CallConv::Fast, - }); - } - - for i in 0..rng.gen_range(1, 0x8) { - m.functions.push(SignatureIndex::new(i)); - } - - if rng.gen_bool(0.8) { - m.memory_plans.push(MemoryPlan { - memory: Memory { - minimum: rng.gen(), - maximum: rng.gen(), - shared: rng.gen(), - }, - style: MemoryStyle::Dynamic, - offset_guard_size: rng.gen(), - }); - } - - if rng.gen_bool(0.4) { - m.globals.push(Global { - ty: ir::Type::int(16).unwrap(), - mutability: rng.gen(), - initializer: GlobalInit::I32Const(rng.gen()), - }); - } - - m -} - -fn new_function_body_inputs<'data>( - rng: &mut impl Rng, - code_container: &'data Vec, -) -> PrimaryMap> { - let len = code_container.len(); - let mut pos = rng.gen_range(0, code_container.len()); - (2..rng.gen_range(4, 14)) - .map(|j| { - let (old_pos, end) = (pos, min(pos + rng.gen_range(0x10, 0x200), len)); - pos = end % len; - FunctionBodyData { - data: &code_container[old_pos..end], - module_offset: (rng.next_u64() + j) as usize, - } - }) - .collect() -} - -fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData { - let funcs = (0..rng.gen_range(0, 10)) - .map(|i| { - let mut sm = SecondaryMap::new(); // doesn't implement from iterator - sm.resize(i as usize * 2); - sm.values_mut().enumerate().for_each(|(j, v)| { - if rng.gen_bool(0.33) { - *v = (j as u32) * 3 / 4 - } - }); - CompiledFunction { - body: (0..(i * 3 / 2)).collect(), - jt_offsets: sm, - unwind_info: CompiledFunctionUnwindInfo::Windows((0..(i * 3 / 2)).collect()), - } - }) - .collect(); - - let relocs = (0..rng.gen_range(1, 0x10)) - .map(|i| { - vec![ - Relocation { - reloc: binemit::Reloc::X86CallPCRel4, - reloc_target: RelocationTarget::UserFunc(FuncIndex::new(i as usize * 42)), - offset: i + rng.next_u32(), - addend: 0, - }, - Relocation { - reloc: binemit::Reloc::Arm32Call, - reloc_target: RelocationTarget::LibCall(ir::LibCall::CeilF64), - offset: rng.gen_range(4, i + 55), - addend: (42 * i) as i64, - }, - ] - }) - .collect(); - - let trans = (4..rng.gen_range(4, 0x10)) - .map(|i| FunctionAddressMap { - instructions: vec![InstructionAddressMap { - srcloc: ir::SourceLoc::new(rng.gen()), - code_offset: rng.gen(), - code_len: i, - }], - start_srcloc: ir::SourceLoc::new(rng.gen()), - end_srcloc: ir::SourceLoc::new(rng.gen()), - body_offset: rng.gen(), - body_len: 0x31337, - }) - .collect(); - - let value_ranges = (4..rng.gen_range(4, 0x10)) - .map(|i| { - (i..i + rng.gen_range(4, 8)) - .map(|k| { - ( - ir::ValueLabel::new(k), - (0..rng.gen_range(0, 4)) - .map(|_| ValueLocRange { - loc: ir::ValueLoc::Reg(rng.gen()), - start: rng.gen(), - end: rng.gen(), - }) - .collect(), - ) - }) - .collect() - }) - .collect(); - - let stack_slots = (0..rng.gen_range(0, 0x6)) - .map(|_| { - let mut slots = ir::StackSlots::new(); - slots.push(ir::StackSlotData { - kind: ir::StackSlotKind::SpillSlot, - size: rng.gen(), - offset: rng.gen(), - }); - slots - }) - .collect(); - - let traps = (0..rng.gen_range(0, 0xd)) - .map(|i| { - (i..i + rng.gen_range(0, 4)) - .map(|_| TrapInformation { - code_offset: rng.gen(), - source_loc: ir::SourceLoc::new(rng.gen()), - trap_code: ir::TrapCode::StackOverflow, - }) - .collect() - }) - .collect(); - - ModuleCacheData::from_tuple(( - Compilation::new(funcs), - relocs, - trans, - value_ranges, - stack_slots, - traps, +fn new_module_cache_data() -> Result { + Ok(( + Compilation::new(PrimaryMap::new()), + PrimaryMap::new(), + PrimaryMap::new(), + PrimaryMap::new(), + PrimaryMap::new(), + PrimaryMap::new(), )) } diff --git a/crates/environ/src/cranelift.rs b/crates/environ/src/cranelift.rs index 3021929e8d..da1dbe151c 100644 --- a/crates/environ/src/cranelift.rs +++ b/crates/environ/src/cranelift.rs @@ -1,13 +1,13 @@ //! Support for compiling with Cranelift. use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; -use crate::cache::{ModuleCacheData, ModuleCacheDataTupleType, ModuleCacheEntry}; +use crate::cache::{ModuleCacheDataTupleType, ModuleCacheEntry}; use crate::compilation::{ Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation, RelocationTarget, TrapInformation, }; use crate::func_environ::{get_func_name, FuncEnvironment}; -use crate::module::Module; +use crate::module::{Module, ModuleLocal}; use crate::module_environ::FunctionBodyData; use crate::CacheConfig; use cranelift_codegen::ir::{self, ExternalName}; @@ -16,6 +16,7 @@ use cranelift_codegen::{binemit, isa, Context}; use cranelift_entity::PrimaryMap; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, ModuleTranslationState}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; +use std::hash::{Hash, Hasher}; /// Implementation of a relocation sink that just saves all the information for later pub struct RelocSink { @@ -160,149 +161,191 @@ pub struct Cranelift; impl crate::compilation::Compiler for Cranelift { /// Compile the module using Cranelift, producing a compilation result with /// associated relocations. - fn compile_module<'data, 'module>( - module: &'module Module, + fn compile_module( + module: &Module, module_translation: &ModuleTranslationState, - function_body_inputs: PrimaryMap>, + function_body_inputs: PrimaryMap>, isa: &dyn isa::TargetIsa, generate_debug_info: bool, cache_config: &CacheConfig, ) -> Result { - let cache_entry = ModuleCacheEntry::new( - module, - &function_body_inputs, - isa, - "cranelift", - generate_debug_info, - cache_config, - ); - - let data = match cache_entry.get_data() { - Some(data) => data, - None => { - let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut value_ranges = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut stack_slots = PrimaryMap::with_capacity(function_body_inputs.len()); - let mut traps = PrimaryMap::with_capacity(function_body_inputs.len()); - - function_body_inputs - .into_iter() - .collect::)>>() - .par_iter() - .map_init(FuncTranslator::new, |func_translator, (i, input)| { - let func_index = module.func_index(*i); - let mut context = Context::new(); - context.func.name = get_func_name(func_index); - context.func.signature = - module.signatures[module.functions[func_index]].clone(); - context.func.collect_frame_layout_info(); - if generate_debug_info { - context.func.collect_debug_info(); - } - - func_translator.translate( - module_translation, - input.data, - input.module_offset, - &mut context.func, - &mut FuncEnvironment::new(isa.frontend_config(), module), - )?; - - let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink::new(func_index); - let mut trap_sink = TrapSink::new(); - let mut stackmap_sink = binemit::NullStackmapSink {}; - context - .compile_and_emit( - isa, - &mut code_buf, - &mut reloc_sink, - &mut trap_sink, - &mut stackmap_sink, - ) - .map_err(|error| { - CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) - })?; - - let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context); - - let address_transform = if generate_debug_info { - let body_len = code_buf.len(); - Some(get_function_address_map(&context, input, body_len, isa)) - } else { - None - }; - - let ranges = if generate_debug_info { - let ranges = - context.build_value_labels_ranges(isa).map_err(|error| { - CompileError::Codegen(pretty_error( - &context.func, - Some(isa), - error, - )) - })?; - Some(ranges) - } else { - None - }; - - Ok(( - code_buf, - context.func.jt_offsets, - reloc_sink.func_relocs, - address_transform, - ranges, - context.func.stack_slots, - trap_sink.traps, - unwind_info, - )) - }) - .collect::, CompileError>>()? - .into_iter() - .for_each( - |( - function, - func_jt_offsets, - relocs, - address_transform, - ranges, - sss, - function_traps, - unwind_info, - )| { - functions.push(CompiledFunction { - body: function, - jt_offsets: func_jt_offsets, - unwind_info, - }); - relocations.push(relocs); - if let Some(address_transform) = address_transform { - address_transforms.push(address_transform); - } - value_ranges.push(ranges.unwrap_or_default()); - stack_slots.push(sss); - traps.push(function_traps); - }, - ); - - // TODO: Reorganize where we create the Vec for the resolved imports. - - let data = ModuleCacheData::from_tuple(( - Compilation::new(functions), - relocations, - address_transforms, - value_ranges, - stack_slots, - traps, - )); - cache_entry.update_data(&data); - data - } - }; + let cache_entry = ModuleCacheEntry::new("cranelift", cache_config); + let data = cache_entry.get_data( + ( + &module.local, + HashedModuleTranslationState(module_translation), + function_body_inputs, + Isa(isa), + generate_debug_info, + ), + compile, + )?; Ok(data.into_tuple()) } } + +fn compile( + ( + module, + HashedModuleTranslationState(module_translation), + function_body_inputs, + Isa(isa), + generate_debug_info, + ): ( + &ModuleLocal, + HashedModuleTranslationState<'_>, + PrimaryMap>, + Isa<'_, '_>, + bool, + ), +) -> Result { + let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut value_ranges = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut stack_slots = PrimaryMap::with_capacity(function_body_inputs.len()); + let mut traps = PrimaryMap::with_capacity(function_body_inputs.len()); + + function_body_inputs + .into_iter() + .collect::)>>() + .par_iter() + .map_init(FuncTranslator::new, |func_translator, (i, input)| { + let func_index = module.func_index(*i); + let mut context = Context::new(); + context.func.name = get_func_name(func_index); + context.func.signature = module.signatures[module.functions[func_index]].clone(); + context.func.collect_frame_layout_info(); + if generate_debug_info { + context.func.collect_debug_info(); + } + + func_translator.translate( + module_translation, + input.data, + input.module_offset, + &mut context.func, + &mut FuncEnvironment::new(isa.frontend_config(), module), + )?; + + let mut code_buf: Vec = Vec::new(); + let mut reloc_sink = RelocSink::new(func_index); + let mut trap_sink = TrapSink::new(); + let mut stackmap_sink = binemit::NullStackmapSink {}; + context + .compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut trap_sink, + &mut stackmap_sink, + ) + .map_err(|error| { + CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) + })?; + + let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context); + + let address_transform = if generate_debug_info { + let body_len = code_buf.len(); + Some(get_function_address_map(&context, input, body_len, isa)) + } else { + None + }; + + let ranges = if generate_debug_info { + let ranges = context.build_value_labels_ranges(isa).map_err(|error| { + CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) + })?; + Some(ranges) + } else { + None + }; + + Ok(( + code_buf, + context.func.jt_offsets, + reloc_sink.func_relocs, + address_transform, + ranges, + context.func.stack_slots, + trap_sink.traps, + unwind_info, + )) + }) + .collect::, CompileError>>()? + .into_iter() + .for_each( + |( + function, + func_jt_offsets, + relocs, + address_transform, + ranges, + sss, + function_traps, + unwind_info, + )| { + functions.push(CompiledFunction { + body: function, + jt_offsets: func_jt_offsets, + unwind_info, + }); + relocations.push(relocs); + if let Some(address_transform) = address_transform { + address_transforms.push(address_transform); + } + value_ranges.push(ranges.unwrap_or_default()); + stack_slots.push(sss); + traps.push(function_traps); + }, + ); + + // TODO: Reorganize where we create the Vec for the resolved imports. + + Ok(( + Compilation::new(functions), + relocations, + address_transforms, + value_ranges, + stack_slots, + traps, + )) +} + +/// This is a wrapper struct to hash the specific bits of `TargetIsa` that +/// affect the output we care about. The trait itself can't implement `Hash` +/// (it's not object safe) so we have to implement our own hashing. +struct Isa<'a, 'b>(&'a (dyn isa::TargetIsa + 'b)); + +impl Hash for Isa<'_, '_> { + fn hash(&self, hasher: &mut H) { + self.0.triple().hash(hasher); + + // TODO: if this `to_string()` is too expensive then we should upstream + // a native hashing ability of flags into cranelift itself, but + // compilation and/or cache loading is relatively expensive so seems + // unlikely. + self.0.flags().to_string().hash(hasher); + + // TODO: ... and should we hash anything else? There's a lot of stuff in + // `TargetIsa`, like registers/encodings/etc. Should we be hashing that + // too? It seems like wasmtime doesn't configure it too too much, but + // this may become an issue at some point. + } +} + +/// A wrapper struct around cranelift's `ModuleTranslationState` to implement +/// `Hash` since it's not `Hash` upstream yet. +/// +/// TODO: we should upstream a `Hash` implementation, it would be very small! At +/// this moment though based on the definition it should be fine to not hash it +/// since we'll re-hash the signatures later. +struct HashedModuleTranslationState<'a>(&'a ModuleTranslationState); + +impl Hash for HashedModuleTranslationState<'_> { + fn hash(&self, _hasher: &mut H) { + // nothing to hash right now + } +} diff --git a/crates/environ/src/func_environ.rs b/crates/environ/src/func_environ.rs index febf0e38a9..14857bbb6d 100644 --- a/crates/environ/src/func_environ.rs +++ b/crates/environ/src/func_environ.rs @@ -1,4 +1,4 @@ -use crate::module::{MemoryPlan, MemoryStyle, Module, TableStyle}; +use crate::module::{MemoryPlan, MemoryStyle, ModuleLocal, TableStyle}; use crate::vmoffsets::VMOffsets; use crate::WASM_PAGE_SIZE; use cranelift_codegen::cursor::FuncCursor; @@ -59,7 +59,7 @@ pub struct FuncEnvironment<'module_environment> { target_config: TargetFrontendConfig, /// The module-level environment which this function-level environment belongs to. - module: &'module_environment Module, + module: &'module_environment ModuleLocal, /// The Cranelift global holding the vmctx address. vmctx: Option, @@ -77,7 +77,10 @@ pub struct FuncEnvironment<'module_environment> { } impl<'module_environment> FuncEnvironment<'module_environment> { - pub fn new(target_config: TargetFrontendConfig, module: &'module_environment Module) -> Self { + pub fn new( + target_config: TargetFrontendConfig, + module: &'module_environment ModuleLocal, + ) -> Self { Self { target_config, module, diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs index 456e0d819c..ba849b54fe 100644 --- a/crates/environ/src/lib.rs +++ b/crates/environ/src/lib.rs @@ -56,7 +56,7 @@ pub use crate::func_environ::BuiltinFunctionIndex; #[cfg(feature = "lightbeam")] pub use crate::lightbeam::Lightbeam; pub use crate::module::{ - Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle, + Export, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle, }; pub use crate::module_environ::{ translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData, diff --git a/crates/environ/src/lightbeam.rs b/crates/environ/src/lightbeam.rs index 513b305559..3abf98301f 100644 --- a/crates/environ/src/lightbeam.rs +++ b/crates/environ/src/lightbeam.rs @@ -32,13 +32,13 @@ impl crate::compilation::Compiler for Lightbeam { return Err(CompileError::DebugInfoNotSupported); } - let env = FuncEnvironment::new(isa.frontend_config(), module); + let env = FuncEnvironment::new(isa.frontend_config(), &module.local); let mut relocations = PrimaryMap::new(); let mut codegen_session: lightbeam::CodeGenSession<_> = lightbeam::CodeGenSession::new(function_body_inputs.len() as u32, &env); for (i, function_body) in &function_body_inputs { - let func_index = module.func_index(i); + let func_index = module.local.func_index(i); let mut reloc_sink = RelocSink::new(func_index); lightbeam::translate_function( diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index f6389c23d2..91a2e52725 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -1,6 +1,5 @@ //! Data structures for representing decoded wasm modules. -use crate::module_environ::FunctionBodyData; use crate::tunables::Tunables; use crate::WASM_MAX_PAGES; use cranelift_codegen::ir; @@ -12,7 +11,6 @@ use cranelift_wasm::{ use indexmap::IndexMap; use more_asserts::assert_ge; use std::collections::HashMap; -use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// A WebAssembly table initializer. @@ -134,14 +132,16 @@ impl TablePlan { /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. -// WARNING: when modifying, make sure that `hash_for_cache` is still valid! #[derive(Debug)] pub struct Module { /// A unique identifier (within this process) for this module. pub id: usize, - /// Unprocessed signatures exactly as provided by `declare_signature()`. - pub signatures: PrimaryMap, + /// Local information about a module which is the bare minimum necessary to + /// translate a function body. This is derived as `Hash` whereas this module + /// isn't, since it contains too much information needed to translate a + /// function. + pub local: ModuleLocal, /// Names of imported functions, as well as the index of the import that /// performed this import. @@ -156,18 +156,6 @@ pub struct Module { /// Names of imported globals. pub imported_globals: PrimaryMap, - /// Types of functions, imported and local. - pub functions: PrimaryMap, - - /// WebAssembly tables. - pub table_plans: PrimaryMap, - - /// WebAssembly linear memory plans. - pub memory_plans: PrimaryMap, - - /// WebAssembly global variables. - pub globals: PrimaryMap, - /// Exported entities. pub exports: IndexMap, @@ -181,6 +169,42 @@ pub struct Module { pub func_names: HashMap, } +/// Local information known about a wasm module, the bare minimum necessary to +/// translate function bodies. +/// +/// This is stored within a `Module` and it implements `Hash`, unlike `Module`, +/// and is used as part of the cache key when we load compiled modules from the +/// global cache. +#[derive(Debug, Hash)] +pub struct ModuleLocal { + /// Unprocessed signatures exactly as provided by `declare_signature()`. + pub signatures: PrimaryMap, + + /// Number of imported functions in the module. + pub num_imported_funcs: usize, + + /// Number of imported tables in the module. + pub num_imported_tables: usize, + + /// Number of imported memories in the module. + pub num_imported_memories: usize, + + /// Number of imported globals in the module. + pub num_imported_globals: usize, + + /// Types of functions, imported and local. + pub functions: PrimaryMap, + + /// WebAssembly tables. + pub table_plans: PrimaryMap, + + /// WebAssembly linear memory plans. + pub memory_plans: PrimaryMap, + + /// WebAssembly global variables. + pub globals: PrimaryMap, +} + impl Module { /// Allocates the module data structures. pub fn new() -> Self { @@ -188,132 +212,115 @@ impl Module { Self { id: NEXT_ID.fetch_add(1, SeqCst), - signatures: PrimaryMap::new(), imported_funcs: PrimaryMap::new(), imported_tables: PrimaryMap::new(), imported_memories: PrimaryMap::new(), imported_globals: PrimaryMap::new(), - functions: PrimaryMap::new(), - table_plans: PrimaryMap::new(), - memory_plans: PrimaryMap::new(), - globals: PrimaryMap::new(), exports: IndexMap::new(), start_func: None, table_elements: Vec::new(), func_names: HashMap::new(), + local: ModuleLocal { + num_imported_funcs: 0, + num_imported_tables: 0, + num_imported_memories: 0, + num_imported_globals: 0, + signatures: PrimaryMap::new(), + functions: PrimaryMap::new(), + table_plans: PrimaryMap::new(), + memory_plans: PrimaryMap::new(), + globals: PrimaryMap::new(), + }, } } +} +impl ModuleLocal { /// Convert a `DefinedFuncIndex` into a `FuncIndex`. pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { - FuncIndex::new(self.imported_funcs.len() + defined_func.index()) + FuncIndex::new(self.num_imported_funcs + defined_func.index()) } /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the /// index is an imported function. pub fn defined_func_index(&self, func: FuncIndex) -> Option { - if func.index() < self.imported_funcs.len() { + if func.index() < self.num_imported_funcs { None } else { Some(DefinedFuncIndex::new( - func.index() - self.imported_funcs.len(), + func.index() - self.num_imported_funcs, )) } } /// Test whether the given function index is for an imported function. pub fn is_imported_function(&self, index: FuncIndex) -> bool { - index.index() < self.imported_funcs.len() + index.index() < self.num_imported_funcs } /// Convert a `DefinedTableIndex` into a `TableIndex`. pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex { - TableIndex::new(self.imported_tables.len() + defined_table.index()) + TableIndex::new(self.num_imported_tables + defined_table.index()) } /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the /// index is an imported table. pub fn defined_table_index(&self, table: TableIndex) -> Option { - if table.index() < self.imported_tables.len() { + if table.index() < self.num_imported_tables { None } else { Some(DefinedTableIndex::new( - table.index() - self.imported_tables.len(), + table.index() - self.num_imported_tables, )) } } /// Test whether the given table index is for an imported table. pub fn is_imported_table(&self, index: TableIndex) -> bool { - index.index() < self.imported_tables.len() + index.index() < self.num_imported_tables } /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`. pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex { - MemoryIndex::new(self.imported_memories.len() + defined_memory.index()) + MemoryIndex::new(self.num_imported_memories + defined_memory.index()) } /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the /// index is an imported memory. pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option { - if memory.index() < self.imported_memories.len() { + if memory.index() < self.num_imported_memories { None } else { Some(DefinedMemoryIndex::new( - memory.index() - self.imported_memories.len(), + memory.index() - self.num_imported_memories, )) } } /// Test whether the given memory index is for an imported memory. pub fn is_imported_memory(&self, index: MemoryIndex) -> bool { - index.index() < self.imported_memories.len() + index.index() < self.num_imported_memories } /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`. pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex { - GlobalIndex::new(self.imported_globals.len() + defined_global.index()) + GlobalIndex::new(self.num_imported_globals + defined_global.index()) } /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the /// index is an imported global. pub fn defined_global_index(&self, global: GlobalIndex) -> Option { - if global.index() < self.imported_globals.len() { + if global.index() < self.num_imported_globals { None } else { Some(DefinedGlobalIndex::new( - global.index() - self.imported_globals.len(), + global.index() - self.num_imported_globals, )) } } /// Test whether the given global index is for an imported global. pub fn is_imported_global(&self, index: GlobalIndex) -> bool { - index.index() < self.imported_globals.len() - } - - /// Computes hash of the module for the purpose of caching. - pub fn hash_for_cache<'data, H>( - &self, - function_body_inputs: &PrimaryMap>, - state: &mut H, - ) where - H: Hasher, - { - // There's no need to cache names (strings), start function - // and data initializers (for both memory and tables) - self.signatures.hash(state); - self.functions.hash(state); - self.table_plans.hash(state); - self.memory_plans.hash(state); - self.globals.hash(state); - // IndexMap (self.export) iterates over values in order of item inserts - // Let's actually sort the values. - let mut exports = self.exports.values().collect::>(); - exports.sort(); - for val in exports { - val.hash(state); - } - function_body_inputs.hash(state); + index.index() < self.num_imported_globals } } diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index e1c3e7a23a..0528a3d56c 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -48,7 +48,7 @@ pub struct ModuleTranslation<'data> { impl<'data> ModuleTranslation<'data> { /// Return a new `FuncEnvironment` for translating a function. pub fn func_env(&self) -> FuncEnvironment<'_> { - FuncEnvironment::new(self.target_config, &self.module) + FuncEnvironment::new(self.target_config, &self.module.local) } } @@ -109,6 +109,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> { self.result .module + .local .signatures .reserve_exact(usize::try_from(num).unwrap()); Ok(()) @@ -117,7 +118,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> { let sig = translate_signature(sig, self.pointer_type()); // TODO: Deduplicate signatures. - self.result.module.signatures.push(sig); + self.result.module.local.signatures.push(sig); Ok(()) } @@ -128,35 +129,37 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data field: &str, ) -> WasmResult<()> { debug_assert_eq!( - self.result.module.functions.len(), + self.result.module.local.functions.len(), self.result.module.imported_funcs.len(), "Imported functions must be declared first" ); - self.result.module.functions.push(sig_index); + self.result.module.local.functions.push(sig_index); self.result.module.imported_funcs.push(( String::from(module), String::from(field), self.imports, )); + self.result.module.local.num_imported_funcs += 1; self.imports += 1; Ok(()) } fn declare_table_import(&mut self, table: Table, module: &str, field: &str) -> WasmResult<()> { debug_assert_eq!( - self.result.module.table_plans.len(), + self.result.module.local.table_plans.len(), self.result.module.imported_tables.len(), "Imported tables must be declared first" ); let plan = TablePlan::for_table(table, &self.result.tunables); - self.result.module.table_plans.push(plan); + self.result.module.local.table_plans.push(plan); self.result.module.imported_tables.push(( String::from(module), String::from(field), self.imports, )); + self.result.module.local.num_imported_tables += 1; self.imports += 1; Ok(()) } @@ -168,18 +171,19 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data field: &str, ) -> WasmResult<()> { debug_assert_eq!( - self.result.module.memory_plans.len(), + self.result.module.local.memory_plans.len(), self.result.module.imported_memories.len(), "Imported memories must be declared first" ); let plan = MemoryPlan::for_memory(memory, &self.result.tunables); - self.result.module.memory_plans.push(plan); + self.result.module.local.memory_plans.push(plan); self.result.module.imported_memories.push(( String::from(module), String::from(field), self.imports, )); + self.result.module.local.num_imported_memories += 1; self.imports += 1; Ok(()) } @@ -191,17 +195,18 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data field: &str, ) -> WasmResult<()> { debug_assert_eq!( - self.result.module.globals.len(), + self.result.module.local.globals.len(), self.result.module.imported_globals.len(), "Imported globals must be declared first" ); - self.result.module.globals.push(global); + self.result.module.local.globals.push(global); self.result.module.imported_globals.push(( String::from(module), String::from(field), self.imports, )); + self.result.module.local.num_imported_globals += 1; self.imports += 1; Ok(()) } @@ -217,6 +222,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { self.result .module + .local .functions .reserve_exact(usize::try_from(num).unwrap()); self.result @@ -226,13 +232,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data } fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> { - self.result.module.functions.push(sig_index); + self.result.module.local.functions.push(sig_index); Ok(()) } fn reserve_tables(&mut self, num: u32) -> WasmResult<()> { self.result .module + .local .table_plans .reserve_exact(usize::try_from(num).unwrap()); Ok(()) @@ -240,13 +247,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_table(&mut self, table: Table) -> WasmResult<()> { let plan = TablePlan::for_table(table, &self.result.tunables); - self.result.module.table_plans.push(plan); + self.result.module.local.table_plans.push(plan); Ok(()) } fn reserve_memories(&mut self, num: u32) -> WasmResult<()> { self.result .module + .local .memory_plans .reserve_exact(usize::try_from(num).unwrap()); Ok(()) @@ -254,20 +262,21 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> { let plan = MemoryPlan::for_memory(memory, &self.result.tunables); - self.result.module.memory_plans.push(plan); + self.result.module.local.memory_plans.push(plan); Ok(()) } fn reserve_globals(&mut self, num: u32) -> WasmResult<()> { self.result .module + .local .globals .reserve_exact(usize::try_from(num).unwrap()); Ok(()) } fn declare_global(&mut self, global: Global) -> WasmResult<()> { - self.result.module.globals.push(global); + self.result.module.local.globals.push(global); Ok(()) } diff --git a/crates/environ/src/vmoffsets.rs b/crates/environ/src/vmoffsets.rs index b97dc967d3..13c2b90d67 100644 --- a/crates/environ/src/vmoffsets.rs +++ b/crates/environ/src/vmoffsets.rs @@ -1,7 +1,7 @@ //! Offsets and sizes of various structs in wasmtime-runtime's vmcontext //! module. -use crate::module::Module; +use crate::module::ModuleLocal; use crate::BuiltinFunctionIndex; use cranelift_codegen::ir; use cranelift_wasm::{ @@ -50,14 +50,14 @@ pub struct VMOffsets { impl VMOffsets { /// Return a new `VMOffsets` instance, for a given pointer size. - pub fn new(pointer_size: u8, module: &Module) -> Self { + pub fn new(pointer_size: u8, module: &ModuleLocal) -> Self { Self { pointer_size, num_signature_ids: cast_to_u32(module.signatures.len()), - num_imported_functions: cast_to_u32(module.imported_funcs.len()), - num_imported_tables: cast_to_u32(module.imported_tables.len()), - num_imported_memories: cast_to_u32(module.imported_memories.len()), - num_imported_globals: cast_to_u32(module.imported_globals.len()), + num_imported_functions: cast_to_u32(module.num_imported_funcs), + num_imported_tables: cast_to_u32(module.num_imported_tables), + num_imported_memories: cast_to_u32(module.num_imported_memories), + num_imported_globals: cast_to_u32(module.num_imported_globals), num_defined_tables: cast_to_u32(module.table_plans.len()), num_defined_memories: cast_to_u32(module.memory_plans.len()), num_defined_globals: cast_to_u32(module.globals.len()), diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index 0e4c8a77b1..31af40531a 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -151,7 +151,7 @@ impl Compiler { // Translate debug info (DWARF) only if at least one function is present. let dbg = if debug_data.is_some() && !allocated_functions.is_empty() { let target_config = self.isa.frontend_config(); - let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); + let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); let mut funcs = Vec::new(); for (i, allocated) in allocated_functions.into_iter() { diff --git a/crates/jit/src/imports.rs b/crates/jit/src/imports.rs index a8f7daa24f..c2151ec94e 100644 --- a/crates/jit/src/imports.rs +++ b/crates/jit/src/imports.rs @@ -27,7 +27,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result { - let import_signature = &module.signatures[module.functions[index]]; + let import_signature = &module.local.signatures[module.local.functions[index]]; if signature != *import_signature { // TODO: If the difference is in the calling convention, // we could emit a wrapper function to fix it up. @@ -68,7 +68,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result { - let import_table = &module.table_plans[index]; + let import_table = &module.local.table_plans[index]; if !is_table_compatible(&table, import_table) { return Err(LinkError(format!( "{}/{}: incompatible import type: exported table incompatible with \ @@ -107,7 +107,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result { - let import_memory = &module.memory_plans[index]; + let import_memory = &module.local.memory_plans[index]; if !is_memory_compatible(&memory, import_memory) { return Err(LinkError(format!( "{}/{}: incompatible import type: exported memory incompatible with \ @@ -167,7 +167,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result { - let imported_global = module.globals[index]; + let imported_global = module.local.globals[index]; if !is_global_compatible(&global, &imported_global) { return Err(LinkError(format!( "{}/{}: incompatible import type: exported global incompatible with \ diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 9c19827128..f860fb2f8d 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -96,6 +96,7 @@ impl<'data> RawCompiledModule<'data> { let signature_registry = compiler.signatures(); translation .module + .local .signatures .values() .map(|sig| signature_registry.register(sig)) diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index d64261d94d..fcd715589a 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -22,7 +22,7 @@ pub fn link_module( for r in function_relocs { use self::libcalls::*; let target_func_address: usize = match r.reloc_target { - RelocationTarget::UserFunc(index) => match module.defined_func_index(index) { + RelocationTarget::UserFunc(index) => match module.local.defined_func_index(index) { Some(f) => { let fatptr: *const [VMFunctionBody] = allocated_functions[f]; fatptr as *const VMFunctionBody as usize @@ -45,7 +45,7 @@ pub fn link_module( } } RelocationTarget::JumpTable(func_index, jt) => { - match module.defined_func_index(func_index) { + match module.local.defined_func_index(func_index) { Some(f) => { let offset = *jt_offsets .get(f) diff --git a/crates/obj/src/context.rs b/crates/obj/src/context.rs index 98cf034714..9faa5768a2 100644 --- a/crates/obj/src/context.rs +++ b/crates/obj/src/context.rs @@ -18,14 +18,14 @@ pub fn layout_vmcontext( module: &Module, target_config: &TargetFrontendConfig, ) -> (Box<[u8]>, Box<[TableRelocation]>) { - let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); + let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); let out_len = ofs.size_of_vmctx() as usize; let mut out = vec![0; out_len]; // Assign unique indicies to unique signatures. let mut signature_registry = HashMap::new(); let mut signature_registry_len = signature_registry.len(); - for (index, sig) in module.signatures.iter() { + for (index, sig) in module.local.signatures.iter() { let offset = ofs.vmctx_vmshared_signature_id(index) as usize; let target_index = match signature_registry.entry(sig) { Entry::Occupied(o) => *o.get(), @@ -43,9 +43,9 @@ pub fn layout_vmcontext( } let num_tables_imports = module.imported_tables.len(); - let mut table_relocs = Vec::with_capacity(module.table_plans.len() - num_tables_imports); - for (index, table) in module.table_plans.iter().skip(num_tables_imports) { - let def_index = module.defined_table_index(index).unwrap(); + let mut table_relocs = Vec::with_capacity(module.local.table_plans.len() - num_tables_imports); + for (index, table) in module.local.table_plans.iter().skip(num_tables_imports) { + let def_index = module.local.defined_table_index(index).unwrap(); let offset = ofs.vmctx_vmtable_definition(def_index) as usize; let current_elements = table.table.minimum; unsafe { @@ -67,8 +67,8 @@ pub fn layout_vmcontext( } let num_globals_imports = module.imported_globals.len(); - for (index, global) in module.globals.iter().skip(num_globals_imports) { - let def_index = module.defined_global_index(index).unwrap(); + for (index, global) in module.local.globals.iter().skip(num_globals_imports) { + let def_index = module.local.defined_global_index(index).unwrap(); let offset = ofs.vmctx_vmglobal_definition(def_index) as usize; let to = unsafe { out.as_mut_ptr().add(offset) }; match global.initializer { diff --git a/crates/obj/src/function.rs b/crates/obj/src/function.rs index 511756dfb1..e5598b2937 100644 --- a/crates/obj/src/function.rs +++ b/crates/obj/src/function.rs @@ -16,7 +16,7 @@ pub fn declare_functions( obj.declare(string_name, Decl::function_import())?; } for (i, _function_relocs) in relocations.iter().rev() { - let func_index = module.func_index(i); + let func_index = module.local.func_index(i); let string_name = format!("_wasm_function_{}", func_index.index()); obj.declare(string_name, Decl::function().global())?; } @@ -43,14 +43,14 @@ pub fn emit_functions( for (i, _function_relocs) in relocations.iter() { let body = &compilation.get(i).body; - let func_index = module.func_index(i); + let func_index = module.local.func_index(i); let string_name = format!("_wasm_function_{}", func_index.index()); obj.define(string_name, body.clone())?; } for (i, function_relocs) in relocations.iter() { - let func_index = module.func_index(i); + let func_index = module.local.func_index(i); let string_name = format!("_wasm_function_{}", func_index.index()); for r in function_relocs { debug_assert_eq!(r.addend, 0); diff --git a/crates/obj/src/module.rs b/crates/obj/src/module.rs index 0117681e61..d5a1b1ed7e 100644 --- a/crates/obj/src/module.rs +++ b/crates/obj/src/module.rs @@ -41,7 +41,7 @@ pub fn emit_module( declare_data_segment(obj, initializer, i)?; } - for i in 0..module.table_plans.len() { + for i in 0..module.local.table_plans.len() { declare_table(obj, i)?; } @@ -51,7 +51,7 @@ pub fn emit_module( emit_data_segment(obj, initializer, i)?; } - for i in 0..module.table_plans.len() { + for i in 0..module.local.table_plans.len() { emit_table(obj, i)?; } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 23545d1a4d..0ed0c4b751 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -273,9 +273,10 @@ impl Instance { pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export { match export { wasmtime_environ::Export::Function(index) => { - let signature = self.module.signatures[self.module.functions[*index]].clone(); + let signature = + self.module.local.signatures[self.module.local.functions[*index]].clone(); let (address, vmctx) = - if let Some(def_index) = self.module.defined_func_index(*index) { + if let Some(def_index) = self.module.local.defined_func_index(*index) { ( self.finished_functions[def_index] as *const _, self.vmctx_ptr(), @@ -292,7 +293,7 @@ impl Instance { } wasmtime_environ::Export::Table(index) => { let (definition, vmctx) = - if let Some(def_index) = self.module.defined_table_index(*index) { + if let Some(def_index) = self.module.local.defined_table_index(*index) { (self.table_ptr(def_index), self.vmctx_ptr()) } else { let import = self.imported_table(*index); @@ -301,12 +302,12 @@ impl Instance { Export::Table { definition, vmctx, - table: self.module.table_plans[*index].clone(), + table: self.module.local.table_plans[*index].clone(), } } wasmtime_environ::Export::Memory(index) => { let (definition, vmctx) = - if let Some(def_index) = self.module.defined_memory_index(*index) { + if let Some(def_index) = self.module.local.defined_memory_index(*index) { (self.memory_ptr(def_index), self.vmctx_ptr()) } else { let import = self.imported_memory(*index); @@ -315,17 +316,18 @@ impl Instance { Export::Memory { definition, vmctx, - memory: self.module.memory_plans[*index].clone(), + memory: self.module.local.memory_plans[*index].clone(), } } wasmtime_environ::Export::Global(index) => Export::Global { - definition: if let Some(def_index) = self.module.defined_global_index(*index) { + definition: if let Some(def_index) = self.module.local.defined_global_index(*index) + { self.global_ptr(def_index) } else { self.imported_global(*index).from }, vmctx: self.vmctx_ptr(), - global: self.module.globals[*index], + global: self.module.local.globals[*index], }, } } @@ -351,7 +353,8 @@ impl Instance { None => return Ok(()), }; - let (callee_address, callee_vmctx) = match self.module.defined_func_index(start_index) { + let (callee_address, callee_vmctx) = match self.module.local.defined_func_index(start_index) + { Some(defined_index) => { let body = *self .finished_functions @@ -574,7 +577,7 @@ impl InstanceHandle { let vmctx_globals = create_globals(&module); - let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, &module); + let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, &module.local); let handle = { let instance = Instance { @@ -829,7 +832,7 @@ fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usi if let Some(base) = init.location.base { let val = unsafe { - if let Some(def_index) = instance.module.defined_global_index(base) { + if let Some(def_index) = instance.module.local.defined_global_index(base) { *instance.global(def_index).as_u32() } else { *(*instance.imported_global(base).from).as_u32() @@ -848,6 +851,7 @@ unsafe fn get_memory_slice<'instance>( ) -> &'instance mut [u8] { let memory = if let Some(defined_memory_index) = instance .module + .local .defined_memory_index(init.location.memory_index) { instance.memory(defined_memory_index) @@ -884,8 +888,8 @@ fn check_memory_init_bounds( fn create_tables(module: &Module) -> BoxedSlice { let num_imports = module.imported_tables.len(); let mut tables: PrimaryMap = - PrimaryMap::with_capacity(module.table_plans.len() - num_imports); - for table in &module.table_plans.values().as_slice()[num_imports..] { + PrimaryMap::with_capacity(module.local.table_plans.len() - num_imports); + for table in &module.local.table_plans.values().as_slice()[num_imports..] { tables.push(Table::new(table)); } tables.into_boxed_slice() @@ -897,7 +901,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize { if let Some(base) = init.base { let val = unsafe { - if let Some(def_index) = instance.module.defined_global_index(base) { + if let Some(def_index) = instance.module.local.defined_global_index(base) { *instance.global(def_index).as_u32() } else { *(*instance.imported_global(base).from).as_u32() @@ -911,7 +915,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize { /// Return a byte-slice view of a table's data. fn get_table<'instance>(init: &TableElements, instance: &'instance Instance) -> &'instance Table { - if let Some(defined_table_index) = instance.module.defined_table_index(init.table_index) { + if let Some(defined_table_index) = instance.module.local.defined_table_index(init.table_index) { &instance.tables[defined_table_index] } else { let import = instance.imported_table(init.table_index); @@ -931,9 +935,9 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> { let table = get_table(init, instance); for (i, func_idx) in init.elements.iter().enumerate() { - let callee_sig = instance.module.functions[*func_idx]; + let callee_sig = instance.module.local.functions[*func_idx]; let (callee_ptr, callee_vmctx) = - if let Some(index) = instance.module.defined_func_index(*func_idx) { + if let Some(index) = instance.module.local.defined_func_index(*func_idx) { (instance.finished_functions[index] as *const _, vmctx) } else { let imported_func = instance.imported_function(*func_idx); @@ -962,8 +966,8 @@ fn create_memories( ) -> Result, InstantiationError> { let num_imports = module.imported_memories.len(); let mut memories: PrimaryMap = - PrimaryMap::with_capacity(module.memory_plans.len() - num_imports); - for plan in &module.memory_plans.values().as_slice()[num_imports..] { + PrimaryMap::with_capacity(module.local.memory_plans.len() - num_imports); + for plan in &module.local.memory_plans.values().as_slice()[num_imports..] { memories.push(LinearMemory::new(plan).map_err(InstantiationError::Resource)?); } Ok(memories.into_boxed_slice()) @@ -990,9 +994,9 @@ fn initialize_memories( /// with initializers applied. fn create_globals(module: &Module) -> BoxedSlice { let num_imports = module.imported_globals.len(); - let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports); + let mut vmctx_globals = PrimaryMap::with_capacity(module.local.globals.len() - num_imports); - for _ in &module.globals.values().as_slice()[num_imports..] { + for _ in &module.local.globals.values().as_slice()[num_imports..] { vmctx_globals.push(VMGlobalDefinition::new()); } @@ -1002,8 +1006,8 @@ fn create_globals(module: &Module) -> BoxedSlice *(*to).as_f64_bits_mut() = x, GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = x.0, GlobalInit::GetGlobal(x) => { - let from = if let Some(def_x) = module.defined_global_index(x) { + let from = if let Some(def_x) = module.local.defined_global_index(x) { instance.global(def_x) } else { *instance.imported_global(x).from diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs index 7567261eaa..a11ddef094 100644 --- a/crates/runtime/src/vmcontext.rs +++ b/crates/runtime/src/vmcontext.rs @@ -27,7 +27,7 @@ mod test_vmfunction_import { #[test] fn check_vmfunction_import_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmfunction_import()) @@ -83,7 +83,7 @@ mod test_vmtable_import { #[test] fn check_vmtable_import_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmtable_import()) @@ -121,7 +121,7 @@ mod test_vmmemory_import { #[test] fn check_vmmemory_import_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmmemory_import()) @@ -156,7 +156,7 @@ mod test_vmglobal_import { #[test] fn check_vmglobal_import_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmglobal_import()) @@ -191,7 +191,7 @@ mod test_vmmemory_definition { #[test] fn check_vmmemory_definition_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmmemory_definition()) @@ -235,7 +235,7 @@ mod test_vmtable_definition { #[test] fn check_vmtable_definition_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmtable_definition()) @@ -281,7 +281,7 @@ mod test_vmglobal_definition { #[test] fn check_vmglobal_definition_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmglobal_definition()) @@ -291,7 +291,7 @@ mod test_vmglobal_definition { #[test] fn check_vmglobal_begins_aligned() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!(offsets.vmctx_globals_begin() % 16, 0); } } @@ -438,7 +438,7 @@ mod test_vmshared_signature_index { #[test] fn check_vmshared_signature_index() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmshared_signature_index()) @@ -492,7 +492,7 @@ mod test_vmcaller_checked_anyfunc { #[test] fn check_vmcaller_checked_anyfunc_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmcaller_checked_anyfunc()) @@ -570,7 +570,7 @@ mod test_vm_invoke_argument { #[test] fn check_vmglobal_definition_offsets() { let module = Module::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local); assert_eq!( size_of::(), usize::from(offsets.size_of_vmglobal_definition()) diff --git a/src/obj.rs b/src/obj.rs index 87c8951b24..95ba2ce05d 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -109,7 +109,7 @@ pub fn compile_to_obj( } let module_vmctx_info = { - let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); + let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); ModuleVmctxInfo { memory_offset: if ofs.num_imported_memories > 0 { ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))