Skip to content

Commit 4f3dcc8

Browse files
committed
Get rid of SHAPE_T_OBJECT
Now the `heap_index` is stored as flags inside `shape_id_t`. This saves having to rebuild the entire tree when moving from one slot size to the other.
1 parent 638658c commit 4f3dcc8

8 files changed

Lines changed: 112 additions & 140 deletions

File tree

ext/objspace/objspace_dump.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -817,9 +817,6 @@ shape_id_i(shape_id_t shape_id, void *data)
817817
dump_append(dc, ",\"edge_name\":");
818818
dump_append_id(dc, shape->edge_name);
819819

820-
break;
821-
case SHAPE_T_OBJECT:
822-
dump_append(dc, ", \"shape_type\":\"T_OBJECT\"");
823820
break;
824821
case SHAPE_OBJ_ID:
825822
dump_append(dc, ", \"shape_type\":\"OBJ_ID\"");

gc.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -379,19 +379,7 @@ rb_gc_set_shape(VALUE obj, uint32_t shape_id)
379379
uint32_t
380380
rb_gc_rebuild_shape(VALUE obj, size_t heap_id)
381381
{
382-
shape_id_t orig_shape_id = rb_obj_shape_id(obj);
383-
if (rb_shape_too_complex_p(orig_shape_id)) {
384-
return (uint32_t)orig_shape_id;
385-
}
386-
387-
shape_id_t initial_shape_id = rb_shape_root(heap_id);
388-
shape_id_t new_shape_id = rb_shape_traverse_from_new_root(initial_shape_id, orig_shape_id);
389-
390-
if (new_shape_id == INVALID_SHAPE_ID) {
391-
return 0;
392-
}
393-
394-
return (uint32_t)new_shape_id;
382+
return (uint32_t)rb_shape_transition_heap(obj, heap_id);
395383
}
396384

397385
void rb_vm_update_references(void *ptr);
@@ -1923,6 +1911,7 @@ object_id0(VALUE obj)
19231911
// rb_shape_object_id_shape may lock if the current shape has
19241912
// multiple children.
19251913
shape_id_t object_id_shape_id = rb_shape_transition_object_id(obj);
1914+
RUBY_ASSERT(rb_shape_has_object_id(object_id_shape_id));
19261915

19271916
id = generate_next_object_id();
19281917
rb_obj_field_set(obj, object_id_shape_id, id);

object.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -339,17 +339,16 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
339339
shape_id_t dest_shape_id = src_shape_id;
340340
shape_id_t initial_shape_id = RBASIC_SHAPE_ID(dest);
341341

342-
if (RSHAPE(initial_shape_id)->heap_index != RSHAPE(src_shape_id)->heap_index || !rb_shape_canonical_p(src_shape_id)) {
343-
RUBY_ASSERT(RSHAPE(initial_shape_id)->type == SHAPE_T_OBJECT);
342+
RUBY_ASSERT(RSHAPE(initial_shape_id)->type == SHAPE_ROOT);
344343

345-
dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
346-
if (UNLIKELY(rb_shape_too_complex_p(dest_shape_id))) {
347-
st_table *table = rb_st_init_numtable_with_size(src_num_ivs);
348-
rb_obj_copy_ivs_to_hash_table(obj, table);
349-
rb_obj_init_too_complex(dest, table);
344+
dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
350345

351-
return;
352-
}
346+
if (UNLIKELY(rb_shape_too_complex_p(dest_shape_id))) {
347+
st_table *table = rb_st_init_numtable_with_size(src_num_ivs);
348+
rb_obj_copy_ivs_to_hash_table(obj, table);
349+
rb_obj_init_too_complex(dest, table);
350+
351+
return;
353352
}
354353

355354
VALUE *src_buf = ROBJECT_FIELDS(obj);
@@ -365,7 +364,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
365364
}
366365

367366
rb_shape_copy_fields(dest, dest_buf, dest_shape_id, obj, src_buf, src_shape_id);
368-
rb_obj_set_shape_id(dest, dest_shape_id);
367+
RBASIC_SET_SHAPE_ID(dest, dest_shape_id);
369368
}
370369

371370
static void

shape.c

Lines changed: 49 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,7 @@ shape_tree_mark(void *data)
304304
rb_shape_t *end = RSHAPE(GET_SHAPE_TREE()->next_shape_id - 1);
305305
while (cursor < end) {
306306
if (cursor->edges && !SINGLE_CHILD_P(cursor->edges)) {
307-
// FIXME: GC compaction may call `rb_shape_traverse_from_new_root`
308-
// to migrate objects from one object slot to another.
309-
// Because of this if we don't pin `cursor->edges` it might be turned
310-
// into a T_MOVED during GC.
311-
// We'd need to eliminate `SHAPE_T_OBJECT` so that GC never need to lookup
312-
// shapes this way.
313-
// rb_gc_mark_movable(cursor->edges);
314-
rb_gc_mark(cursor->edges);
307+
rb_gc_mark_movable(cursor->edges);
315308
}
316309
cursor++;
317310
}
@@ -368,7 +361,16 @@ shape_id(rb_shape_t *shape, shape_id_t previous_shape_id)
368361
return INVALID_SHAPE_ID;
369362
}
370363
shape_id_t raw_id = (shape_id_t)(shape - GET_SHAPE_TREE()->shape_list);
371-
return raw_id | (previous_shape_id & SHAPE_ID_FLAGS_MASK);
364+
shape_id_t new_id = raw_id | (previous_shape_id & SHAPE_ID_FLAGS_MASK);
365+
366+
attr_index_t embed_capa = RSHAPE_EMBED_CAPA(previous_shape_id);
367+
if (embed_capa > shape->next_field_index) {
368+
new_id |= SHAPE_ID_FL_EMBEDDED;
369+
}
370+
else {
371+
new_id &= ~SHAPE_ID_FL_EMBEDDED;
372+
}
373+
return new_id;
372374
}
373375

374376
#if RUBY_DEBUG
@@ -455,7 +457,6 @@ rb_shape_alloc(ID edge_name, rb_shape_t *parent, enum shape_type type)
455457
{
456458
rb_shape_t *shape = rb_shape_alloc_with_parent_id(edge_name, raw_shape_id(parent));
457459
shape->type = (uint8_t)type;
458-
shape->heap_index = parent->heap_index;
459460
shape->capacity = parent->capacity;
460461
shape->edges = 0;
461462
return shape;
@@ -515,7 +516,6 @@ rb_shape_alloc_new_child(ID id, rb_shape_t *shape, enum shape_type shape_type)
515516
}
516517
break;
517518
case SHAPE_ROOT:
518-
case SHAPE_T_OBJECT:
519519
rb_bug("Unreachable");
520520
break;
521521
}
@@ -743,7 +743,7 @@ rb_shape_transition_frozen(VALUE obj)
743743
{
744744
RUBY_ASSERT(RB_OBJ_FROZEN(obj));
745745

746-
shape_id_t shape_id = rb_obj_shape_id(obj);
746+
shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
747747
return shape_id | SHAPE_ID_FL_FROZEN;
748748
}
749749

@@ -753,6 +753,12 @@ rb_shape_transition_complex(VALUE obj)
753753
return transition_frozen(RBASIC_SHAPE_ID(obj));
754754
}
755755

756+
shape_id_t
757+
rb_shape_transition_heap(VALUE obj, size_t heap_index)
758+
{
759+
return (RBASIC_SHAPE_ID(obj) & (~SHAPE_ID_HEAP_INDEX_MASK)) | rb_shape_root(heap_index);
760+
}
761+
756762
shape_id_t
757763
rb_shape_transition_object_id(VALUE obj)
758764
{
@@ -822,7 +828,6 @@ shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
822828
*value = shape->next_field_index - 1;
823829
return true;
824830
case SHAPE_ROOT:
825-
case SHAPE_T_OBJECT:
826831
return false;
827832
case SHAPE_OBJ_ID:
828833
rb_bug("Ivar should not exist on transition");
@@ -1019,61 +1024,6 @@ rb_shape_id_offset(void)
10191024
return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
10201025
}
10211026

1022-
static rb_shape_t *
1023-
shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
1024-
{
1025-
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
1026-
rb_shape_t *next_shape = initial_shape;
1027-
1028-
if (dest_shape->type != initial_shape->type) {
1029-
next_shape = shape_traverse_from_new_root(initial_shape, RSHAPE(dest_shape->parent_id));
1030-
if (!next_shape) {
1031-
return NULL;
1032-
}
1033-
}
1034-
1035-
switch ((enum shape_type)dest_shape->type) {
1036-
case SHAPE_IVAR:
1037-
case SHAPE_OBJ_ID:
1038-
if (!next_shape->edges) {
1039-
return NULL;
1040-
}
1041-
1042-
VALUE lookup_result;
1043-
if (SINGLE_CHILD_P(next_shape->edges)) {
1044-
rb_shape_t *child = SINGLE_CHILD(next_shape->edges);
1045-
if (child->edge_name == dest_shape->edge_name) {
1046-
return child;
1047-
}
1048-
else {
1049-
return NULL;
1050-
}
1051-
}
1052-
else {
1053-
if (rb_managed_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
1054-
next_shape = (rb_shape_t *)lookup_result;
1055-
}
1056-
else {
1057-
return NULL;
1058-
}
1059-
}
1060-
break;
1061-
case SHAPE_ROOT:
1062-
case SHAPE_T_OBJECT:
1063-
break;
1064-
}
1065-
1066-
return next_shape;
1067-
}
1068-
1069-
shape_id_t
1070-
rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
1071-
{
1072-
rb_shape_t *initial_shape = RSHAPE(initial_shape_id);
1073-
rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
1074-
return shape_id(shape_traverse_from_new_root(initial_shape, dest_shape), dest_shape_id);
1075-
}
1076-
10771027
// Rebuild a similar shape with the same ivars but starting from
10781028
// a different SHAPE_T_OBJECT, and don't cary over non-canonical transitions
10791029
// such as SHAPE_OBJ_ID.
@@ -1082,7 +1032,7 @@ shape_rebuild(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
10821032
{
10831033
rb_shape_t *midway_shape;
10841034

1085-
RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT || initial_shape->type == SHAPE_ROOT);
1035+
RUBY_ASSERT(initial_shape->type == SHAPE_ROOT);
10861036

10871037
if (dest_shape->type != initial_shape->type) {
10881038
midway_shape = shape_rebuild(initial_shape, RSHAPE(dest_shape->parent_id));
@@ -1100,7 +1050,6 @@ shape_rebuild(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
11001050
break;
11011051
case SHAPE_OBJ_ID:
11021052
case SHAPE_ROOT:
1103-
case SHAPE_T_OBJECT:
11041053
break;
11051054
}
11061055

@@ -1113,7 +1062,14 @@ rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
11131062
RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
11141063
RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
11151064

1116-
return raw_shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)));
1065+
if (rb_shape_has_object_id(dest_shape_id)) {
1066+
// The tree contains a SHAPE_OBJ_ID. We need to rebuild a need tree without it
1067+
return shape_id(shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id)), initial_shape_id & ~SHAPE_ID_FL_NON_CANONICAL_MASK);
1068+
}
1069+
else {
1070+
// In the simple case we keep the tree from `dest_shape_id` and the flags from `initial_shape_id`.
1071+
return (dest_shape_id & ~SHAPE_ID_FLAGS_MASK) | (initial_shape_id & SHAPE_ID_FLAGS_MASK);
1072+
}
11171073
}
11181074

11191075
void
@@ -1122,6 +1078,10 @@ rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALU
11221078
rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
11231079
rb_shape_t *src_shape = RSHAPE(src_shape_id);
11241080

1081+
if (!dest_shape->next_field_index) {
1082+
return;
1083+
}
1084+
11251085
if (src_shape->next_field_index == dest_shape->next_field_index) {
11261086
// Happy path, we can just memcpy the ivptr content
11271087
MEMCPY(dest_buf, src_buf, VALUE, dest_shape->next_field_index);
@@ -1135,6 +1095,9 @@ rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALU
11351095
while (src_shape->parent_id != INVALID_SHAPE_ID) {
11361096
if (src_shape->type == SHAPE_IVAR) {
11371097
while (dest_shape->edge_name != src_shape->edge_name) {
1098+
if (UNLIKELY(dest_shape->parent_id == INVALID_SHAPE_ID)) {
1099+
rb_bug("Lost field %s", rb_id2name(src_shape->edge_name));
1100+
}
11381101
dest_shape = RSHAPE(dest_shape->parent_id);
11391102
}
11401103

@@ -1212,6 +1175,16 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
12121175
}
12131176
}
12141177

1178+
if (RB_TYPE_P(obj, T_OBJECT)) {
1179+
size_t slot_size = rb_gc_obj_slot_size(obj);
1180+
size_t real_embed_capa = (slot_size - sizeof(struct RBasic)) / sizeof(VALUE);
1181+
size_t shape_embed_capa = RSHAPE_EMBED_CAPA(shape_id);
1182+
1183+
if (real_embed_capa != shape_embed_capa) {
1184+
rb_bug("Expected real_embed_capa=%lu to match shape_embed_capa=%lu\n", real_embed_capa, shape_embed_capa);
1185+
}
1186+
}
1187+
12151188
return true;
12161189
}
12171190
#endif
@@ -1259,13 +1232,15 @@ shape_id_t_to_rb_cShape(shape_id_t shape_id)
12591232
{
12601233
VALUE rb_cShape = rb_const_get(rb_cRubyVM, rb_intern("Shape"));
12611234
rb_shape_t *shape = RSHAPE(shape_id);
1235+
size_t heap_index = rb_shape_heap_index(shape_id);
12621236

12631237
VALUE obj = rb_struct_new(rb_cShape,
12641238
INT2NUM(shape_id),
1239+
INT2NUM(shape_id & SHAPE_ID_OFFSET_MASK),
12651240
INT2NUM(shape->parent_id),
12661241
rb_shape_edge_name(shape),
12671242
INT2NUM(shape->next_field_index),
1268-
INT2NUM(shape->heap_index),
1243+
heap_index ? SIZET2NUM(heap_index - 1) : Qnil,
12691244
INT2NUM(shape->type),
12701245
INT2NUM(shape->capacity));
12711246
rb_obj_freeze(obj);
@@ -1473,28 +1448,15 @@ Init_default_shapes(void)
14731448
rb_shape_t *root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
14741449
root->capacity = 0;
14751450
root->type = SHAPE_ROOT;
1476-
root->heap_index = 0;
14771451
GET_SHAPE_TREE()->root_shape = root;
14781452
RUBY_ASSERT(raw_shape_id(GET_SHAPE_TREE()->root_shape) == ROOT_SHAPE_ID);
14791453

14801454
rb_shape_t *root_with_obj_id = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
14811455
root_with_obj_id->type = SHAPE_OBJ_ID;
14821456
root_with_obj_id->edge_name = ruby_internal_object_id;
14831457
root_with_obj_id->next_field_index++;
1484-
root_with_obj_id->heap_index = 0;
14851458
RUBY_ASSERT(raw_shape_id(root_with_obj_id) == ROOT_SHAPE_WITH_OBJ_ID);
1486-
1487-
// Make shapes for T_OBJECT
1488-
size_t *sizes = rb_gc_heap_sizes();
1489-
for (int i = 0; sizes[i] > 0; i++) {
1490-
rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
1491-
t_object_shape->type = SHAPE_T_OBJECT;
1492-
t_object_shape->heap_index = i;
1493-
t_object_shape->capacity = (uint32_t)((sizes[i] - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
1494-
t_object_shape->edges = rb_managed_id_table_new(256);
1495-
t_object_shape->ancestor_index = LEAF;
1496-
RUBY_ASSERT(t_object_shape == RSHAPE(rb_shape_root(i)));
1497-
}
1459+
RUBY_ASSERT(RSHAPE(ROOT_TOO_COMPLEX_WITH_OBJ_ID)->type == SHAPE_OBJ_ID);
14981460
}
14991461

15001462
void
@@ -1511,6 +1473,7 @@ Init_shape(void)
15111473
* :nodoc: */
15121474
VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
15131475
"id",
1476+
"raw_id",
15141477
"parent_id",
15151478
"edge_name",
15161479
"next_field_index",
@@ -1528,11 +1491,9 @@ Init_shape(void)
15281491

15291492
rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
15301493
rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
1531-
rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
15321494
rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS));
15331495
rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
15341496
rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
1535-
rb_define_const(rb_cShape, "FIRST_T_OBJECT_SHAPE_ID", INT2NUM(FIRST_T_OBJECT_SHAPE_ID));
15361497
rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
15371498
rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t)));
15381499
rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t)));

0 commit comments

Comments
 (0)