Skip to content

Commit 675f335

Browse files
committed
Get rid of TOO_COMPLEX shape type
Instead it's now a `shape_id` flag. This allows to check if an object is complex without having to chase the `rb_shape_t` pointer.
1 parent 8d49c05 commit 675f335

File tree

13 files changed

+82
-118
lines changed

13 files changed

+82
-118
lines changed

ext/objspace/objspace_dump.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,9 +821,6 @@ shape_id_i(shape_id_t shape_id, void *data)
821821
case SHAPE_T_OBJECT:
822822
dump_append(dc, ", \"shape_type\":\"T_OBJECT\"");
823823
break;
824-
case SHAPE_OBJ_TOO_COMPLEX:
825-
dump_append(dc, ", \"shape_type\":\"OBJ_TOO_COMPLEX\"");
826-
break;
827824
case SHAPE_OBJ_ID:
828825
dump_append(dc, ", \"shape_type\":\"OBJ_ID\"");
829826
break;

shape.c

Lines changed: 40 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
2121
#endif
2222

23-
#define ROOT_TOO_COMPLEX_SHAPE_ID 0x1
24-
2523
#define REDBLACK_CACHE_SIZE (SHAPE_BUFFER_SIZE * 32)
2624

2725
/* This depends on that the allocated memory by Ruby's allocator or
@@ -381,12 +379,6 @@ shape_frozen_p(shape_id_t shape_id)
381379
}
382380
#endif
383381

384-
static inline bool
385-
shape_too_complex_p(rb_shape_t *shape)
386-
{
387-
return shape->flags & SHAPE_FL_TOO_COMPLEX;
388-
}
389-
390382
void
391383
rb_shape_each_shape_id(each_shape_callback callback, void *data)
392384
{
@@ -531,7 +523,6 @@ rb_shape_alloc_new_child(ID id, rb_shape_t *shape, enum shape_type shape_type)
531523
redblack_cache_ancestors(new_shape);
532524
}
533525
break;
534-
case SHAPE_OBJ_TOO_COMPLEX:
535526
case SHAPE_ROOT:
536527
case SHAPE_T_OBJECT:
537528
rb_bug("Unreachable");
@@ -541,8 +532,6 @@ rb_shape_alloc_new_child(ID id, rb_shape_t *shape, enum shape_type shape_type)
541532
return new_shape;
542533
}
543534

544-
static rb_shape_t *shape_transition_too_complex(rb_shape_t *original_shape);
545-
546535
#define RUBY_ATOMIC_VALUE_LOAD(x) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x))
547536

548537
static rb_shape_t *
@@ -581,7 +570,7 @@ get_next_shape_internal_atomic(rb_shape_t *shape, ID id, enum shape_type shape_t
581570
// If we're not allowed to create a new variation, of if we're out of shapes
582571
// we return TOO_COMPLEX_SHAPE.
583572
if (!new_variations_allowed || GET_SHAPE_TREE()->next_shape_id > MAX_SHAPE_ID) {
584-
res = shape_transition_too_complex(shape);
573+
res = NULL;
585574
}
586575
else {
587576
VALUE new_edges = 0;
@@ -623,9 +612,6 @@ get_next_shape_internal_atomic(rb_shape_t *shape, ID id, enum shape_type shape_t
623612
static rb_shape_t *
624613
get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bool *variation_created, bool new_variations_allowed)
625614
{
626-
// There should never be outgoing edges from "too complex", except for SHAPE_OBJ_ID
627-
RUBY_ASSERT(!shape_too_complex_p(shape) || shape_type == SHAPE_OBJ_ID);
628-
629615
if (rb_multi_ractor_p()) {
630616
return get_next_shape_internal_atomic(shape, id, shape_type, variation_created, new_variations_allowed);
631617
}
@@ -660,7 +646,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo
660646
// If we're not allowed to create a new variation, of if we're out of shapes
661647
// we return TOO_COMPLEX_SHAPE.
662648
if (!new_variations_allowed || GET_SHAPE_TREE()->next_shape_id > MAX_SHAPE_ID) {
663-
res = shape_transition_too_complex(shape);
649+
res = NULL;
664650
}
665651
else {
666652
rb_shape_t *new_shape = rb_shape_alloc_new_child(id, shape, shape_type);
@@ -695,6 +681,7 @@ remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
695681
if (shape->parent_id == INVALID_SHAPE_ID) {
696682
// We've hit the top of the shape tree and couldn't find the
697683
// IV we wanted to remove, so return NULL
684+
*removed_shape = NULL;
698685
return NULL;
699686
}
700687
else {
@@ -710,23 +697,14 @@ remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
710697
// We found a new parent. Create a child of the new parent that
711698
// has the same attributes as this shape.
712699
if (new_parent) {
713-
if (UNLIKELY(shape_too_complex_p(new_parent))) {
714-
return new_parent;
715-
}
716-
717700
bool dont_care;
718701
rb_shape_t *new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
719-
if (UNLIKELY(shape_too_complex_p(new_child))) {
720-
return new_child;
721-
}
722-
723-
RUBY_ASSERT(new_child->capacity <= shape->capacity);
724-
702+
RUBY_ASSERT(!new_child || new_child->capacity <= shape->capacity);
725703
return new_child;
726704
}
727705
else {
728706
// We went all the way to the top of the shape tree and couldn't
729-
// find an IV to remove, so return NULL
707+
// find an IV to remove so return NULL.
730708
return NULL;
731709
}
732710
}
@@ -736,19 +714,27 @@ remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
736714
shape_id_t
737715
rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id)
738716
{
739-
shape_id_t shape_id = rb_obj_shape_id(obj);
740-
rb_shape_t *shape = RSHAPE(shape_id);
717+
shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
741718

742-
RUBY_ASSERT(!shape_too_complex_p(shape));
743-
RUBY_ASSERT(!shape_frozen_p(shape_id));
719+
RUBY_ASSERT(!rb_shape_too_complex_p(original_shape_id));
720+
RUBY_ASSERT(!shape_frozen_p(original_shape_id));
744721

745722
rb_shape_t *removed_shape = NULL;
746-
rb_shape_t *new_shape = remove_shape_recursive(shape, id, &removed_shape);
747-
if (new_shape) {
723+
rb_shape_t *new_shape = remove_shape_recursive(RSHAPE(original_shape_id), id, &removed_shape);
724+
725+
if (removed_shape) {
748726
*removed_shape_id = raw_shape_id(removed_shape);
749-
return raw_shape_id(new_shape);
750727
}
751-
return shape_id;
728+
729+
if (new_shape) {
730+
return shape_id(new_shape, original_shape_id);
731+
}
732+
else if (removed_shape) {
733+
// We found the shape to remove, but couldn't create a new variation.
734+
// We must transition to TOO_COMPLEX.
735+
return ROOT_TOO_COMPLEX_SHAPE_ID | (original_shape_id & SHAPE_ID_FLAGS_MASK);
736+
}
737+
return original_shape_id;
752738
}
753739

754740
shape_id_t
@@ -760,24 +746,11 @@ rb_shape_transition_frozen(VALUE obj)
760746
return shape_id | SHAPE_ID_FL_FROZEN;
761747
}
762748

763-
static rb_shape_t *
764-
shape_transition_too_complex(rb_shape_t *original_shape)
765-
{
766-
rb_shape_t *next_shape = RSHAPE(ROOT_TOO_COMPLEX_SHAPE_ID);
767-
768-
if (original_shape->flags & SHAPE_FL_HAS_OBJECT_ID) {
769-
bool dont_care;
770-
next_shape = get_next_shape_internal(next_shape, ruby_internal_object_id, SHAPE_OBJ_ID, &dont_care, false);
771-
}
772-
773-
return next_shape;
774-
}
775-
776749
shape_id_t
777750
rb_shape_transition_complex(VALUE obj)
778751
{
779752
shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
780-
return shape_id(shape_transition_too_complex(RSHAPE(original_shape_id)), original_shape_id);
753+
return ROOT_TOO_COMPLEX_SHAPE_ID | (original_shape_id & SHAPE_ID_FLAGS_MASK);
781754
}
782755

783756
static inline bool
@@ -849,7 +822,6 @@ shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
849822
case SHAPE_ROOT:
850823
case SHAPE_T_OBJECT:
851824
return false;
852-
case SHAPE_OBJ_TOO_COMPLEX:
853825
case SHAPE_OBJ_ID:
854826
rb_bug("Ivar should not exist on transition");
855827
}
@@ -865,9 +837,6 @@ static inline rb_shape_t *
865837
shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings)
866838
{
867839
RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
868-
if (UNLIKELY(shape_too_complex_p(shape))) {
869-
return shape;
870-
}
871840

872841
#if RUBY_DEBUG
873842
attr_index_t index;
@@ -891,6 +860,11 @@ shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings)
891860
bool variation_created = false;
892861
rb_shape_t *new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
893862

863+
if (!new_shape) {
864+
// We could create a new variation, transitioning to TOO_COMPLEX.
865+
return NULL;
866+
}
867+
894868
// Check if we should update max_iv_count on the object's class
895869
if (obj != klass && new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass)) {
896870
RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index);
@@ -1016,11 +990,11 @@ shape_cache_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
1016990
bool
1017991
rb_shape_get_iv_index(shape_id_t shape_id, ID id, attr_index_t *value)
1018992
{
1019-
rb_shape_t *shape = RSHAPE(shape_id);
1020-
1021993
// It doesn't make sense to ask for the index of an IV that's stored
1022994
// on an object that is "too complex" as it uses a hash for storing IVs
1023-
RUBY_ASSERT(!shape_too_complex_p(shape));
995+
RUBY_ASSERT(!rb_shape_too_complex_p(shape_id));
996+
997+
rb_shape_t *shape = RSHAPE(shape_id);
1024998

1025999
if (!shape_cache_get_iv_index(shape, id, value)) {
10261000
// If it wasn't in the ancestor cache, then don't do a linear search
@@ -1083,9 +1057,6 @@ shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
10831057
case SHAPE_ROOT:
10841058
case SHAPE_T_OBJECT:
10851059
break;
1086-
case SHAPE_OBJ_TOO_COMPLEX:
1087-
rb_bug("Unreachable");
1088-
break;
10891060
}
10901061

10911062
return next_shape;
@@ -1102,20 +1073,17 @@ rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t dest_sha
11021073
// Rebuild a similar shape with the same ivars but starting from
11031074
// a different SHAPE_T_OBJECT, and don't cary over non-canonical transitions
11041075
// such as SHAPE_OBJ_ID.
1105-
rb_shape_t *
1106-
rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
1076+
static rb_shape_t *
1077+
shape_rebuild(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
11071078
{
1108-
RUBY_ASSERT(raw_shape_id(initial_shape) != ROOT_TOO_COMPLEX_SHAPE_ID);
1109-
RUBY_ASSERT(raw_shape_id(dest_shape) != ROOT_TOO_COMPLEX_SHAPE_ID);
1110-
11111079
rb_shape_t *midway_shape;
11121080

11131081
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT || initial_shape->type == SHAPE_ROOT);
11141082

11151083
if (dest_shape->type != initial_shape->type) {
1116-
midway_shape = rb_shape_rebuild_shape(initial_shape, RSHAPE(dest_shape->parent_id));
1117-
if (UNLIKELY(raw_shape_id(midway_shape) == ROOT_TOO_COMPLEX_SHAPE_ID)) {
1118-
return midway_shape;
1084+
midway_shape = shape_rebuild(initial_shape, RSHAPE(dest_shape->parent_id));
1085+
if (UNLIKELY(!midway_shape)) {
1086+
return NULL;
11191087
}
11201088
}
11211089
else {
@@ -1130,9 +1098,6 @@ rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
11301098
case SHAPE_ROOT:
11311099
case SHAPE_T_OBJECT:
11321100
break;
1133-
case SHAPE_OBJ_TOO_COMPLEX:
1134-
rb_bug("Unreachable");
1135-
break;
11361101
}
11371102

11381103
return midway_shape;
@@ -1141,7 +1106,10 @@ rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
11411106
shape_id_t
11421107
rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
11431108
{
1144-
return raw_shape_id(rb_shape_rebuild_shape(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)));
1109+
RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
1110+
RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
1111+
1112+
return raw_shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)));
11451113
}
11461114

11471115
void
@@ -1185,18 +1153,6 @@ rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_t
11851153
rb_obj_init_too_complex(dest, table);
11861154
}
11871155

1188-
RUBY_FUNC_EXPORTED bool
1189-
rb_shape_obj_too_complex_p(VALUE obj)
1190-
{
1191-
return shape_too_complex_p(obj_shape(obj));
1192-
}
1193-
1194-
bool
1195-
rb_shape_too_complex_p(shape_id_t shape_id)
1196-
{
1197-
return shape_too_complex_p(RSHAPE(shape_id));
1198-
}
1199-
12001156
size_t
12011157
rb_shape_edges_count(shape_id_t shape_id)
12021158
{
@@ -1233,8 +1189,7 @@ static VALUE
12331189
shape_too_complex(VALUE self)
12341190
{
12351191
shape_id_t shape_id = NUM2INT(rb_struct_getmember(self, rb_intern("id")));
1236-
rb_shape_t *shape = RSHAPE(shape_id);
1237-
return RBOOL(shape_too_complex_p(shape));
1192+
return RBOOL(rb_shape_too_complex_p(shape_id));
12381193
}
12391194

12401195
static VALUE
@@ -1486,13 +1441,6 @@ Init_default_shapes(void)
14861441
GET_SHAPE_TREE()->root_shape = root;
14871442
RUBY_ASSERT(raw_shape_id(GET_SHAPE_TREE()->root_shape) == ROOT_SHAPE_ID);
14881443

1489-
bool dont_care;
1490-
rb_shape_t *too_complex_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
1491-
too_complex_shape->type = SHAPE_OBJ_TOO_COMPLEX;
1492-
too_complex_shape->flags |= SHAPE_FL_TOO_COMPLEX;
1493-
too_complex_shape->heap_index = 0;
1494-
RUBY_ASSERT(too_complex_shape == RSHAPE(ROOT_TOO_COMPLEX_SHAPE_ID));
1495-
14961444
// Make shapes for T_OBJECT
14971445
size_t *sizes = rb_gc_heap_sizes();
14981446
for (int i = 0; sizes[i] > 0; i++) {
@@ -1504,10 +1452,6 @@ Init_default_shapes(void)
15041452
t_object_shape->ancestor_index = LEAF;
15051453
RUBY_ASSERT(t_object_shape == RSHAPE(rb_shape_root(i)));
15061454
}
1507-
1508-
// Prebuild TOO_COMPLEX variations so that they already exist if we ever need them after we
1509-
// ran out of shapes.
1510-
get_next_shape_internal(too_complex_shape, ruby_internal_object_id, SHAPE_OBJ_ID, &dont_care, true);
15111455
}
15121456

15131457
void

shape.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
1414
#define SHAPE_ID_OFFSET_MASK (SHAPE_BUFFER_SIZE - 1)
1515
#define SHAPE_ID_FLAGS_MASK (shape_id_t)(((1 << (SHAPE_ID_NUM_BITS - SHAPE_ID_OFFSET_NUM_BITS)) - 1) << SHAPE_ID_OFFSET_NUM_BITS)
1616
#define SHAPE_ID_FL_FROZEN (SHAPE_FL_FROZEN << SHAPE_ID_OFFSET_NUM_BITS)
17+
#define SHAPE_ID_FL_TOO_COMPLEX (SHAPE_FL_TOO_COMPLEX << SHAPE_ID_OFFSET_NUM_BITS)
1718
#define SHAPE_ID_READ_ONLY_MASK (~SHAPE_ID_FL_FROZEN)
1819

1920
typedef uint32_t redblack_id_t;
@@ -28,9 +29,9 @@ typedef uint32_t redblack_id_t;
2829
#define ATTR_INDEX_NOT_SET ((attr_index_t)-1)
2930

3031
#define ROOT_SHAPE_ID 0x0
31-
// ROOT_TOO_COMPLEX_SHAPE_ID 0x1
32+
#define ROOT_TOO_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_TOO_COMPLEX)
3233
#define SPECIAL_CONST_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_FROZEN)
33-
#define FIRST_T_OBJECT_SHAPE_ID 0x2
34+
#define FIRST_T_OBJECT_SHAPE_ID 0x1
3435

3536
extern ID ruby_internal_object_id;
3637

@@ -62,7 +63,6 @@ enum shape_type {
6263
SHAPE_IVAR,
6364
SHAPE_OBJ_ID,
6465
SHAPE_T_OBJECT,
65-
SHAPE_OBJ_TOO_COMPLEX,
6666
};
6767

6868
enum shape_flags {
@@ -142,8 +142,6 @@ RUBY_FUNC_EXPORTED shape_id_t rb_obj_shape_id(VALUE obj);
142142
shape_id_t rb_shape_get_next_iv_shape(shape_id_t shape_id, ID id);
143143
bool rb_shape_get_iv_index(shape_id_t shape_id, ID id, attr_index_t *value);
144144
bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint);
145-
RUBY_FUNC_EXPORTED bool rb_shape_obj_too_complex_p(VALUE obj);
146-
bool rb_shape_too_complex_p(shape_id_t shape_id);
147145
bool rb_shape_has_object_id(shape_id_t shape_id);
148146

149147
shape_id_t rb_shape_transition_frozen(VALUE obj);
@@ -159,6 +157,18 @@ shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_i
159157
void rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALUE src, VALUE *src_buf, shape_id_t src_shape_id);
160158
void rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table);
161159

160+
static inline bool
161+
rb_shape_too_complex_p(shape_id_t shape_id)
162+
{
163+
return shape_id & SHAPE_ID_FL_TOO_COMPLEX;
164+
}
165+
166+
static inline bool
167+
rb_shape_obj_too_complex_p(VALUE obj)
168+
{
169+
return !RB_SPECIAL_CONST_P(obj) && rb_shape_too_complex_p(RBASIC_SHAPE_ID(obj));
170+
}
171+
162172
static inline bool
163173
rb_shape_canonical_p(shape_id_t shape_id)
164174
{

variable.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,10 +2240,6 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
22402240
}
22412241
}
22422242
return false;
2243-
case SHAPE_OBJ_TOO_COMPLEX:
2244-
default:
2245-
rb_bug("Unreachable");
2246-
UNREACHABLE_RETURN(false);
22472243
}
22482244
}
22492245

0 commit comments

Comments
 (0)