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:
Nick Fitzgerald
2020-07-02 14:04:01 -07:00
committed by GitHub
parent 3fa3ff2ece
commit bffd54c016
19 changed files with 520 additions and 79 deletions

View File

@@ -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!(),
}
}
}

View File

@@ -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!()
}
}

View File

@@ -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);
}

View File

@@ -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))
}
}

View File

@@ -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() {