Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasmtime: Implement global.{get,set} for externref globals #1969

Merged
merged 2 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,7 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
("simd", "simd_splat") => return true, // FIXME Unsupported feature: proposed SIMD operator I32x4TruncSatF32x4S

// Still working on implementing these. See #929.
("reference_types", "global")
| ("reference_types", "linking")
| ("reference_types", "ref_func")
| ("reference_types", "ref_null")
| ("reference_types", "table_fill") => {
("reference_types", "table_fill") => {
return true;
}

Expand Down
108 changes: 81 additions & 27 deletions crates/environ/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ declare_builtin_functions! {
/// Returns an index to do a GC and then insert a `VMExternRef` into the
/// `VMExternRefActivationsTable`.
activations_table_insert_with_gc(vmctx, reference) -> ();
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_get(vmctx, i32) -> (reference);
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_set(vmctx, i32, reference) -> ();
}

impl BuiltinFunctionIndex {
Expand Down Expand Up @@ -432,6 +436,28 @@ impl<'module_environment> FuncEnvironment<'module_environment> {

new_ref_count
}

fn get_global_location(
&mut self,
func: &mut ir::Function,
index: GlobalIndex,
) -> (ir::GlobalValue, i32) {
let pointer_type = self.pointer_type();
let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.defined_global_index(index) {
let offset = i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
(vmctx, offset)
} else {
let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
let global = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::try_from(from_offset).unwrap()),
global_type: pointer_type,
readonly: true,
});
(global, 0)
}
}
}

// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
Expand Down Expand Up @@ -1042,19 +1068,56 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m

fn translate_custom_global_get(
&mut self,
_: cranelift_codegen::cursor::FuncCursor<'_>,
_: cranelift_wasm::GlobalIndex,
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
index: cranelift_wasm::GlobalIndex,
) -> WasmResult<ir::Value> {
unreachable!("we don't make any custom globals")
debug_assert_eq!(
self.module.globals[index].wasm_ty,
WasmType::ExternRef,
"We only use GlobalVariable::Custom for externref"
);

let builtin_index = BuiltinFunctionIndex::externref_global_get();
let builtin_sig = self
.builtin_function_signatures
.externref_global_get(&mut pos.func);

let (vmctx, builtin_addr) =
self.translate_load_builtin_function_address(&mut pos, builtin_index);

let global_index_arg = pos.ins().iconst(I32, index.as_u32() as i64);
let call_inst =
pos.ins()
.call_indirect(builtin_sig, builtin_addr, &[vmctx, global_index_arg]);

Ok(pos.func.dfg.first_result(call_inst))
}

fn translate_custom_global_set(
&mut self,
_: cranelift_codegen::cursor::FuncCursor<'_>,
_: cranelift_wasm::GlobalIndex,
_: ir::Value,
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
index: cranelift_wasm::GlobalIndex,
value: ir::Value,
) -> WasmResult<()> {
unreachable!("we don't make any custom globals")
debug_assert_eq!(
self.module.globals[index].wasm_ty,
WasmType::ExternRef,
"We only use GlobalVariable::Custom for externref"
);

let builtin_index = BuiltinFunctionIndex::externref_global_set();
let builtin_sig = self
.builtin_function_signatures
.externref_global_set(&mut pos.func);

let (vmctx, builtin_addr) =
self.translate_load_builtin_function_address(&mut pos, builtin_index);

let global_index_arg = pos.ins().iconst(I32, index.as_u32() as i64);
pos.ins()
.call_indirect(builtin_sig, builtin_addr, &[vmctx, global_index_arg, value]);

Ok(())
}

fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap> {
Expand Down Expand Up @@ -1141,28 +1204,19 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
func: &mut ir::Function,
index: GlobalIndex,
) -> WasmResult<GlobalVariable> {
let pointer_type = self.pointer_type();

let (ptr, offset) = {
let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.defined_global_index(index) {
let offset =
i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
(vmctx, offset)
} else {
let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
let global = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::try_from(from_offset).unwrap()),
global_type: pointer_type,
readonly: true,
});
(global, 0)
}
};
// Although `ExternRef`s live at the same memory location as any other
// type of global at the same index would, getting or setting them
// requires ref counting barriers. Therefore, we need to use
// `GlobalVariable::Custom`, as that is the only kind of
// `GlobalVariable` for which `cranelift-wasm` supports custom access
// translation.
if self.module.globals[index].wasm_ty == WasmType::ExternRef {
return Ok(GlobalVariable::Custom);
}

let (gv, offset) = self.get_global_location(func, index);
Ok(GlobalVariable::Memory {
gv: ptr,
gv,
offset: offset.into(),
ty: self.module.globals[index].ty,
})
Expand Down
27 changes: 25 additions & 2 deletions crates/runtime/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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()) }
Expand Down Expand Up @@ -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!(),
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions crates/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}
}
50 changes: 48 additions & 2 deletions crates/runtime/src/libcalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
14 changes: 14 additions & 0 deletions crates/runtime/src/sig_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
35 changes: 35 additions & 0 deletions crates/runtime/src/vmcontext.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down
Loading