Merge pull request #1832 from fitzgen/externref-stack-maps

externref: implement stack map-based garbage collection
This commit is contained in:
Nick Fitzgerald
2020-06-15 18:26:24 -07:00
committed by GitHub
31 changed files with 1355 additions and 165 deletions

View File

@@ -194,7 +194,8 @@ macro_rules! getters {
>(export.address);
let mut ret = None;
$(let $args = $args.into_abi();)*
catch_traps(export.vmctx, &instance.store, || {
invoke_wasm_and_catch_traps(export.vmctx, &instance.store, || {
ret = Some(fnptr(export.vmctx, ptr::null_mut(), $($args,)*));
})?;
@@ -265,14 +266,14 @@ impl Func {
// values produced are correct. There could be a bug in `func` that
// produces the wrong number or wrong types of values, and we need
// to catch that here.
for (i, (ret, ty)) in returns.iter_mut().zip(ty_clone.results()).enumerate() {
for (i, (ret, ty)) in returns.into_iter().zip(ty_clone.results()).enumerate() {
if ret.ty() != *ty {
return Err(Trap::new(
"function attempted to return an incompatible value",
));
}
unsafe {
ret.write_value_to(values_vec.add(i));
ret.write_value_to(&store, values_vec.add(i));
}
}
Ok(())
@@ -535,7 +536,7 @@ impl Func {
// Store the argument values into `values_vec`.
let param_tys = my_ty.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
for ((arg, slot), ty) in params.iter().cloned().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty {
bail!(
"argument type mismatch: found {} but expected {}",
@@ -547,12 +548,12 @@ impl Func {
bail!("cross-`Store` values are not currently supported");
}
unsafe {
arg.write_value_to(slot);
arg.write_value_to(&self.instance.store, slot);
}
}
// Call the trampoline.
catch_traps(self.export.vmctx, &self.instance.store, || unsafe {
invoke_wasm_and_catch_traps(self.export.vmctx, &self.instance.store, || unsafe {
(self.trampoline)(
self.export.vmctx,
ptr::null_mut(),
@@ -729,13 +730,18 @@ impl fmt::Debug for Func {
}
}
pub(crate) fn catch_traps(
pub(crate) fn invoke_wasm_and_catch_traps(
vmctx: *mut VMContext,
store: &Store,
closure: impl FnMut(),
) -> Result<(), Trap> {
let signalhandler = store.signal_handler();
unsafe {
let canary = 0;
let _auto_reset_canary = store
.externref_activations_table()
.set_stack_canary(&canary);
wasmtime_runtime::catch_traps(
vmctx,
store.engine().config().max_wasm_stack,

View File

@@ -50,6 +50,8 @@ fn instantiate(
config.memory_creator.as_ref().map(|a| a as _),
store.interrupts().clone(),
host,
&*store.externref_activations_table() as *const _ as *mut _,
&*store.stack_map_registry() as *const _ as *mut _,
)?;
// After we've created the `InstanceHandle` we still need to run
@@ -89,7 +91,7 @@ fn instantiate(
};
let vmctx_ptr = instance.handle.vmctx_ptr();
unsafe {
super::func::catch_traps(vmctx_ptr, store, || {
super::func::invoke_wasm_and_catch_traps(vmctx_ptr, store, || {
mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
@@ -183,10 +185,14 @@ impl Instance {
bail!("cross-`Engine` instantiation is not currently supported");
}
let info = module.register_frame_info();
store.register_jit_code(module.compiled_module().jit_code_ranges());
let host_info = Box::new({
let frame_info_registration = module.register_frame_info();
store.register_jit_code(module.compiled_module().jit_code_ranges());
store.register_stack_maps(&module);
frame_info_registration
});
let handle = instantiate(store, module.compiled_module(), imports, Box::new(info))?;
let handle = instantiate(store, module.compiled_module(), imports, host_info)?;
Ok(Instance {
handle,

View File

@@ -36,6 +36,11 @@ impl ExternRef {
&*self.inner
}
/// Get the strong reference count for this `ExternRef`.
pub fn strong_count(&self) -> usize {
self.inner.strong_count()
}
/// Does this `ExternRef` point to the same inner value as `other`?0
///
/// This is *only* pointer equality, and does *not* run any inner value's

View File

@@ -1,6 +1,7 @@
use crate::externals::MemoryCreator;
use crate::r#ref::ExternRef;
use crate::trampoline::{MemoryCreatorProxy, StoreInstanceHandle};
use crate::Module;
use anyhow::{bail, Result};
use std::any::Any;
use std::cell::RefCell;
@@ -14,12 +15,13 @@ use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
use wasmtime_environ::settings::{self, Configurable};
use wasmtime_environ::{ir, wasm, CacheConfig, Tunables};
use wasmtime_environ::{ir, isa::TargetIsa, wasm, CacheConfig, Tunables};
use wasmtime_jit::{native, CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{
debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, SignatureRegistry,
VMExternRef, VMInterrupts, VMSharedSignatureIndex,
StackMapRegistry, VMExternRef, VMExternRefActivationsTable, VMInterrupts,
VMSharedSignatureIndex,
};
// Runtime Environment
@@ -194,10 +196,16 @@ impl Config {
self.validating_config
.operator_config
.enable_reference_types = enable;
// The reference types proposal depends on the bulk memory proposal
self.flags
.set("enable_safepoints", if enable { "true" } else { "false" })
.unwrap();
// The reference types proposal depends on the bulk memory proposal.
if enable {
self.wasm_bulk_memory(true);
}
self
}
@@ -591,8 +599,12 @@ impl Config {
self
}
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
native::builder().finish(settings::Flags::new(self.flags.clone()))
}
fn build_compiler(&self) -> Compiler {
let isa = native::builder().finish(settings::Flags::new(self.flags.clone()));
let isa = self.target_isa();
Compiler::new(
isa,
self.strategy,
@@ -792,6 +804,8 @@ pub(crate) struct StoreInner {
signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>,
jit_code_ranges: RefCell<Vec<(usize, usize)>>,
host_info: RefCell<HashMap<HostInfoKey, Rc<RefCell<dyn Any>>>>,
externref_activations_table: Rc<VMExternRefActivationsTable>,
stack_map_registry: Rc<StackMapRegistry>,
}
struct HostInfoKey(VMExternRef);
@@ -832,6 +846,8 @@ impl Store {
signal_handler: RefCell::new(None),
jit_code_ranges: RefCell::new(Vec::new()),
host_info: RefCell::new(HashMap::new()),
externref_activations_table: Rc::new(VMExternRefActivationsTable::new()),
stack_map_registry: Rc::new(StackMapRegistry::default()),
}),
}
}
@@ -904,6 +920,24 @@ impl Store {
}
}
pub(crate) fn register_stack_maps(&self, module: &Module) {
let module = &module.compiled_module();
self.stack_map_registry().register_stack_maps(
module
.finished_functions()
.values()
.zip(module.stack_maps().values())
.map(|(func, stack_maps)| unsafe {
let ptr = (**func).as_ptr();
let len = (**func).len();
let start = ptr as usize;
let end = ptr as usize + len;
let range = start..end;
(range, &stack_maps[..])
}),
);
}
pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle {
self.inner.instances.borrow_mut().push(handle.clone());
StoreInstanceHandle {
@@ -1074,6 +1108,27 @@ impl Store {
bail!("interrupts aren't enabled for this `Store`")
}
}
pub(crate) fn externref_activations_table(&self) -> &Rc<VMExternRefActivationsTable> {
&self.inner.externref_activations_table
}
pub(crate) fn stack_map_registry(&self) -> &Rc<StackMapRegistry> {
&self.inner.stack_map_registry
}
/// Perform garbage collection of `ExternRef`s.
pub fn gc(&self) {
// For this crate's API, we ensure that `set_stack_canary` invariants
// are upheld for all host-->Wasm calls, and we register every module
// used with this store in `self.inner.stack_map_registry`.
unsafe {
wasmtime_runtime::gc(
&*self.inner.stack_map_registry,
&*self.inner.externref_activations_table,
);
}
}
}
impl Default for Store {

View File

@@ -46,6 +46,8 @@ pub(crate) fn create_handle(
signatures.into_boxed_slice(),
state,
store.interrupts().clone(),
&*store.externref_activations_table() as *const _ as *mut _,
&*store.stack_map_registry() as *const _ as *mut _,
)?;
Ok(store.add_instance(handle))
}

View File

@@ -208,11 +208,7 @@ pub fn create_handle_with_function(
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
store: &Store,
) -> Result<(StoreInstanceHandle, VMTrampoline)> {
let isa = {
let isa_builder = native::builder();
let flag_builder = settings::builder();
isa_builder.finish(settings::Flags::new(flag_builder))
};
let isa = store.engine().config().target_isa();
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {

View File

@@ -106,6 +106,10 @@ impl ValType {
ValType::F32 => Some(ir::types::F32),
ValType::F64 => Some(ir::types::F64),
ValType::V128 => Some(ir::types::I8X16),
#[cfg(target_pointer_width = "64")]
ValType::ExternRef => Some(ir::types::R64),
#[cfg(target_pointer_width = "32")]
ValType::ExternRef => Some(ir::types::R32),
_ => None,
}
}
@@ -117,6 +121,10 @@ impl ValType {
ir::types::F32 => Some(ValType::F32),
ir::types::F64 => Some(ValType::F64),
ir::types::I8X16 => Some(ValType::V128),
#[cfg(target_pointer_width = "64")]
ir::types::R64 => Some(ValType::ExternRef),
#[cfg(target_pointer_width = "32")]
ir::types::R32 => Some(ValType::ExternRef),
_ => None,
}
}

View File

@@ -79,15 +79,21 @@ impl Val {
}
}
pub(crate) unsafe fn write_value_to(&self, p: *mut u128) {
pub(crate) unsafe fn write_value_to(self, store: &Store, p: *mut u128) {
match self {
Val::I32(i) => ptr::write(p as *mut i32, *i),
Val::I64(i) => ptr::write(p as *mut i64, *i),
Val::F32(u) => ptr::write(p as *mut u32, *u),
Val::F64(u) => ptr::write(p as *mut u64, *u),
Val::V128(b) => ptr::write(p as *mut u128, *b),
Val::I32(i) => ptr::write(p as *mut i32, i),
Val::I64(i) => ptr::write(p as *mut i64, i),
Val::F32(u) => ptr::write(p as *mut u32, u),
Val::F64(u) => ptr::write(p as *mut u64, u),
Val::V128(b) => ptr::write(p as *mut u128, b),
Val::ExternRef(None) => ptr::write(p, 0),
Val::ExternRef(Some(x)) => ptr::write(p as *mut *mut u8, x.inner.clone().into_raw()),
Val::ExternRef(Some(x)) => {
let externref_ptr = x.inner.as_raw();
store
.externref_activations_table()
.insert_with_gc(x.inner, store.stack_map_registry());
ptr::write(p as *mut *mut u8, externref_ptr)
}
_ => unimplemented!("Val::write_value_to"),
}
}
@@ -105,7 +111,7 @@ impl Val {
Val::ExternRef(None)
} else {
Val::ExternRef(Some(ExternRef {
inner: VMExternRef::from_raw(raw),
inner: VMExternRef::clone_from_raw(raw),
store: store.weak(),
}))
}