Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/gc.rs
1691 views
1
//! Target- and pointer-width-agnostic definitions of GC-related types and
2
//! constants.
3
//!
4
//! These definitions are suitable for use both during compilation and at
5
//! runtime.
6
//!
7
//! Note: We don't bother gating these on `cfg(feature = "gc")` because that
8
//! makes downstream uses pretty annoying, and the primary thing we want to gate
9
//! on our various `gc` cargo features is the actual garbage collection
10
//! functions and their associated impact on binary size anyways.
11
12
#[cfg(feature = "gc-drc")]
13
pub mod drc;
14
15
#[cfg(feature = "gc-null")]
16
pub mod null;
17
18
use crate::{
19
WasmArrayType, WasmCompositeInnerType, WasmCompositeType, WasmStorageType, WasmStructType,
20
WasmValType,
21
};
22
use crate::{WasmExnType, prelude::*};
23
use core::alloc::Layout;
24
25
/// Discriminant to check whether GC reference is an `i31ref` or not.
26
pub const I31_DISCRIMINANT: u32 = 1;
27
28
/// The size of the `VMGcHeader` in bytes.
29
pub const VM_GC_HEADER_SIZE: u32 = 8;
30
31
/// The minimum alignment of the `VMGcHeader` in bytes.
32
pub const VM_GC_HEADER_ALIGN: u32 = 8;
33
34
/// The offset of the `VMGcKind` field in the `VMGcHeader`.
35
pub const VM_GC_HEADER_KIND_OFFSET: u32 = 0;
36
37
/// The offset of the `VMSharedTypeIndex` field in the `VMGcHeader`.
38
pub const VM_GC_HEADER_TYPE_INDEX_OFFSET: u32 = 4;
39
40
/// Get the byte size of the given Wasm type when it is stored inside the GC
41
/// heap.
42
pub fn byte_size_of_wasm_ty_in_gc_heap(ty: &WasmStorageType) -> u32 {
43
match ty {
44
WasmStorageType::I8 => 1,
45
WasmStorageType::I16 => 2,
46
WasmStorageType::Val(ty) => match ty {
47
WasmValType::I32 | WasmValType::F32 | WasmValType::Ref(_) => 4,
48
WasmValType::I64 | WasmValType::F64 => 8,
49
WasmValType::V128 => 16,
50
},
51
}
52
}
53
54
/// Align `offset` up to `bytes`, updating `max_align` if `align` is the
55
/// new maximum alignment, and returning the aligned offset.
56
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
57
fn align_up(offset: &mut u32, max_align: &mut u32, align: u32) -> u32 {
58
debug_assert!(max_align.is_power_of_two());
59
debug_assert!(align.is_power_of_two());
60
*offset = offset.checked_add(align - 1).unwrap() & !(align - 1);
61
*max_align = core::cmp::max(*max_align, align);
62
*offset
63
}
64
65
/// Define a new field of size and alignment `bytes`, updating the object's
66
/// total `size` and `align` as necessary. The offset of the new field is
67
/// returned.
68
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
69
fn field(size: &mut u32, align: &mut u32, bytes: u32) -> u32 {
70
let offset = align_up(size, align, bytes);
71
*size += bytes;
72
offset
73
}
74
75
/// Common code to define a GC array's layout, given the size and alignment of
76
/// the collector's GC header and its expected offset of the array length field.
77
#[cfg(any(feature = "gc-drc", feature = "gc-null"))]
78
fn common_array_layout(
79
ty: &WasmArrayType,
80
header_size: u32,
81
header_align: u32,
82
expected_array_length_offset: u32,
83
) -> GcArrayLayout {
84
use core::mem;
85
86
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
87
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);
88
89
let mut size = header_size;
90
let mut align = header_align;
91
92
let length_field_size = u32::try_from(mem::size_of::<u32>()).unwrap();
93
let length_field_offset = field(&mut size, &mut align, length_field_size);
94
assert_eq!(length_field_offset, expected_array_length_offset);
95
96
let elem_size = byte_size_of_wasm_ty_in_gc_heap(&ty.0.element_type);
97
let elems_offset = align_up(&mut size, &mut align, elem_size);
98
assert_eq!(elems_offset, size);
99
100
let elems_are_gc_refs = ty.0.element_type.is_vmgcref_type_and_not_i31();
101
if elems_are_gc_refs {
102
debug_assert_eq!(
103
length_field_offset + length_field_size,
104
elems_offset,
105
"DRC collector relies on GC ref elements appearing directly after the length field, without any padding",
106
);
107
}
108
109
GcArrayLayout {
110
base_size: size,
111
align,
112
elem_size,
113
elems_are_gc_refs,
114
}
115
}
116
117
/// Shared layout code for structs and exception objects, which are
118
/// identical except for the tag field (present in
119
/// exceptions). Returns `(size, align, fields)`.
120
#[cfg(any(feature = "gc-null", feature = "gc-drc"))]
121
fn common_struct_or_exn_layout(
122
fields: &[crate::WasmFieldType],
123
header_size: u32,
124
header_align: u32,
125
) -> (u32, u32, Vec<GcStructLayoutField>) {
126
// Process each field, aligning it to its natural alignment.
127
//
128
// We don't try and do any fancy field reordering to minimize padding (yet?)
129
// because (a) the toolchain probably already did that and (b) we're just
130
// doing the simple thing first, and (c) this is tricky in the presence of
131
// subtyping where we need a subtype's fields to be assigned the same
132
// offsets as its supertype's fields. We can come back and improve things
133
// here if we find that (a) isn't actually holding true in practice.
134
135
let mut size = header_size;
136
let mut align = header_align;
137
138
let fields = fields
139
.iter()
140
.map(|f| {
141
let field_size = byte_size_of_wasm_ty_in_gc_heap(&f.element_type);
142
let offset = field(&mut size, &mut align, field_size);
143
let is_gc_ref = f.element_type.is_vmgcref_type_and_not_i31();
144
GcStructLayoutField { offset, is_gc_ref }
145
})
146
.collect();
147
148
// Ensure that the final size is a multiple of the alignment, for
149
// simplicity.
150
let align_size_to = align;
151
align_up(&mut size, &mut align, align_size_to);
152
153
(size, align, fields)
154
}
155
156
/// Common code to define a GC struct's layout, given the size and alignment of
157
/// the collector's GC header and its expected offset of the array length field.
158
#[cfg(any(feature = "gc-null", feature = "gc-drc"))]
159
fn common_struct_layout(
160
ty: &WasmStructType,
161
header_size: u32,
162
header_align: u32,
163
) -> GcStructLayout {
164
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
165
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);
166
167
let (size, align, fields) = common_struct_or_exn_layout(&ty.fields, header_size, header_align);
168
169
GcStructLayout {
170
size,
171
align,
172
fields,
173
is_exception: false,
174
}
175
}
176
177
/// Common code to define a GC exception object's layout, given the
178
/// size and alignment of the collector's GC header and its expected
179
/// offset of the array length field.
180
#[cfg(any(feature = "gc-null", feature = "gc-drc"))]
181
fn common_exn_layout(ty: &WasmExnType, header_size: u32, header_align: u32) -> GcStructLayout {
182
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
183
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);
184
185
// Compute a struct layout, with extra header size for the
186
// `(instance_idx, tag_idx)` fields.
187
assert!(header_align >= 8);
188
let header_size = header_size + 2 * u32::try_from(core::mem::size_of::<u32>()).unwrap();
189
190
let (size, align, fields) = common_struct_or_exn_layout(&ty.fields, header_size, header_align);
191
192
GcStructLayout {
193
size,
194
align,
195
fields,
196
is_exception: true,
197
}
198
}
199
200
/// A trait for getting the layout of a Wasm GC struct or array inside a
201
/// particular collector.
202
pub trait GcTypeLayouts {
203
/// The offset of an array's length field.
204
///
205
/// This must be the same for all arrays in the heap, regardless of their
206
/// element type.
207
fn array_length_field_offset(&self) -> u32;
208
209
/// The offset of an exception object's tag reference: defining
210
/// instance index field.
211
///
212
/// This must be the same for all exception objects in the heap,
213
/// regardless of their specific signature.
214
fn exception_tag_instance_offset(&self) -> u32;
215
216
/// The offset of an exception object's tag reference: defined tag
217
/// index field.
218
///
219
/// This must be the same for all exception objects in the heap,
220
/// regardless of their specific signature.
221
fn exception_tag_defined_offset(&self) -> u32;
222
223
/// Get this collector's layout for the given composite type.
224
///
225
/// Returns `None` if the type is a function type, as functions are not
226
/// managed by the GC.
227
fn gc_layout(&self, ty: &WasmCompositeType) -> Option<GcLayout> {
228
assert!(!ty.shared);
229
match &ty.inner {
230
WasmCompositeInnerType::Array(ty) => Some(self.array_layout(ty).into()),
231
WasmCompositeInnerType::Struct(ty) => Some(self.struct_layout(ty).into()),
232
WasmCompositeInnerType::Func(_) => None,
233
WasmCompositeInnerType::Cont(_) => {
234
unimplemented!("Stack switching feature not compatbile with GC, yet")
235
}
236
WasmCompositeInnerType::Exn(ty) => Some(self.exn_layout(ty).into()),
237
}
238
}
239
240
/// Get this collector's layout for the given array type.
241
fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout;
242
243
/// Get this collector's layout for the given struct type.
244
fn struct_layout(&self, ty: &WasmStructType) -> GcStructLayout;
245
246
/// Get this collector's layout for the given exception type.
247
fn exn_layout(&self, ty: &WasmExnType) -> GcStructLayout;
248
}
249
250
/// The layout of a GC-managed object.
251
#[derive(Clone, Debug)]
252
pub enum GcLayout {
253
/// The layout of a GC-managed array object.
254
Array(GcArrayLayout),
255
256
/// The layout of a GC-managed struct or exception object.
257
Struct(GcStructLayout),
258
}
259
260
impl From<GcArrayLayout> for GcLayout {
261
fn from(layout: GcArrayLayout) -> Self {
262
Self::Array(layout)
263
}
264
}
265
266
impl From<GcStructLayout> for GcLayout {
267
fn from(layout: GcStructLayout) -> Self {
268
Self::Struct(layout)
269
}
270
}
271
272
impl GcLayout {
273
/// Get the underlying `GcStructLayout`, or panic.
274
#[track_caller]
275
pub fn unwrap_struct(&self) -> &GcStructLayout {
276
match self {
277
Self::Struct(s) => s,
278
_ => panic!("GcLayout::unwrap_struct on non-struct GC layout"),
279
}
280
}
281
282
/// Get the underlying `GcArrayLayout`, or panic.
283
#[track_caller]
284
pub fn unwrap_array(&self) -> &GcArrayLayout {
285
match self {
286
Self::Array(a) => a,
287
_ => panic!("GcLayout::unwrap_array on non-array GC layout"),
288
}
289
}
290
}
291
292
/// The layout of a GC-managed array.
293
///
294
/// This layout is only valid for use with the GC runtime that created it. It is
295
/// not valid to use one GC runtime's layout with another GC runtime, doing so
296
/// is memory safe but will lead to general incorrectness like panics and wrong
297
/// results.
298
///
299
/// All offsets are from the start of the object; that is, the size of the GC
300
/// header (for example) is included in the offset.
301
///
302
/// All arrays are composed of the generic `VMGcHeader`, followed by
303
/// collector-specific fields, followed by the contiguous array elements
304
/// themselves. The array elements must be aligned to the element type's natural
305
/// alignment.
306
#[derive(Clone, Debug)]
307
pub struct GcArrayLayout {
308
/// The size of this array object, without any elements.
309
///
310
/// The array's elements, if any, must begin at exactly this offset.
311
pub base_size: u32,
312
313
/// The alignment of this array.
314
pub align: u32,
315
316
/// The size and natural alignment of each element in this array.
317
pub elem_size: u32,
318
319
/// Whether or not the elements of this array are GC references or not.
320
pub elems_are_gc_refs: bool,
321
}
322
323
impl GcArrayLayout {
324
/// Get the total size of this array for a given length of elements.
325
#[inline]
326
pub fn size_for_len(&self, len: u32) -> u32 {
327
self.elem_offset(len)
328
}
329
330
/// Get the offset of the `i`th element in an array with this layout.
331
#[inline]
332
pub fn elem_offset(&self, i: u32) -> u32 {
333
self.base_size + i * self.elem_size
334
}
335
336
/// Get a `core::alloc::Layout` for an array of this type with the given
337
/// length.
338
pub fn layout(&self, len: u32) -> Layout {
339
let size = self.size_for_len(len);
340
let size = usize::try_from(size).unwrap();
341
let align = usize::try_from(self.align).unwrap();
342
Layout::from_size_align(size, align).unwrap()
343
}
344
}
345
346
/// The layout for a GC-managed struct type or exception type.
347
///
348
/// This layout is only valid for use with the GC runtime that created it. It is
349
/// not valid to use one GC runtime's layout with another GC runtime, doing so
350
/// is memory safe but will lead to general incorrectness like panics and wrong
351
/// results.
352
///
353
/// All offsets are from the start of the object; that is, the size of the GC
354
/// header (for example) is included in the offset.
355
///
356
/// Note that these are reused between structs and exceptions to avoid
357
/// unnecessary code duplication. In both cases, the objects are
358
/// tuples of typed fields with a certain size. The only difference in
359
/// practice is that an exception object also carries a tag reference
360
/// (at a fixed offset as per `GcTypeLayouts::exception_tag_offset`).
361
#[derive(Clone, Debug)]
362
pub struct GcStructLayout {
363
/// The size (in bytes) of this struct.
364
pub size: u32,
365
366
/// The alignment (in bytes) of this struct.
367
pub align: u32,
368
369
/// The fields of this struct. The `i`th entry contains information about
370
/// the `i`th struct field's layout.
371
pub fields: Vec<GcStructLayoutField>,
372
373
/// Whether this is an exception object layout.
374
pub is_exception: bool,
375
}
376
377
impl GcStructLayout {
378
/// Get a `core::alloc::Layout` for a struct of this type.
379
pub fn layout(&self) -> Layout {
380
let size = usize::try_from(self.size).unwrap();
381
let align = usize::try_from(self.align).unwrap();
382
Layout::from_size_align(size, align).unwrap()
383
}
384
}
385
386
/// A field in a `GcStructLayout`.
387
#[derive(Clone, Copy, Debug)]
388
pub struct GcStructLayoutField {
389
/// The offset (in bytes) of this field inside instances of this type.
390
pub offset: u32,
391
392
/// Whether or not this field might contain a reference to another GC
393
/// object.
394
///
395
/// Note: it is okay for this to be `false` for `i31ref`s, since they never
396
/// actually reference another GC object.
397
pub is_gc_ref: bool,
398
}
399
400
/// The kind of an object in a GC heap.
401
///
402
/// Note that this type is accessed from Wasm JIT code.
403
///
404
/// `VMGcKind` is a bitset where to test if `a` is a subtype of an
405
/// "abstract-ish" type `b`, we can simply use a single bitwise-and operation:
406
///
407
/// ```ignore
408
/// a <: b iff a & b == b
409
/// ```
410
///
411
/// For example, because `VMGcKind::AnyRef` has the high bit set, every kind
412
/// representing some subtype of `anyref` also has its high bit set.
413
///
414
/// We say "abstract-ish" type because in addition to the abstract heap types
415
/// (other than `i31`) we also have variants for `externref`s that have been
416
/// converted into an `anyref` via `extern.convert_any` and `externref`s that
417
/// have been converted into an `anyref` via `any.convert_extern`. Note that in
418
/// the latter case, because `any.convert_extern $foo` produces a value that is
419
/// not an instance of `eqref`, `VMGcKind::AnyOfExternRef & VMGcKind::EqRef !=
420
/// VMGcKind::EqRef`.
421
///
422
/// Furthermore, this type only uses the highest 6 bits of its `u32`
423
/// representation, allowing the lower 26 bits to be bitpacked with other stuff
424
/// as users see fit.
425
#[repr(u32)]
426
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
427
#[rustfmt::skip]
428
#[expect(missing_docs, reason = "self-describing variants")]
429
pub enum VMGcKind {
430
ExternRef = 0b010000 << 26,
431
AnyRef = 0b100000 << 26,
432
EqRef = 0b101000 << 26,
433
ArrayRef = 0b101010 << 26,
434
StructRef = 0b101100 << 26,
435
ExnRef = 0b000001 << 26,
436
}
437
438
/// The size of the `VMGcKind` in bytes.
439
pub const VM_GC_KIND_SIZE: u8 = 4;
440
441
const _: () = assert!(VM_GC_KIND_SIZE as usize == core::mem::size_of::<VMGcKind>());
442
443
impl VMGcKind {
444
/// Mask this value with a `u32` to get just the bits that `VMGcKind` uses.
445
pub const MASK: u32 = 0b111111 << 26;
446
447
/// Mask this value with a `u32` that potentially contains a `VMGcKind` to
448
/// get the bits that `VMGcKind` doesn't use.
449
pub const UNUSED_MASK: u32 = !Self::MASK;
450
451
/// Does the given value fit in the unused bits of a `VMGcKind`?
452
#[inline]
453
pub fn value_fits_in_unused_bits(value: u32) -> bool {
454
(value & Self::UNUSED_MASK) == value
455
}
456
457
/// Convert the given value into a `VMGcKind` by masking off the unused
458
/// bottom bits.
459
#[inline]
460
pub fn from_high_bits_of_u32(val: u32) -> VMGcKind {
461
let masked = val & Self::MASK;
462
match masked {
463
x if x == Self::ExternRef.as_u32() => Self::ExternRef,
464
x if x == Self::AnyRef.as_u32() => Self::AnyRef,
465
x if x == Self::EqRef.as_u32() => Self::EqRef,
466
x if x == Self::ArrayRef.as_u32() => Self::ArrayRef,
467
x if x == Self::StructRef.as_u32() => Self::StructRef,
468
x if x == Self::ExnRef.as_u32() => Self::ExnRef,
469
_ => panic!("invalid `VMGcKind`: {masked:#032b}"),
470
}
471
}
472
473
/// Does this kind match the other kind?
474
///
475
/// That is, is this kind a subtype of the other kind?
476
#[inline]
477
pub fn matches(self, other: Self) -> bool {
478
(self.as_u32() & other.as_u32()) == other.as_u32()
479
}
480
481
/// Get this `VMGcKind` as a raw `u32`.
482
#[inline]
483
pub fn as_u32(self) -> u32 {
484
self as u32
485
}
486
}
487
488
#[cfg(test)]
489
mod tests {
490
use super::VMGcKind::*;
491
use crate::prelude::*;
492
493
#[test]
494
fn kind_matches() {
495
let all = [ExternRef, AnyRef, EqRef, ArrayRef, StructRef, ExnRef];
496
497
for (sup, subs) in [
498
(ExternRef, vec![]),
499
(AnyRef, vec![EqRef, ArrayRef, StructRef]),
500
// N.B.: exnref is not an eqref.
501
(EqRef, vec![ArrayRef, StructRef]),
502
(ArrayRef, vec![]),
503
(StructRef, vec![]),
504
(ExnRef, vec![]),
505
] {
506
assert!(sup.matches(sup));
507
for sub in &subs {
508
assert!(sub.matches(sup));
509
}
510
for kind in all.iter().filter(|k| **k != sup && !subs.contains(k)) {
511
assert!(!kind.matches(sup));
512
}
513
}
514
}
515
}
516
517