shape.c: refactor frozen shape to no longer be final

This opens the door to store more informations in shapes, such
as the `object_id` or object address in case it has been observed
and the object has to be moved.
This commit is contained in:
Jean Boussier 2025-04-21 12:01:01 +09:00
parent 6c9b3ac232
commit d34c150547
Notes: git 2025-05-08 05:58:19 +00:00
3 changed files with 20 additions and 2 deletions

View File

@ -358,7 +358,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
rb_shape_t *initial_shape = rb_shape_get_shape(dest);
if (initial_shape->heap_index != src_shape->heap_index) {
if (initial_shape->heap_index != src_shape->heap_index || !rb_shape_canonical_p(src_shape)) {
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);

13
shape.c
View File

@ -50,6 +50,12 @@ static ID id_t_object;
#define BLACK 0x0
#define RED 0x1
enum shape_flags {
SHAPE_FL_FROZEN = 1 << 0,
SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN,
};
static redblack_node_t *
redblack_left(redblack_node_t *node)
{
@ -418,6 +424,7 @@ rb_shape_alloc(ID edge_name, rb_shape_t *parent, enum shape_type type)
{
rb_shape_t *shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent));
shape->type = (uint8_t)type;
shape->flags = parent->flags;
shape->heap_index = parent->heap_index;
shape->capacity = parent->capacity;
shape->edges = 0;
@ -478,6 +485,7 @@ rb_shape_alloc_new_child(ID id, rb_shape_t *shape, enum shape_type shape_type)
break;
case SHAPE_FROZEN:
new_shape->next_field_index = shape->next_field_index;
new_shape->flags |= SHAPE_FL_FROZEN;
break;
case SHAPE_OBJ_TOO_COMPLEX:
case SHAPE_ROOT:
@ -576,7 +584,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo
bool
rb_shape_frozen_shape_p(rb_shape_t *shape)
{
return SHAPE_FROZEN == (enum shape_type)shape->type;
return SHAPE_FL_FROZEN & shape->flags;
}
static rb_shape_t *
@ -975,6 +983,9 @@ rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shap
return next_shape;
}
// 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_FROZEN.
rb_shape_t *
rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
{

View File

@ -48,6 +48,7 @@ struct rb_shape {
attr_index_t capacity; // Total capacity of the object with this shape
uint8_t type;
uint8_t heap_index;
uint8_t flags;
shape_id_t parent_id;
redblack_node_t *ancestor_index;
};
@ -171,6 +172,12 @@ rb_shape_t *rb_shape_get_next_no_warnings(rb_shape_t *shape, VALUE obj, ID id);
rb_shape_t *rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape);
static inline bool
rb_shape_canonical_p(rb_shape_t *shape)
{
return !shape->flags;
}
static inline uint32_t
ROBJECT_FIELDS_CAPACITY(VALUE obj)
{