From c8ab1e293e8457c61e34a74baad6649f446dbdc7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 26 Feb 2020 16:18:02 -0600 Subject: [PATCH] Improve robustness of cache loading/storing (#974) * Improve robustness of cache loading/storing Today wasmtime incorrectly loads compiled compiled modules from the global cache when toggling settings such as optimizations. For example if you execute `wasmtime foo.wasm` that will cache globally an unoptimized version of the wasm module. If you then execute `wasmtime -O foo.wasm` it would then reload the unoptimized version from cache, not realizing the compilation settings were different, and use that instead. This can lead to very surprising behavior naturally! This commit updates how the cache is managed in an attempt to make it much more robust against these sorts of issues. This takes a leaf out of rustc's playbook and models the cache with a function that looks like: fn load( &self, data: T, compute: fn(T) -> CacheEntry, ) -> CacheEntry; The goal here is that it guarantees that all the `data` necessary to `compute` the result of the cache entry is hashable and stored into the hash key entry. This was previously open-coded and manually managed where items were hashed explicitly, but this construction guarantees that everything reasonable `compute` could use to compile the module is stored in `data`, which is itself hashable. This refactoring then resulted in a few workarounds and a few fixes, including the original issue: * The `Module` type was split into `Module` and `ModuleLocal` where only the latter is hashed. The previous hash function for a `Module` left out items like the `start_func` and didn't hash items like the imports of the module. Omitting the `start_func` was fine since compilation didn't actually use it, but omitting imports seemed uncomfortable because while compilation didn't use the import values it did use the *number* of imports, which seems like it should then be put into the cache key. The `ModuleLocal` type now derives `Hash` to guarantee that all of its contents affect the hash key. * The `ModuleTranslationState` from `cranelift-wasm` doesn't implement `Hash` which means that we have a manual wrapper to work around that. This will be fixed with an upstream implementation, since this state affects the generated wasm code. Currently this is just a map of signatures, which is present in `Module` anyway, so we should be good for the time being. * Hashing `dyn TargetIsa` was also added, where previously it was not fully hashed. Previously only the target name was used as part of the cache key, but crucially the flags of compilation were omitted (for example the optimization flags). Unfortunately the trait object itself is not hashable so we still have to manually write a wrapper to hash it, but we likely want to add upstream some utilities to hash isa objects into cranelift itself. For now though we can continue to add hashed fields as necessary. Overall the goal here was to use the compiler to expose what we're not hashing, and then make sure we organize data and write the right code to ensure everything is hashed, and nothing more. * Update crates/environ/src/module.rs Co-Authored-By: Peter Huene * Fix lightbeam * Fix compilation of tests * Update the expected structure of the cache * Revert "Update the expected structure of the cache" This reverts commit 2b53fee426a4e411c313d8c1e424841ba304a9cd. * Separate the cache dir a bit * Add a test the cache is busted with opt levels * rustfmt Co-authored-by: Peter Huene --- Cargo.lock | 1 + crates/api/Cargo.toml | 1 + crates/api/src/frame_info.rs | 2 +- crates/api/src/runtime.rs | 68 +++++ crates/api/src/trampoline/create_handle.rs | 1 + crates/api/src/trampoline/func.rs | 14 +- crates/api/src/trampoline/global.rs | 2 +- crates/api/src/trampoline/memory.rs | 2 +- crates/api/src/trampoline/table.rs | 2 +- crates/environ/build.rs | 5 +- crates/environ/src/cache.rs | 129 ++++----- crates/environ/src/cache/config.rs | 31 ++ crates/environ/src/cache/tests.rs | 302 ++----------------- crates/environ/src/cranelift.rs | 321 ++++++++++++--------- crates/environ/src/func_environ.rs | 9 +- crates/environ/src/lib.rs | 2 +- crates/environ/src/lightbeam.rs | 4 +- crates/environ/src/module.rs | 133 +++++---- crates/environ/src/module_environ.rs | 37 ++- crates/environ/src/vmoffsets.rs | 12 +- crates/jit/src/compiler.rs | 2 +- crates/jit/src/imports.rs | 8 +- crates/jit/src/instantiate.rs | 1 + crates/jit/src/link.rs | 4 +- crates/obj/src/context.rs | 14 +- crates/obj/src/function.rs | 6 +- crates/obj/src/module.rs | 4 +- crates/runtime/src/instance.rs | 52 ++-- crates/runtime/src/vmcontext.rs | 22 +- src/obj.rs | 2 +- 30 files changed, 550 insertions(+), 643 deletions(-) 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)))