Add an extra layer of indirection for shared globals

This commit is contained in:
Mark McCaskey
2020-07-02 18:23:19 -07:00
parent c717a25770
commit d4d738d97e
8 changed files with 153 additions and 92 deletions

View File

@@ -699,20 +699,19 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro
let (ptr, offset) = { let (ptr, offset) = {
let vmctx = self.vmctx(func); let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.local_global_index(index) { let from_offset = if let Some(def_index) = self.module.local_global_index(index) {
let offset = self.offsets.vmctx_vmglobal_definition(def_index)
i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
(vmctx, offset)
} else { } else {
let from_offset = self.offsets.vmctx_vmglobal_import_definition(index); self.offsets.vmctx_vmglobal_import_definition(index)
let global = func.create_global_value(ir::GlobalValueData::Load { };
base: vmctx, let global = func.create_global_value(ir::GlobalValueData::Load {
offset: Offset32::new(i32::try_from(from_offset).unwrap()), base: vmctx,
global_type: pointer_type, offset: Offset32::new(i32::try_from(from_offset).unwrap()),
readonly: true, global_type: pointer_type,
}); readonly: true,
(global, 0) });
}
(global, 0)
}; };
Ok(GlobalVariable::Memory { Ok(GlobalVariable::Memory {

View File

@@ -834,34 +834,34 @@ impl<'ctx, 'a> CtxType<'ctx, 'a> {
let global_value_type = global_type.ty; let global_value_type = global_type.ty;
let global_mutability = global_type.mutability; let global_mutability = global_type.mutability;
let global_ptr = let offset = if let Some(local_global_index) = wasm_module.local_global_index(index)
if let Some(local_global_index) = wasm_module.local_global_index(index) { {
let offset = offsets.vmctx_vmglobal_definition(local_global_index); offsets.vmctx_vmglobal_definition(local_global_index)
let offset = intrinsics.i32_ty.const_int(offset.into(), false); } else {
unsafe { cache_builder.build_gep(ctx_ptr_value, &[offset], "") } offsets.vmctx_vmglobal_import(index)
} else { };
let offset = offsets.vmctx_vmglobal_import(index); let offset = intrinsics.i32_ty.const_int(offset.into(), false);
let offset = intrinsics.i32_ty.const_int(offset.into(), false); let global_ptr = {
let global_ptr_ptr = let global_ptr_ptr =
unsafe { cache_builder.build_gep(ctx_ptr_value, &[offset], "") }; unsafe { cache_builder.build_gep(ctx_ptr_value, &[offset], "") };
let global_ptr_ptr = cache_builder let global_ptr_ptr = cache_builder
.build_bitcast( .build_bitcast(
global_ptr_ptr, global_ptr_ptr,
intrinsics.i32_ptr_ty.ptr_type(AddressSpace::Generic), intrinsics.i32_ptr_ty.ptr_type(AddressSpace::Generic),
"", "",
) )
.into_pointer_value(); .into_pointer_value();
let global_ptr = cache_builder let global_ptr = cache_builder
.build_load(global_ptr_ptr, "") .build_load(global_ptr_ptr, "")
.into_pointer_value(); .into_pointer_value();
tbaa_label( tbaa_label(
module, module,
intrinsics, intrinsics,
format!("global_ptr {}", index.as_u32()), format!("global_ptr {}", index.as_u32()),
global_ptr.as_instruction_value().unwrap(), global_ptr.as_instruction_value().unwrap(),
); );
global_ptr global_ptr
}; };
let global_ptr = cache_builder let global_ptr = cache_builder
.build_bitcast( .build_bitcast(
global_ptr, global_ptr,

View File

@@ -1898,7 +1898,13 @@ impl<'a> FuncGen<'a> {
self.module.local_global_index(global_index) self.module.local_global_index(global_index)
{ {
let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index); let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
Location::Memory(Machine::get_vmctx_reg(), offset as i32) self.emit_relaxed_binop(
Assembler::emit_mov,
Size::S64,
Location::Memory(Machine::get_vmctx_reg(), offset as i32),
Location::GPR(tmp),
);
Location::Memory(tmp, 0)
} else { } else {
// Imported globals require one level of indirection. // Imported globals require one level of indirection.
let offset = self let offset = self
@@ -1924,7 +1930,13 @@ impl<'a> FuncGen<'a> {
self.module.local_global_index(global_index) self.module.local_global_index(global_index)
{ {
let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index); let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
Location::Memory(Machine::get_vmctx_reg(), offset as i32) self.emit_relaxed_binop(
Assembler::emit_mov,
Size::S64,
Location::Memory(Machine::get_vmctx_reg(), offset as i32),
Location::GPR(tmp),
);
Location::Memory(tmp, 0)
} else { } else {
// Imported globals require one level of indirection. // Imported globals require one level of indirection.
let offset = self let offset = self

View File

@@ -109,7 +109,7 @@ pub trait Artifact {
.map_err(InstantiationError::Link)? .map_err(InstantiationError::Link)?
.into_boxed_slice(); .into_boxed_slice();
let finished_globals = tunables let finished_globals = tunables
.create_globals(&module) .create_globals(&module, &imports)
.map_err(InstantiationError::Link)? .map_err(InstantiationError::Link)?
.into_boxed_slice(); .into_boxed_slice();

View File

@@ -6,7 +6,7 @@ use wasm_common::{
MemoryType, Mutability, TableIndex, TableType, MemoryType, Mutability, TableIndex, TableType,
}; };
use wasmer_runtime::MemoryError; use wasmer_runtime::MemoryError;
use wasmer_runtime::{Global, Memory, ModuleInfo, Table}; use wasmer_runtime::{Global, Imports, Memory, ModuleInfo, Table};
use wasmer_runtime::{MemoryPlan, TablePlan}; use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunables for an engine /// Tunables for an engine
@@ -26,10 +26,12 @@ pub trait Tunables {
/// Create a global with the given value. /// Create a global with the given value.
fn create_initialized_global( fn create_initialized_global(
&self, &self,
module: &ModuleInfo,
imports: &Imports,
mutability: Mutability, mutability: Mutability,
init: GlobalInit, init: GlobalInit,
) -> Result<Arc<Global>, String> { ) -> Result<Arc<Global>, String> {
Ok(Arc::new(Global::new_with_init(mutability, init))) Ok(Global::new_with_init(module, imports, mutability, init).map_err(|e| e.to_string())?)
} }
/// Create a global with a default value. /// Create a global with a default value.
@@ -77,17 +79,23 @@ pub trait Tunables {
fn create_globals( fn create_globals(
&self, &self,
module: &ModuleInfo, module: &ModuleInfo,
imports: &Imports,
) -> Result<PrimaryMap<LocalGlobalIndex, Arc<Global>>, LinkError> { ) -> Result<PrimaryMap<LocalGlobalIndex, Arc<Global>>, LinkError> {
let num_imports = module.num_imported_globals; let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports); let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for (idx, &global_type) in module.globals.iter().skip(num_imports) { for (idx, &global_type) in module.globals.iter().skip(num_imports) {
let idx = LocalGlobalIndex::new(idx.index()); let idx = module.local_global_index(idx).unwrap();
vmctx_globals.push( vmctx_globals.push(
if let Some(&initializer) = module.global_initializers.get(idx) { if let Some(&initializer) = module.global_initializers.get(idx) {
self.create_initialized_global(global_type.mutability, initializer) self.create_initialized_global(
.map_err(LinkError::Resource)? module,
imports,
global_type.mutability,
initializer,
)
.map_err(LinkError::Resource)?
} else { } else {
self.create_global(global_type) self.create_global(global_type)
.map_err(LinkError::Resource)? .map_err(LinkError::Resource)?

View File

@@ -1,6 +1,9 @@
use crate::imports::Imports;
use crate::module::ModuleInfo;
use crate::vmcontext::VMGlobalDefinition; use crate::vmcontext::VMGlobalDefinition;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use thiserror::Error; use thiserror::Error;
use wasm_common::{GlobalInit, GlobalType, Mutability, Type, Value}; use wasm_common::{GlobalInit, GlobalType, Mutability, Type, Value};
@@ -52,53 +55,92 @@ impl Global {
} }
/// Create a new, zero bit-pattern initialized global from a [`GlobalType`]. /// Create a new, zero bit-pattern initialized global from a [`GlobalType`].
pub fn new_with_init(mutability: Mutability, value: GlobalInit) -> Self { pub fn new_with_init(
module: &ModuleInfo,
imports: &Imports,
mutability: Mutability,
value: GlobalInit,
) -> Result<Arc<Self>, GlobalError> {
let mut vm_global_definition = VMGlobalDefinition::new(); let mut vm_global_definition = VMGlobalDefinition::new();
let global_type = unsafe { let ty = unsafe {
match value { match value {
GlobalInit::I32Const(v) => { GlobalInit::I32Const(v) => {
*vm_global_definition.as_i32_mut() = v; *vm_global_definition.as_i32_mut() = v;
GlobalType { Type::I32
ty: Type::I32,
mutability,
}
} }
GlobalInit::I64Const(v) => { GlobalInit::I64Const(v) => {
*vm_global_definition.as_i64_mut() = v; *vm_global_definition.as_i64_mut() = v;
GlobalType { Type::I64
ty: Type::I64,
mutability,
}
} }
GlobalInit::F32Const(v) => { GlobalInit::F32Const(v) => {
*vm_global_definition.as_f32_mut() = v; *vm_global_definition.as_f32_mut() = v;
GlobalType { Type::F32
ty: Type::F32,
mutability,
}
} }
GlobalInit::F64Const(v) => { GlobalInit::F64Const(v) => {
*vm_global_definition.as_f64_mut() = v; *vm_global_definition.as_f64_mut() = v;
GlobalType { Type::F64
ty: Type::F64, }
mutability, GlobalInit::GetGlobal(global_index) => {
/*let other_type = module.globals.get(global_index).map_err(|e| "global does not exist")?;
*/
if let Some(global_value) = imports.globals.get(global_index) {
return Ok(global_value.from.clone());
} else {
let idx = module.local_global_index(global_index).unwrap();
if let Some(&init) = module.global_initializers.get(idx) {
// TODO: test for cycles/infinite loops here
return Self::new_with_init(module, imports, mutability, init);
}
// global may not exist here.
// TODO: tests should test if initializing with local global with no initializer and
// initializing with an invalid global index
todo!("check for initializer?, otherwise 0")
} }
} }
_ => unimplemented!("Global is not implemented for initializer {:?}", value), _ => unimplemented!(
"Global `new_with_init` is not implemented for initializer {:?}",
value
),
}
};
Ok(Arc::new(Self {
ty: GlobalType { ty, mutability },
vm_global_definition: Box::new(UnsafeCell::new(vm_global_definition)),
lock: Mutex::new(()),
}))
}
/// Create a new global initialized with the given value.
pub fn new_with_value<T>(mutability: Mutability, value: Value<T>) -> Self {
let mut vm_global_definition = VMGlobalDefinition::new();
let ty = unsafe {
match value {
Value::I32(v) => {
*vm_global_definition.as_i32_mut() = v;
Type::I32
}
Value::I64(v) => {
*vm_global_definition.as_i64_mut() = v;
Type::I64
}
Value::F32(v) => {
*vm_global_definition.as_f32_mut() = v;
Type::F32
}
Value::F64(v) => {
*vm_global_definition.as_f64_mut() = v;
Type::F64
}
_ => unimplemented!("Global `new_with_value` is not implemented for {:?}", value),
} }
}; };
Self { Self {
ty: global_type, ty: GlobalType { ty, mutability },
vm_global_definition: Box::new(UnsafeCell::new(vm_global_definition)), vm_global_definition: Box::new(UnsafeCell::new(vm_global_definition)),
lock: Mutex::new(()), lock: Mutex::new(()),
} }
} }
/// Create a new, zero bit-pattern initialized global from a [`GlobalType`].
pub fn new_with_value<T>(mutability: Mutability, value: Value<T>) -> Self {
Self::new_with_init(mutability, GlobalInit::from_value(value))
}
/// Get the type of the global. /// Get the type of the global.
pub fn ty(&self) -> &GlobalType { pub fn ty(&self) -> &GlobalType {
&self.ty &self.ty

View File

@@ -252,11 +252,12 @@ impl Instance {
/// Return the indexed `VMGlobalDefinition`. /// Return the indexed `VMGlobalDefinition`.
fn global_ptr(&self, index: LocalGlobalIndex) -> NonNull<VMGlobalDefinition> { fn global_ptr(&self, index: LocalGlobalIndex) -> NonNull<VMGlobalDefinition> {
let index = usize::try_from(index.as_u32()).unwrap(); let index = usize::try_from(index.as_u32()).unwrap();
NonNull::new(unsafe { self.globals_ptr().add(index) }).unwrap() // TODO:
NonNull::new(unsafe { *self.globals_ptr().add(index) }).unwrap()
} }
/// Return a pointer to the `VMGlobalDefinition`s. /// Return a pointer to the `VMGlobalDefinition`s.
fn globals_ptr(&self) -> *mut VMGlobalDefinition { fn globals_ptr(&self) -> *mut *mut VMGlobalDefinition {
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) } unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) }
} }
@@ -822,10 +823,7 @@ impl InstanceHandle {
let vmctx_globals = finished_globals let vmctx_globals = finished_globals
.values() .values()
.map(|m| { .map(|m| m.vmglobal())
let vmglobal_ptr = m.as_ref().vmglobal();
vmglobal_ptr.as_ref().clone()
})
.collect::<PrimaryMap<LocalGlobalIndex, _>>() .collect::<PrimaryMap<LocalGlobalIndex, _>>()
.into_boxed_slice(); .into_boxed_slice();
@@ -897,7 +895,7 @@ impl InstanceHandle {
); );
ptr::copy( ptr::copy(
vmctx_globals.values().as_slice().as_ptr(), vmctx_globals.values().as_slice().as_ptr(),
instance.globals_ptr() as *mut VMGlobalDefinition, instance.globals_ptr() as *mut NonNull<VMGlobalDefinition>,
vmctx_globals.len(), vmctx_globals.len(),
); );
ptr::write( ptr::write(
@@ -1279,11 +1277,12 @@ fn initialize_globals(instance: &Instance) {
GlobalInit::F64Const(x) => *(*to).as_f64_mut() = *x, GlobalInit::F64Const(x) => *(*to).as_f64_mut() = *x,
GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = *x.bytes(), GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = *x.bytes(),
GlobalInit::GetGlobal(x) => { GlobalInit::GetGlobal(x) => {
let from = if let Some(def_x) = module.local_global_index(*x) { let from: VMGlobalDefinition =
instance.global(def_x) if let Some(def_x) = module.local_global_index(*x) {
} else { instance.global(def_x)
instance.imported_global(*x).definition.as_ref().clone() } else {
}; instance.imported_global(*x).definition.as_ref().clone()
};
*to = from; *to = from;
} }
GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(), GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(),

View File

@@ -277,16 +277,17 @@ impl VMOffsets {
} }
} }
/// Offsets for [`VMGlobalDefinition`]. /// Offsets for a non-null pointer to a [`VMGlobalDefinition`] used as a local global.
/// ///
/// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition /// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition
impl VMOffsets { impl VMOffsets {
/// Return the size of [`VMGlobalDefinition`]; this is the size of the largest value type (i.e. a /// Return the size of a pointer to a [`VMGlobalDefinition`];
/// V128).
/// ///
/// The underlying global itself is the size of the largest value type (i.e. a V128),
/// however the size of this type is just the size of a pointer.
/// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition /// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition
pub const fn size_of_vmglobal_definition(&self) -> u8 { pub const fn size_of_vmglobal_local(&self) -> u8 {
16 self.pointer_size
} }
} }
@@ -426,7 +427,7 @@ impl VMOffsets {
self.vmctx_globals_begin() self.vmctx_globals_begin()
.checked_add( .checked_add(
self.num_local_globals self.num_local_globals
.checked_mul(u32::from(self.size_of_vmglobal_definition())) .checked_mul(u32::from(self.size_of_vmglobal_local()))
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap()
@@ -559,7 +560,7 @@ impl VMOffsets {
.checked_add( .checked_add(
index index
.as_u32() .as_u32()
.checked_mul(u32::from(self.size_of_vmglobal_definition())) .checked_mul(u32::from(self.size_of_vmglobal_local()))
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap()