From 5d3ac10186f18a2007991d0840e83d983b37969d Mon Sep 17 00:00:00 2001 From: Matt Valentine-House Date: Fri, 14 Aug 2026 11:34:02 +0100 Subject: [PATCH] Allow shapes to carry zero embedded capacity rb_shape_verify_consistency asserted that every checked object has room for at least one embedded field. The default GC always satisfies this because its smallest slot is 32 bytes so this assertion never fires. MMTk gives an empty imemo_fields a 16-byte slot, so its shape carries zero capacity bits, and boot failed on the first class freeze. Instead of forcing MMTk to have a minimum object size, we should just allow shapes to support a capacity of 0. --- gc/mmtk/mmtk.c | 15 +++------------ shape.c | 1 - 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 7895a098781..a7aec6e6833 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -704,15 +704,6 @@ rb_mmtk_align_obj_size(size_t object_size) return (object_size + MMTk_MIN_OBJ_ALIGN - 1) & ~((size_t)MMTk_MIN_OBJ_ALIGN - 1); } -static inline size_t -rb_mmtk_total_obj_size(size_t payload_size) -{ - if (payload_size < sizeof(struct RBasic) + sizeof(VALUE)) { - payload_size = sizeof(struct RBasic) + sizeof(VALUE); - } - return rb_mmtk_align_obj_size(payload_size + sizeof(VALUE)); -} - bool rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath) @@ -720,7 +711,7 @@ rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE fl #if USE_ZJIT struct objspace *objspace = objspace_ptr; - size_t total_size = rb_mmtk_total_obj_size(alloc_size); + size_t total_size = rb_mmtk_align_obj_size(alloc_size + sizeof(VALUE)); size_t object_size = total_size - sizeof(VALUE); size_t value_size_shift = sizeof(VALUE) == 8 ? 3 : 2; @@ -1035,7 +1026,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags } // Layout: [hidden size header (sizeof(VALUE))][payload (alloc_size)] - size_t total_size = rb_mmtk_total_obj_size(alloc_size); + size_t total_size = rb_mmtk_align_obj_size(alloc_size + sizeof(VALUE)); size_t object_size = total_size - sizeof(VALUE); MMTk_AllocationSemantics semantics = total_size > objspace->max_non_los_default_alloc_bytes ? MMTK_ALLOCATION_SEMANTICS_LOS @@ -1093,7 +1084,7 @@ rb_gc_impl_size_slot_size(void *objspace_ptr, size_t size) rb_bug("rb_gc_impl_size_slot_size: size too large (size=%"PRIuSIZE")", size); } - return rb_mmtk_total_obj_size(size) - sizeof(VALUE); + return rb_mmtk_align_obj_size(size + sizeof(VALUE)) - sizeof(VALUE); } bool diff --git a/shape.c b/shape.c index e9037acf6a3..17df38ddd54 100644 --- a/shape.c +++ b/shape.c @@ -1343,7 +1343,6 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) if (rb_shape_verify_capacity_consistency_p(obj)) { attr_index_t shape_id_capacity = rb_shape_embedded_capacity(shape_id); - RUBY_ASSERT(shape_id_capacity > 0); size_t shape_id_slot_size = shape_id_capacity * sizeof(VALUE) + sizeof(struct RBasic); size_t actual_slot_size = rb_gc_obj_slot_size(obj);