Skip to content

Commit f6aba5e

Browse files
committed
Introduce T_IMEMO/class_fields
This behave almost exactly as a T_OBJECT, the layout is entirely compatible.
1 parent 04970ed commit f6aba5e

12 files changed

Lines changed: 299 additions & 55 deletions

File tree

class.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
318318
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
319319

320320
if (orig->fields_obj) {
321-
ext->fields_obj = rb_obj_clone(orig->fields_obj);
321+
RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_class_fields_clone(orig->fields_obj));
322322
}
323323

324324
if (RCLASSEXT_SHARED_CONST_TBL(orig)) {

gc.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,8 +1486,6 @@ internal_object_p(VALUE obj)
14861486
return rb_singleton_class_internal_p(obj);
14871487
}
14881488
return 0;
1489-
case T_OBJECT:
1490-
if (FL_TEST_RAW(obj, ROBJECT_HIDDEN)) break;
14911489
default:
14921490
if (!RBASIC(obj)->klass) break;
14931491
return 0;

imemo.c

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "id_table.h"
44
#include "internal.h"
55
#include "internal/imemo.h"
6+
#include "internal/st.h"
67
#include "vm_callinfo.h"
78

89
size_t rb_iseq_memsize(const rb_iseq_t *iseq);
@@ -29,10 +30,10 @@ rb_imemo_name(enum imemo_type type)
2930
IMEMO_NAME(svar);
3031
IMEMO_NAME(throw_data);
3132
IMEMO_NAME(tmpbuf);
33+
IMEMO_NAME(class_fields);
3234
#undef IMEMO_NAME
33-
default:
34-
rb_bug("unreachable");
3535
}
36+
rb_bug("unreachable");
3637
}
3738

3839
/* =========================================================================
@@ -109,6 +110,63 @@ rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
109110
return tmpbuf;
110111
}
111112

113+
static VALUE
114+
imemo_class_fields_new(VALUE klass, size_t capa)
115+
{
116+
size_t embedded_size = sizeof(struct rb_class_fields) + ((capa - 1) * sizeof(VALUE));
117+
if (rb_gc_size_allocatable_p(embedded_size)) {
118+
VALUE fields = rb_imemo_new(imemo_class_fields, klass, embedded_size);
119+
RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_class_fields));
120+
return fields;
121+
}
122+
else {
123+
VALUE fields = rb_imemo_new(imemo_class_fields, klass, sizeof(VALUE *));
124+
FL_SET_RAW(fields, OBJ_FIELD_EXTERNAL);
125+
IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa);
126+
return fields;
127+
}
128+
}
129+
130+
VALUE
131+
rb_imemo_class_fields_new(VALUE klass, size_t capa)
132+
{
133+
return imemo_class_fields_new(rb_singleton_class(klass), capa);
134+
}
135+
136+
static VALUE
137+
imemo_class_fields_new_complex(VALUE klass, size_t capa)
138+
{
139+
VALUE fields = imemo_class_fields_new(klass, sizeof(struct rb_class_fields));
140+
FL_SET_RAW(fields, OBJ_FIELD_COMPLEX);
141+
IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa);
142+
return fields;
143+
}
144+
145+
VALUE
146+
rb_imemo_class_fields_new_complex(VALUE klass, size_t capa)
147+
{
148+
return imemo_class_fields_new_complex(rb_singleton_class(klass), capa);
149+
}
150+
151+
VALUE
152+
rb_imemo_class_fields_clone(VALUE fields_obj)
153+
{
154+
rb_shape_t *shape = rb_obj_shape(fields_obj);
155+
VALUE clone;
156+
157+
if (rb_shape_too_complex_p(shape)) {
158+
clone = rb_imemo_class_fields_new_complex(CLASS_OF(fields_obj), 0);
159+
st_table *src_table = rb_imemo_class_fields_complex_tbl(fields_obj);
160+
st_replace(rb_imemo_class_fields_complex_tbl(clone), src_table);
161+
}
162+
else {
163+
clone = imemo_class_fields_new(CLASS_OF(fields_obj), shape->capacity);
164+
MEMCPY(rb_imemo_class_fields_ptr(clone), rb_imemo_class_fields_ptr(fields_obj), VALUE, shape->next_field_index);
165+
}
166+
167+
return clone;
168+
}
169+
112170
/* =========================================================================
113171
* memsize
114172
* ========================================================================= */
@@ -155,6 +213,14 @@ rb_imemo_memsize(VALUE obj)
155213
case imemo_tmpbuf:
156214
size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE);
157215

216+
break;
217+
case imemo_class_fields:
218+
if (FL_TEST_RAW(obj, OBJ_FIELD_COMPLEX)) {
219+
size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table);
220+
}
221+
else if (FL_TEST_RAW(obj, OBJ_FIELD_EXTERNAL)) {
222+
size += 0; // TODO: Get record the size
223+
}
158224
break;
159225
default:
160226
rb_bug("unreachable");
@@ -420,6 +486,19 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
420486

421487
break;
422488
}
489+
case imemo_class_fields: {
490+
if (rb_shape_obj_too_complex_p(obj)) {
491+
rb_mark_tbl_no_pin(rb_imemo_class_fields_complex_tbl(obj));
492+
}
493+
else {
494+
VALUE *fields = rb_imemo_class_fields_ptr(obj);
495+
uint32_t len = RSHAPE(rb_obj_shape_id(obj))->next_field_index;
496+
for (uint32_t i = 0; i < len; i++) {
497+
rb_gc_mark_and_move(&fields[i]);
498+
}
499+
}
500+
break;
501+
}
423502
default:
424503
rb_bug("unreachable");
425504
}
@@ -513,6 +592,17 @@ rb_cc_tbl_free(struct rb_id_table *cc_tbl, VALUE klass)
513592
rb_id_table_free(cc_tbl);
514593
}
515594

595+
static inline void
596+
imemo_class_fields_free(struct rb_class_fields *fields)
597+
{
598+
if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_COMPLEX)) {
599+
st_free_table(fields->as.complex.table);
600+
}
601+
else if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_EXTERNAL)) {
602+
xfree(fields->as.external.ptr);
603+
}
604+
}
605+
516606
void
517607
rb_imemo_free(VALUE obj)
518608
{
@@ -576,6 +666,7 @@ rb_imemo_free(VALUE obj)
576666
break;
577667
case imemo_svar:
578668
RB_DEBUG_COUNTER_INC(obj_imemo_svar);
669+
579670
break;
580671
case imemo_throw_data:
581672
RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
@@ -585,6 +676,10 @@ rb_imemo_free(VALUE obj)
585676
xfree(((rb_imemo_tmpbuf_t *)obj)->ptr);
586677
RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
587678

679+
break;
680+
case imemo_class_fields:
681+
imemo_class_fields_free(IMEMO_OBJ_FIELDS(obj));
682+
RB_DEBUG_COUNTER_INC(obj_imemo_class_fields);
588683
break;
589684
default:
590685
rb_bug("unreachable");

include/ruby/internal/core/robject.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ enum ruby_robject_flags {
7272
* store instance variables. Might better be hidden.
7373
*/
7474
ROBJECT_EMBED = RUBY_FL_USER1,
75-
76-
ROBJECT_HIDDEN = RUBY_FL_USER2, // HACK
7775
};
7876

7977
struct st_table;

internal/class.h

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,6 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE
258258
static inline void RCLASS_SET_SUPER(VALUE klass, VALUE super);
259259
static inline void RCLASS_WRITE_SUPER(VALUE klass, VALUE super);
260260
static inline st_table * RCLASS_WRITABLE_FIELDS_HASH(VALUE obj);
261-
static inline void RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *table);
262-
static inline void RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *table);
263261
// TODO: rename RCLASS_SET_M_TBL_WORKAROUND (and _WRITE_) to RCLASS_SET_M_TBL with write barrier
264262
static inline void RCLASS_SET_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted);
265263
static inline void RCLASS_WRITE_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted);
@@ -538,21 +536,13 @@ RCLASS_FIELDS_OBJ(VALUE obj)
538536
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_READABLE(obj));
539537
}
540538

541-
static inline VALUE
542-
rb_allocate_fields_obj(VALUE klass)
543-
{
544-
VALUE fields_obj = rb_class_allocate_instance(rb_singleton_class(klass));
545-
FL_SET_RAW(fields_obj, ROBJECT_HIDDEN); // HACK
546-
return fields_obj;
547-
}
548-
549539
static inline VALUE
550540
RCLASS_ENSURE_FIELDS_OBJ(VALUE obj)
551541
{
552542
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
553543
rb_classext_t *ext = RCLASS_EXT_READABLE(obj);
554544
if (!ext->fields_obj) {
555-
RB_OBJ_WRITE(obj, &ext->fields_obj, rb_allocate_fields_obj(obj));
545+
RB_OBJ_WRITE(obj, &ext->fields_obj, rb_imemo_class_fields_new(obj, 1));
556546
}
557547
return ext->fields_obj;
558548
}
@@ -565,33 +555,18 @@ RCLASS_WRITABLE_FIELDS_OBJ(VALUE obj)
565555
}
566556

567557
static inline void
568-
RCLASSEXT_SET_FIELDS_HASH(VALUE obj, rb_classext_t *ext, const st_table *tbl)
558+
RCLASSEXT_SET_FIELDS_OBJ(VALUE obj, rb_classext_t *ext, VALUE fields_obj)
569559
{
570560
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
571-
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
572-
573-
if (!ext->fields_obj) {
574-
// FIXME: We can trigger GC here and `*tbl` may not be marked
575-
RB_OBJ_WRITE(obj, &ext->fields_obj, rb_allocate_fields_obj(obj));
576-
}
577-
ROBJECT_SET_FIELDS_HASH(ext->fields_obj, tbl);
561+
RB_OBJ_WRITE(obj, &ext->fields_obj, fields_obj);
578562
}
579563

580564
static inline void
581-
RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *tbl)
565+
RCLASS_SET_FIELDS_OBJ(VALUE obj, VALUE fields_obj)
582566
{
583567
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
584-
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
585-
586-
RCLASSEXT_SET_FIELDS_HASH(obj, RCLASS_EXT_PRIME(obj), tbl);
587-
}
588568

589-
static inline void
590-
RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *tbl)
591-
{
592-
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
593-
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
594-
RCLASSEXT_SET_FIELDS_HASH(obj, RCLASS_EXT_WRITABLE(obj), tbl);
569+
RCLASSEXT_SET_FIELDS_OBJ(obj, RCLASS_EXT_PRIME(obj), fields_obj);
595570
}
596571

597572
#define RCLASS_SET_M_TBL_EVEN_WHEN_PROMOTED(klass, table) RCLASS_SET_M_TBL_WORKAROUND(klass, table, false)

internal/imemo.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum imemo_type {
4242
imemo_callinfo = 11,
4343
imemo_callcache = 12,
4444
imemo_constcache = 13,
45+
imemo_class_fields = 14,
4546
};
4647

4748
/* CREF (Class REFerence) is defined in method.h */
@@ -257,4 +258,60 @@ MEMO_V2_SET(struct MEMO *m, VALUE v)
257258
RB_OBJ_WRITE(m, &m->v2, v);
258259
}
259260

261+
struct rb_class_fields {
262+
VALUE flags;
263+
VALUE klass;
264+
union {
265+
struct {
266+
VALUE fields[1];
267+
} embed;
268+
struct {
269+
VALUE *ptr;
270+
} external;
271+
struct {
272+
st_table *table; // TODO: embed
273+
} complex;
274+
} as;
275+
};
276+
277+
#define OBJ_FIELD_EXTERNAL IMEMO_FL_USER0
278+
#define OBJ_FIELD_COMPLEX IMEMO_FL_USER1
279+
#define IMEMO_OBJ_FIELDS(fields) ((struct rb_class_fields *)fields)
280+
281+
VALUE rb_imemo_class_fields_new(VALUE klass, size_t capa);
282+
VALUE rb_imemo_class_fields_new_complex(VALUE klass, size_t capa);
283+
VALUE rb_imemo_class_fields_clone(VALUE fields_obj);
284+
285+
static inline VALUE *
286+
rb_imemo_class_fields_ptr(VALUE obj_fields)
287+
{
288+
if (!obj_fields) {
289+
return NULL;
290+
}
291+
292+
RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields));
293+
// vm_getivar unconditionally ask for a fields ptr.
294+
// RUBY_ASSERT(!FL_TEST_RAW(obj_fields, OBJ_FIELD_COMPLEX));
295+
296+
if (RB_UNLIKELY(FL_TEST_RAW(obj_fields, OBJ_FIELD_EXTERNAL))) {
297+
return IMEMO_OBJ_FIELDS(obj_fields)->as.external.ptr;
298+
}
299+
else {
300+
return IMEMO_OBJ_FIELDS(obj_fields)->as.embed.fields;
301+
}
302+
}
303+
304+
static inline st_table *
305+
rb_imemo_class_fields_complex_tbl(VALUE obj_fields)
306+
{
307+
if (!obj_fields) {
308+
return NULL;
309+
}
310+
311+
RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields));
312+
RUBY_ASSERT(FL_TEST_RAW(obj_fields, OBJ_FIELD_COMPLEX));
313+
314+
return IMEMO_OBJ_FIELDS(obj_fields)->as.complex.table;
315+
}
316+
260317
#endif /* INTERNAL_IMEMO_H */

shape.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings)
825825
#endif
826826

827827
VALUE klass;
828-
if (RB_TYPE_P(obj, T_OBJECT) && FL_TEST_RAW(obj, ROBJECT_HIDDEN)) { // HACK
828+
if (IMEMO_TYPE_P(obj, imemo_class_fields)) { // HACK
829829
klass = CLASS_OF(obj);
830830
}
831831
else {

shape.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ static inline shape_id_t
9494
get_shape_id_from_flags(VALUE obj)
9595
{
9696
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
97-
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO));
97+
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields));
9898
RUBY_ASSERT(!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE));
9999

100100
return (shape_id_t)((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT);
@@ -104,7 +104,7 @@ static inline void
104104
set_shape_id_in_flags(VALUE obj, shape_id_t shape_id)
105105
{
106106
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
107-
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO));
107+
RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields));
108108
// Ractors are occupying the upper 32 bits of flags, but only in debug mode
109109
// Object shapes are occupying top bits
110110
RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
@@ -129,14 +129,14 @@ RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
129129
static inline shape_id_t
130130
ROBJECT_SHAPE_ID(VALUE obj)
131131
{
132-
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
132+
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT) || IMEMO_TYPE_P(obj, imemo_class_fields));
133133
return get_shape_id_from_flags(obj);
134134
}
135135

136136
static inline void
137137
ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
138138
{
139-
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
139+
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT) || IMEMO_TYPE_P(obj, imemo_class_fields));
140140
set_shape_id_in_flags(obj, shape_id);
141141
}
142142

0 commit comments

Comments
 (0)