Replace the global-exports mechanism with a caller-vmctx mechanism. (#789)

* Replace the global-exports mechanism with a caller-vmctx mechanism.

This eliminates the global exports mechanism, and instead adds a
caller-vmctx argument to wasm functions so that WASI can obtain the
memory and other things from the caller rather than looking them up in a
global registry.

This replaces #390.

* Fixup some merge conflicts

* Rustfmt

* Ensure VMContext is aligned to 16 bytes

With the removal of `global_exports` it "just so happens" that this
isn't happening naturally any more.

* Fixup some bugs with double vmctx in wasmtime crate

* Trampoline stub needed adjusting
* Use pointer type instead of always using I64 for caller vmctx
* Don't store `ir::Signature` in `Func` since we don't know the pointer
  size at creation time.
* Skip the first 2 arguments in IR signatures since that's the two vmctx
  parameters.

* Update cranelift to 0.56.0

* Handle more merge conflicts

* Rustfmt

Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
Dan Gohman
2020-01-21 14:50:59 -08:00
committed by GitHub
parent de72435576
commit 9a88d3d894
33 changed files with 362 additions and 330 deletions

View File

@@ -3,6 +3,7 @@ use crate::trampoline::{generate_func_export, take_api_trap};
use crate::trap::Trap;
use crate::types::FuncType;
use crate::values::Val;
use std::ptr;
use std::rc::Rc;
use wasmtime_environ::ir;
use wasmtime_jit::InstanceHandle;
@@ -150,6 +151,7 @@ impl WrappedCallable for WasmtimeFn {
self.instance.with_signals_on(|| {
wasmtime_runtime::wasmtime_call_trampoline(
vmctx,
ptr::null_mut(),
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
)

View File

@@ -395,12 +395,10 @@ impl Module {
}
fn compile(store: &Store, binary: &[u8], module_name: Option<&str>) -> Result<CompiledModule> {
let exports = store.global_exports().clone();
let compiled_module = CompiledModule::new(
&mut store.compiler_mut(),
binary,
module_name,
exports,
store.engine().config().debug_info,
)?;

View File

@@ -342,7 +342,6 @@ pub struct Store {
struct StoreInner {
engine: Engine,
compiler: RefCell<Compiler>,
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
signature_cache: RefCell<HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>>,
}
@@ -355,7 +354,6 @@ impl Store {
inner: Rc::new(StoreInner {
engine: engine.clone(),
compiler: RefCell::new(compiler),
global_exports: Rc::new(RefCell::new(HashMap::new())),
signature_cache: RefCell::new(HashMap::new()),
}),
}
@@ -370,14 +368,6 @@ impl Store {
self.inner.compiler.borrow_mut()
}
// Specific to wasmtime: hack to pass memory around to wasi
#[doc(hidden)]
pub fn global_exports(
&self,
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
&self.inner.global_exports
}
pub(crate) fn register_wasmtime_signature(
&self,
signature: &ir::Signature,

View File

@@ -3,8 +3,7 @@
use crate::runtime::Store;
use anyhow::Result;
use std::any::Any;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use std::rc::Rc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::DefinedFuncIndex;
@@ -17,9 +16,6 @@ pub(crate) fn create_handle(
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
state: Box<dyn Any>,
) -> Result<InstanceHandle> {
let global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> =
Rc::new(RefCell::new(HashMap::new()));
let imports = Imports::new(
HashSet::new(),
PrimaryMap::new(),
@@ -42,7 +38,6 @@ pub(crate) fn create_handle(
Ok(InstanceHandle::new(
Rc::new(module),
global_exports,
finished_functions.into_boxed_slice(),
imports,
&data_initializers,

View File

@@ -64,7 +64,12 @@ impl Drop for TrampolineState {
}
}
unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i128) -> u32 {
unsafe extern "C" fn stub_fn(
vmctx: *mut VMContext,
_caller_vmctx: *mut VMContext,
call_id: u32,
values_vec: *mut i128,
) -> u32 {
let mut instance = InstanceHandle::from_vmctx(vmctx);
let (args, returns_len) = {
@@ -72,9 +77,9 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *m
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
let mut args = Vec::new();
for i in 1..signature.params.len() {
for i in 2..signature.params.len() {
args.push(Val::read_value_from(
values_vec.offset(i as isize - 1),
values_vec.offset(i as isize - 2),
signature.params[i].value_type,
))
}
@@ -116,12 +121,15 @@ fn make_trampoline(
let pointer_type = isa.pointer_type();
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Add the `vmctx` parameter.
// Add the caller/callee `vmctx` parameters.
stub_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
// Add the caller `vmctx` parameter.
stub_sig.params.push(ir::AbiParam::new(pointer_type));
// Add the `call_id` parameter.
stub_sig.params.push(ir::AbiParam::new(types::I32));
@@ -131,9 +139,10 @@ fn make_trampoline(
// Add error/trap return.
stub_sig.returns.push(ir::AbiParam::new(types::I32));
// Compute the size of the values vector. The vmctx and caller vmctx are passed separately.
let value_size = 16;
let values_vec_len = ((value_size as usize)
* cmp::max(signature.params.len() - 1, signature.returns.len()))
* cmp::max(signature.params.len() - 2, signature.returns.len()))
as u32;
let mut context = Context::new();
@@ -155,7 +164,7 @@ fn make_trampoline(
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
let mflags = MemFlags::trusted();
for i in 1..signature.params.len() {
for i in 2..signature.params.len() {
if i == 0 {
continue;
}
@@ -165,14 +174,21 @@ fn make_trampoline(
mflags,
val,
values_vec_ptr_val,
((i - 1) * value_size) as i32,
((i - 2) * value_size) as i32,
);
}
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
let ebb_params = builder.func.dfg.ebb_params(block0);
let vmctx_ptr_val = ebb_params[0];
let caller_vmctx_ptr_val = ebb_params[1];
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val];
let callee_args = vec![
vmctx_ptr_val,
caller_vmctx_ptr_val,
call_id_val,
values_vec_ptr_val,
];
let new_sig = builder.import_signature(stub_sig);
@@ -236,17 +252,18 @@ pub fn create_handle_with_function(
func: &Rc<dyn Callable + 'static>,
store: &Store,
) -> Result<InstanceHandle> {
let sig = match ft.get_wasmtime_signature() {
Some(sig) => sig.clone(),
None => bail!("not a supported core wasm signature {:?}", ft),
};
let isa = {
let isa_builder = native::builder();
let flag_builder = settings::builder();
isa_builder.finish(settings::Flags::new(flag_builder))
};
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {
Some(sig) => sig.clone(),
None => bail!("not a supported core wasm signature {:?}", ft),
};
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =

View File

@@ -191,12 +191,6 @@ fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
pub struct FuncType {
params: Box<[ValType]>,
results: Box<[ValType]>,
// `None` if params/results aren't wasm-compatible (e.g. use wasm interface
// types), or if they're not implemented (like anyref at the time of this
// writing)
//
// `Some` if they're all wasm-compatible.
signature: Option<ir::Signature>,
}
impl FuncType {
@@ -205,34 +199,7 @@ impl FuncType {
/// The function descriptor returned will represent a function which takes
/// `params` as arguments and returns `results` when it is finished.
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
use wasmtime_environ::ir::{types, AbiParam, ArgumentPurpose, Signature};
use wasmtime_jit::native;
let call_conv = native::call_conv();
let signature = params
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()
.and_then(|params| {
results
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()
.map(|results| (params, results))
})
.map(|(mut params, returns)| {
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
Signature {
params,
returns,
call_conv,
}
});
FuncType {
params,
results,
signature,
}
FuncType { params, results }
}
/// Returns the list of parameter types for this function.
@@ -248,8 +215,28 @@ impl FuncType {
/// Returns `Some` if this function signature was compatible with cranelift,
/// or `None` if one of the types/results wasn't supported or compatible
/// with cranelift.
pub(crate) fn get_wasmtime_signature(&self) -> Option<&ir::Signature> {
self.signature.as_ref()
pub(crate) fn get_wasmtime_signature(&self, pointer_type: ir::Type) -> Option<ir::Signature> {
use wasmtime_environ::ir::{types, AbiParam, ArgumentPurpose, Signature};
use wasmtime_jit::native;
let call_conv = native::call_conv();
let mut params = self
.params
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()?;
let returns = self
.results
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()?;
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
params.insert(1, AbiParam::new(pointer_type));
Some(Signature {
params,
returns,
call_conv,
})
}
/// Returns `None` if any types in the signature can't be converted to the
@@ -259,7 +246,7 @@ impl FuncType {
let params = signature
.params
.iter()
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
.skip(2) // skip the caller/callee vmctx
.map(|p| from_wasmtime_abiparam(p))
.collect::<Option<Vec<_>>>()?;
let results = signature
@@ -270,7 +257,6 @@ impl FuncType {
Some(FuncType {
params: params.into_boxed_slice(),
results: results.into_boxed_slice(),
signature: Some(signature),
})
}
}