From 9afcd608a5334560f07756c5fab1100aaa87ccc0 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 13 Sep 2024 13:14:19 -0700 Subject: [PATCH] Split GC type layout computation into a shared trait in `wasmtime_environ` This stuff was previously living in the `GcRuntime` trait, but it turns out that the compiler will also need to know the layout of GC types, who'd have thunk it. --- crates/cranelift/src/gc.rs | 5 + crates/cranelift/src/gc/enabled.rs | 15 ++- crates/environ/src/gc.rs | 125 ++++++++++++++++++ crates/environ/src/gc/drc.rs | 100 ++++++++++++++ .../src/runtime/gc/enabled/arrayref.rs | 4 +- .../src/runtime/gc/enabled/structref.rs | 4 +- crates/wasmtime/src/runtime/type_registry.rs | 8 +- crates/wasmtime/src/runtime/vm/gc.rs | 2 +- crates/wasmtime/src/runtime/vm/gc/enabled.rs | 2 +- .../src/runtime/vm/gc/enabled/arrayref.rs | 3 +- .../wasmtime/src/runtime/vm/gc/enabled/drc.rs | 124 +++++------------ .../src/runtime/vm/gc/enabled/structref.rs | 3 +- .../wasmtime/src/runtime/vm/gc/gc_runtime.rs | 122 +---------------- 13 files changed, 288 insertions(+), 229 deletions(-) create mode 100644 crates/environ/src/gc/drc.rs diff --git a/crates/cranelift/src/gc.rs b/crates/cranelift/src/gc.rs index 8e5f5db82ed6..8cc9f0d26165 100644 --- a/crates/cranelift/src/gc.rs +++ b/crates/cranelift/src/gc.rs @@ -19,6 +19,7 @@ use enabled as imp; mod disabled; #[cfg(not(feature = "gc"))] use disabled as imp; +use wasmtime_environ::GcTypeLayouts; /// Get the GC compiler configured for the given function environment. pub fn gc_compiler(func_env: &FuncEnvironment<'_>) -> Box { @@ -82,6 +83,10 @@ pub fn gc_ref_table_fill_builtin( /// A trait for different collectors to emit any GC barriers they might require. pub trait GcCompiler { + /// Get the GC type layouts for this GC compiler. + #[allow(dead_code)] // Used in future PRs. + fn layouts(&self) -> &dyn GcTypeLayouts; + /// Emit a read barrier for when we are cloning a GC reference onto the Wasm /// stack. /// diff --git a/crates/cranelift/src/gc/enabled.rs b/crates/cranelift/src/gc/enabled.rs index f9c8deae910f..1d3fe0cf53e1 100644 --- a/crates/cranelift/src/gc/enabled.rs +++ b/crates/cranelift/src/gc/enabled.rs @@ -3,11 +3,13 @@ use crate::func_environ::FuncEnvironment; use cranelift_codegen::ir::{self, condcodes::IntCC, InstBuilder}; use cranelift_frontend::FunctionBuilder; use cranelift_wasm::{TargetEnvironment, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult}; -use wasmtime_environ::{PtrSize, I31_DISCRIMINANT, NON_NULL_NON_I31_MASK}; +use wasmtime_environ::{ + drc::DrcTypeLayouts, GcTypeLayouts, PtrSize, I31_DISCRIMINANT, NON_NULL_NON_I31_MASK, +}; /// Get the default GC compiler. pub fn gc_compiler(_func_env: &FuncEnvironment<'_>) -> Box { - Box::new(DrcCompiler) + Box::new(DrcCompiler::default()) } pub fn unbarriered_load_gc_ref( @@ -184,7 +186,10 @@ impl FuncEnvironment<'_> { } } -struct DrcCompiler; +#[derive(Default)] +struct DrcCompiler { + layouts: DrcTypeLayouts, +} impl DrcCompiler { /// Generate code to load the given GC reference's ref count. @@ -276,6 +281,10 @@ impl DrcCompiler { } impl GcCompiler for DrcCompiler { + fn layouts(&self) -> &dyn GcTypeLayouts { + &self.layouts + } + fn translate_read_gc_reference( &mut self, func_env: &mut FuncEnvironment<'_>, diff --git a/crates/environ/src/gc.rs b/crates/environ/src/gc.rs index c28bfcff8e6f..46e95d7d6665 100644 --- a/crates/environ/src/gc.rs +++ b/crates/environ/src/gc.rs @@ -9,6 +9,12 @@ //! on our various `gc` cargo features is the actual garbage collection //! functions and their associated impact on binary size anyways. +#[cfg(feature = "gc")] +pub mod drc; + +use crate::prelude::*; +use wasmtime_types::{WasmArrayType, WasmStructType}; + /// Discriminant to check whether GC reference is an `i31ref` or not. pub const I31_DISCRIMINANT: u64 = 1; @@ -16,6 +22,125 @@ pub const I31_DISCRIMINANT: u64 = 1; /// with a single bitwise-and operation. pub const NON_NULL_NON_I31_MASK: u64 = !I31_DISCRIMINANT; +/// TODO FITZGEN +pub trait GcTypeLayouts { + /// Get this collector's layout for the given array type. + fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout; + + /// Get this collector's layout for the given struct type. + fn struct_layout(&self, ty: &WasmStructType) -> GcStructLayout; +} + +/// The layout of a GC-managed object. +#[derive(Clone, Debug)] +pub enum GcLayout { + /// The layout of a GC-managed array object. + Array(GcArrayLayout), + + /// The layout of a GC-managed struct object. + Struct(GcStructLayout), +} + +impl From for GcLayout { + fn from(layout: GcArrayLayout) -> Self { + Self::Array(layout) + } +} + +impl From for GcLayout { + fn from(layout: GcStructLayout) -> Self { + Self::Struct(layout) + } +} + +impl GcLayout { + /// Get the underlying `GcStructLayout`, or panic. + #[track_caller] + pub fn unwrap_struct(&self) -> &GcStructLayout { + match self { + Self::Struct(s) => s, + _ => panic!("GcLayout::unwrap_struct on non-struct GC layout"), + } + } + + /// Get the underlying `GcArrayLayout`, or panic. + #[track_caller] + pub fn unwrap_array(&self) -> &GcArrayLayout { + match self { + Self::Array(a) => a, + _ => panic!("GcLayout::unwrap_array on non-array GC layout"), + } + } +} + +/// The layout of a GC-managed array. +/// +/// This layout is only valid for use with the GC runtime that created it. It is +/// not valid to use one GC runtime's layout with another GC runtime, doing so +/// is memory safe but will lead to general incorrectness like panics and wrong +/// results. +/// +/// All offsets are from the start of the object; that is, the size of the GC +/// header (for example) is included in the offset. +/// +/// All arrays are composed of the generic `VMGcHeader`, followed by +/// collector-specific fields, followed by the contiguous array elements +/// themselves. The array elements must be aligned to the element type's natural +/// alignment. +#[derive(Clone, Debug)] +#[allow(dead_code)] // Not used yet, but added for completeness. +pub struct GcArrayLayout { + /// The size of this array object, ignoring its elements. + pub size: u32, + + /// The alignment of this array. + pub align: u32, + + /// The offset of the array's length. + pub length_field_offset: u32, + + /// The offset from where this array's contiguous elements begin. + pub elems_offset: u32, + + /// The size and natural alignment of each element in this array. + pub elem_size: u32, +} + +impl GcArrayLayout { + /// Get the total size of this array for a given length of elements. + pub fn size_for_len(&self, len: u32) -> u32 { + self.size + len * self.elem_size + } + + /// Get the offset of the `i`th element in an array with this layout. + #[inline] + pub fn elem_offset(&self, i: u32, elem_size: u32) -> u32 { + self.elems_offset + i * elem_size + } +} + +/// The layout for a GC-managed struct type. +/// +/// This layout is only valid for use with the GC runtime that created it. It is +/// not valid to use one GC runtime's layout with another GC runtime, doing so +/// is memory safe but will lead to general incorrectness like panics and wrong +/// results. +/// +/// All offsets are from the start of the object; that is, the size of the GC +/// header (for example) is included in the offset. +#[derive(Clone, Debug)] +pub struct GcStructLayout { + /// The size of this struct. + pub size: u32, + + /// The alignment of this struct. + pub align: u32, + + /// The fields of this struct. The `i`th entry is the `i`th struct field's + /// offset in the struct. + pub fields: Vec, +} + /// The kind of an object in a GC heap. /// /// Note that this type is accessed from Wasm JIT code. diff --git a/crates/environ/src/gc/drc.rs b/crates/environ/src/gc/drc.rs new file mode 100644 index 000000000000..80c683d1b11a --- /dev/null +++ b/crates/environ/src/gc/drc.rs @@ -0,0 +1,100 @@ +//! Layout of Wasm GC objects in the deferred reference-counting collector. + +use super::*; +use wasmtime_types::{WasmStorageType, WasmValType}; + +/// The size of the `VMDrcHeader` header for GC objects. +pub const HEADER_SIZE: u32 = 16; + +/// The align of the `VMDrcHeader` header for GC objects. +pub const HEADER_ALIGN: u32 = 8; + +/// The offset of the length field in a `VMDrcArrayHeader`. +pub const ARRAY_LENGTH_OFFSET: u32 = HEADER_SIZE; + +/// Align `offset` up to `bytes`, updating `max_align` if `align` is the +/// new maximum alignment, and returning the aligned offset. +fn align_up(offset: &mut u32, max_align: &mut u32, align: u32) -> u32 { + debug_assert!(max_align.is_power_of_two()); + debug_assert!(align.is_power_of_two()); + *offset = offset.checked_add(align - 1).unwrap() & !(align - 1); + *max_align = core::cmp::max(*max_align, align); + *offset +} + +/// Define a new field of size and alignment `bytes`, updating the object's +/// total `size` and `align` as necessary. The offset of the new field is +/// returned. +fn field(size: &mut u32, align: &mut u32, bytes: u32) -> u32 { + let offset = align_up(size, align, bytes); + *size += bytes; + offset +} + +fn size_of_wasm_ty(ty: &WasmStorageType) -> u32 { + match ty { + WasmStorageType::I8 => 1, + WasmStorageType::I16 => 2, + WasmStorageType::Val(ty) => match ty { + WasmValType::I32 | WasmValType::F32 | WasmValType::Ref(_) => 4, + WasmValType::I64 | WasmValType::F64 => 8, + WasmValType::V128 => 16, + }, + } +} + +/// The layout of Wasm GC objects in the deferred reference-counting collector. +#[derive(Default)] +pub struct DrcTypeLayouts; + +impl GcTypeLayouts for DrcTypeLayouts { + fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout { + let mut size = HEADER_SIZE; + let mut align = HEADER_ALIGN; + + let length_field_offset = field(&mut size, &mut align, 4); + debug_assert_eq!(length_field_offset, ARRAY_LENGTH_OFFSET); + + let elem_size = size_of_wasm_ty(&ty.0.element_type); + let elems_offset = align_up(&mut size, &mut align, elem_size); + + GcArrayLayout { + size, + align, + length_field_offset, + elems_offset, + elem_size, + } + } + + fn struct_layout(&self, ty: &WasmStructType) -> GcStructLayout { + // Process each field, aligning it to its natural alignment. + // + // We don't try and do any fancy field reordering to minimize padding + // (yet?) because (a) the toolchain probably already did that and (b) + // we're just doing the simple thing first. We can come back and improve + // things here if we find that (a) isn't actually holding true in + // practice. + let mut size = HEADER_SIZE; + let mut align = HEADER_ALIGN; + + let fields = ty + .fields + .iter() + .map(|f| { + let field_size = size_of_wasm_ty(&f.element_type); + field(&mut size, &mut align, field_size) + }) + .collect(); + + // Ensure that the final size is a multiple of the alignment, for + // simplicity. + align_up(&mut size, &mut 16, align); + + GcStructLayout { + size, + align, + fields, + } + } +} diff --git a/crates/wasmtime/src/runtime/gc/enabled/arrayref.rs b/crates/wasmtime/src/runtime/gc/enabled/arrayref.rs index dae784bace86..fb23cfaef384 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/arrayref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/arrayref.rs @@ -2,7 +2,7 @@ use crate::runtime::vm::VMGcRef; use crate::store::StoreId; -use crate::vm::{GcArrayLayout, GcLayout, VMArrayRef, VMGcHeader}; +use crate::vm::{VMArrayRef, VMGcHeader}; use crate::{ prelude::*, store::{AutoAssertNoGc, StoreContextMut, StoreOpaque}, @@ -11,7 +11,7 @@ use crate::{ }; use crate::{AnyRef, FieldType}; use core::mem::{self, MaybeUninit}; -use wasmtime_environ::{VMGcKind, VMSharedTypeIndex}; +use wasmtime_environ::{GcArrayLayout, GcLayout, VMGcKind, VMSharedTypeIndex}; /// An allocator for a particular Wasm GC array type. /// diff --git a/crates/wasmtime/src/runtime/gc/enabled/structref.rs b/crates/wasmtime/src/runtime/gc/enabled/structref.rs index 48849e405751..1438a0d56f3b 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/structref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/structref.rs @@ -2,7 +2,7 @@ use crate::runtime::vm::VMGcRef; use crate::store::StoreId; -use crate::vm::{GcLayout, GcStructLayout, VMGcHeader, VMStructRef}; +use crate::vm::{VMGcHeader, VMStructRef}; use crate::{ prelude::*, store::{AutoAssertNoGc, StoreContextMut, StoreOpaque}, @@ -11,7 +11,7 @@ use crate::{ }; use crate::{AnyRef, FieldType}; use core::mem::{self, MaybeUninit}; -use wasmtime_environ::{VMGcKind, VMSharedTypeIndex}; +use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex}; /// An allocator for a particular Wasm GC struct type. /// diff --git a/crates/wasmtime/src/runtime/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs index 1a4b63eedf5b..611f579c98d3 100644 --- a/crates/wasmtime/src/runtime/type_registry.rs +++ b/crates/wasmtime/src/runtime/type_registry.rs @@ -5,7 +5,7 @@ use crate::prelude::*; use crate::sync::RwLock; -use crate::vm::{GcLayout, GcRuntime}; +use crate::vm::GcRuntime; use crate::Engine; use alloc::borrow::Cow; use alloc::sync::Arc; @@ -22,7 +22,7 @@ use core::{ }; use hashbrown::HashSet; use wasmtime_environ::{ - iter_entity_range, packed_option::PackedOption, EngineOrModuleTypeIndex, + iter_entity_range, packed_option::PackedOption, EngineOrModuleTypeIndex, GcLayout, ModuleInternedTypeIndex, ModuleTypes, PrimaryMap, SecondaryMap, TypeTrace, VMSharedTypeIndex, WasmRecGroup, WasmSubType, }; @@ -749,10 +749,10 @@ impl TypeRegistryInner { let gc_layout = match &ty.composite_type { wasmtime_environ::WasmCompositeType::Func(_) => None, wasmtime_environ::WasmCompositeType::Array(a) => { - Some(gc_runtime.array_layout(a).into()) + Some(gc_runtime.layouts().array_layout(a).into()) } wasmtime_environ::WasmCompositeType::Struct(s) => { - Some(gc_runtime.struct_layout(s).into()) + Some(gc_runtime.layouts().struct_layout(s).into()) } }; diff --git a/crates/wasmtime/src/runtime/vm/gc.rs b/crates/wasmtime/src/runtime/vm/gc.rs index beafb0c71951..cfbc6677649f 100644 --- a/crates/wasmtime/src/runtime/vm/gc.rs +++ b/crates/wasmtime/src/runtime/vm/gc.rs @@ -22,7 +22,7 @@ use crate::prelude::*; use crate::runtime::vm::GcHeapAllocationIndex; use core::ptr; use core::{any::Any, num::NonZeroUsize}; -use wasmtime_environ::{StackMap, VMGcKind, VMSharedTypeIndex}; +use wasmtime_environ::{GcArrayLayout, GcStructLayout, StackMap, VMGcKind, VMSharedTypeIndex}; /// Used by the runtime to lookup information about a module given a /// program counter value. diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled.rs b/crates/wasmtime/src/runtime/vm/gc/enabled.rs index 6708a509cc0a..0a7466537608 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled.rs @@ -17,7 +17,7 @@ use crate::runtime::vm::GcRuntime; /// Get the default GC runtime. pub fn default_gc_runtime() -> impl GcRuntime { - DrcCollector + DrcCollector::default() } /// The default GC heap capacity: 512KiB. diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/arrayref.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/arrayref.rs index 6385b5e0ec37..412ccabbd759 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/arrayref.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/arrayref.rs @@ -3,11 +3,10 @@ use crate::{ prelude::*, runtime::vm::{GcHeap, GcStore, VMGcRef}, store::{AutoAssertNoGc, StoreOpaque}, - vm::GcArrayLayout, AnyRef, ExternRef, HeapType, RootedGcRefImpl, StorageType, Val, ValType, }; use core::fmt; -use wasmtime_environ::VMGcKind; +use wasmtime_environ::{GcArrayLayout, VMGcKind}; /// A `VMGcRef` that we know points to a `array`. /// diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs index abc1a799bf4f..48988e1d8b6c 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs @@ -45,9 +45,8 @@ use super::free_list::FreeList; use super::{VMArrayRef, VMGcObjectDataMut, VMStructRef}; use crate::prelude::*; use crate::runtime::vm::{ - ExternRefHostDataId, ExternRefHostDataTable, GarbageCollection, GcArrayLayout, GcHeap, - GcHeapObject, GcProgress, GcRootsIter, GcRuntime, GcStructLayout, Mmap, TypedGcRef, - VMExternRef, VMGcHeader, VMGcRef, + ExternRefHostDataId, ExternRefHostDataTable, GarbageCollection, GcHeap, GcHeapObject, + GcProgress, GcRootsIter, GcRuntime, Mmap, TypedGcRef, VMExternRef, VMGcHeader, VMGcRef, }; use core::ops::{Deref, DerefMut}; use core::{ @@ -59,7 +58,8 @@ use core::{ ptr::{self, NonNull}, }; use hashbrown::HashSet; -use wasmtime_environ::{VMGcKind, VMSharedTypeIndex, WasmStorageType, WasmValType}; +use wasmtime_environ::drc::DrcTypeLayouts; +use wasmtime_environ::{GcArrayLayout, GcStructLayout, GcTypeLayouts, VMGcKind, VMSharedTypeIndex}; /// The deferred reference-counting (DRC) collector. /// @@ -68,93 +68,20 @@ use wasmtime_environ::{VMGcKind, VMSharedTypeIndex, WasmStorageType, WasmValType /// /// This is not a moving collector; it doesn't have a nursery or do any /// compaction. -pub struct DrcCollector; - -/// Align `offset` up to `bytes`, updating `max_align` if `align` is the -/// new maximum alignment, and returning the aligned offset. -fn align_up(offset: &mut u32, max_align: &mut u32, align: u32) -> u32 { - debug_assert!(max_align.is_power_of_two()); - debug_assert!(align.is_power_of_two()); - *offset = offset.checked_add(align - 1).unwrap() & !(align - 1); - *max_align = core::cmp::max(*max_align, align); - *offset +#[derive(Default)] +pub struct DrcCollector { + layouts: DrcTypeLayouts, } -/// Define a new field of size and alignment `bytes`, updating the object's -/// total `size` and `align` as necessary. The offset of the new field is -/// returned. -fn field(size: &mut u32, align: &mut u32, bytes: u32) -> u32 { - let offset = align_up(size, align, bytes); - *size += bytes; - offset -} - -fn size_of_wasm_ty(ty: &WasmStorageType) -> u32 { - match ty { - WasmStorageType::I8 => 1, - WasmStorageType::I16 => 2, - WasmStorageType::Val(ty) => match ty { - WasmValType::I32 | WasmValType::F32 | WasmValType::Ref(_) => 4, - WasmValType::I64 | WasmValType::F64 => 8, - WasmValType::V128 => 16, - }, +unsafe impl GcRuntime for DrcCollector { + fn layouts(&self) -> &dyn GcTypeLayouts { + &self.layouts } -} -unsafe impl GcRuntime for DrcCollector { fn new_gc_heap(&self) -> Result> { let heap = DrcHeap::new()?; Ok(Box::new(heap) as _) } - - fn array_layout(&self, ty: &wasmtime_environ::WasmArrayType) -> GcArrayLayout { - let mut size = VMDrcHeader::HEADER_SIZE; - let mut align = VMDrcHeader::HEADER_ALIGN; - let length_field_offset = field(&mut size, &mut align, 4); - debug_assert_eq!( - length_field_offset, - u32::try_from(core::mem::offset_of!(VMDrcArrayHeader, length)).unwrap(), - ); - let elem_size = size_of_wasm_ty(&ty.0.element_type); - let elems_offset = align_up(&mut size, &mut align, elem_size); - GcArrayLayout { - size, - align, - length_field_offset, - elems_offset, - elem_size, - } - } - - fn struct_layout(&self, ty: &wasmtime_environ::WasmStructType) -> GcStructLayout { - // Process each field, aligning it to its natural alignment. - // - // We don't try and do any fancy field reordering to minimize padding - // (yet?) because (a) the toolchain probably already did that and (b) - // we're just doing the simple thing first. We can come back and improve - // things here if we find that (a) isn't actually holding true in - // practice. - let mut size = VMDrcHeader::HEADER_SIZE; - let mut align = VMDrcHeader::HEADER_ALIGN; - let fields = ty - .fields - .iter() - .map(|f| { - let field_size = size_of_wasm_ty(&f.element_type); - field(&mut size, &mut align, field_size) - }) - .collect(); - - // Ensure that the final size is a multiple of the alignment, for - // simplicity. - align_up(&mut size, &mut 16, align); - - GcStructLayout { - size, - align, - fields, - } - } } /// A deferred reference-counting (DRC) heap. @@ -561,18 +488,7 @@ unsafe impl GcHeapObject for VMDrcHeader { } } -const _: () = { - assert!((VMDrcHeader::HEADER_SIZE as usize) == core::mem::size_of::()); - assert!((VMDrcHeader::HEADER_ALIGN as usize) == core::mem::align_of::()); -}; - impl VMDrcHeader { - /// The size of `VMDrcHeader` on *all* architectures. - const HEADER_SIZE: u32 = VMGcHeader::HEADER_SIZE + 8; - - /// The alignment of `VMDrcHeader` on *all* architectures. - const HEADER_ALIGN: u32 = 8; - /// The size of this header's object. /// /// This is stored in the inner `VMGcHeader`'s reserved bits. @@ -1076,6 +992,26 @@ impl DerefMut for DebugOnly { mod tests { use super::*; + #[test] + fn vm_drc_header_size_align() { + assert_eq!( + (wasmtime_environ::drc::HEADER_SIZE as usize), + core::mem::size_of::() + ); + assert_eq!( + (wasmtime_environ::drc::HEADER_ALIGN as usize), + core::mem::align_of::() + ); + } + + #[test] + fn vm_drc_array_header_length_offset() { + assert_eq!( + wasmtime_environ::drc::ARRAY_LENGTH_OFFSET, + u32::try_from(core::mem::offset_of!(VMDrcArrayHeader, length)).unwrap(), + ); + } + #[test] fn ref_count_is_at_correct_offset() { let extern_data = VMDrcHeader { diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/structref.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/structref.rs index db773b7f9cda..a7f48d86e1fc 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/structref.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/structref.rs @@ -3,11 +3,10 @@ use crate::{ prelude::*, runtime::vm::{GcHeap, GcStore, VMGcRef}, store::AutoAssertNoGc, - vm::GcStructLayout, AnyRef, ExternRef, HeapType, RootedGcRefImpl, StorageType, Val, ValType, }; use core::fmt; -use wasmtime_environ::VMGcKind; +use wasmtime_environ::{GcStructLayout, VMGcKind}; /// A `VMGcRef` that we know points to a `struct`. /// diff --git a/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs b/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs index d36e64ecd740..07980b362fef 100644 --- a/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs +++ b/crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs @@ -8,7 +8,7 @@ use crate::runtime::vm::{ use core::marker; use core::ptr; use core::{any::Any, num::NonZeroUsize}; -use wasmtime_environ::{VMSharedTypeIndex, WasmArrayType, WasmStructType}; +use wasmtime_environ::{GcArrayLayout, GcStructLayout, GcTypeLayouts, VMSharedTypeIndex}; use super::VMGcObjectDataMut; @@ -34,14 +34,11 @@ use super::VMGcObjectDataMut; /// safety. Implementations of this trait may not add new safety invariants, not /// already documented in this trait's interface, that callers need to uphold. pub unsafe trait GcRuntime: 'static + Send + Sync { + /// Get this collector's GC type layouts. + fn layouts(&self) -> &dyn GcTypeLayouts; + /// Construct a new GC heap. fn new_gc_heap(&self) -> Result>; - - /// Get this collector's layout for the given array type. - fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout; - - /// Get this collector's layout for the given struct type. - fn struct_layout(&self, ty: &WasmStructType) -> GcStructLayout; } /// A heap that manages garbage-collected objects. @@ -386,117 +383,6 @@ pub unsafe trait GcHeap: 'static + Send + Sync { fn reset(&mut self); } -/// The layout of a GC-managed object. -#[derive(Clone, Debug)] -pub enum GcLayout { - /// The layout of a GC-managed array object. - #[allow(dead_code)] // Not used yet, but added for completeness. - Array(GcArrayLayout), - - /// The layout of a GC-managed struct object. - Struct(GcStructLayout), -} - -impl From for GcLayout { - fn from(layout: GcArrayLayout) -> Self { - Self::Array(layout) - } -} - -impl From for GcLayout { - fn from(layout: GcStructLayout) -> Self { - Self::Struct(layout) - } -} - -impl GcLayout { - /// Get the underlying `GcStructLayout`, or panic. - #[track_caller] - pub fn unwrap_struct(&self) -> &GcStructLayout { - match self { - Self::Struct(s) => s, - _ => panic!("GcLayout::unwrap_struct on non-struct GC layout"), - } - } - - /// Get the underlying `GcArrayLayout`, or panic. - #[track_caller] - pub fn unwrap_array(&self) -> &GcArrayLayout { - match self { - Self::Array(a) => a, - _ => panic!("GcLayout::unwrap_array on non-array GC layout"), - } - } -} - -/// The layout of a GC-managed array. -/// -/// This layout is only valid for use with the GC runtime that created it. It is -/// not valid to use one GC runtime's layout with another GC runtime, doing so -/// is memory safe but will lead to general incorrectness like panics and wrong -/// results. -/// -/// All offsets are from the start of the object; that is, the size of the GC -/// header (for example) is included in the offset. -/// -/// All arrays are composed of the generic `VMGcHeader`, followed by -/// collector-specific fields, followed by the contiguous array elements -/// themselves. The array elements must be aligned to the element type's natural -/// alignment. -#[derive(Clone, Debug)] -#[allow(dead_code)] // Not used yet, but added for completeness. -pub struct GcArrayLayout { - /// The size of this array object, ignoring its elements. - pub size: u32, - - /// The alignment of this array. - pub align: u32, - - /// The offset of the array's length. - pub length_field_offset: u32, - - /// The offset from where this array's contiguous elements begin. - pub elems_offset: u32, - - /// The size and natural alignment of each element in this array. - pub elem_size: u32, -} - -impl GcArrayLayout { - /// Get the total size of this array for a given length of elements. - pub fn size_for_len(&self, len: u32) -> u32 { - self.size + len * self.elem_size - } - - /// Get the offset of the `i`th element in an array with this layout. - #[inline] - pub fn elem_offset(&self, i: u32, elem_size: u32) -> u32 { - self.elems_offset + i * elem_size - } -} - -/// The layout for a GC-managed struct type. -/// -/// This layout is only valid for use with the GC runtime that created it. It is -/// not valid to use one GC runtime's layout with another GC runtime, doing so -/// is memory safe but will lead to general incorrectness like panics and wrong -/// results. -/// -/// All offsets are from the start of the object; that is, the size of the GC -/// header (for example) is included in the offset. -#[derive(Clone, Debug)] -pub struct GcStructLayout { - /// The size of this struct. - pub size: u32, - - /// The alignment of this struct. - pub align: u32, - - /// The fields of this struct. The `i`th entry is the `i`th struct field's - /// offset in the struct. - pub fields: Vec, -} - /// A list of GC roots. /// /// This is effectively a builder for a `GcRootsIter` that will be given to a GC