diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 772898b8c76b3..2844b510edc74 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -188,12 +188,19 @@ fn bench_call_instance(c: &mut Criterion) { }, ), ( - "pooling_vanilla", + "pooling_vanilla_fresh", Method::Compiled { instantiation_strategy: InstantiationStrategy::Pooling, precompile: false, }, ), + ( + "pooling_vanilla_precompiled", + Method::Compiled { + instantiation_strategy: InstantiationStrategy::Pooling, + precompile: true, + }, + ), ( "pooling_cow_fresh", Method::Compiled { diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs index c45478ec46a37..82e62b4a5dd3c 100644 --- a/client/executor/wasmtime/src/lib.rs +++ b/client/executor/wasmtime/src/lib.rs @@ -37,6 +37,7 @@ mod util; mod tests; pub use runtime::{ - create_runtime, create_runtime_from_artifact, prepare_runtime_artifact, Config, - DeterministicStackLimit, InstantiationStrategy, Semantics, + create_runtime, create_runtime_from_artifact, create_runtime_from_artifact_bytes, + prepare_runtime_artifact, Config, DeterministicStackLimit, InstantiationStrategy, Semantics, + WasmtimeRuntime, }; diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index c9a2c83e0493c..23b069870aa36 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -435,7 +435,7 @@ pub struct DeterministicStackLimit { /// All of the CoW strategies (with `CopyOnWrite` suffix) are only supported when either: /// a) we're running on Linux, /// b) we're running on an Unix-like system and we're precompiling -/// our module beforehand. +/// our module beforehand and instantiating from a file. /// /// If the CoW variant of a strategy is unsupported the executor will /// fall back to the non-CoW equivalent. @@ -537,7 +537,7 @@ enum CodeSupplyMode<'a> { /// The runtime is instantiated using the given runtime blob. Fresh(RuntimeBlob), - /// The runtime is instantiated using a precompiled module. + /// The runtime is instantiated using a precompiled module at the given path. /// /// This assumes that the code is already prepared for execution and the same `Config` was /// used. @@ -545,6 +545,12 @@ enum CodeSupplyMode<'a> { /// We use a `Path` here instead of simply passing a byte slice to allow `wasmtime` to /// map the runtime's linear memory on supported platforms in a copy-on-write fashion. Precompiled(&'a Path), + + /// The runtime is instantiated using a precompiled module with the given bytes. + /// + /// This assumes that the code is already prepared for execution and the same `Config` was + /// used. + PrecompiledBytes(&'a [u8]), } /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to @@ -589,6 +595,31 @@ where do_create_runtime::(CodeSupplyMode::Precompiled(compiled_artifact_path), config) } +/// The same as [`create_runtime`] but takes the bytes of a precompiled artifact, +/// which makes this function considerably faster than [`create_runtime`], +/// but slower than the more optimized [`create_runtime_from_artifact`]. +/// This is especially slow on non-Linux Unix systems. Useful in very niche cases. +/// +/// # Safety +/// +/// The caller must ensure that the compiled artifact passed here was: +/// 1) produced by [`prepare_runtime_artifact`], +/// 2) was not modified, +/// +/// Failure to adhere to these requirements might lead to crashes and arbitrary code execution. +/// +/// It is ok though if the compiled artifact was created by code of another version or with +/// different configuration flags. In such case the caller will receive an `Err` deterministically. +pub unsafe fn create_runtime_from_artifact_bytes( + compiled_artifact_bytes: &[u8], + config: Config, +) -> std::result::Result +where + H: HostFunctions, +{ + do_create_runtime::(CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes), config) +} + /// # Safety /// /// This is only unsafe if called with [`CodeSupplyMode::Artifact`]. See @@ -663,6 +694,22 @@ where (module, InternalInstantiationStrategy::Builtin) }, + CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes) => { + if let InstantiationStrategy::LegacyInstanceReuse = + config.semantics.instantiation_strategy + { + return Err(WasmError::Other("the legacy instance reuse instantiation strategy is incompatible with precompiled modules".into())); + } + + // SAFETY: The unsafety of `deserialize` is covered by this function. The + // responsibilities to maintain the invariants are passed to the caller. + // + // See [`create_runtime_from_artifact_bytes`] for more details. + let module = wasmtime::Module::deserialize(&engine, compiled_artifact_bytes) + .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:#}", e)))?; + + (module, InternalInstantiationStrategy::Builtin) + }, }; let mut linker = wasmtime::Linker::new(&engine);