Skip to content

Commit 36106c9

Browse files
committed
WIP drc transitive dec ref
1 parent ad21d95 commit 36106c9

File tree

8 files changed

+243
-38
lines changed

8 files changed

+243
-38
lines changed

crates/cranelift/src/gc/enabled.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ pub fn translate_struct_get(
316316
let struct_size = struct_layout.size;
317317
let struct_size_val = builder.ins().iconst(ir::types::I32, i64::from(struct_size));
318318

319-
let field_offset = struct_layout.fields[field_index];
319+
let field_offset = struct_layout.fields[field_index].offset;
320320
let field_ty = &func_env.types.unwrap_struct(interned_type_index)?.fields[field_index];
321321
let field_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty.element_type);
322322
assert!(field_offset + field_size <= struct_size);
@@ -361,7 +361,7 @@ pub fn translate_struct_set(
361361
let struct_size = struct_layout.size;
362362
let struct_size_val = builder.ins().iconst(ir::types::I32, i64::from(struct_size));
363363

364-
let field_offset = struct_layout.fields[field_index];
364+
let field_offset = struct_layout.fields[field_index].offset;
365365
let field_ty = &func_env.types.unwrap_struct(interned_type_index)?.fields[field_index];
366366
let field_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty.element_type);
367367
assert!(field_offset + field_size <= struct_size);
@@ -1194,7 +1194,7 @@ fn initialize_struct_fields(
11941194
) -> WasmResult<()> {
11951195
let struct_layout = func_env.struct_layout(struct_ty);
11961196
let struct_size = struct_layout.size;
1197-
let field_offsets: SmallVec<[_; 8]> = struct_layout.fields.iter().copied().collect();
1197+
let field_offsets: SmallVec<[_; 8]> = struct_layout.fields.iter().map(|f| f.offset).collect();
11981198
assert_eq!(field_offsets.len(), field_values.len());
11991199

12001200
assert!(!func_env.types[struct_ty].composite_type.shared);

crates/cranelift/src/gc/enabled/drc.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,13 @@ impl DrcCompiler {
261261

262262
/// Emit CLIF to call the `gc_raw_alloc` libcall.
263263
///
264-
/// It is the caller's responsibility to ensure that `size` fits within the
265-
/// `VMGcKind`'s unused bits.
264+
/// It is the caller's responsibility to ensure that `num_gc_refs` fits within
265+
/// the `VMGcKind`'s unused bits.
266266
fn emit_gc_raw_alloc(
267267
func_env: &mut FuncEnvironment<'_>,
268268
builder: &mut FunctionBuilder<'_>,
269269
kind: VMGcKind,
270+
num_gc_refs: ir::Value,
270271
ty: ModuleInternedTypeIndex,
271272
size: ir::Value,
272273
align: u32,
@@ -277,15 +278,17 @@ fn emit_gc_raw_alloc(
277278
let kind = builder
278279
.ins()
279280
.iconst(ir::types::I32, i64::from(kind.as_u32()));
281+
let kind_and_reserved = builder.ins().bor(kind, num_gc_refs);
280282

281283
let ty = builder.ins().iconst(ir::types::I32, i64::from(ty.as_u32()));
282284

283285
assert!(align.is_power_of_two());
284286
let align = builder.ins().iconst(ir::types::I32, i64::from(align));
285287

286-
let call_inst = builder
287-
.ins()
288-
.call(gc_alloc_raw_builtin, &[vmctx, kind, ty, size, align]);
288+
let call_inst = builder.ins().call(
289+
gc_alloc_raw_builtin,
290+
&[vmctx, kind_and_reserved, ty, size, align],
291+
);
289292

290293
let gc_ref = builder.func.dfg.first_result(call_inst);
291294
let gc_ref = builder.ins().ireduce(ir::types::I32, gc_ref);
@@ -318,13 +321,19 @@ impl GcCompiler for DrcCompiler {
318321
// First, compute the array's total size from its base size, element
319322
// size, and length.
320323
let size = emit_array_size(func_env, builder, &array_layout, init);
324+
let num_gc_refs = if array_layout.elems_are_gc_refs {
325+
size
326+
} else {
327+
builder.ins().iconst(ir::types::I32, 0)
328+
};
321329

322330
// Second, now that we have the array object's total size, call the
323331
// `gc_alloc_raw` builtin libcall to allocate the array.
324332
let array_ref = emit_gc_raw_alloc(
325333
func_env,
326334
builder,
327335
VMGcKind::ArrayRef,
336+
num_gc_refs,
328337
interned_type_index,
329338
size,
330339
align,
@@ -385,10 +394,15 @@ impl GcCompiler for DrcCompiler {
385394
assert_eq!(VMGcKind::UNUSED_MASK & struct_size, struct_size);
386395
let struct_size_val = builder.ins().iconst(ir::types::I32, i64::from(struct_size));
387396

397+
let num_gc_refs = struct_layout.fields.iter().filter(|f| f.is_gc_ref).count();
398+
let num_gc_refs = u32::try_from(num_gc_refs).unwrap();
399+
let num_gc_refs = builder.ins().iconst(ir::types::I32, i64::from(num_gc_refs));
400+
388401
let struct_ref = emit_gc_raw_alloc(
389402
func_env,
390403
builder,
391404
VMGcKind::StructRef,
405+
num_gc_refs,
392406
interned_type_index,
393407
struct_size_val,
394408
struct_align,

crates/environ/src/gc.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,36 @@ fn common_array_layout(
8181
header_align: u32,
8282
expected_array_length_offset: u32,
8383
) -> GcArrayLayout {
84+
use core::mem;
85+
8486
assert!(header_size >= crate::VM_GC_HEADER_SIZE);
8587
assert!(header_align >= crate::VM_GC_HEADER_ALIGN);
8688

8789
let mut size = header_size;
8890
let mut align = header_align;
8991

90-
let length_field_offset = field(&mut size, &mut align, 4);
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);
9194
assert_eq!(length_field_offset, expected_array_length_offset);
9295

9396
let elem_size = byte_size_of_wasm_ty_in_gc_heap(&ty.0.element_type);
9497
let elems_offset = align_up(&mut size, &mut align, elem_size);
9598
assert_eq!(elems_offset, size);
9699

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+
97109
GcArrayLayout {
98110
base_size: size,
99111
align,
100112
elem_size,
113+
elems_are_gc_refs,
101114
}
102115
}
103116

@@ -127,7 +140,9 @@ fn common_struct_layout(
127140
.iter()
128141
.map(|f| {
129142
let field_size = byte_size_of_wasm_ty_in_gc_heap(&f.element_type);
130-
field(&mut size, &mut align, field_size)
143+
let offset = field(&mut size, &mut align, field_size);
144+
let is_gc_ref = f.element_type.is_vmgcref_type_and_not_i31();
145+
GcStructLayoutField { offset, is_gc_ref }
131146
})
132147
.collect();
133148

@@ -241,6 +256,9 @@ pub struct GcArrayLayout {
241256

242257
/// The size and natural alignment of each element in this array.
243258
pub elem_size: u32,
259+
260+
/// Whether or not the elements of this array are GC references or not.
261+
pub elems_are_gc_refs: bool,
244262
}
245263

246264
impl GcArrayLayout {
@@ -283,9 +301,9 @@ pub struct GcStructLayout {
283301
/// The alignment (in bytes) of this struct.
284302
pub align: u32,
285303

286-
/// The fields of this struct. The `i`th entry is the `i`th struct field's
287-
/// offset (in bytes) in the struct.
288-
pub fields: Vec<u32>,
304+
/// The fields of this struct. The `i`th entry contains information about
305+
/// the `i`th struct field's layout.
306+
pub fields: Vec<GcStructLayoutField>,
289307
}
290308

291309
impl GcStructLayout {
@@ -297,6 +315,20 @@ impl GcStructLayout {
297315
}
298316
}
299317

318+
/// A field in a `GcStructLayout`.
319+
#[derive(Clone, Copy, Debug)]
320+
pub struct GcStructLayoutField {
321+
/// The offset (in bytes) of this field inside instances of this type.
322+
pub offset: u32,
323+
324+
/// Whether or not this field might contain a reference to another GC
325+
/// object.
326+
///
327+
/// Note: it is okay for this to be `false` for `i31ref`s, since they never
328+
/// actually reference another GC object.
329+
pub is_gc_ref: bool,
330+
}
331+
300332
/// The kind of an object in a GC heap.
301333
///
302334
/// Note that this type is accessed from Wasm JIT code.

crates/environ/src/gc/drc.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! Layout of Wasm GC objects in the deferred reference-counting collector.
22
33
use super::*;
4+
use core::cmp;
45

56
/// The size of the `VMDrcHeader` header for GC objects.
6-
pub const HEADER_SIZE: u32 = 16;
7+
pub const HEADER_SIZE: u32 = 24;
78

89
/// The align of the `VMDrcHeader` header for GC objects.
910
pub const HEADER_ALIGN: u32 = 8;
@@ -25,6 +26,54 @@ impl GcTypeLayouts for DrcTypeLayouts {
2526
}
2627

2728
fn struct_layout(&self, ty: &WasmStructType) -> GcStructLayout {
28-
common_struct_layout(ty, HEADER_SIZE, HEADER_ALIGN)
29+
// Sort the struct fields into the order in which we will lay them out.
30+
//
31+
// NB: we put all GC refs first so that we can simply store the number
32+
// of GC refs in any object in the `VMDrcHeader` and then uniformly
33+
// trace all structs types.
34+
let mut fields: Vec<_> = ty.fields.iter().enumerate().collect();
35+
fields.sort_by_key(|(i, f)| {
36+
let is_gc_ref = f.element_type.is_vmgcref_type_and_not_i31();
37+
let size = byte_size_of_wasm_ty_in_gc_heap(&f.element_type);
38+
(cmp::Reverse(is_gc_ref), cmp::Reverse(size), *i)
39+
});
40+
41+
// Compute the offset of each field as well as the size and alignment of
42+
// the whole struct.
43+
let mut size = HEADER_SIZE;
44+
let mut align = HEADER_ALIGN;
45+
let mut fields: Vec<_> = fields
46+
.into_iter()
47+
.map(|(i, f)| {
48+
let field_size = byte_size_of_wasm_ty_in_gc_heap(&f.element_type);
49+
let offset = field(&mut size, &mut align, field_size);
50+
let is_gc_ref = f.element_type.is_vmgcref_type_and_not_i31();
51+
(i, GcStructLayoutField { offset, is_gc_ref })
52+
})
53+
.collect();
54+
if let Some((_i, f)) = fields.get(0) {
55+
if f.is_gc_ref {
56+
debug_assert_eq!(
57+
f.offset, HEADER_SIZE,
58+
"GC refs should come directly after the header, without any padding",
59+
);
60+
}
61+
}
62+
63+
// Re-sort the fields into their definition (rather than layout) order
64+
// and throw away the definition index.
65+
fields.sort_by_key(|(i, _f)| *i);
66+
let fields: Vec<_> = fields.into_iter().map(|(_i, f)| f).collect();
67+
68+
// Ensure that the final size is a multiple of the alignment, for
69+
// simplicity.
70+
let align_size_to = align;
71+
align_up(&mut size, &mut align, align_size_to);
72+
73+
GcStructLayout {
74+
size,
75+
align,
76+
fields,
77+
}
2978
}
3079
}

crates/environ/src/types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,20 @@ impl TypeTrace for WasmStorageType {
862862
}
863863
}
864864

865+
impl WasmStorageType {
866+
/// Is this a type that is represented as a `VMGcRef` and is additionally
867+
/// not an `i31`?
868+
///
869+
/// That is, is this a a type that actually refers to an object allocated in
870+
/// a GC heap?
871+
pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
872+
match self {
873+
WasmStorageType::I8 | WasmStorageType::I16 => false,
874+
WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(),
875+
}
876+
}
877+
}
878+
865879
/// The type of a struct field or array element.
866880
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
867881
pub struct WasmFieldType {

0 commit comments

Comments
 (0)