Skip to content

Commit 8c4e368

Browse files
committed
shape.c: ensure heap_index is consistent for complex shapes
1 parent 54edc93 commit 8c4e368

File tree

3 files changed

+114
-64
lines changed

3 files changed

+114
-64
lines changed

internal/variable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ int rb_gen_fields_tbl_get(VALUE obj, ID id, struct gen_fields_tbl **fields_tbl);
5252
void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table);
5353
void rb_obj_init_too_complex(VALUE obj, st_table *table);
5454
void rb_evict_ivars_to_hash(VALUE obj);
55-
void rb_evict_fields_to_hash(VALUE obj);
55+
shape_id_t rb_evict_fields_to_hash(VALUE obj);
5656
VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id);
5757
void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
5858
void rb_obj_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val);

shape.c

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -723,15 +723,67 @@ remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
723723
}
724724
}
725725

726+
727+
static shape_id_t
728+
shape_transition_object_id(shape_id_t original_shape_id)
729+
{
730+
RUBY_ASSERT(!rb_shape_has_object_id(original_shape_id));
731+
732+
bool dont_care;
733+
rb_shape_t *shape = get_next_shape_internal(RSHAPE(original_shape_id), ruby_internal_object_id, SHAPE_OBJ_ID, &dont_care, true);
734+
735+
RUBY_ASSERT(shape);
736+
737+
return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
738+
}
739+
740+
shape_id_t
741+
rb_shape_transition_object_id(VALUE obj)
742+
{
743+
return shape_transition_object_id(RBASIC_SHAPE_ID(obj));
744+
}
745+
746+
shape_id_t
747+
rb_shape_object_id(shape_id_t original_shape_id)
748+
{
749+
RUBY_ASSERT(rb_shape_has_object_id(original_shape_id));
750+
751+
rb_shape_t *shape = RSHAPE(original_shape_id);
752+
while (shape->type != SHAPE_OBJ_ID) {
753+
if (UNLIKELY(shape->parent_id == INVALID_SHAPE_ID)) {
754+
rb_bug("Missing object_id in shape tree");
755+
}
756+
shape = RSHAPE(shape->parent_id);
757+
}
758+
759+
return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
760+
}
761+
726762
static inline shape_id_t
727763
transition_complex(shape_id_t shape_id)
728764
{
729-
if (rb_shape_has_object_id(shape_id)) {
730-
return ROOT_TOO_COMPLEX_WITH_OBJ_ID | (shape_id & SHAPE_ID_FLAGS_MASK);
765+
uint8_t heap_index = RSHAPE(shape_id)->heap_index;
766+
shape_id_t next_shape_id;
767+
768+
if (heap_index) {
769+
next_shape_id = rb_shape_root(heap_index - 1) | SHAPE_ID_FL_TOO_COMPLEX;
770+
if (rb_shape_has_object_id(shape_id)) {
771+
next_shape_id = shape_transition_object_id(next_shape_id);
772+
}
773+
}
774+
else {
775+
if (rb_shape_has_object_id(shape_id)) {
776+
next_shape_id = ROOT_TOO_COMPLEX_WITH_OBJ_ID | (shape_id & SHAPE_ID_FLAGS_MASK);
777+
}
778+
else {
779+
next_shape_id = ROOT_TOO_COMPLEX_SHAPE_ID | (shape_id & SHAPE_ID_FLAGS_MASK);
780+
}
731781
}
732-
return ROOT_TOO_COMPLEX_SHAPE_ID | (shape_id & SHAPE_ID_FLAGS_MASK);
733-
}
734782

783+
RUBY_ASSERT(rb_shape_has_object_id(shape_id) == rb_shape_has_object_id(next_shape_id));
784+
785+
return next_shape_id;
786+
}
735787

736788
shape_id_t
737789
rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id)
@@ -754,7 +806,9 @@ rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id)
754806
else if (removed_shape) {
755807
// We found the shape to remove, but couldn't create a new variation.
756808
// We must transition to TOO_COMPLEX.
757-
return transition_complex(original_shape_id);
809+
shape_id_t next_shape_id = transition_complex(original_shape_id);
810+
RUBY_ASSERT(rb_shape_has_object_id(next_shape_id) == rb_shape_has_object_id(original_shape_id));
811+
return next_shape_id;
758812
}
759813
return original_shape_id;
760814
}
@@ -774,41 +828,6 @@ rb_shape_transition_complex(VALUE obj)
774828
return transition_complex(RBASIC_SHAPE_ID(obj));
775829
}
776830

777-
shape_id_t
778-
rb_shape_transition_object_id(VALUE obj)
779-
{
780-
shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
781-
782-
RUBY_ASSERT(!rb_shape_has_object_id(original_shape_id));
783-
784-
rb_shape_t *shape = NULL;
785-
if (!rb_shape_too_complex_p(original_shape_id)) {
786-
bool dont_care;
787-
shape = get_next_shape_internal(RSHAPE(original_shape_id), ruby_internal_object_id, SHAPE_OBJ_ID, &dont_care, true);
788-
}
789-
790-
if (!shape) {
791-
shape = RSHAPE(ROOT_TOO_COMPLEX_WITH_OBJ_ID);
792-
}
793-
return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
794-
}
795-
796-
shape_id_t
797-
rb_shape_object_id(shape_id_t original_shape_id)
798-
{
799-
RUBY_ASSERT(rb_shape_has_object_id(original_shape_id));
800-
801-
rb_shape_t *shape = RSHAPE(original_shape_id);
802-
while (shape->type != SHAPE_OBJ_ID) {
803-
if (UNLIKELY(shape->parent_id == INVALID_SHAPE_ID)) {
804-
rb_bug("Missing object_id in shape tree");
805-
}
806-
shape = RSHAPE(shape->parent_id);
807-
}
808-
809-
return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
810-
}
811-
812831
/*
813832
* This function is used for assertions where we don't want to increment
814833
* max_iv_count
@@ -918,7 +937,13 @@ rb_shape_transition_add_ivar(VALUE obj, ID id)
918937
shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
919938
RUBY_ASSERT(!shape_frozen_p(original_shape_id));
920939

921-
return shape_id(shape_get_next(RSHAPE(original_shape_id), obj, id, true), original_shape_id);
940+
rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), obj, id, true);
941+
if (next_shape) {
942+
return shape_id(next_shape, original_shape_id);
943+
}
944+
else {
945+
return transition_complex(original_shape_id);
946+
}
922947
}
923948

924949
shape_id_t
@@ -927,7 +952,13 @@ rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id)
927952
shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
928953
RUBY_ASSERT(!shape_frozen_p(original_shape_id));
929954

930-
return shape_id(shape_get_next(RSHAPE(original_shape_id), obj, id, false), original_shape_id);
955+
rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), obj, id, false);
956+
if (next_shape) {
957+
return shape_id(next_shape, original_shape_id);
958+
}
959+
else {
960+
return transition_complex(original_shape_id);
961+
}
931962
}
932963

933964
// Same as rb_shape_get_iv_index, but uses a provided valid shape id and index
@@ -1139,7 +1170,13 @@ rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
11391170
RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
11401171
RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
11411172

1142-
return shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)), initial_shape_id);
1173+
rb_shape_t *next_shape = shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id));
1174+
if (next_shape) {
1175+
return shape_id(next_shape, initial_shape_id);
1176+
}
1177+
else {
1178+
return transition_complex(initial_shape_id | (dest_shape_id & SHAPE_ID_FL_HAS_OBJECT_ID));
1179+
}
11431180
}
11441181

11451182
void
@@ -1217,6 +1254,10 @@ rb_shape_memsize(shape_id_t shape_id)
12171254
bool
12181255
rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
12191256
{
1257+
if (shape_id == INVALID_SHAPE_ID) {
1258+
rb_bug("Can't set INVALID_SHAPE_ID on an object");
1259+
}
1260+
12201261
rb_shape_t *shape = RSHAPE(shape_id);
12211262

12221263
bool has_object_id = false;
@@ -1241,12 +1282,9 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
12411282
}
12421283
}
12431284

1244-
// All complex shape are in heap_index=0, it's a limitation
1245-
if (!rb_shape_too_complex_p(shape_id)) {
1246-
uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
1247-
if (flags_heap_index != shape->heap_index) {
1248-
rb_bug("shape_id heap_index flags mismatch: flags=%u, transition=%u\n", flags_heap_index, shape->heap_index);
1249-
}
1285+
uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
1286+
if (flags_heap_index != shape->heap_index) {
1287+
rb_bug("shape_id heap_index flags mismatch: flags=%u, transition=%u\n", flags_heap_index, shape->heap_index);
12501288
}
12511289

12521290
return true;
@@ -1537,7 +1575,8 @@ Init_default_shapes(void)
15371575

15381576
// Make shapes for T_OBJECT
15391577
size_t *sizes = rb_gc_heap_sizes();
1540-
for (int i = 0; sizes[i] > 0; i++) {
1578+
int i;
1579+
for (i = 0; sizes[i] > 0; i++) {
15411580
rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
15421581
t_object_shape->type = SHAPE_T_OBJECT;
15431582
t_object_shape->heap_index = i + 1;
@@ -1546,6 +1585,13 @@ Init_default_shapes(void)
15461585
t_object_shape->ancestor_index = LEAF;
15471586
RUBY_ASSERT(t_object_shape == RSHAPE(rb_shape_root(i)));
15481587
}
1588+
1589+
// Prebuild all ROOT + OBJ_ID shapes so that even when we run out of shape we can always transtion to
1590+
// COMPLEX + OBJ_ID.
1591+
bool dont_care;
1592+
for (i = 0; sizes[i] > 0; i++) {
1593+
get_next_shape_internal(RSHAPE(rb_shape_root(i)), ruby_internal_object_id, SHAPE_OBJ_ID, &dont_care, true);
1594+
}
15491595
}
15501596

15511597
void

variable.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,7 @@ rb_attr_delete(VALUE obj, ID id)
16111611
return rb_ivar_delete(obj, id, Qnil);
16121612
}
16131613

1614-
static void
1614+
static shape_id_t
16151615
obj_transition_too_complex(VALUE obj, st_table *table)
16161616
{
16171617
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
@@ -1659,6 +1659,7 @@ obj_transition_too_complex(VALUE obj, st_table *table)
16591659
}
16601660

16611661
xfree(old_fields);
1662+
return shape_id;
16621663
}
16631664

16641665
void
@@ -1673,7 +1674,7 @@ rb_obj_init_too_complex(VALUE obj, st_table *table)
16731674
}
16741675

16751676
// Copy all object fields, including ivars and internal object_id, etc
1676-
void
1677+
shape_id_t
16771678
rb_evict_fields_to_hash(VALUE obj)
16781679
{
16791680
void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table);
@@ -1682,9 +1683,10 @@ rb_evict_fields_to_hash(VALUE obj)
16821683

16831684
st_table *table = st_init_numtable_with_size(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)));
16841685
rb_obj_copy_fields_to_hash_table(obj, table);
1685-
obj_transition_too_complex(obj, table);
1686+
shape_id_t new_shape_id = obj_transition_too_complex(obj, table);
16861687

16871688
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
1689+
return new_shape_id;
16881690
}
16891691

16901692
void
@@ -1711,7 +1713,7 @@ general_ivar_set(VALUE obj, ID id, VALUE val, void *data,
17111713
VALUE *(*shape_fields_func)(VALUE, void *),
17121714
void (*shape_resize_fields_func)(VALUE, attr_index_t, attr_index_t, void *),
17131715
void (*set_shape_id_func)(VALUE, shape_id_t, void *),
1714-
void (*transition_too_complex_func)(VALUE, void *),
1716+
shape_id_t (*transition_too_complex_func)(VALUE, void *),
17151717
st_table *(*too_complex_table_func)(VALUE, void *))
17161718
{
17171719
struct general_ivar_set_result result = {
@@ -1736,7 +1738,7 @@ general_ivar_set(VALUE obj, ID id, VALUE val, void *data,
17361738

17371739
shape_id_t next_shape_id = rb_shape_transition_add_ivar(obj, id);
17381740
if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) {
1739-
transition_too_complex_func(obj, data);
1741+
current_shape_id = transition_too_complex_func(obj, data);
17401742
goto too_complex;
17411743
}
17421744
else if (UNLIKELY(RSHAPE_CAPACITY(next_shape_id) != RSHAPE_CAPACITY(current_shape_id))) {
@@ -1772,14 +1774,14 @@ general_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val, void *data,
17721774
VALUE *(*shape_fields_func)(VALUE, void *),
17731775
void (*shape_resize_fields_func)(VALUE, attr_index_t, attr_index_t, void *),
17741776
void (*set_shape_id_func)(VALUE, shape_id_t, void *),
1775-
void (*transition_too_complex_func)(VALUE, void *),
1777+
shape_id_t (*transition_too_complex_func)(VALUE, void *),
17761778
st_table *(*too_complex_table_func)(VALUE, void *))
17771779
{
17781780
shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
17791781

17801782
if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) {
17811783
if (UNLIKELY(!rb_shape_too_complex_p(current_shape_id))) {
1782-
transition_too_complex_func(obj, data);
1784+
current_shape_id = transition_too_complex_func(obj, data);
17831785
}
17841786

17851787
st_table *table = too_complex_table_func(obj, data);
@@ -1788,6 +1790,7 @@ general_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val, void *data,
17881790
RBASIC_SET_SHAPE_ID(obj, target_shape_id);
17891791
}
17901792

1793+
RUBY_ASSERT(RSHAPE_EDGE_NAME(target_shape_id));
17911794
st_insert(table, (st_data_t)RSHAPE_EDGE_NAME(target_shape_id), (st_data_t)val);
17921795
RB_OBJ_WRITTEN(obj, Qundef, val);
17931796
}
@@ -1877,11 +1880,12 @@ generic_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *data)
18771880
fields_lookup->shape_id = shape_id;
18781881
}
18791882

1880-
static void
1883+
static shape_id_t
18811884
generic_ivar_set_transition_too_complex(VALUE obj, void *_data)
18821885
{
1883-
rb_evict_fields_to_hash(obj);
1886+
shape_id_t new_shape_id = rb_evict_fields_to_hash(obj);
18841887
FL_SET_RAW(obj, FL_EXIVAR);
1888+
return new_shape_id;
18851889
}
18861890

18871891
static st_table *
@@ -1997,10 +2001,10 @@ obj_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data)
19972001
rb_obj_set_shape_id(obj, shape_id);
19982002
}
19992003

2000-
static void
2004+
static shape_id_t
20012005
obj_ivar_set_transition_too_complex(VALUE obj, void *_data)
20022006
{
2003-
rb_evict_fields_to_hash(obj);
2007+
return rb_evict_fields_to_hash(obj);
20042008
}
20052009

20062010
static st_table *
@@ -4691,10 +4695,10 @@ class_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data)
46914695
rb_obj_set_shape_id(obj, shape_id);
46924696
}
46934697

4694-
static void
4698+
static shape_id_t
46954699
class_ivar_set_transition_too_complex(VALUE obj, void *_data)
46964700
{
4697-
rb_evict_fields_to_hash(obj);
4701+
return rb_evict_fields_to_hash(obj);
46984702
}
46994703

47004704
static st_table *

0 commit comments

Comments
 (0)