Simplify rb_gc_rebuild_shape

Now that there no longer multiple shape roots, all we need to do
when moving an object from one slot to the other is to update the
`heap_index` part of the shape_id.

Since this never need to create a shape transition, it will always
work and never result in a complex shape.
This commit is contained in:
Jean Boussier 2025-06-07 16:48:26 +02:00
parent 191f6e3b87
commit a640723d31
Notes: git 2025-06-07 16:30:58 +00:00
3 changed files with 9 additions and 71 deletions

14
gc.c
View File

@ -381,19 +381,9 @@ rb_gc_set_shape(VALUE obj, uint32_t shape_id)
uint32_t
rb_gc_rebuild_shape(VALUE obj, size_t heap_id)
{
shape_id_t orig_shape_id = rb_obj_shape_id(obj);
if (rb_shape_too_complex_p(orig_shape_id)) {
return (uint32_t)orig_shape_id;
}
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT));
shape_id_t initial_shape_id = rb_shape_root(heap_id);
shape_id_t new_shape_id = rb_shape_traverse_from_new_root(initial_shape_id, orig_shape_id);
if (new_shape_id == INVALID_SHAPE_ID) {
return 0;
}
return (uint32_t)new_shape_id;
return (uint32_t)rb_shape_transition_heap(obj, heap_id);
}
void rb_vm_update_references(void *ptr);

63
shape.c
View File

@ -823,6 +823,12 @@ rb_shape_transition_complex(VALUE obj)
return transition_complex(RBASIC_SHAPE_ID(obj));
}
shape_id_t
rb_shape_transition_heap(VALUE obj, size_t heap_index)
{
return (RBASIC_SHAPE_ID(obj) & (~SHAPE_ID_HEAP_INDEX_MASK)) | rb_shape_root(heap_index);
}
/*
* This function is used for assertions where we don't want to increment
* max_iv_count
@ -1065,63 +1071,6 @@ rb_shape_id_offset(void)
return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
}
static rb_shape_t *
shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
{
RUBY_ASSERT(initial_shape->type == SHAPE_ROOT);
rb_shape_t *next_shape = initial_shape;
if (dest_shape->type != initial_shape->type) {
next_shape = shape_traverse_from_new_root(initial_shape, RSHAPE(dest_shape->parent_id));
if (!next_shape) {
return NULL;
}
}
switch ((enum shape_type)dest_shape->type) {
case SHAPE_IVAR:
case SHAPE_OBJ_ID:
if (!next_shape->edges) {
return NULL;
}
VALUE lookup_result;
if (SINGLE_CHILD_P(next_shape->edges)) {
rb_shape_t *child = SINGLE_CHILD(next_shape->edges);
if (child->edge_name == dest_shape->edge_name) {
return child;
}
else {
return NULL;
}
}
else {
if (rb_managed_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
next_shape = (rb_shape_t *)lookup_result;
}
else {
return NULL;
}
}
break;
case SHAPE_ROOT:
break;
}
return next_shape;
}
shape_id_t
rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
{
rb_shape_t *initial_shape = RSHAPE(initial_shape_id);
rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
// Keep all dest_shape_id flags except for the heap_index.
shape_id_t dest_flags = (dest_shape_id & ~SHAPE_ID_HEAP_INDEX_MASK) | (initial_shape_id & SHAPE_ID_HEAP_INDEX_MASK);
return shape_id(shape_traverse_from_new_root(initial_shape, dest_shape), dest_flags);
}
// Rebuild a similar shape with the same ivars but starting from
// a different SHAPE_T_OBJECT, and don't cary over non-canonical transitions
// such as SHAPE_OBJ_ID.

View File

@ -164,6 +164,7 @@ shape_id_t rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed
shape_id_t rb_shape_transition_add_ivar(VALUE obj, ID id);
shape_id_t rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id);
shape_id_t rb_shape_transition_object_id(VALUE obj);
shape_id_t rb_shape_transition_heap(VALUE obj, size_t heap_index);
shape_id_t rb_shape_object_id(shape_id_t original_shape_id);
void rb_shape_free_all(void);
@ -302,8 +303,6 @@ RBASIC_FIELDS_COUNT(VALUE obj)
return RSHAPE(rb_obj_shape_id(obj))->next_field_index;
}
shape_id_t rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t orig_shape_id);
bool rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id);
static inline bool