Skip to content

Commit 6916367

Browse files
committed
Refactor rb_shape_transition_remove_ivar
Move the fields management logic in `rb_ivar_delete`, and keep shape managment logic in `rb_shape_transition_remove_ivar`.
1 parent 70f8f7c commit 6916367

3 files changed

Lines changed: 70 additions & 55 deletions

File tree

shape.c

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -632,62 +632,21 @@ remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
632632
}
633633
}
634634

635-
bool
636-
rb_shape_transition_remove_ivar(VALUE obj, ID id, VALUE *removed)
635+
shape_id_t
636+
rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id)
637637
{
638-
rb_shape_t *shape = rb_obj_shape(obj);
638+
shape_id_t shape_id = rb_obj_shape_id(obj);
639+
rb_shape_t *shape = RSHAPE(shape_id);
639640

640-
if (UNLIKELY(rb_shape_too_complex_p(shape))) {
641-
return false;
642-
}
641+
RUBY_ASSERT(!rb_shape_too_complex_p(shape));
643642

644643
rb_shape_t *removed_shape = NULL;
645644
rb_shape_t *new_shape = remove_shape_recursive(shape, id, &removed_shape);
646645
if (new_shape) {
647-
RUBY_ASSERT(removed_shape != NULL);
648-
649-
if (UNLIKELY(rb_shape_too_complex_p(new_shape))) {
650-
return false;
651-
}
652-
653-
RUBY_ASSERT(new_shape->next_field_index == shape->next_field_index - 1);
654-
655-
VALUE *fields;
656-
switch(BUILTIN_TYPE(obj)) {
657-
case T_CLASS:
658-
case T_MODULE:
659-
fields = RCLASS_PRIME_FIELDS(obj);
660-
break;
661-
case T_OBJECT:
662-
fields = ROBJECT_FIELDS(obj);
663-
break;
664-
default: {
665-
struct gen_fields_tbl *fields_tbl;
666-
rb_gen_fields_tbl_get(obj, id, &fields_tbl);
667-
fields = fields_tbl->as.shape.fields;
668-
break;
669-
}
670-
}
671-
672-
*removed = fields[removed_shape->next_field_index - 1];
673-
674-
memmove(&fields[removed_shape->next_field_index - 1], &fields[removed_shape->next_field_index],
675-
((new_shape->next_field_index + 1) - removed_shape->next_field_index) * sizeof(VALUE));
676-
677-
// Re-embed objects when instances become small enough
678-
// This is necessary because YJIT assumes that objects with the same shape
679-
// have the same embeddedness for efficiency (avoid extra checks)
680-
if (BUILTIN_TYPE(obj) == T_OBJECT &&
681-
!RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
682-
rb_obj_embedded_size(new_shape->next_field_index) <= rb_gc_obj_slot_size(obj)) {
683-
RB_FL_SET_RAW(obj, ROBJECT_EMBED);
684-
memcpy(ROBJECT_FIELDS(obj), fields, new_shape->next_field_index * sizeof(VALUE));
685-
xfree(fields);
686-
}
687-
688-
rb_shape_set_shape(obj, new_shape);
646+
*removed_shape_id = rb_shape_id(removed_shape);
647+
return rb_shape_id(new_shape);
689648
}
690-
return true;
649+
return shape_id;
691650
}
692651

693652
shape_id_t

shape.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ bool rb_shape_id_too_complex_p(shape_id_t shape_id);
168168
void rb_shape_set_shape(VALUE obj, rb_shape_t *shape);
169169
shape_id_t rb_shape_transition_frozen(VALUE obj);
170170
shape_id_t rb_shape_transition_complex(VALUE obj);
171-
bool rb_shape_transition_remove_ivar(VALUE obj, ID id, VALUE *removed);
171+
shape_id_t rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id);
172172
shape_id_t rb_shape_transition_add_ivar(VALUE obj, ID id);
173173
shape_id_t rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id);
174174
shape_id_t rb_shape_transition_object_id(VALUE obj);

variable.c

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,11 +1542,68 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
15421542
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
15431543
}
15441544

1545-
if (!rb_shape_transition_remove_ivar(obj, id, &val)) {
1546-
if (!rb_shape_obj_too_complex_p(obj)) {
1547-
rb_evict_fields_to_hash(obj);
1548-
}
1545+
shape_id_t old_shape_id = rb_obj_shape_id(obj);
1546+
if (rb_shape_id_too_complex_p(old_shape_id)) {
1547+
goto too_complex;
1548+
}
1549+
1550+
shape_id_t removed_shape_id = 0;
1551+
shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id);
1552+
1553+
if (next_shape_id == old_shape_id) {
1554+
return undef;
1555+
}
1556+
1557+
if (UNLIKELY(rb_shape_id_too_complex_p(next_shape_id))) {
1558+
rb_evict_fields_to_hash(obj);
1559+
goto too_complex;
1560+
}
1561+
1562+
RUBY_ASSERT(RSHAPE(next_shape_id)->next_field_index == RSHAPE(old_shape_id)->next_field_index - 1);
1563+
1564+
VALUE *fields;
1565+
switch(BUILTIN_TYPE(obj)) {
1566+
case T_CLASS:
1567+
case T_MODULE:
1568+
fields = RCLASS_PRIME_FIELDS(obj);
1569+
break;
1570+
case T_OBJECT:
1571+
fields = ROBJECT_FIELDS(obj);
1572+
break;
1573+
default: {
1574+
struct gen_fields_tbl *fields_tbl;
1575+
rb_gen_fields_tbl_get(obj, id, &fields_tbl);
1576+
fields = fields_tbl->as.shape.fields;
1577+
break;
1578+
}
1579+
}
1580+
1581+
RUBY_ASSERT(removed_shape_id != INVALID_SHAPE_ID);
1582+
1583+
attr_index_t new_fields_count = RSHAPE(next_shape_id)->next_field_index;
1584+
1585+
attr_index_t removed_index = RSHAPE(removed_shape_id)->next_field_index - 1;
1586+
val = fields[removed_index];
1587+
size_t trailing_fields = new_fields_count - removed_index;
15491588

1589+
MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
1590+
1591+
if (RB_TYPE_P(obj, T_OBJECT) &&
1592+
!RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
1593+
rb_obj_embedded_size(new_fields_count) <= rb_gc_obj_slot_size(obj)) {
1594+
// Re-embed objects when instances become small enough
1595+
// This is necessary because YJIT assumes that objects with the same shape
1596+
// have the same embeddedness for efficiency (avoid extra checks)
1597+
RB_FL_SET_RAW(obj, ROBJECT_EMBED);
1598+
MEMCPY(ROBJECT_FIELDS(obj), fields, VALUE, new_fields_count);
1599+
xfree(fields);
1600+
}
1601+
rb_shape_set_shape_id(obj, next_shape_id);
1602+
1603+
return val;
1604+
1605+
too_complex:
1606+
{
15501607
st_table *table = NULL;
15511608
switch (BUILTIN_TYPE(obj)) {
15521609
case T_CLASS:
@@ -1573,7 +1630,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
15731630
}
15741631
}
15751632
}
1576-
15771633
return val;
15781634
}
15791635

0 commit comments

Comments
 (0)