From adc8cc4643e7538da4675dccfa7b4641e1218b17 Mon Sep 17 00:00:00 2001 From: Lann Date: Tue, 18 Apr 2023 17:21:16 -0400 Subject: [PATCH] Add Engine::precompile_compatibility_hash (#5826) This method returns a Hash, the output of which can be used to index precompiled binaries from one Engine instance that can be deserialized by another Engine instance. --- crates/wasmtime/src/engine.rs | 33 +++++++++++++++++++++++++++++++++ crates/wasmtime/src/module.rs | 6 +++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index a900a435fe3a..2db8602fc7d0 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -239,6 +239,18 @@ impl Engine { Ok(mmap.to_vec()) } + /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility. + /// + /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`] + /// are compatible with a different [`Engine`] instance only if the two engines use + /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries + /// from one are guaranteed to deserialize in the other. + #[cfg(compiler)] + #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs + pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ { + crate::module::HashedEngineCompileEnv(self) + } + pub(crate) fn run_maybe_parallel< A: Send, B: Send, @@ -628,6 +640,11 @@ impl Default for Engine { #[cfg(test)] mod tests { + use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + }; + use crate::{Config, Engine, Module, OptLevel}; use anyhow::Result; @@ -693,4 +710,20 @@ mod tests { Ok(()) } + + #[test] + fn precompile_compatibility_key_accounts_for_opt_level() { + fn hash_for_config(cfg: &Config) -> u64 { + let engine = Engine::new(cfg).expect("Config should be valid"); + let mut hasher = DefaultHasher::new(); + engine.precompile_compatibility_hash().hash(&mut hasher); + hasher.finish() + } + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::None); + let opt_none_hash = hash_for_config(&cfg); + cfg.cranelift_opt_level(OptLevel::Speed); + let opt_speed_hash = hash_for_config(&cfg); + assert_ne!(opt_none_hash, opt_speed_hash) + } } diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index 3d72c7b27a6a..8e366bf50cbe 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -1144,10 +1144,10 @@ fn _assert_send_sync() { /// The hash computed for this structure is used to key the global wasmtime /// cache and dictates whether artifacts are reused. Consequently the contents /// of this hash dictate when artifacts are or aren't re-used. -#[cfg(all(feature = "cache", compiler))] -struct HashedEngineCompileEnv<'a>(&'a Engine); +#[cfg(compiler)] +pub(crate) struct HashedEngineCompileEnv<'a>(pub &'a Engine); -#[cfg(all(feature = "cache", compiler))] +#[cfg(compiler)] impl std::hash::Hash for HashedEngineCompileEnv<'_> { fn hash(&self, hasher: &mut H) { // Hash the compiler's state based on its target and configuration.