Reuse wasmtime instances, the PR (#5567)

* Preserve a single wasmtime instance.

* Sketch of wasm instance reusing.

* Refactor and docs.

* Rename state_snapshot to util module.

* Renaming.

* Comments.

* Error handling

* More fixes.
This commit is contained in:
Sergei Pepyakin
2020-04-08 18:45:25 +02:00
committed by GitHub
parent 01cb097ab4
commit 0629f999ff
10 changed files with 446 additions and 156 deletions
@@ -0,0 +1,130 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::InstanceWrapper;
use sc_executor_common::{
error::{Error, Result},
};
use sp_wasm_interface::Value;
use cranelift_codegen::ir;
use cranelift_wasm::GlobalIndex;
/// 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 {
handle: wasmtime_runtime::InstanceHandle,
preserved_mut_globals: Vec<(*mut wasmtime_runtime::VMGlobalDefinition, Value)>,
}
impl GlobalsSnapshot {
/// Take a snapshot of global variables for a given instance.
pub fn take(instance_wrapper: &InstanceWrapper) -> Result<Self> {
// EVIL:
// Usage of an undocumented function.
let handle = instance_wrapper.instance.handle().clone();
let mut preserved_mut_globals = vec![];
for global_idx in instance_wrapper.imported_globals_count..instance_wrapper.globals_count {
let (def, global) = match handle.lookup_by_declaration(
&wasmtime_environ::Export::Global(GlobalIndex::from_u32(global_idx)),
) {
wasmtime_runtime::Export::Global {
definition, global, ..
} => (definition, global),
_ => unreachable!("only globals can be returned for a global request"),
};
// skip immutable globals.
if !global.mutability {
continue;
}
let value = unsafe {
// Safety of this function solely depends on the correctness of the reference and
// the type information of the global.
read_global(def, global.ty)?
};
preserved_mut_globals.push((def, value));
}
Ok(Self {
preserved_mut_globals,
handle,
})
}
/// 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<()> {
if instance_wrapper.instance.handle() != &self.handle {
return Err(Error::from("unexpected instance handle".to_string()));
}
for (def, value) in &self.preserved_mut_globals {
unsafe {
// The following writes are safe if the precondition that this is the same instance
// this snapshot was created with:
//
// 1. These pointers must be still not-NULL and allocated.
// 2. The set of global variables is fixed for the lifetime of the same instance.
// 3. We obviously assume that the wasmtime references are correct in the first place.
// 4. We write the data with the same type it was read in the first place.
write_global(*def, *value)?;
}
}
Ok(())
}
}
unsafe fn read_global(
def: *const wasmtime_runtime::VMGlobalDefinition,
ty: ir::Type,
) -> Result<Value> {
let def = def
.as_ref()
.ok_or_else(|| Error::from("wasmtime global reference is null during read".to_string()))?;
let val = match ty {
ir::types::I32 => Value::I32(*def.as_i32()),
ir::types::I64 => Value::I64(*def.as_i64()),
ir::types::F32 => Value::F32(*def.as_u32()),
ir::types::F64 => Value::F64(*def.as_u64()),
_ => {
return Err(Error::from(format!(
"unsupported global variable type: {}",
ty
)))
}
};
Ok(val)
}
unsafe fn write_global(def: *mut wasmtime_runtime::VMGlobalDefinition, value: Value) -> Result<()> {
let def = def
.as_mut()
.ok_or_else(|| Error::from("wasmtime global reference is null during write".to_string()))?;
match value {
Value::I32(v) => *def.as_i32_mut() = v,
Value::I64(v) => *def.as_i64_mut() = v,
Value::F32(v) => *def.as_u32_mut() = v,
Value::F64(v) => *def.as_u64_mut() = v,
}
Ok(())
}