diff --git a/Cargo.lock b/Cargo.lock
index c35aefbb9656c..903712907fb77 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7055,6 +7055,7 @@ dependencies = [
"derive_more",
"parity-scale-codec",
"parity-wasm 0.41.0",
+ "pwasm-utils 0.14.0",
"sp-allocator",
"sp-core",
"sp-serializer",
diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml
index 7e13e37d33fbe..95c090686e83b 100644
--- a/client/executor/common/Cargo.toml
+++ b/client/executor/common/Cargo.toml
@@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
derive_more = "0.99.2"
parity-wasm = "0.41.0"
+pwasm-utils = "0.14.0"
codec = { package = "parity-scale-codec", version = "2.0.0" }
wasmi = "0.6.2"
sp-core = { version = "3.0.0", path = "../../../primitives/core" }
diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs
index 050bad27d6c30..25e06314aba39 100644
--- a/client/executor/common/src/lib.rs
+++ b/client/executor/common/src/lib.rs
@@ -23,5 +23,5 @@
pub mod error;
pub mod sandbox;
-pub mod util;
pub mod wasm_runtime;
+pub mod runtime_blob;
diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs
similarity index 64%
rename from client/executor/common/src/util.rs
rename to client/executor/common/src/runtime_blob/data_segments_snapshot.rs
index 5947be4469cd0..3850ec6753bef 100644
--- a/client/executor/common/src/util.rs
+++ b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs
@@ -1,6 +1,6 @@
// This file is part of Substrate.
-// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
+// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
@@ -16,53 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-//! A set of utilities for resetting a wasm instance to its initial state.
-
use crate::error::{self, Error};
+use super::RuntimeBlob;
use std::mem;
-use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};
-
-/// A bunch of information collected from a WebAssembly module.
-pub struct WasmModuleInfo {
- raw_module: RawModule,
-}
-
-impl WasmModuleInfo {
- /// Create `WasmModuleInfo` from the given wasm code.
- ///
- /// Returns `None` if the wasm code cannot be deserialized.
- pub fn new(wasm_code: &[u8]) -> Option {
- let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?;
- Some(Self { raw_module })
- }
-
- /// Extract the data segments from the given wasm code.
- ///
- /// Returns `Err` if the given wasm code cannot be deserialized.
- fn data_segments(&self) -> Vec {
- self.raw_module
- .data_section()
- .map(|ds| ds.entries())
- .unwrap_or(&[])
- .to_vec()
- }
-
- /// The number of globals defined in locally in this module.
- pub fn declared_globals_count(&self) -> u32 {
- self.raw_module
- .global_section()
- .map(|gs| gs.entries().len() as u32)
- .unwrap_or(0)
- }
-
- /// The number of imports of globals.
- pub fn imported_globals_count(&self) -> u32 {
- self.raw_module
- .import_section()
- .map(|is| is.globals() as u32)
- .unwrap_or(0)
- }
-}
+use parity_wasm::elements::Instruction;
/// This is a snapshot of data segments specialzied for a particular instantiation.
///
@@ -75,7 +32,7 @@ pub struct DataSegmentsSnapshot {
impl DataSegmentsSnapshot {
/// Create a snapshot from the data segments from the module.
- pub fn take(module: &WasmModuleInfo) -> error::Result {
+ pub fn take(module: &RuntimeBlob) -> error::Result {
let data_segments = module
.data_segments()
.into_iter()
@@ -105,9 +62,7 @@ impl DataSegmentsSnapshot {
// if/when we gain those.
return Err(Error::ImportedGlobalsUnsupported);
}
- insn => {
- return Err(Error::InvalidInitializerExpression(format!("{:?}", insn)))
- }
+ insn => return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))),
};
Ok((offset, contents))
diff --git a/client/executor/common/src/runtime_blob/globals_snapshot.rs b/client/executor/common/src/runtime_blob/globals_snapshot.rs
new file mode 100644
index 0000000000000..a43814e1d4e14
--- /dev/null
+++ b/client/executor/common/src/runtime_blob/globals_snapshot.rs
@@ -0,0 +1,110 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use super::RuntimeBlob;
+
+/// Saved value of particular exported global.
+struct SavedValue {
+ /// The handle of this global which can be used to refer to this global.
+ handle: Global,
+ /// The global value that was observed during the snapshot creation.
+ value: sp_wasm_interface::Value,
+}
+
+/// An adapter for a wasm module instance that is focused on getting and setting globals.
+pub trait InstanceGlobals {
+ /// A handle to a global which can be used to get or set a global variable. This is supposed to
+ /// be a lightweight handle, like an index or an Rc-like smart-pointer, which is cheap to clone.
+ type Global: Clone;
+ /// Get a handle to a global by it's export name.
+ ///
+ /// The requested export is must exist in the exported list, and it should be a mutable global.
+ fn get_global(&self, export_name: &str) -> Self::Global;
+ /// Get the current value of the global.
+ fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value;
+ /// Update the current value of the global.
+ ///
+ /// The global behind the handle is guaranteed to be mutable and the value to be the same type
+ /// as the global.
+ fn set_global_value(&self, global: &Self::Global, value: sp_wasm_interface::Value);
+}
+
+/// A set of exposed mutable globals.
+///
+/// This is set of globals required to create a [`GlobalsSnapshot`] and that are collected from
+/// a runtime blob that was instrumented by [`InstrumentModule::expose_mutable_globals`].
+///
+/// If the code wasn't instrumented then it would be empty and snapshot would do nothing.
+pub struct ExposedMutableGlobalsSet(Vec);
+
+impl ExposedMutableGlobalsSet {
+ /// Collect the set from the given runtime blob. See the struct documentation for details.
+ pub fn collect(runtime_blob: &RuntimeBlob) -> Self {
+ let global_names = runtime_blob
+ .exported_internal_global_names()
+ .map(ToOwned::to_owned)
+ .collect();
+ Self(global_names)
+ }
+}
+
+/// A snapshot of a global variables values. This snapshot can be later used for restoring the
+/// values to the preserved state.
+///
+/// Technically, a snapshot stores only values of mutable global variables. This is because
+/// immutable global variables always have the same values.
+///
+/// We take it from an instance rather from a module because the start function could potentially
+/// change any of the mutable global values.
+pub struct GlobalsSnapshot(Vec>);
+
+impl GlobalsSnapshot {
+ /// Take a snapshot of global variables for a given instance.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if the instance doesn't correspond to the module from which the
+ /// [`ExposedMutableGlobalsSet`] was collected.
+ pub fn take(mutable_globals: &ExposedMutableGlobalsSet, instance: &Instance) -> Self
+ where
+ Instance: InstanceGlobals,
+ {
+ let global_names = &mutable_globals.0;
+ let mut saved_values = Vec::with_capacity(global_names.len());
+
+ for global_name in global_names {
+ let handle = instance.get_global(global_name);
+ let value = instance.get_global_value(&handle);
+ saved_values.push(SavedValue { handle, value });
+ }
+
+ Self(saved_values)
+ }
+
+ /// Apply the snapshot to the given instance.
+ ///
+ /// This instance must be the same that was used for creation of this snapshot.
+ pub fn apply(&self, instance: &Instance)
+ where
+ Instance: InstanceGlobals,
+ {
+ for saved_value in &self.0 {
+ instance.set_global_value(&saved_value.handle, saved_value.value);
+ }
+ }
+}
diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs
new file mode 100644
index 0000000000000..372df7bd97eb7
--- /dev/null
+++ b/client/executor/common/src/runtime_blob/mod.rs
@@ -0,0 +1,57 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! This module allows for inspection and instrumentation, i.e. modifying the module to alter it's
+//! structure or behavior, of a wasm module.
+//!
+//! ## Instrumentation
+//!
+//! In ideal world, there would be no instrumentation. However, in the real world the execution
+//! engines we use are somewhat limited in their APIs or abilities.
+//!
+//! To give you some examples:
+//!
+//! - wasmi allows reaching to non-exported mutable globals so that we could reset them.
+//! Wasmtime doesn’t support that.
+//!
+//! We need to reset the globals because when we
+//! execute the Substrate Runtime, we do not drop and create the instance anew, instead
+//! we restore some selected parts of the state.
+//!
+//! - stack depth metering can be performed via instrumentation or deferred to the engine and say
+//! be added directly in machine code. Implementing this in machine code is rather cumbersome so
+//! instrumentation looks like a good solution.
+//!
+//! Stack depth metering is needed to make a wasm blob
+//! execution deterministic, which in turn is needed by the Parachain Validation Function in Polkadot.
+//!
+//! ## Inspection
+//!
+//! Inspection of a wasm module may be needed to extract some useful information, such as to extract
+//! data segment snapshot, which is helpful for quickly restoring the initial state of instances.
+//! Inspection can be also useful to prove that a wasm module possesses some properties, such as,
+//! is free of any floating point operations, which is a useful step towards making instances produced
+//! from such a module deterministic.
+
+mod data_segments_snapshot;
+mod globals_snapshot;
+mod runtime_blob;
+
+pub use data_segments_snapshot::DataSegmentsSnapshot;
+pub use globals_snapshot::{GlobalsSnapshot, ExposedMutableGlobalsSet, InstanceGlobals};
+pub use runtime_blob::RuntimeBlob;
diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs
new file mode 100644
index 0000000000000..d90a48fde0c81
--- /dev/null
+++ b/client/executor/common/src/runtime_blob/runtime_blob.rs
@@ -0,0 +1,93 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use parity_wasm::elements::{DataSegment, Module as RawModule, deserialize_buffer, serialize};
+
+use crate::error::WasmError;
+
+/// A bunch of information collected from a WebAssembly module.
+#[derive(Clone)]
+pub struct RuntimeBlob {
+ raw_module: RawModule,
+}
+
+impl RuntimeBlob {
+ /// Create `RuntimeBlob` from the given wasm code.
+ ///
+ /// Returns `Err` if the wasm code cannot be deserialized.
+ pub fn new(wasm_code: &[u8]) -> Result {
+ let raw_module: RawModule = deserialize_buffer(wasm_code)
+ .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
+ Ok(Self { raw_module })
+ }
+
+ /// Extract the data segments from the given wasm code.
+ pub(super) fn data_segments(&self) -> Vec {
+ self.raw_module
+ .data_section()
+ .map(|ds| ds.entries())
+ .unwrap_or(&[])
+ .to_vec()
+ }
+
+ /// The number of globals defined in locally in this module.
+ pub fn declared_globals_count(&self) -> u32 {
+ self.raw_module
+ .global_section()
+ .map(|gs| gs.entries().len() as u32)
+ .unwrap_or(0)
+ }
+
+ /// The number of imports of globals.
+ pub fn imported_globals_count(&self) -> u32 {
+ self.raw_module
+ .import_section()
+ .map(|is| is.globals() as u32)
+ .unwrap_or(0)
+ }
+
+ /// Perform an instrumentation that makes sure that the mutable globals are exported.
+ pub fn expose_mutable_globals(&mut self) {
+ pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global");
+ }
+
+ /// Returns an iterator of all globals which were exported by [`expose_mutable_globals`].
+ pub(super) fn exported_internal_global_names<'module>(
+ &'module self,
+ ) -> impl Iterator {
+ let exports = self
+ .raw_module
+ .export_section()
+ .map(|es| es.entries())
+ .unwrap_or(&[]);
+ exports.iter().filter_map(|export| match export.internal() {
+ parity_wasm::elements::Internal::Global(_)
+ if export.field().starts_with("exported_internal_global") =>
+ {
+ Some(export.field())
+ }
+ _ => None,
+ })
+ }
+
+ /// Consumes this runtime blob and serializes it.
+ pub fn serialize(self) -> Vec {
+ serialize(self.raw_module)
+ .expect("serializing into a vec should succeed; qed")
+ }
+}
diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs
index 351a2b5f40f00..268a060182876 100644
--- a/client/executor/src/wasm_runtime.rs
+++ b/client/executor/src/wasm_runtime.rs
@@ -300,14 +300,22 @@ pub fn create_wasm_runtime_with_code(
.map(|runtime| -> Arc { Arc::new(runtime) })
}
#[cfg(feature = "wasmtime")]
- WasmExecutionMethod::Compiled =>
+ WasmExecutionMethod::Compiled => {
+ let blob = sc_executor_common::runtime_blob::RuntimeBlob::new(code)?;
sc_executor_wasmtime::create_runtime(
- code,
- heap_pages,
+ sc_executor_wasmtime::CodeSupplyMode::Verbatim { blob },
+ sc_executor_wasmtime::Config {
+ heap_pages: heap_pages as u32,
+ allow_missing_func_imports,
+ cache_path: cache_path.map(ToOwned::to_owned),
+ semantics: sc_executor_wasmtime::Semantics {
+ fast_instance_reuse: true,
+ stack_depth_metering: false,
+ },
+ },
host_functions,
- allow_missing_func_imports,
- cache_path,
- ).map(|runtime| -> Arc { Arc::new(runtime) }),
+ ).map(|runtime| -> Arc { Arc::new(runtime) })
+ },
}
}
diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs
index e6a6ef3a61039..0163e07e654bf 100644
--- a/client/executor/wasmi/src/lib.rs
+++ b/client/executor/wasmi/src/lib.rs
@@ -36,7 +36,7 @@ use sc_executor_common::{
error::{Error, WasmError},
sandbox,
};
-use sc_executor_common::util::{DataSegmentsSnapshot, WasmModuleInfo};
+use sc_executor_common::runtime_blob::{RuntimeBlob, DataSegmentsSnapshot};
struct FunctionExecutor<'a> {
sandbox_store: sandbox::Store,
@@ -661,11 +661,8 @@ pub fn create_runtime(
)
.map_err(|e| WasmError::Instantiation(e.to_string()))?;
- let data_segments_snapshot = DataSegmentsSnapshot::take(
- &WasmModuleInfo::new(code)
- .ok_or_else(|| WasmError::Other("cannot deserialize module".to_string()))?,
- )
- .map_err(|e| WasmError::Other(e.to_string()))?;
+ let data_segments_snapshot = DataSegmentsSnapshot::take(&RuntimeBlob::new(code)?)
+ .map_err(|e| WasmError::Other(e.to_string()))?;
let global_vals_snapshot = GlobalValsSnapshot::take(&instance);
(data_segments_snapshot, global_vals_snapshot)
diff --git a/client/executor/wasmtime/src/imports.rs b/client/executor/wasmtime/src/imports.rs
index 08cedd434e366..21b7728c323c8 100644
--- a/client/executor/wasmtime/src/imports.rs
+++ b/client/executor/wasmtime/src/imports.rs
@@ -16,9 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use crate::state_holder;
+use crate::{state_holder, util};
use sc_executor_common::error::WasmError;
-use sp_wasm_interface::{Function, Value, ValueType};
+use sp_wasm_interface::{Function, ValueType};
use std::any::Any;
use wasmtime::{
Extern, ExternType, Func, FuncType, ImportType, Limits, Memory, MemoryType, Module,
@@ -187,12 +187,12 @@ fn call_static(
qed
",
);
- // `into_value` panics if it encounters a value that doesn't fit into the values
+ // `from_wasmtime_val` panics if it encounters a value that doesn't fit into the values
// available in substrate.
//
// This, however, cannot happen since the signature of this function is created from
// a `dyn Function` signature of which cannot have a non substrate value by definition.
- let mut params = wasmtime_params.iter().cloned().map(into_value);
+ let mut params = wasmtime_params.iter().cloned().map(util::from_wasmtime_val);
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
static_func.execute(&mut host_ctx, &mut params)
@@ -211,7 +211,7 @@ fn call_static(
"wasmtime function signature, therefore the number of results, should always \
correspond to the number of results returned by the host function",
);
- wasmtime_results[0] = into_wasmtime_val(ret_val);
+ wasmtime_results[0] = util::into_wasmtime_val(ret_val);
Ok(())
}
Ok(None) => {
@@ -295,28 +295,6 @@ fn into_wasmtime_val_type(val_ty: ValueType) -> wasmtime::ValType {
}
}
-/// Converts a `Val` into a substrate runtime interface `Value`.
-///
-/// Panics if the given value doesn't have a corresponding variant in `Value`.
-pub fn into_value(val: Val) -> Value {
- match val {
- Val::I32(v) => Value::I32(v),
- Val::I64(v) => Value::I64(v),
- Val::F32(f_bits) => Value::F32(f_bits),
- Val::F64(f_bits) => Value::F64(f_bits),
- _ => panic!("Given value type is unsupported by substrate"),
- }
-}
-
-pub fn into_wasmtime_val(value: Value) -> wasmtime::Val {
- match value {
- Value::I32(v) => Val::I32(v),
- Value::I64(v) => Val::I64(v),
- Value::F32(f_bits) => Val::F32(f_bits),
- Value::F64(f_bits) => Val::F64(f_bits),
- }
-}
-
/// Attempt to convert a opaque panic payload to a string.
fn stringify_panic_payload(payload: Box) -> String {
match payload.downcast::<&'static str>() {
diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs
index f0543a7ef9506..fec88a472fb9b 100644
--- a/client/executor/wasmtime/src/instance_wrapper.rs
+++ b/client/executor/wasmtime/src/instance_wrapper.rs
@@ -25,53 +25,11 @@ use crate::imports::Imports;
use std::{slice, marker};
use sc_executor_common::{
error::{Error, Result},
- util::{WasmModuleInfo, DataSegmentsSnapshot},
+ runtime_blob,
wasm_runtime::InvokeMethod,
};
use sp_wasm_interface::{Pointer, WordSize, Value};
-use wasmtime::{Engine, Instance, Module, Memory, Table, Val, Func, Extern, Global, Store};
-use parity_wasm::elements;
-
-mod globals_snapshot;
-
-pub use globals_snapshot::GlobalsSnapshot;
-
-pub struct ModuleWrapper {
- module: Module,
- data_segments_snapshot: DataSegmentsSnapshot,
-}
-
-impl ModuleWrapper {
- pub fn new(engine: &Engine, code: &[u8]) -> Result {
- let mut raw_module: elements::Module = elements::deserialize_buffer(code)
- .map_err(|e| Error::from(format!("cannot decode module: {}", e)))?;
- pwasm_utils::export_mutable_globals(&mut raw_module, "exported_internal_global");
- let instrumented_code = elements::serialize(raw_module)
- .map_err(|e| Error::from(format!("cannot encode module: {}", e)))?;
-
- let module = Module::new(engine, &instrumented_code)
- .map_err(|e| Error::from(format!("cannot create module: {}", e)))?;
-
- let module_info = WasmModuleInfo::new(code)
- .ok_or_else(|| Error::from("cannot deserialize module".to_string()))?;
-
- let data_segments_snapshot = DataSegmentsSnapshot::take(&module_info)
- .map_err(|e| Error::from(format!("cannot take data segments snapshot: {}", e)))?;
-
- Ok(Self {
- module,
- data_segments_snapshot,
- })
- }
-
- pub fn module(&self) -> &Module {
- &self.module
- }
-
- pub fn data_segments_snapshot(&self) -> &DataSegmentsSnapshot {
- &self.data_segments_snapshot
- }
-}
+use wasmtime::{Instance, Module, Memory, Table, Val, Func, Extern, Global, Store};
/// Invoked entrypoint format.
pub enum EntryPointType {
@@ -197,8 +155,8 @@ fn extern_func(extern_: &Extern) -> Option<&Func> {
impl InstanceWrapper {
/// Create a new instance wrapper from the given wasm module.
- pub fn new(store: &Store, module_wrapper: &ModuleWrapper, imports: &Imports, heap_pages: u32) -> Result {
- let instance = Instance::new(store, &module_wrapper.module, &imports.externs)
+ pub fn new(store: &Store, module: &Module, imports: &Imports, heap_pages: u32) -> Result {
+ let instance = Instance::new(store, module, &imports.externs)
.map_err(|e| Error::from(format!("cannot instantiate: {}", e)))?;
let memory = match imports.memory_import_index {
@@ -462,3 +420,23 @@ impl InstanceWrapper {
}
}
}
+
+impl runtime_blob::InstanceGlobals for InstanceWrapper {
+ type Global = wasmtime::Global;
+
+ fn get_global(&self, export_name: &str) -> Self::Global {
+ self.instance
+ .get_global(export_name)
+ .expect("get_global is guaranteed to be called with an export name of a global; qed")
+ }
+
+ fn get_global_value(&self, global: &Self::Global) -> Value {
+ util::from_wasmtime_val(global.get())
+ }
+
+ fn set_global_value(&self, global: &Self::Global, value: Value) {
+ global.set(util::into_wasmtime_val(value)).expect(
+ "the value is guaranteed to be of the same value; the global is guaranteed to be mutable; qed",
+ );
+ }
+}
diff --git a/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs b/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs
deleted file mode 100644
index a6b1ed394150d..0000000000000
--- a/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-use super::InstanceWrapper;
-use sc_executor_common::error::{Result, Error};
-use sp_wasm_interface::Value;
-use crate::imports::{into_value, into_wasmtime_val};
-
-/// Saved value of particular exported global.
-struct SavedValue {
- /// Index of the export.
- index: usize,
- /// Global value.
- value: Value,
-}
-
-/// A snapshot of a global variables values. This snapshot can be used later for restoring the
-/// values to the preserved state.
-///
-/// Technically, a snapshot stores only values of mutable global variables. This is because
-/// immutable global variables always have the same values.
-pub struct GlobalsSnapshot(Vec);
-
-impl GlobalsSnapshot {
- /// Take a snapshot of global variables for a given instance.
- pub fn take(instance_wrapper: &InstanceWrapper) -> Result {
- let data = instance_wrapper.instance
- .exports()
- .enumerate()
- .filter_map(|(index, export)| {
- if export.name().starts_with("exported_internal_global") {
- export.into_global().map(
- |g| SavedValue { index, value: into_value(g.get()) }
- )
- } else { None }
- })
- .collect::>();
-
- Ok(Self(data))
- }
-
- /// Apply the snapshot to the given instance.
- ///
- /// This instance must be the same that was used for creation of this snapshot.
- pub fn apply(&self, instance_wrapper: &InstanceWrapper) -> Result<()> {
- // This is a pointer over saved items, it moves forward when the loop value below takes over it's current value.
- // Since both pointers (`current` and `index` below) are over ordered lists, they eventually hit all
- // equal referenced values.
- let mut current = 0;
- for (index, export) in instance_wrapper.instance.exports().enumerate() {
- if current >= self.0.len() { break; }
- let current_saved = &self.0[current];
- if index < current_saved.index { continue; }
- else if index > current_saved.index { current += 1; continue; }
- else {
- export.into_global()
- .ok_or_else(|| Error::Other(
- "Wrong instance in GlobalsSnapshot::apply: what should be global is not global.".to_string()
- ))?
- .set(into_wasmtime_val(current_saved.value))
- .map_err(|_e| Error::Other(
- "Wrong instance in GlobalsSnapshot::apply: global saved type does not matched applied.".to_string()
- ))?;
- }
- }
-
- Ok(())
- }
-}
diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs
index db7776d4c5845..3679c15249653 100644
--- a/client/executor/wasmtime/src/lib.rs
+++ b/client/executor/wasmtime/src/lib.rs
@@ -17,12 +17,11 @@
// along with this program. If not, see .
///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute.
-
mod host;
-mod runtime;
-mod state_holder;
mod imports;
mod instance_wrapper;
+mod runtime;
+mod state_holder;
mod util;
-pub use runtime::create_runtime;
+pub use runtime::{create_runtime, prepare_runtime_artifact, CodeSupplyMode, Config, Semantics};
diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs
index 64ad5a1f4e49f..103b37a681e8b 100644
--- a/client/executor/wasmtime/src/runtime.rs
+++ b/client/executor/wasmtime/src/runtime.rs
@@ -20,27 +20,57 @@
use crate::host::HostState;
use crate::imports::{Imports, resolve_imports};
-use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot, EntryPoint};
+use crate::instance_wrapper::{InstanceWrapper, EntryPoint};
use crate::state_holder;
-use std::rc::Rc;
+use std::{path::PathBuf, rc::Rc};
use std::sync::Arc;
use std::path::Path;
use sc_executor_common::{
error::{Result, WasmError},
+ runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob},
wasm_runtime::{WasmModule, WasmInstance, InvokeMethod},
};
use sp_allocator::FreeingBumpHeapAllocator;
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{Function, Pointer, WordSize, Value};
-use wasmtime::{Config, Engine, Store};
+use wasmtime::{Engine, Store};
+
+enum Strategy {
+ FastInstanceReuse {
+ instance_wrapper: Rc,
+ globals_snapshot: GlobalsSnapshot,
+ data_segments_snapshot: Arc,
+ heap_base: u32,
+ },
+ RecreateInstance(InstanceCreator),
+}
+
+struct InstanceCreator {
+ store: Store,
+ module: Arc,
+ imports: Arc,
+ heap_pages: u32,
+}
+
+impl InstanceCreator {
+ fn instantiate(&self) -> Result {
+ InstanceWrapper::new(&self.store, &*self.module, &*self.imports, self.heap_pages)
+ }
+}
+
+/// Data required for creating instances with the fast instance reuse strategy.
+struct InstanceSnapshotData {
+ mutable_globals: ExposedMutableGlobalsSet,
+ data_segments_snapshot: Arc,
+}
/// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code
/// and execute the compiled code.
pub struct WasmtimeRuntime {
- module_wrapper: Arc,
- heap_pages: u32,
- allow_missing_func_imports: bool,
+ module: Arc,
+ snapshot_data: Option,
+ config: Config,
host_functions: Vec<&'static dyn Function>,
engine: Engine,
}
@@ -51,41 +81,52 @@ impl WasmModule for WasmtimeRuntime {
// Scan all imports, find the matching host functions, and create stubs that adapt arguments
// and results.
+ //
+ // NOTE: Attentive reader may notice that this could've been moved in `WasmModule` creation.
+ // However, I am not sure if that's a good idea since it would be pushing our luck further
+ // by assuming that `Store` not only `Send` but also `Sync`.
let imports = resolve_imports(
&store,
- self.module_wrapper.module(),
+ &self.module,
&self.host_functions,
- self.heap_pages,
- self.allow_missing_func_imports,
+ self.config.heap_pages,
+ self.config.allow_missing_func_imports,
)?;
- let instance_wrapper =
- InstanceWrapper::new(&store, &self.module_wrapper, &imports, self.heap_pages)?;
- let heap_base = instance_wrapper.extract_heap_base()?;
- let globals_snapshot = GlobalsSnapshot::take(&instance_wrapper)?;
-
- Ok(Box::new(WasmtimeInstance {
- store,
- instance_wrapper: Rc::new(instance_wrapper),
- module_wrapper: Arc::clone(&self.module_wrapper),
- imports,
- globals_snapshot,
- heap_pages: self.heap_pages,
- heap_base,
- }))
+ let strategy = if let Some(ref snapshot_data) = self.snapshot_data {
+ let instance_wrapper =
+ InstanceWrapper::new(&store, &self.module, &imports, self.config.heap_pages)?;
+ let heap_base = instance_wrapper.extract_heap_base()?;
+
+ // This function panics if the instance was created from a runtime blob different from which
+ // the mutable globals were collected. Here, it is easy to see that there is only a single
+ // runtime blob and thus it's the same that was used for both creating the instance and
+ // collecting the mutable globals.
+ let globals_snapshot = GlobalsSnapshot::take(&snapshot_data.mutable_globals, &instance_wrapper);
+
+ Strategy::FastInstanceReuse {
+ instance_wrapper: Rc::new(instance_wrapper),
+ globals_snapshot,
+ data_segments_snapshot: snapshot_data.data_segments_snapshot.clone(),
+ heap_base,
+ }
+ } else {
+ Strategy::RecreateInstance(InstanceCreator {
+ imports: Arc::new(imports),
+ module: self.module.clone(),
+ store,
+ heap_pages: self.config.heap_pages,
+ })
+ };
+
+ Ok(Box::new(WasmtimeInstance { strategy }))
}
}
/// A `WasmInstance` implementation that reuses compiled module and spawns instances
/// to execute the compiled code.
pub struct WasmtimeInstance {
- store: Store,
- module_wrapper: Arc,
- instance_wrapper: Rc,
- globals_snapshot: GlobalsSnapshot,
- imports: Imports,
- heap_pages: u32,
- heap_base: u32,
+ strategy: Strategy,
}
// This is safe because `WasmtimeInstance` does not leak reference to `self.imports`
@@ -94,29 +135,43 @@ unsafe impl Send for WasmtimeInstance {}
impl WasmInstance for WasmtimeInstance {
fn call(&self, method: InvokeMethod, data: &[u8]) -> Result> {
- let entrypoint = self.instance_wrapper.resolve_entrypoint(method)?;
- let allocator = FreeingBumpHeapAllocator::new(self.heap_base);
-
- self.module_wrapper
- .data_segments_snapshot()
- .apply(|offset, contents| {
- self.instance_wrapper
- .write_memory_from(Pointer::new(offset), contents)
- })?;
-
- self.globals_snapshot.apply(&*self.instance_wrapper)?;
-
- perform_call(
- data,
- Rc::clone(&self.instance_wrapper),
- entrypoint,
- allocator,
- )
+ match &self.strategy {
+ Strategy::FastInstanceReuse {
+ instance_wrapper,
+ globals_snapshot,
+ data_segments_snapshot,
+ heap_base,
+ } => {
+ let entrypoint = instance_wrapper.resolve_entrypoint(method)?;
+
+ data_segments_snapshot.apply(|offset, contents| {
+ instance_wrapper.write_memory_from(Pointer::new(offset), contents)
+ })?;
+ globals_snapshot.apply(&**instance_wrapper);
+ let allocator = FreeingBumpHeapAllocator::new(*heap_base);
+
+ perform_call(data, Rc::clone(&instance_wrapper), entrypoint, allocator)
+ }
+ Strategy::RecreateInstance(instance_creator) => {
+ let instance_wrapper = instance_creator.instantiate()?;
+ let heap_base = instance_wrapper.extract_heap_base()?;
+ let entrypoint = instance_wrapper.resolve_entrypoint(method)?;
+
+ let allocator = FreeingBumpHeapAllocator::new(heap_base);
+ perform_call(data, Rc::new(instance_wrapper), entrypoint, allocator)
+ }
+ }
}
fn get_global_const(&self, name: &str) -> Result