Statically register host WASM functions (#10394)

* Statically register host WASM functions

* Fix `substrate-test-client` compilation

* Move `ExtendedHostFunctions` to `sp-wasm-interface`

* Fix `sp-runtime-interface` tests' compilation

* Fix `sc-executor-wasmtime` tests' compilation

* Use `runtime_interface` macro in `test-runner`

* Fix `sc-executor` tests' compilation

* Reformatting/`rustfmt`

* Add an extra comment regarding the `H` generic arg in `create_runtime`

* Even more `rustfmt`

* Depend on `wasmtime` without default features in `sp-wasm-interface`

* Bump version of `sp-wasm-interface` to 4.0.1

* Bump `sp-wasm-interface` in `Cargo.lock` too

* Bump all of the `sp-wasm-interface` requirements to 4.0.1

Maybe this will appease cargo-unleash?

* Revert "Bump all of the `sp-wasm-interface` requirements to 4.0.1"

This reverts commit 0f7ccf8e0f371542861121b145ab87af6541ac30.

* Make `cargo-unleash` happy (maybe)

* Use `cargo-unleash` to bump the crates' versions

* Align to review comments
This commit is contained in:
Koute
2021-12-14 17:26:40 +09:00
committed by GitHub
parent 23c5b6755b
commit 7711f5266e
36 changed files with 742 additions and 570 deletions
@@ -23,6 +23,7 @@ use crate::{
instance_wrapper::{EntryPoint, InstanceWrapper},
util,
};
use core::marker::PhantomData;
use sc_allocator::FreeingBumpHeapAllocator;
use sc_executor_common::{
@@ -33,7 +34,7 @@ use sc_executor_common::{
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{Function, Pointer, Value, WordSize};
use sp_wasm_interface::{HostFunctions, Pointer, Value, WordSize};
use std::{
path::{Path, PathBuf},
sync::{
@@ -79,29 +80,31 @@ impl StoreData {
pub(crate) type Store = wasmtime::Store<StoreData>;
enum Strategy {
enum Strategy<H> {
FastInstanceReuse {
instance_wrapper: InstanceWrapper,
globals_snapshot: GlobalsSnapshot<wasmtime::Global>,
data_segments_snapshot: Arc<DataSegmentsSnapshot>,
heap_base: u32,
},
RecreateInstance(InstanceCreator),
RecreateInstance(InstanceCreator<H>),
}
struct InstanceCreator {
struct InstanceCreator<H> {
module: Arc<wasmtime::Module>,
host_functions: Vec<&'static dyn Function>,
heap_pages: u64,
allow_missing_func_imports: bool,
max_memory_size: Option<usize>,
phantom: PhantomData<H>,
}
impl InstanceCreator {
impl<H> InstanceCreator<H>
where
H: HostFunctions,
{
fn instantiate(&mut self) -> Result<InstanceWrapper> {
InstanceWrapper::new(
InstanceWrapper::new::<H>(
&*self.module,
&self.host_functions,
self.heap_pages,
self.allow_missing_func_imports,
self.max_memory_size,
@@ -141,19 +144,21 @@ struct InstanceSnapshotData {
/// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code
/// and execute the compiled code.
pub struct WasmtimeRuntime {
pub struct WasmtimeRuntime<H> {
module: Arc<wasmtime::Module>,
snapshot_data: Option<InstanceSnapshotData>,
config: Config,
host_functions: Vec<&'static dyn Function>,
phantom: PhantomData<H>,
}
impl WasmModule for WasmtimeRuntime {
impl<H> WasmModule for WasmtimeRuntime<H>
where
H: HostFunctions,
{
fn new_instance(&self) -> Result<Box<dyn WasmInstance>> {
let strategy = if let Some(ref snapshot_data) = self.snapshot_data {
let mut instance_wrapper = InstanceWrapper::new(
let mut instance_wrapper = InstanceWrapper::new::<H>(
&self.module,
&self.host_functions,
self.config.heap_pages,
self.config.allow_missing_func_imports,
self.config.max_memory_size,
@@ -169,19 +174,19 @@ impl WasmModule for WasmtimeRuntime {
&mut InstanceGlobals { instance: &mut instance_wrapper },
);
Strategy::FastInstanceReuse {
Strategy::<H>::FastInstanceReuse {
instance_wrapper,
globals_snapshot,
data_segments_snapshot: snapshot_data.data_segments_snapshot.clone(),
heap_base,
}
} else {
Strategy::RecreateInstance(InstanceCreator {
Strategy::<H>::RecreateInstance(InstanceCreator {
module: self.module.clone(),
host_functions: self.host_functions.clone(),
heap_pages: self.config.heap_pages,
allow_missing_func_imports: self.config.allow_missing_func_imports,
max_memory_size: self.config.max_memory_size,
phantom: PhantomData,
})
};
@@ -191,11 +196,14 @@ impl WasmModule for WasmtimeRuntime {
/// A `WasmInstance` implementation that reuses compiled module and spawns instances
/// to execute the compiled code.
pub struct WasmtimeInstance {
strategy: Strategy,
pub struct WasmtimeInstance<H> {
strategy: Strategy<H>,
}
impl WasmInstance for WasmtimeInstance {
impl<H> WasmInstance for WasmtimeInstance<H>
where
H: HostFunctions,
{
fn call(&mut self, method: InvokeMethod, data: &[u8]) -> Result<Vec<u8>> {
match &mut self.strategy {
Strategy::FastInstanceReuse {
@@ -483,13 +491,18 @@ enum CodeSupplyMode<'a> {
/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to
/// machine code, which can be computationally heavy.
pub fn create_runtime(
///
/// The `H` generic parameter is used to statically pass a set of host functions which are exposed
/// to the runtime.
pub fn create_runtime<H>(
blob: RuntimeBlob,
config: Config,
host_functions: Vec<&'static dyn Function>,
) -> std::result::Result<WasmtimeRuntime, WasmError> {
) -> std::result::Result<WasmtimeRuntime<H>, WasmError>
where
H: HostFunctions,
{
// SAFETY: this is safe because it doesn't use `CodeSupplyMode::Artifact`.
unsafe { do_create_runtime(CodeSupplyMode::Verbatim { blob }, config, host_functions) }
unsafe { do_create_runtime::<H>(CodeSupplyMode::Verbatim { blob }, config) }
}
/// The same as [`create_runtime`] but takes a precompiled artifact, which makes this function
@@ -503,23 +516,27 @@ pub fn create_runtime(
///
/// 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(
pub unsafe fn create_runtime_from_artifact<H>(
compiled_artifact: &[u8],
config: Config,
host_functions: Vec<&'static dyn Function>,
) -> std::result::Result<WasmtimeRuntime, WasmError> {
do_create_runtime(CodeSupplyMode::Artifact { compiled_artifact }, config, host_functions)
) -> std::result::Result<WasmtimeRuntime<H>, WasmError>
where
H: HostFunctions,
{
do_create_runtime::<H>(CodeSupplyMode::Artifact { compiled_artifact }, config)
}
/// # Safety
///
/// This is only unsafe if called with [`CodeSupplyMode::Artifact`]. See
/// [`create_runtime_from_artifact`] to get more details.
unsafe fn do_create_runtime(
unsafe fn do_create_runtime<H>(
code_supply_mode: CodeSupplyMode<'_>,
config: Config,
host_functions: Vec<&'static dyn Function>,
) -> std::result::Result<WasmtimeRuntime, WasmError> {
) -> std::result::Result<WasmtimeRuntime<H>, WasmError>
where
H: HostFunctions,
{
// Create the engine, store and finally the module from the given code.
let mut wasmtime_config = common_config(&config.semantics)?;
if let Some(ref cache_path) = config.cache_path {
@@ -566,7 +583,7 @@ unsafe fn do_create_runtime(
},
};
Ok(WasmtimeRuntime { module: Arc::new(module), snapshot_data, config, host_functions })
Ok(WasmtimeRuntime { module: Arc::new(module), snapshot_data, config, phantom: PhantomData })
}
fn instrument(