wasmtime: Implement global.{get,set} for externref globals (#1969)
* wasmtime: Implement `global.{get,set}` for externref globals
We use libcalls to implement these -- unlike `table.{get,set}`, for which we
create inline JIT fast paths -- because no known toolchain actually uses
externref globals.
Part of #929
* wasmtime: Enable `{extern,func}ref` globals in the API
This commit is contained in:
@@ -29,7 +29,7 @@ use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityR
|
||||
use wasmtime_environ::wasm::{
|
||||
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
||||
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableElementType,
|
||||
TableIndex,
|
||||
TableIndex, WasmType,
|
||||
};
|
||||
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
|
||||
|
||||
@@ -226,6 +226,21 @@ impl Instance {
|
||||
unsafe { self.globals_ptr().add(index) }
|
||||
}
|
||||
|
||||
/// Get a raw pointer to the global at the given index regardless whether it
|
||||
/// is defined locally or imported from another module.
|
||||
///
|
||||
/// Panics if the index is out of bound or is the reserved value.
|
||||
pub(crate) fn defined_or_imported_global_ptr(
|
||||
&self,
|
||||
index: GlobalIndex,
|
||||
) -> *mut VMGlobalDefinition {
|
||||
if let Some(index) = self.module.local.defined_global_index(index) {
|
||||
self.global_ptr(index)
|
||||
} else {
|
||||
self.imported_global(index).from
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a pointer to the `VMGlobalDefinition`s.
|
||||
fn globals_ptr(&self) -> *mut VMGlobalDefinition {
|
||||
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) }
|
||||
@@ -1390,8 +1405,16 @@ fn initialize_globals(instance: &Instance) {
|
||||
};
|
||||
*to = from;
|
||||
}
|
||||
GlobalInit::RefFunc(f) => {
|
||||
*(*to).as_anyfunc_mut() = instance.get_caller_checked_anyfunc(f).unwrap()
|
||||
as *const VMCallerCheckedAnyfunc;
|
||||
}
|
||||
GlobalInit::RefNullConst => match global.wasm_ty {
|
||||
WasmType::FuncRef => *(*to).as_anyfunc_mut() = ptr::null(),
|
||||
WasmType::ExternRef => *(*to).as_externref_mut() = None,
|
||||
ty => panic!("unsupported reference type for global: {:?}", ty),
|
||||
},
|
||||
GlobalInit::Import => panic!("locally-defined global initialized as import"),
|
||||
GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,3 +67,14 @@ pub fn ref_type() -> wasmtime_environ::ir::Type {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Cranelift IR type used for pointer types for this target architecture.
|
||||
pub fn pointer_type() -> wasmtime_environ::ir::Type {
|
||||
if cfg!(target_pointer_width = "32") {
|
||||
wasmtime_environ::ir::types::I32
|
||||
} else if cfg!(target_pointer_width = "64") {
|
||||
wasmtime_environ::ir::types::I64
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,11 @@ use crate::externref::VMExternRef;
|
||||
use crate::table::Table;
|
||||
use crate::traphandlers::raise_lib_trap;
|
||||
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
|
||||
use std::ptr::NonNull;
|
||||
use std::mem;
|
||||
use std::ptr::{self, NonNull};
|
||||
use wasmtime_environ::wasm::{
|
||||
DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableElementType, TableIndex,
|
||||
DataIndex, DefinedMemoryIndex, ElemIndex, GlobalIndex, MemoryIndex, TableElementType,
|
||||
TableIndex,
|
||||
};
|
||||
|
||||
/// Implementation of f32.ceil
|
||||
@@ -430,3 +432,47 @@ pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc(
|
||||
let registry = &**instance.stack_map_registry();
|
||||
activations_table.insert_with_gc(externref, registry);
|
||||
}
|
||||
|
||||
/// Perform a Wasm `global.get` for `externref` globals.
|
||||
pub unsafe extern "C" fn wasmtime_externref_global_get(
|
||||
vmctx: *mut VMContext,
|
||||
index: u32,
|
||||
) -> *mut u8 {
|
||||
let index = GlobalIndex::from_u32(index);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
let global = instance.defined_or_imported_global_ptr(index);
|
||||
match (*global).as_externref().clone() {
|
||||
None => ptr::null_mut(),
|
||||
Some(externref) => {
|
||||
let raw = externref.as_raw();
|
||||
let activations_table = &**instance.externref_activations_table();
|
||||
let registry = &**instance.stack_map_registry();
|
||||
activations_table.insert_with_gc(externref, registry);
|
||||
raw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a Wasm `global.set` for `externref` globals.
|
||||
pub unsafe extern "C" fn wasmtime_externref_global_set(
|
||||
vmctx: *mut VMContext,
|
||||
index: u32,
|
||||
externref: *mut u8,
|
||||
) {
|
||||
let externref = if externref.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(VMExternRef::clone_from_raw(externref))
|
||||
};
|
||||
|
||||
let index = GlobalIndex::from_u32(index);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
let global = instance.defined_or_imported_global_ptr(index);
|
||||
|
||||
// Swap the new `externref` value into the global before we drop the old
|
||||
// value. This protects against an `externref` with a `Drop` implementation
|
||||
// that calls back into Wasm and touches this global again (we want to avoid
|
||||
// it observing a halfway-deinitialized value).
|
||||
let old = mem::replace((*global).as_externref_mut(), externref);
|
||||
drop(old);
|
||||
}
|
||||
|
||||
@@ -65,4 +65,18 @@ impl SignatureRegistry {
|
||||
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
|
||||
self.index2wasm.get(&idx).cloned()
|
||||
}
|
||||
|
||||
/// Looks up both a shared Wasm function signature and its associated native
|
||||
/// `ir::Signature` within this registry.
|
||||
///
|
||||
/// Note that for this operation to be semantically correct the `idx` must
|
||||
/// have previously come from a call to `register` of this same object.
|
||||
pub fn lookup_wasm_and_native_signatures(
|
||||
&self,
|
||||
idx: VMSharedSignatureIndex,
|
||||
) -> Option<(WasmFuncType, ir::Signature)> {
|
||||
let wasm = self.lookup_wasm(idx)?;
|
||||
let native = self.lookup_native(idx)?;
|
||||
Some((wasm, native))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! This file declares `VMContext` and several related structs which contain
|
||||
//! fields that compiled wasm code accesses directly.
|
||||
|
||||
use crate::externref::VMExternRef;
|
||||
use crate::instance::Instance;
|
||||
use std::any::Any;
|
||||
use std::ptr::NonNull;
|
||||
@@ -267,6 +268,7 @@ pub struct VMGlobalDefinition {
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal_definition {
|
||||
use super::VMGlobalDefinition;
|
||||
use crate::externref::VMExternRef;
|
||||
use more_asserts::assert_ge;
|
||||
use std::mem::{align_of, size_of};
|
||||
use wasmtime_environ::{Module, VMOffsets};
|
||||
@@ -296,6 +298,11 @@ mod test_vmglobal_definition {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||
assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_can_contain_externref() {
|
||||
assert!(size_of::<VMExternRef>() <= size_of::<VMGlobalDefinition>());
|
||||
}
|
||||
}
|
||||
|
||||
impl VMGlobalDefinition {
|
||||
@@ -423,6 +430,30 @@ impl VMGlobalDefinition {
|
||||
pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut [u8; 16])
|
||||
}
|
||||
|
||||
/// Return a reference to the value as an externref.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe fn as_externref(&self) -> &Option<VMExternRef> {
|
||||
&*(self.storage.as_ref().as_ptr() as *const Option<VMExternRef>)
|
||||
}
|
||||
|
||||
/// Return a mutable reference to the value as an externref.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe fn as_externref_mut(&mut self) -> &mut Option<VMExternRef> {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut Option<VMExternRef>)
|
||||
}
|
||||
|
||||
/// Return a reference to the value as an anyfunc.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe fn as_anyfunc(&self) -> *const VMCallerCheckedAnyfunc {
|
||||
*(self.storage.as_ref().as_ptr() as *const *const VMCallerCheckedAnyfunc)
|
||||
}
|
||||
|
||||
/// Return a mutable reference to the value as an anyfunc.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
pub unsafe fn as_anyfunc_mut(&mut self) -> &mut *const VMCallerCheckedAnyfunc {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut *const VMCallerCheckedAnyfunc)
|
||||
}
|
||||
}
|
||||
|
||||
/// An index into the shared signature registry, usable for checking signatures
|
||||
@@ -559,6 +590,10 @@ impl VMBuiltinFunctionsArray {
|
||||
wasmtime_drop_externref as usize;
|
||||
ptrs[BuiltinFunctionIndex::activations_table_insert_with_gc().index() as usize] =
|
||||
wasmtime_activations_table_insert_with_gc as usize;
|
||||
ptrs[BuiltinFunctionIndex::externref_global_get().index() as usize] =
|
||||
wasmtime_externref_global_get as usize;
|
||||
ptrs[BuiltinFunctionIndex::externref_global_set().index() as usize] =
|
||||
wasmtime_externref_global_set as usize;
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
for i in 0..ptrs.len() {
|
||||
|
||||
Reference in New Issue
Block a user