Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
87bf2d4
executor: Use non wasmi-specific execution in tests.
jimpo Oct 18, 2019
f37b9d8
executor: Move all runtime execution tests into tests file.
jimpo Oct 18, 2019
36bdc5c
executor: Use test_case macro to easily execute tests with different
jimpo Oct 18, 2019
727ce43
executor: Convert errors to strings with Display, not Debug.
jimpo Oct 21, 2019
1bcb472
node-executor: Rewrite benchmarks with criterion.
jimpo Oct 21, 2019
3908f76
executor: Begin implementation of Wasm runtime.
jimpo Oct 21, 2019
447c31a
executor: Define and implement basic FunctionExecutor.
jimpo Oct 21, 2019
828fbf0
executor: Implement host function trampoline generation.
jimpo Oct 21, 2019
81535c5
executor: Instantiate and link runtime module to env module.
jimpo Oct 21, 2019
a3d2b35
executor: Provide input data during wasmtime execution.
jimpo Oct 21, 2019
a7bb759
executor: Implement SandboxCapabilites::invoke for wasmtime executor.
jimpo Oct 21, 2019
9076ca0
executor: Integrate and test wasmtime execution method.
jimpo Oct 21, 2019
5a903a8
executor: Improve FunctionExecution error messages.
jimpo Oct 21, 2019
a77e9bd
Scope the unsafe blocks to be smaller.
jimpo Oct 23, 2019
c964753
Rename TrampolineState to EnvState.
jimpo Oct 23, 2019
5e0d828
Let EnvState own its own compiler instead of unsafe lifetime cast.
jimpo Oct 24, 2019
7623f12
Refactor out some common wasmi/wasmtime logic.
jimpo Oct 25, 2019
7467e35
Typos and cosmetic changes.
jimpo Oct 25, 2019
b73465c
More trampoline comments.
jimpo Oct 25, 2019
d5b72c6
Cargo.lock update.
jimpo Oct 28, 2019
779d4b4
cli: CLI option for running Substrate with compiled Wasm execution.
jimpo Oct 25, 2019
5ffda81
executor: Switch dependency from fork to official wasmtime repo.
jimpo Oct 28, 2019
70896c0
Quiet down cranelift logs.
jimpo Oct 28, 2019
4b0cdc8
Explicitly catch panics during host calls.
jimpo Oct 30, 2019
28f7c11
Additional checks and clarifications in make_trampoline.
jimpo Oct 30, 2019
f4016dd
Merge branch 'master' into wasmtime-real
jimpo Oct 31, 2019
b76f733
Fixes after merge from master and panic safety for wasmtime
jimpo Oct 31, 2019
0ec6caf
Merge branch 'master' into wasmtime-real
jimpo Nov 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
executor: Instantiate and link runtime module to env module.
  • Loading branch information
jimpo committed Oct 28, 2019
commit 81535c57788b8fdf65374baeafc8af73d189860b
119 changes: 113 additions & 6 deletions core/executor/src/wasmtime/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,30 @@
//! Defines the compiled Wasm runtime that uses Wasmtime internally.

use crate::error::{Error, Result, WasmError};
use crate::host_interface::SubstrateExternals;
use crate::wasm_runtime::WasmRuntime;
use crate::wasmtime::util::read_memory_into;
use crate::wasmtime::function_executor::FunctionExecutorState;
use crate::wasmtime::trampoline::{TrampolineState, make_trampoline};
use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into};
use crate::{Externalities, RuntimeVersion};

use codec::Decode;
use cranelift_codegen::isa::TargetIsa;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_frontend::FunctionBuilderContext;
use cranelift_wasm::DefinedFuncIndex;
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryFrom;
use wasm_interface::Pointer;
use std::mem;
use std::rc::Rc;
use wasm_interface::{HostFunctions, Pointer};
use wasmtime_environ::{Module, translate_signature};
use wasmtime_jit::{
ActionOutcome, ActionError, CompilationStrategy, CompiledModule, Context,
ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context,
SetupError, RuntimeValue,
};
use wasmtime_runtime::{Export, InstanceHandle};
use wasmtime_runtime::{Export, Imports, InstanceHandle, VMFunctionBody};

/// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native
/// and execute the compiled code.
Expand Down Expand Up @@ -123,7 +133,10 @@ fn create_compiled_unit(code: &[u8])
// Enable/disable producing of debug info.
context.set_debug_info(false);

// TODO: Instantiate and link the env module.
// Instantiate and link the env module.
let global_exports = context.get_global_exports();
let env_module = instantiate_env_module(global_exports)?;
context.name_instance("env".to_owned(), env_module);

// Compile the wasm module.
let module = context.compile_module(&code)
Expand Down Expand Up @@ -158,6 +171,18 @@ fn call_method(
// However, the wasmtime API doesn't support modifying the size of memory on instantiation
// at this time.
grow_memory(&mut instance, heap_pages)?;

// Initialize the function executor state.
let executor_state = FunctionExecutorState::new(
// Unsafely extend the reference lifetime to static. This is necessary because the
// host state must be `Any`. This is OK as we will drop this object either before
// exiting the function in the happy case or in the worst case on the next runtime
// call, which also resets the host state. Thus this reference can never actually
// outlive the Context.
mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()),
get_heap_base(&instance)?,
);
reset_host_state(context, Some(executor_state))?;
}

// TODO: Construct arguments properly by allocating heap space with data.
Expand All @@ -169,6 +194,7 @@ fn call_method(
.invoke(&mut instance, method, &args[..])
.map_err(Error::Wasmtime)
})?;
let trap_error = reset_host_state(context, None)?;
let (output_ptr, output_len) = match outcome {
ActionOutcome::Returned { values } => {
if values.len() != 1 {
Expand All @@ -183,7 +209,9 @@ fn call_method(
}
}
ActionOutcome::Trapped { message } =>
return Err(format!("Wasm execution trapped: {}", message).into()),
return Err(trap_error.unwrap_or_else(||
format!("Wasm execution trapped: {}", message).into()
)),
};

// Read the output data from guest memory.
Expand All @@ -193,6 +221,60 @@ fn call_method(
Ok(output)
}

/// The implementation is based on wasmtime_wasi::instantiate_wasi.
fn instantiate_env_module(global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>)
-> std::result::Result<InstanceHandle, WasmError>
{
let isa = target_isa()?;
let pointer_type = isa.pointer_type();
let call_conv = isa.default_call_conv();

let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions = <PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>>::new();
let mut code_memory = CodeMemory::new();

for function in SubstrateExternals::functions().iter() {
let sig = translate_signature(
cranelift_ir_signature(function.signature(), &call_conv),
pointer_type
);
let sig_id = module.signatures.push(sig.clone());
let func_id = module.functions.push(sig_id);
module
.exports
.insert(function.name().to_string(), wasmtime_environ::Export::Function(func_id));

let trampoline = make_trampoline(
isa.as_ref(),
&mut code_memory,
&mut fn_builder_ctx,
func_id.index() as u32,
&sig,
);
finished_functions.push(trampoline);
}

code_memory.publish();

let imports = Imports::none();
let data_initializers = Vec::new();
let signatures = PrimaryMap::new();
let host_state = TrampolineState::new::<SubstrateExternals>(code_memory);

let result = InstanceHandle::new(
Rc::new(module),
global_exports,
finished_functions.into_boxed_slice(),
imports,
&data_initializers,
signatures.into_boxed_slice(),
None,
Box::new(host_state),
);
result.map_err(|e| WasmError::WasmtimeSetup(SetupError::Instantiate(e)))
}

/// Build a new TargetIsa for the host machine.
fn target_isa() -> std::result::Result<Box<dyn TargetIsa>, WasmError> {
let isa_builder = cranelift_native::builder()
Expand All @@ -218,6 +300,23 @@ unsafe fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> {
.ok_or_else(|| "requested heap_pages would exceed maximum memory size".into())
}

fn get_host_state(context: &mut Context) -> Result<&mut TrampolineState> {
let env_instance = context.get_instance("env")
.map_err(|err| format!("cannot find \"env\" module: {}", err))?;
env_instance
.host_state()
.downcast_mut::<TrampolineState>()
.ok_or_else(|| "cannot get \"env\" module host state".into())
}

fn reset_host_state(context: &mut Context, executor_state: Option<FunctionExecutorState>)
-> Result<Option<Error>>
{
let trampoline_state = get_host_state(context)?;
trampoline_state.executor_state = executor_state;
Ok(trampoline_state.reset_trap())
}

unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> {
match instance.lookup("memory") {
Some(Export::Memory { definition, vmctx: _, memory: _ }) =>
Expand All @@ -229,6 +328,14 @@ unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> {
}
}

unsafe fn get_heap_base(instance: &InstanceHandle) -> Result<u32> {
match instance.lookup_immutable("__heap_base") {
Some(Export::Global { definition, vmctx: _, global: _ }) =>
Ok(*(*definition).as_u32()),
_ => return Err(Error::HeapBaseNotFoundOrInvalid),
}
}

/// Checks whether the heap_pages parameter is within the valid range and converts it to a u32.
/// Returns None if heaps_pages in not in range.
fn heap_pages_valid(heap_pages: u64, max_heap_pages: Option<u32>)
Expand Down