Replicate heap_index
in shape_id flags.
This is preparation to getting rid of `T_OBJECT` transitions. By first only replicating the information it's easier to ensure consistency.
This commit is contained in:
parent
42cf301254
commit
689ec51146
Notes:
git
2025-06-07 16:31:01 +00:00
2
object.c
2
object.c
@ -339,7 +339,6 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
|||||||
shape_id_t dest_shape_id = src_shape_id;
|
shape_id_t dest_shape_id = src_shape_id;
|
||||||
shape_id_t initial_shape_id = RBASIC_SHAPE_ID(dest);
|
shape_id_t initial_shape_id = RBASIC_SHAPE_ID(dest);
|
||||||
|
|
||||||
if (RSHAPE(initial_shape_id)->heap_index != RSHAPE(src_shape_id)->heap_index || !rb_shape_canonical_p(src_shape_id)) {
|
|
||||||
RUBY_ASSERT(RSHAPE(initial_shape_id)->type == SHAPE_T_OBJECT);
|
RUBY_ASSERT(RSHAPE(initial_shape_id)->type == SHAPE_T_OBJECT);
|
||||||
|
|
||||||
dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
|
dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
|
||||||
@ -350,7 +349,6 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VALUE *src_buf = ROBJECT_FIELDS(obj);
|
VALUE *src_buf = ROBJECT_FIELDS(obj);
|
||||||
VALUE *dest_buf = ROBJECT_FIELDS(dest);
|
VALUE *dest_buf = ROBJECT_FIELDS(dest);
|
||||||
|
19
shape.c
19
shape.c
@ -1092,7 +1092,10 @@ rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t dest_sha
|
|||||||
{
|
{
|
||||||
rb_shape_t *initial_shape = RSHAPE(initial_shape_id);
|
rb_shape_t *initial_shape = RSHAPE(initial_shape_id);
|
||||||
rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
|
rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
|
||||||
return shape_id(shape_traverse_from_new_root(initial_shape, dest_shape), 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
|
// Rebuild a similar shape with the same ivars but starting from
|
||||||
@ -1136,7 +1139,7 @@ rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
|
|||||||
RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
|
RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
|
||||||
RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
|
RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
|
||||||
|
|
||||||
return raw_shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)));
|
return shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)), initial_shape_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1238,6 +1241,14 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All complex shape are in heap_index=0, it's a limitation
|
||||||
|
if (!rb_shape_too_complex_p(shape_id)) {
|
||||||
|
uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
|
||||||
|
if (flags_heap_index != shape->heap_index) {
|
||||||
|
rb_bug("shape_id heap_index flags mismatch: flags=%u, transition=%u\n", flags_heap_index, shape->heap_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1288,6 +1299,7 @@ shape_id_t_to_rb_cShape(shape_id_t shape_id)
|
|||||||
|
|
||||||
VALUE obj = rb_struct_new(rb_cShape,
|
VALUE obj = rb_struct_new(rb_cShape,
|
||||||
INT2NUM(shape_id),
|
INT2NUM(shape_id),
|
||||||
|
INT2NUM(shape_id & SHAPE_ID_OFFSET_MASK),
|
||||||
INT2NUM(shape->parent_id),
|
INT2NUM(shape->parent_id),
|
||||||
rb_shape_edge_name(shape),
|
rb_shape_edge_name(shape),
|
||||||
INT2NUM(shape->next_field_index),
|
INT2NUM(shape->next_field_index),
|
||||||
@ -1528,7 +1540,7 @@ Init_default_shapes(void)
|
|||||||
for (int i = 0; sizes[i] > 0; i++) {
|
for (int i = 0; sizes[i] > 0; i++) {
|
||||||
rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
|
rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
|
||||||
t_object_shape->type = SHAPE_T_OBJECT;
|
t_object_shape->type = SHAPE_T_OBJECT;
|
||||||
t_object_shape->heap_index = i;
|
t_object_shape->heap_index = i + 1;
|
||||||
t_object_shape->capacity = (uint32_t)((sizes[i] - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
|
t_object_shape->capacity = (uint32_t)((sizes[i] - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
|
||||||
t_object_shape->edges = rb_managed_id_table_new(256);
|
t_object_shape->edges = rb_managed_id_table_new(256);
|
||||||
t_object_shape->ancestor_index = LEAF;
|
t_object_shape->ancestor_index = LEAF;
|
||||||
@ -1552,6 +1564,7 @@ Init_shape(void)
|
|||||||
* :nodoc: */
|
* :nodoc: */
|
||||||
VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
|
VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
|
||||||
"id",
|
"id",
|
||||||
|
"raw_id",
|
||||||
"parent_id",
|
"parent_id",
|
||||||
"edge_name",
|
"edge_name",
|
||||||
"next_field_index",
|
"next_field_index",
|
||||||
|
24
shape.h
24
shape.h
@ -16,8 +16,18 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
|
|||||||
#define SHAPE_ID_FL_FROZEN (SHAPE_FL_FROZEN << SHAPE_ID_OFFSET_NUM_BITS)
|
#define SHAPE_ID_FL_FROZEN (SHAPE_FL_FROZEN << SHAPE_ID_OFFSET_NUM_BITS)
|
||||||
#define SHAPE_ID_FL_HAS_OBJECT_ID (SHAPE_FL_HAS_OBJECT_ID << SHAPE_ID_OFFSET_NUM_BITS)
|
#define SHAPE_ID_FL_HAS_OBJECT_ID (SHAPE_FL_HAS_OBJECT_ID << SHAPE_ID_OFFSET_NUM_BITS)
|
||||||
#define SHAPE_ID_FL_TOO_COMPLEX (SHAPE_FL_TOO_COMPLEX << SHAPE_ID_OFFSET_NUM_BITS)
|
#define SHAPE_ID_FL_TOO_COMPLEX (SHAPE_FL_TOO_COMPLEX << SHAPE_ID_OFFSET_NUM_BITS)
|
||||||
|
#define SHAPE_ID_FL_EMBEDDED (SHAPE_FL_EMBEDDED << SHAPE_ID_OFFSET_NUM_BITS)
|
||||||
#define SHAPE_ID_FL_NON_CANONICAL_MASK (SHAPE_FL_NON_CANONICAL_MASK << SHAPE_ID_OFFSET_NUM_BITS)
|
#define SHAPE_ID_FL_NON_CANONICAL_MASK (SHAPE_FL_NON_CANONICAL_MASK << SHAPE_ID_OFFSET_NUM_BITS)
|
||||||
#define SHAPE_ID_READ_ONLY_MASK (~SHAPE_ID_FL_FROZEN)
|
|
||||||
|
#define SHAPE_ID_HEAP_INDEX_BITS 3
|
||||||
|
#define SHAPE_ID_HEAP_INDEX_OFFSET (SHAPE_ID_NUM_BITS - SHAPE_ID_HEAP_INDEX_BITS)
|
||||||
|
#define SHAPE_ID_HEAP_INDEX_MAX ((1 << SHAPE_ID_HEAP_INDEX_BITS) - 1)
|
||||||
|
#define SHAPE_ID_HEAP_INDEX_MASK (SHAPE_ID_HEAP_INDEX_MAX << SHAPE_ID_HEAP_INDEX_OFFSET)
|
||||||
|
|
||||||
|
// The interpreter doesn't care about embeded or frozen status when reading ivars.
|
||||||
|
// So we normalize shape_id by clearing these bits to improve cache hits.
|
||||||
|
// JITs however might care about it.
|
||||||
|
#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_EMBEDDED | SHAPE_ID_HEAP_INDEX_MASK))
|
||||||
|
|
||||||
typedef uint32_t redblack_id_t;
|
typedef uint32_t redblack_id_t;
|
||||||
|
|
||||||
@ -72,6 +82,7 @@ enum shape_flags {
|
|||||||
SHAPE_FL_FROZEN = 1 << 0,
|
SHAPE_FL_FROZEN = 1 << 0,
|
||||||
SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
|
SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
|
||||||
SHAPE_FL_TOO_COMPLEX = 1 << 2,
|
SHAPE_FL_TOO_COMPLEX = 1 << 2,
|
||||||
|
SHAPE_FL_EMBEDDED = 1 << 3,
|
||||||
|
|
||||||
SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
|
SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
|
||||||
};
|
};
|
||||||
@ -189,10 +200,19 @@ rb_shape_canonical_p(shape_id_t shape_id)
|
|||||||
return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
|
return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint8_t
|
||||||
|
rb_shape_heap_index(shape_id_t shape_id)
|
||||||
|
{
|
||||||
|
return (uint8_t)((shape_id & SHAPE_ID_HEAP_INDEX_MASK) >> SHAPE_ID_HEAP_INDEX_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
static inline shape_id_t
|
static inline shape_id_t
|
||||||
rb_shape_root(size_t heap_id)
|
rb_shape_root(size_t heap_id)
|
||||||
{
|
{
|
||||||
return (shape_id_t)(heap_id + FIRST_T_OBJECT_SHAPE_ID);
|
shape_id_t heap_index = (shape_id_t)heap_id;
|
||||||
|
|
||||||
|
shape_id_t shape_id = (heap_index + FIRST_T_OBJECT_SHAPE_ID);
|
||||||
|
return shape_id | ((heap_index + 1) << SHAPE_ID_HEAP_INDEX_OFFSET) | SHAPE_ID_FL_EMBEDDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
|
@ -976,7 +976,7 @@ class TestShapes < Test::Unit::TestCase
|
|||||||
example.add_foo # makes a transition
|
example.add_foo # makes a transition
|
||||||
add_foo_shape = RubyVM::Shape.of(example)
|
add_foo_shape = RubyVM::Shape.of(example)
|
||||||
assert_equal([:@foo], example.instance_variables)
|
assert_equal([:@foo], example.instance_variables)
|
||||||
assert_equal(initial_shape.id, add_foo_shape.parent.id)
|
assert_equal(initial_shape.raw_id, add_foo_shape.parent.raw_id)
|
||||||
assert_equal(1, add_foo_shape.next_field_index)
|
assert_equal(1, add_foo_shape.next_field_index)
|
||||||
|
|
||||||
example.remove_foo # makes a transition
|
example.remove_foo # makes a transition
|
||||||
@ -987,7 +987,7 @@ class TestShapes < Test::Unit::TestCase
|
|||||||
example.add_bar # makes a transition
|
example.add_bar # makes a transition
|
||||||
bar_shape = RubyVM::Shape.of(example)
|
bar_shape = RubyVM::Shape.of(example)
|
||||||
assert_equal([:@bar], example.instance_variables)
|
assert_equal([:@bar], example.instance_variables)
|
||||||
assert_equal(initial_shape.id, bar_shape.parent_id)
|
assert_equal(initial_shape.raw_id, bar_shape.parent_id)
|
||||||
assert_equal(1, bar_shape.next_field_index)
|
assert_equal(1, bar_shape.next_field_index)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user