Merge pull request #1832 from fitzgen/externref-stack-maps
externref: implement stack map-based garbage collection
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user