Skip to content

Commit 5257e12

Browse files
committed
Replace ROBJECT_EMBED by ROBJECT_HEAP
The embed layout is way more common than the heap one, especially since WVA. I think it makes for more readable code to inverse the flag.
1 parent 5ff7b2c commit 5257e12

15 files changed

Lines changed: 97 additions & 89 deletions

File tree

ext/objspace/objspace_dump.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ dump_object(VALUE obj, struct dump_config *dc)
587587
break;
588588

589589
case T_OBJECT:
590-
if (FL_TEST(obj, ROBJECT_EMBED)) {
590+
if (!FL_TEST_RAW(obj, ROBJECT_HEAP)) {
591591
dump_append(dc, ", \"embedded\":true");
592592
}
593593

gc.c

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,16 +1264,18 @@ rb_gc_obj_free(void *objspace, VALUE obj)
12641264

12651265
switch (BUILTIN_TYPE(obj)) {
12661266
case T_OBJECT:
1267-
if (rb_shape_obj_too_complex_p(obj)) {
1268-
RB_DEBUG_COUNTER_INC(obj_obj_too_complex);
1269-
st_free_table(ROBJECT_FIELDS_HASH(obj));
1270-
}
1271-
else if (RBASIC(obj)->flags & ROBJECT_EMBED) {
1272-
RB_DEBUG_COUNTER_INC(obj_obj_embed);
1267+
if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
1268+
if (rb_shape_obj_too_complex_p(obj)) {
1269+
RB_DEBUG_COUNTER_INC(obj_obj_too_complex);
1270+
st_free_table(ROBJECT_FIELDS_HASH(obj));
1271+
}
1272+
else {
1273+
xfree(ROBJECT(obj)->as.heap.fields);
1274+
RB_DEBUG_COUNTER_INC(obj_obj_ptr);
1275+
}
12731276
}
12741277
else {
1275-
xfree(ROBJECT(obj)->as.heap.fields);
1276-
RB_DEBUG_COUNTER_INC(obj_obj_ptr);
1278+
RB_DEBUG_COUNTER_INC(obj_obj_embed);
12771279
}
12781280
break;
12791281
case T_MODULE:
@@ -2313,11 +2315,13 @@ rb_obj_memsize_of(VALUE obj)
23132315

23142316
switch (BUILTIN_TYPE(obj)) {
23152317
case T_OBJECT:
2316-
if (rb_shape_obj_too_complex_p(obj)) {
2317-
size += rb_st_memsize(ROBJECT_FIELDS_HASH(obj));
2318-
}
2319-
else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
2320-
size += ROBJECT_FIELDS_CAPACITY(obj) * sizeof(VALUE);
2318+
if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
2319+
if (rb_shape_obj_too_complex_p(obj)) {
2320+
size += rb_st_memsize(ROBJECT_FIELDS_HASH(obj));
2321+
}
2322+
else {
2323+
size += ROBJECT_FIELDS_CAPACITY(obj) * sizeof(VALUE);
2324+
}
23212325
}
23222326
break;
23232327
case T_MODULE:
@@ -3543,19 +3547,21 @@ gc_ref_update_object(void *objspace, VALUE v)
35433547
{
35443548
VALUE *ptr = ROBJECT_FIELDS(v);
35453549

3546-
if (rb_shape_obj_too_complex_p(v)) {
3547-
gc_ref_update_table_values_only(ROBJECT_FIELDS_HASH(v));
3548-
return;
3549-
}
3550+
if (FL_TEST_RAW(v, ROBJECT_HEAP)) {
3551+
if (rb_shape_obj_too_complex_p(v)) {
3552+
gc_ref_update_table_values_only(ROBJECT_FIELDS_HASH(v));
3553+
return;
3554+
}
35503555

3551-
size_t slot_size = rb_gc_obj_slot_size(v);
3552-
size_t embed_size = rb_obj_embedded_size(ROBJECT_FIELDS_CAPACITY(v));
3553-
if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) {
3554-
// Object can be re-embedded
3555-
memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_FIELDS_COUNT(v));
3556-
RB_FL_SET_RAW(v, ROBJECT_EMBED);
3557-
xfree(ptr);
3558-
ptr = ROBJECT(v)->as.ary;
3556+
size_t slot_size = rb_gc_obj_slot_size(v);
3557+
size_t embed_size = rb_obj_embedded_size(ROBJECT_FIELDS_CAPACITY(v));
3558+
if (slot_size >= embed_size) {
3559+
// Object can be re-embedded
3560+
memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * ROBJECT_FIELDS_COUNT(v));
3561+
FL_UNSET_RAW(v, ROBJECT_HEAP);
3562+
xfree(ptr);
3563+
ptr = ROBJECT(v)->as.ary;
3564+
}
35593565
}
35603566

35613567
for (uint32_t i = 0; i < ROBJECT_FIELDS_COUNT(v); i++) {
@@ -4773,21 +4779,18 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
47734779
}
47744780
case T_OBJECT:
47754781
{
4776-
if (rb_shape_obj_too_complex_p(obj)) {
4777-
size_t hash_len = rb_st_table_size(ROBJECT_FIELDS_HASH(obj));
4778-
APPEND_F("(too_complex) len:%zu", hash_len);
4779-
}
4780-
else {
4781-
uint32_t len = ROBJECT_FIELDS_CAPACITY(obj);
4782-
4783-
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
4784-
APPEND_F("(embed) len:%d", len);
4782+
if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
4783+
if (rb_shape_obj_too_complex_p(obj)) {
4784+
size_t hash_len = rb_st_table_size(ROBJECT_FIELDS_HASH(obj));
4785+
APPEND_F("(too_complex) len:%zu", hash_len);
47854786
}
47864787
else {
4787-
VALUE *ptr = ROBJECT_FIELDS(obj);
4788-
APPEND_F("len:%d ptr:%p", len, (void *)ptr);
4788+
APPEND_F("(embed) len:%d", ROBJECT_FIELDS_CAPACITY(obj));
47894789
}
47904790
}
4791+
else {
4792+
APPEND_F("len:%d ptr:%p", ROBJECT_FIELDS_CAPACITY(obj), (void *)ROBJECT_FIELDS(obj));
4793+
}
47914794
}
47924795
break;
47934796
case T_DATA: {

imemo.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ imemo_fields_new(VALUE owner, size_t capa)
116116
if (rb_gc_size_allocatable_p(embedded_size)) {
117117
VALUE fields = rb_imemo_new(imemo_fields, owner, embedded_size);
118118
RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields));
119-
FL_SET_RAW(fields, OBJ_FIELD_EMBED);
120119
return fields;
121120
}
122121
else {
123122
VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields));
124123
IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa);
124+
FL_SET_RAW(fields, OBJ_FIELD_HEAP);
125125
return fields;
126126
}
127127
}
@@ -137,7 +137,7 @@ imemo_fields_new_complex(VALUE owner, size_t capa)
137137
{
138138
VALUE fields = imemo_fields_new(owner, 1);
139139
IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa);
140-
FL_UNSET_RAW(fields, OBJ_FIELD_EMBED);
140+
FL_SET_RAW(fields, OBJ_FIELD_HEAP);
141141
return fields;
142142
}
143143

@@ -166,8 +166,8 @@ VALUE
166166
rb_imemo_fields_new_complex_tbl(VALUE owner, st_table *tbl)
167167
{
168168
VALUE fields = imemo_fields_new(owner, sizeof(struct rb_fields));
169-
FL_UNSET_RAW(fields, OBJ_FIELD_EMBED);
170169
IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl;
170+
FL_SET_RAW(fields, OBJ_FIELD_HEAP);
171171
st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields);
172172
return fields;
173173
}
@@ -257,11 +257,13 @@ rb_imemo_memsize(VALUE obj)
257257

258258
break;
259259
case imemo_fields:
260-
if (rb_shape_obj_too_complex_p(obj)) {
261-
size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table);
262-
}
263-
else if (!FL_TEST_RAW(obj, OBJ_FIELD_EMBED)) {
264-
size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE);
260+
if (FL_TEST_RAW(obj, OBJ_FIELD_HEAP)) {
261+
if (rb_shape_obj_too_complex_p(obj)) {
262+
size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table);
263+
}
264+
else {
265+
size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE);
266+
}
265267
}
266268
break;
267269
default:
@@ -535,11 +537,13 @@ rb_free_const_table(struct rb_id_table *tbl)
535537
static inline void
536538
imemo_fields_free(struct rb_fields *fields)
537539
{
538-
if (rb_shape_obj_too_complex_p((VALUE)fields)) {
539-
st_free_table(fields->as.complex.table);
540-
}
541-
else if (!FL_TEST_RAW((VALUE)fields, OBJ_FIELD_EMBED)) {
542-
xfree(fields->as.external.ptr);
540+
if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_HEAP)) {
541+
if (rb_shape_obj_too_complex_p((VALUE)fields)) {
542+
st_free_table(fields->as.complex.table);
543+
}
544+
else {
545+
xfree(fields->as.external.ptr);
546+
}
543547
}
544548
}
545549

include/ruby/internal/core/robject.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
#define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj))
4444
/** @cond INTERNAL_MACRO */
4545
#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX
46-
#define ROBJECT_EMBED ROBJECT_EMBED
46+
#define ROBJECT_HEAP ROBJECT_HEAP
4747
#define ROBJECT_FIELDS_CAPACITY ROBJECT_FIELDS_CAPACITY
4848
#define ROBJECT_FIELDS ROBJECT_FIELDS
4949
/** @endcond */
@@ -55,10 +55,12 @@
5555
*/
5656
enum ruby_robject_flags {
5757
/**
58-
* This flag has something to do with memory footprint. If the object is
59-
* "small" enough, ruby tries to be creative to abuse padding bits of
60-
* struct ::RObject for storing instance variables. This flag denotes that
61-
* situation.
58+
* This flag has marks that the object's instance variables are stored in an
59+
* external heap buffer.
60+
* Normally, instance variable references are stored inside the object slot,
61+
* but if it overflow, Ruby may have to allocate a separate buffer and spills
62+
* the instance variables there.
63+
* This flag denotes that situation.
6264
*
6365
* @warning This bit has to be considered read-only. Setting/clearing
6466
* this bit without corresponding fix up must cause immediate
@@ -71,7 +73,7 @@ enum ruby_robject_flags {
7173
* 3rd parties must not be aware that there even is more than one way to
7274
* store instance variables. Might better be hidden.
7375
*/
74-
ROBJECT_EMBED = RUBY_FL_USER4
76+
ROBJECT_HEAP = RUBY_FL_USER4
7577
};
7678

7779
struct st_table;
@@ -129,11 +131,11 @@ ROBJECT_FIELDS(VALUE obj)
129131

130132
struct RObject *const ptr = ROBJECT(obj);
131133

132-
if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
133-
return ptr->as.ary;
134+
if (RB_UNLIKELY(RB_FL_ANY_RAW(obj, ROBJECT_HEAP))) {
135+
return ptr->as.heap.fields;
134136
}
135137
else {
136-
return ptr->as.heap.fields;
138+
return ptr->as.ary;
137139
}
138140
}
139141

internal/imemo.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ struct rb_fields {
272272

273273
// IMEMO/fields and T_OBJECT have exactly the same layout.
274274
// This is useful for JIT and common codepaths.
275-
#define OBJ_FIELD_EMBED ROBJECT_EMBED
276-
STATIC_ASSERT(imemo_fields_flags, OBJ_FIELD_EMBED == IMEMO_FL_USER0);
275+
#define OBJ_FIELD_HEAP ROBJECT_HEAP
276+
STATIC_ASSERT(imemo_fields_flags, OBJ_FIELD_HEAP == IMEMO_FL_USER0);
277277
STATIC_ASSERT(imemo_fields_embed_offset, offsetof(struct RObject, as.ary) == offsetof(struct rb_fields, as.embed.fields));
278278
STATIC_ASSERT(imemo_fields_embed_offset, offsetof(struct RObject, as.heap.fields) == offsetof(struct rb_fields, as.external.ptr));
279279
STATIC_ASSERT(imemo_fields_embed_offset, offsetof(struct RObject, as.heap.fields) == offsetof(struct rb_fields, as.complex.table));
@@ -303,11 +303,11 @@ rb_imemo_fields_ptr(VALUE fields_obj)
303303

304304
RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields) || RB_TYPE_P(fields_obj, T_OBJECT));
305305

306-
if (RB_LIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_EMBED))) {
307-
return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields;
306+
if (UNLIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP))) {
307+
return IMEMO_OBJ_FIELDS(fields_obj)->as.external.ptr;
308308
}
309309
else {
310-
return IMEMO_OBJ_FIELDS(fields_obj)->as.external.ptr;
310+
return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields;
311311
}
312312
}
313313

@@ -319,7 +319,7 @@ rb_imemo_fields_complex_tbl(VALUE fields_obj)
319319
}
320320

321321
RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields) || RB_TYPE_P(fields_obj, T_OBJECT));
322-
RUBY_ASSERT(!FL_TEST_RAW(fields_obj, OBJ_FIELD_EMBED));
322+
RUBY_ASSERT(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP));
323323

324324
// Some codepaths unconditionally access the fields_ptr, and assume it can be used as st_table if the
325325
// shape is too_complex.

object.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,9 @@
4646

4747
/* Flags of RObject
4848
*
49-
* 4: ROBJECT_EMBED
50-
* The object has its instance variables embedded (the array of
51-
* instance variables directly follow the object, rather than being
52-
* on a separately allocated buffer).
49+
* 4: ROBJECT_HEAP
50+
* The object has its instance variables in a separately allocated buffer.
51+
* This can be either a flat buffer of reference, or an st_table for complex objects.
5352
*/
5453

5554
/*!
@@ -126,7 +125,7 @@ rb_class_allocate_instance(VALUE klass)
126125
}
127126

128127
NEWOBJ_OF(o, struct RObject, klass,
129-
T_OBJECT | ROBJECT_EMBED | (RGENGC_WB_PROTECTED_OBJECT ? FL_WB_PROTECTED : 0), size, 0);
128+
T_OBJECT | (RGENGC_WB_PROTECTED_OBJECT ? FL_WB_PROTECTED : 0), size, 0);
130129
VALUE obj = (VALUE)o;
131130

132131
RUBY_ASSERT(RSHAPE_TYPE_P(RBASIC_SHAPE_ID(obj), SHAPE_ROOT));

ractor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1913,7 +1913,7 @@ move_leave(VALUE obj, struct obj_traverse_replace_data *data)
19131913
rb_replace_generic_ivar(data->replacement, obj);
19141914
}
19151915

1916-
VALUE flags = T_OBJECT | FL_FREEZE | ROBJECT_EMBED | (RBASIC(obj)->flags & FL_PROMOTED);
1916+
VALUE flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & FL_PROMOTED);
19171917

19181918
// Avoid mutations using bind_call, etc.
19191919
MEMZERO((char *)obj, char, sizeof(struct RBasic));

shape.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
12731273

12741274
// Ensure complex object don't appear as embedded
12751275
if (RB_TYPE_P(obj, T_OBJECT) || IMEMO_TYPE_P(obj, imemo_fields)) {
1276-
RUBY_ASSERT(!FL_TEST_RAW(obj, ROBJECT_EMBED));
1276+
RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP));
12771277
}
12781278
}
12791279
else {

shape.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ ROBJECT_FIELDS_HASH(VALUE obj)
351351
{
352352
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
353353
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
354-
RUBY_ASSERT(!FL_TEST_RAW(obj, ROBJECT_EMBED));
354+
RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP));
355355

356356
return (st_table *)ROBJECT(obj)->as.heap.fields;
357357
}
@@ -361,7 +361,7 @@ ROBJECT_SET_FIELDS_HASH(VALUE obj, const st_table *tbl)
361361
{
362362
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
363363
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
364-
RUBY_ASSERT(!FL_TEST_RAW(obj, ROBJECT_EMBED));
364+
RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP));
365365

366366
ROBJECT(obj)->as.heap.fields = (VALUE *)tbl;
367367
}

variable.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,11 +1480,11 @@ obj_transition_too_complex(VALUE obj, st_table *table)
14801480
case T_OBJECT:
14811481
{
14821482
VALUE *old_fields = NULL;
1483-
if (FL_TEST_RAW(obj, ROBJECT_EMBED)) {
1484-
FL_UNSET_RAW(obj, ROBJECT_EMBED);
1483+
if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
1484+
old_fields = ROBJECT_FIELDS(obj);
14851485
}
14861486
else {
1487-
old_fields = ROBJECT_FIELDS(obj);
1487+
FL_SET_RAW(obj, ROBJECT_HEAP);
14881488
}
14891489
RBASIC_SET_SHAPE_ID(obj, shape_id);
14901490
ROBJECT_SET_FIELDS_HASH(obj, table);
@@ -1614,12 +1614,12 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
16141614
}
16151615

16161616
if (RB_TYPE_P(obj, T_OBJECT) &&
1617-
!RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
1617+
FL_TEST_RAW(obj, ROBJECT_HEAP) &&
16181618
rb_obj_embedded_size(new_fields_count) <= rb_gc_obj_slot_size(obj)) {
16191619
// Re-embed objects when instances become small enough
16201620
// This is necessary because YJIT assumes that objects with the same shape
16211621
// have the same embeddedness for efficiency (avoid extra checks)
1622-
RB_FL_SET_RAW(obj, ROBJECT_EMBED);
1622+
FL_UNSET_RAW(obj, ROBJECT_HEAP);
16231623
MEMCPY(ROBJECT_FIELDS(obj), fields, VALUE, new_fields_count);
16241624
xfree(fields);
16251625
}
@@ -1813,16 +1813,16 @@ rb_ensure_iv_list_size(VALUE obj, uint32_t current_len, uint32_t new_capacity)
18131813
{
18141814
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
18151815

1816-
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
1816+
if (FL_TEST_RAW(obj, ROBJECT_HEAP)) {
1817+
REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity);
1818+
}
1819+
else {
18171820
VALUE *ptr = ROBJECT_FIELDS(obj);
18181821
VALUE *newptr = ALLOC_N(VALUE, new_capacity);
18191822
MEMCPY(newptr, ptr, VALUE, current_len);
1820-
RB_FL_UNSET_RAW(obj, ROBJECT_EMBED);
1823+
FL_SET_RAW(obj, ROBJECT_HEAP);
18211824
ROBJECT(obj)->as.heap.fields = newptr;
18221825
}
1823-
else {
1824-
REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity);
1825-
}
18261826
}
18271827

18281828
static int

0 commit comments

Comments
 (0)