diff --git a/class.c b/class.c index fd3276990a..480bdb7c14 100644 --- a/class.c +++ b/class.c @@ -297,16 +297,8 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass); - // TODO: consider shapes for performance - if (RCLASSEXT_FIELDS(orig)) { - RUBY_ASSERT(!RB_TYPE_P(klass, T_ICLASS)); - RCLASSEXT_FIELDS(ext) = (VALUE *)st_copy((st_table *)RCLASSEXT_FIELDS(orig)); - rb_autoload_copy_table_for_namespace((st_table *)RCLASSEXT_FIELDS(ext), ns); - } - else { - if (!RB_TYPE_P(klass, T_ICLASS)) { - RCLASSEXT_FIELDS(ext) = (VALUE *)st_init_numtable(); - } + if (orig->fields_obj) { + RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_class_fields_clone(orig->fields_obj)); } if (RCLASSEXT_SHARED_CONST_TBL(orig)) { diff --git a/common.mk b/common.mk index 3f67263e29..98f4baf938 100644 --- a/common.mk +++ b/common.mk @@ -8117,6 +8117,7 @@ imemo.$(OBJEXT): $(top_srcdir)/internal/namespace.h imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h imemo.$(OBJEXT): $(top_srcdir)/internal/set_table.h +imemo.$(OBJEXT): $(top_srcdir)/internal/st.h imemo.$(OBJEXT): $(top_srcdir)/internal/static_assert.h imemo.$(OBJEXT): $(top_srcdir)/internal/variable.h imemo.$(OBJEXT): $(top_srcdir)/internal/vm.h diff --git a/debug_counter.h b/debug_counter.h index c4ee26534f..3142ada0c3 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -315,6 +315,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm) RB_DEBUG_COUNTER(obj_imemo_callinfo) RB_DEBUG_COUNTER(obj_imemo_callcache) RB_DEBUG_COUNTER(obj_imemo_constcache) +RB_DEBUG_COUNTER(obj_imemo_class_fields) RB_DEBUG_COUNTER(opt_new_hit) RB_DEBUG_COUNTER(opt_new_miss) diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index acd4a6864d..754c998ac6 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -504,6 +504,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) INIT_IMEMO_TYPE_ID(imemo_callinfo); INIT_IMEMO_TYPE_ID(imemo_callcache); INIT_IMEMO_TYPE_ID(imemo_constcache); + INIT_IMEMO_TYPE_ID(imemo_class_fields); #undef INIT_IMEMO_TYPE_ID } diff --git a/gc.c b/gc.c index 05cefc739b..aefe8a116b 100644 --- a/gc.c +++ b/gc.c @@ -1201,7 +1201,6 @@ rb_data_free(void *objspace, VALUE obj) struct classext_foreach_args { VALUE klass; - bool obj_too_complex; rb_objspace_t *objspace; // used for update_* }; @@ -1213,12 +1212,6 @@ classext_free(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg) rb_id_table_free(RCLASSEXT_M_TBL(ext)); rb_cc_tbl_free(RCLASSEXT_CC_TBL(ext), args->klass); - if (args->obj_too_complex) { - st_free_table((st_table *)RCLASSEXT_FIELDS(ext)); - } - else { - xfree(RCLASSEXT_FIELDS(ext)); - } if (!RCLASSEXT_SHARED_CONST_TBL(ext) && (tbl = RCLASSEXT_CONST_TBL(ext)) != NULL) { rb_free_const_table(tbl); } @@ -1292,8 +1285,6 @@ rb_gc_obj_free(void *objspace, VALUE obj) case T_MODULE: case T_CLASS: args.klass = obj; - args.obj_too_complex = rb_shape_obj_too_complex_p(obj) ? true : false; - rb_class_classext_foreach(obj, classext_free, (void *)&args); if (RCLASS(obj)->ns_classext_tbl) { st_free_table(RCLASS(obj)->ns_classext_tbl); @@ -2305,18 +2296,6 @@ classext_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg) *size += s; } -static void -classext_fields_hash_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg) -{ - size_t *size = (size_t *)arg; - size_t count; - RB_VM_LOCKING() { - count = rb_st_table_size((st_table *)RCLASSEXT_FIELDS(ext)); - } - // class IV sizes are allocated as powers of two - *size += SIZEOF_VALUE << bit_length(count); -} - static void classext_superclasses_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg) { @@ -2354,15 +2333,6 @@ rb_obj_memsize_of(VALUE obj) case T_MODULE: case T_CLASS: rb_class_classext_foreach(obj, classext_memsize, (void *)&size); - - if (rb_shape_obj_too_complex_p(obj)) { - rb_class_classext_foreach(obj, classext_fields_hash_memsize, (void *)&size); - } - else { - // class IV sizes are allocated as powers of two - size += SIZEOF_VALUE << bit_length(RCLASS_FIELDS_COUNT(obj)); - } - rb_class_classext_foreach(obj, classext_superclasses_memsize, (void *)&size); break; case T_ICLASS: @@ -3135,10 +3105,7 @@ gc_mark_classext_module(rb_classext_t *ext, bool prime, VALUE namespace, void *a gc_mark_internal(RCLASSEXT_SUPER(ext)); } mark_m_tbl(objspace, RCLASSEXT_M_TBL(ext)); - if (rb_shape_obj_too_complex_p(obj)) { - gc_mark_tbl_no_pin((st_table *)RCLASSEXT_FIELDS(ext)); - // for the case ELSE is written in rb_gc_mark_children() because it's per RClass, not classext - } + gc_mark_internal(RCLASSEXT_FIELDS_OBJ(ext)); if (!RCLASSEXT_SHARED_CONST_TBL(ext) && RCLASSEXT_CONST_TBL(ext)) { mark_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext)); } @@ -3218,12 +3185,6 @@ rb_gc_mark_children(void *objspace, VALUE obj) foreach_args.objspace = objspace; foreach_args.obj = obj; rb_class_classext_foreach(obj, gc_mark_classext_module, (void *)&foreach_args); - - if (!rb_shape_obj_too_complex_p(obj)) { - for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(obj); i++) { - gc_mark_internal(RCLASS_PRIME_FIELDS(obj)[i]); - } - } break; case T_ICLASS: @@ -3849,7 +3810,6 @@ static void update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg) { struct classext_foreach_args *args = (struct classext_foreach_args *)arg; - VALUE klass = args->klass; rb_objspace_t *objspace = args->objspace; if (RCLASSEXT_SUPER(ext)) { @@ -3858,16 +3818,7 @@ update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg) update_m_tbl(objspace, RCLASSEXT_M_TBL(ext)); - if (args->obj_too_complex) { - gc_ref_update_table_values_only((st_table *)RCLASSEXT_FIELDS(ext)); - } - else { - // Classext is not copied in this case - for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(klass); i++) { - UPDATE_IF_MOVED(objspace, RCLASSEXT_FIELDS(RCLASS_EXT_PRIME(klass))[i]); - } - } - + UPDATE_IF_MOVED(objspace, ext->fields_obj); if (!RCLASSEXT_SHARED_CONST_TBL(ext)) { update_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext)); } @@ -4255,7 +4206,6 @@ rb_gc_update_object_references(void *objspace, VALUE obj) // Continue to the shared T_CLASS/T_MODULE case T_MODULE: args.klass = obj; - args.obj_too_complex = rb_shape_obj_too_complex_p(obj); args.objspace = objspace; rb_class_classext_foreach(obj, update_classext, (void *)&args); break; diff --git a/imemo.c b/imemo.c index 2245434e21..6477be9d78 100644 --- a/imemo.c +++ b/imemo.c @@ -3,6 +3,7 @@ #include "id_table.h" #include "internal.h" #include "internal/imemo.h" +#include "internal/st.h" #include "vm_callinfo.h" size_t rb_iseq_memsize(const rb_iseq_t *iseq); @@ -29,10 +30,10 @@ rb_imemo_name(enum imemo_type type) IMEMO_NAME(svar); IMEMO_NAME(throw_data); IMEMO_NAME(tmpbuf); + IMEMO_NAME(class_fields); #undef IMEMO_NAME - default: - rb_bug("unreachable"); } + rb_bug("unreachable"); } /* ========================================================================= @@ -109,6 +110,62 @@ rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt) return tmpbuf; } +static VALUE +imemo_class_fields_new(VALUE klass, size_t capa) +{ + size_t embedded_size = offsetof(struct rb_class_fields, as.embed) + capa * sizeof(VALUE); + if (rb_gc_size_allocatable_p(embedded_size)) { + VALUE fields = rb_imemo_new(imemo_class_fields, klass, embedded_size); + RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_class_fields)); + return fields; + } + else { + VALUE fields = rb_imemo_new(imemo_class_fields, klass, sizeof(struct rb_class_fields)); + FL_SET_RAW(fields, OBJ_FIELD_EXTERNAL); + IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa); + return fields; + } +} + +VALUE +rb_imemo_class_fields_new(VALUE klass, size_t capa) +{ + return imemo_class_fields_new(rb_singleton_class(klass), capa); +} + +static VALUE +imemo_class_fields_new_complex(VALUE klass, size_t capa) +{ + VALUE fields = imemo_class_fields_new(klass, sizeof(struct rb_class_fields)); + IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa); + return fields; +} + +VALUE +rb_imemo_class_fields_new_complex(VALUE klass, size_t capa) +{ + return imemo_class_fields_new_complex(rb_singleton_class(klass), capa); +} + +VALUE +rb_imemo_class_fields_clone(VALUE fields_obj) +{ + shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); + VALUE clone; + + if (rb_shape_too_complex_p(shape_id)) { + clone = rb_imemo_class_fields_new_complex(CLASS_OF(fields_obj), 0); + st_table *src_table = rb_imemo_class_fields_complex_tbl(fields_obj); + st_replace(rb_imemo_class_fields_complex_tbl(clone), src_table); + } + else { + clone = imemo_class_fields_new(CLASS_OF(fields_obj), RSHAPE_CAPACITY(shape_id)); + MEMCPY(rb_imemo_class_fields_ptr(clone), rb_imemo_class_fields_ptr(fields_obj), VALUE, RSHAPE_LEN(shape_id)); + } + + return clone; +} + /* ========================================================================= * memsize * ========================================================================= */ @@ -155,6 +212,14 @@ rb_imemo_memsize(VALUE obj) case imemo_tmpbuf: size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE); + break; + case imemo_class_fields: + if (rb_shape_obj_too_complex_p(obj)) { + size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); + } + else if (FL_TEST_RAW(obj, OBJ_FIELD_EXTERNAL)) { + size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE); + } break; default: rb_bug("unreachable"); @@ -420,6 +485,27 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating) break; } + case imemo_class_fields: { + rb_gc_mark_and_move((VALUE *)&RBASIC(obj)->klass); + + if (rb_shape_obj_too_complex_p(obj)) { + st_table *tbl = rb_imemo_class_fields_complex_tbl(obj); + if (reference_updating) { + rb_gc_ref_update_table_values_only(tbl); + } + else { + rb_mark_tbl_no_pin(tbl); + } + } + else { + VALUE *fields = rb_imemo_class_fields_ptr(obj); + attr_index_t len = RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); + for (attr_index_t i = 0; i < len; i++) { + rb_gc_mark_and_move(&fields[i]); + } + } + break; + } default: rb_bug("unreachable"); } @@ -513,6 +599,17 @@ rb_cc_tbl_free(struct rb_id_table *cc_tbl, VALUE klass) rb_id_table_free(cc_tbl); } +static inline void +imemo_class_fields_free(struct rb_class_fields *fields) +{ + if (rb_shape_obj_too_complex_p((VALUE)fields)) { + st_free_table(fields->as.complex.table); + } + else if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_EXTERNAL)) { + xfree(fields->as.external.ptr); + } +} + void rb_imemo_free(VALUE obj) { @@ -576,6 +673,7 @@ rb_imemo_free(VALUE obj) break; case imemo_svar: RB_DEBUG_COUNTER_INC(obj_imemo_svar); + break; case imemo_throw_data: RB_DEBUG_COUNTER_INC(obj_imemo_throw_data); @@ -585,6 +683,10 @@ rb_imemo_free(VALUE obj) xfree(((rb_imemo_tmpbuf_t *)obj)->ptr); RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf); + break; + case imemo_class_fields: + imemo_class_fields_free(IMEMO_OBJ_FIELDS(obj)); + RB_DEBUG_COUNTER_INC(obj_imemo_class_fields); break; default: rb_bug("unreachable"); diff --git a/internal/class.h b/internal/class.h index 5601978292..ff3486472a 100644 --- a/internal/class.h +++ b/internal/class.h @@ -79,7 +79,7 @@ struct rb_cvar_class_tbl_entry { struct rb_classext_struct { const rb_namespace_t *ns; VALUE super; - VALUE *fields; // Fields are either ivar or other internal properties stored inline + VALUE fields_obj; // Fields are either ivar or other internal properties stored inline struct rb_id_table *m_tbl; struct rb_id_table *const_tbl; struct rb_id_table *callable_m_tbl; @@ -175,7 +175,8 @@ static inline rb_classext_t * RCLASS_EXT_WRITABLE(VALUE obj); #define RCLASSEXT_NS(ext) (ext->ns) #define RCLASSEXT_SUPER(ext) (ext->super) -#define RCLASSEXT_FIELDS(ext) (ext->fields) +#define RCLASSEXT_FIELDS(ext) (ext->fields_obj ? ROBJECT_FIELDS(ext->fields_obj) : NULL) +#define RCLASSEXT_FIELDS_OBJ(ext) (ext->fields_obj) #define RCLASSEXT_M_TBL(ext) (ext->m_tbl) #define RCLASSEXT_CONST_TBL(ext) (ext->const_tbl) #define RCLASSEXT_CALLABLE_M_TBL(ext) (ext->callable_m_tbl) @@ -205,7 +206,7 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE #define RCLASS_PRIME_NS(c) (RCLASS_EXT_PRIME(c)->ns) // To invalidate CC by inserting&invalidating method entry into tables containing the target cme // See clear_method_cache_by_id_in_class() -#define RCLASS_PRIME_FIELDS(c) (RCLASS_EXT_PRIME(c)->fields) +#define RCLASS_PRIME_FIELDS_OBJ(c) (RCLASS_EXT_PRIME(c)->fields_obj) #define RCLASS_PRIME_M_TBL(c) (RCLASS_EXT_PRIME(c)->m_tbl) #define RCLASS_PRIME_CONST_TBL(c) (RCLASS_EXT_PRIME(c)->const_tbl) #define RCLASS_PRIME_CALLABLE_M_TBL(c) (RCLASS_EXT_PRIME(c)->callable_m_tbl) @@ -255,11 +256,6 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE static inline void RCLASS_SET_SUPER(VALUE klass, VALUE super); static inline void RCLASS_WRITE_SUPER(VALUE klass, VALUE super); -static inline st_table * RCLASS_FIELDS_HASH(VALUE obj); -static inline st_table * RCLASS_WRITABLE_FIELDS_HASH(VALUE obj); -static inline uint32_t RCLASS_FIELDS_COUNT(VALUE obj); -static inline void RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *table); -static inline void RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *table); // TODO: rename RCLASS_SET_M_TBL_WORKAROUND (and _WRITE_) to RCLASS_SET_M_TBL with write barrier static inline void RCLASS_SET_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted); static inline void RCLASS_WRITE_M_TBL_WORKAROUND(VALUE klass, struct rb_id_table *table, bool check_promoted); @@ -528,56 +524,60 @@ RCLASS_WRITE_SUPER(VALUE klass, VALUE super) RB_OBJ_WRITE(klass, &RCLASSEXT_SUPER(RCLASS_EXT_WRITABLE(klass)), super); } -static inline st_table * -RCLASS_FIELDS_HASH(VALUE obj) +static inline VALUE +RCLASS_FIELDS_OBJ(VALUE obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); - return (st_table *)RCLASSEXT_FIELDS(RCLASS_EXT_READABLE(obj)); + return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_READABLE(obj)); } -static inline st_table * -RCLASS_WRITABLE_FIELDS_HASH(VALUE obj) +static inline VALUE +RCLASS_ENSURE_FIELDS_OBJ(VALUE obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); - return (st_table *)RCLASSEXT_FIELDS(RCLASS_EXT_WRITABLE(obj)); + rb_classext_t *ext = RCLASS_EXT_READABLE(obj); + if (!ext->fields_obj) { + RB_OBJ_WRITE(obj, &ext->fields_obj, rb_imemo_class_fields_new(obj, 1)); + } + return ext->fields_obj; +} + +static inline VALUE +RCLASS_WRITABLE_FIELDS_OBJ(VALUE obj) +{ + RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); + return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_WRITABLE(obj)); } static inline void -RCLASS_SET_FIELDS_HASH(VALUE obj, const st_table *tbl) +RCLASSEXT_SET_FIELDS_OBJ(VALUE obj, rb_classext_t *ext, VALUE fields_obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); - RCLASSEXT_FIELDS(RCLASS_EXT_PRIME(obj)) = (VALUE *)tbl; + RB_OBJ_WRITE(obj, &ext->fields_obj, fields_obj); } static inline void -RCLASS_WRITE_FIELDS_HASH(VALUE obj, const st_table *tbl) +RCLASS_SET_FIELDS_OBJ(VALUE obj, VALUE fields_obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); - RCLASSEXT_FIELDS(RCLASS_EXT_WRITABLE(obj)) = (VALUE *)tbl; + + RCLASSEXT_SET_FIELDS_OBJ(obj, RCLASS_EXT_PRIME(obj), fields_obj); } static inline uint32_t RCLASS_FIELDS_COUNT(VALUE obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); - if (rb_shape_obj_too_complex_p(obj)) { - uint32_t count; - - // "Too complex" classes could have their IV hash mutated in - // parallel, so lets lock around getting the hash size. - RB_VM_LOCKING() { - count = (uint32_t)rb_st_table_size(RCLASS_FIELDS_HASH(obj)); + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + if (rb_shape_obj_too_complex_p(fields_obj)) { + return (uint32_t)rb_st_table_size(rb_imemo_class_fields_complex_tbl(fields_obj)); + } + else { + return RSHAPE_LEN(RBASIC_SHAPE_ID(fields_obj)); } - - return count; - } - else { - return RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); } + return 0; } #define RCLASS_SET_M_TBL_EVEN_WHEN_PROMOTED(klass, table) RCLASS_SET_M_TBL_WORKAROUND(klass, table, false) diff --git a/internal/imemo.h b/internal/imemo.h index 305d12d240..0806baa9a6 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -42,6 +42,7 @@ enum imemo_type { imemo_callinfo = 11, imemo_callcache = 12, imemo_constcache = 13, + imemo_class_fields = 14, }; /* CREF (Class REFerence) is defined in method.h */ @@ -257,4 +258,57 @@ MEMO_V2_SET(struct MEMO *m, VALUE v) RB_OBJ_WRITE(m, &m->v2, v); } +struct rb_class_fields { + struct RBasic basic; + union { + struct { + VALUE fields[1]; + } embed; + struct { + VALUE *ptr; + } external; + struct { + // Note: the st_table could be embedded, but complex T_CLASS should be rare to + // non-existent, so not really worth the trouble. + st_table *table; + } complex; + } as; +}; + +#define OBJ_FIELD_EXTERNAL IMEMO_FL_USER0 +#define IMEMO_OBJ_FIELDS(fields) ((struct rb_class_fields *)fields) + +VALUE rb_imemo_class_fields_new(VALUE klass, size_t capa); +VALUE rb_imemo_class_fields_new_complex(VALUE klass, size_t capa); +VALUE rb_imemo_class_fields_clone(VALUE fields_obj); + +static inline VALUE * +rb_imemo_class_fields_ptr(VALUE obj_fields) +{ + if (!obj_fields) { + return NULL; + } + + RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields)); + + if (RB_UNLIKELY(FL_TEST_RAW(obj_fields, OBJ_FIELD_EXTERNAL))) { + return IMEMO_OBJ_FIELDS(obj_fields)->as.external.ptr; + } + else { + return IMEMO_OBJ_FIELDS(obj_fields)->as.embed.fields; + } +} + +static inline st_table * +rb_imemo_class_fields_complex_tbl(VALUE obj_fields) +{ + if (!obj_fields) { + return NULL; + } + + RUBY_ASSERT(IMEMO_TYPE_P(obj_fields, imemo_class_fields)); + + return IMEMO_OBJ_FIELDS(obj_fields)->as.complex.table; +} + #endif /* INTERNAL_IMEMO_H */ diff --git a/shape.c b/shape.c index 021ecb1a9e..6f187552de 100644 --- a/shape.c +++ b/shape.c @@ -396,6 +396,13 @@ rb_obj_shape_id(VALUE obj) return SPECIAL_CONST_SHAPE_ID; } + if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + return RBASIC_SHAPE_ID(fields_obj); + } + return ROOT_SHAPE_ID; + } return RBASIC_SHAPE_ID(obj); } @@ -881,14 +888,11 @@ shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings) #endif VALUE klass; - switch (BUILTIN_TYPE(obj)) { - case T_CLASS: - case T_MODULE: - klass = rb_singleton_class(obj); - break; - default: + if (IMEMO_TYPE_P(obj, imemo_class_fields)) { // HACK + klass = CLASS_OF(obj); + } + else { klass = rb_obj_class(obj); - break; } bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS; diff --git a/shape.h b/shape.h index 65e7595923..ac50e58f71 100644 --- a/shape.h +++ b/shape.h @@ -113,7 +113,7 @@ static inline shape_id_t RBASIC_SHAPE_ID(VALUE obj) { RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); - RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO)); + RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields)); #if RBASIC_SHAPE_ID_FIELD return (shape_id_t)((RBASIC(obj)->shape_id)); #else @@ -137,8 +137,9 @@ static inline void RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) { RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); - RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO)); + RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_class_fields)); RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id)); + #if RBASIC_SHAPE_ID_FIELD RBASIC(obj)->shape_id = (VALUE)shape_id; #else diff --git a/variable.c b/variable.c index 38249b4e82..3c8b2c6cc2 100644 --- a/variable.c +++ b/variable.c @@ -1305,13 +1305,21 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id) RUBY_ASSERT(!SPECIAL_CONST_P(obj)); RUBY_ASSERT(RSHAPE_TYPE_P(target_shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(target_shape_id, SHAPE_OBJ_ID)); + if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { + ASSERT_vm_locking(); + VALUE field_obj = RCLASS_FIELDS_OBJ(obj); + if (field_obj) { + return rb_obj_field_get(field_obj, target_shape_id); + } + return Qundef; + } + if (rb_shape_too_complex_p(target_shape_id)) { st_table *fields_hash; switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - ASSERT_vm_locking(); - fields_hash = RCLASS_FIELDS_HASH(obj); + rb_bug("Unreachable"); break; case T_OBJECT: fields_hash = ROBJECT_FIELDS_HASH(obj); @@ -1342,8 +1350,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id) switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - ASSERT_vm_locking(); - fields = RCLASS_PRIME_FIELDS(obj); + rb_bug("Unreachable"); break; case T_OBJECT: fields = ROBJECT_FIELDS(obj); @@ -1364,6 +1371,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) { if (SPECIAL_CONST_P(obj)) return undef; + if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { + VALUE val = undef; + RB_VM_LOCK_ENTER(); + { + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + val = rb_ivar_lookup(fields_obj, id, undef); + } + } + RB_VM_LOCK_LEAVE(); + + if (val != undef && + rb_is_instance_id(id) && + UNLIKELY(!rb_ractor_main_p()) && + !rb_ractor_shareable_p(val)) { + rb_raise(rb_eRactorIsolationError, + "can not get unshareable values from instance variables of classes/modules from non-main Ractors"); + } + return val; + } + shape_id_t shape_id; VALUE * ivar_list; shape_id = RBASIC_SHAPE_ID(obj); @@ -1372,43 +1400,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) case T_CLASS: case T_MODULE: { - bool found = false; - VALUE val; + rb_bug("Unreachable"); + } + case T_IMEMO: + // Handled like T_OBJECT + { + RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields)); - RB_VM_LOCKING() { - if (rb_shape_too_complex_p(shape_id)) { - st_table * iv_table = RCLASS_FIELDS_HASH(obj); - if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { - found = true; - } - else { - val = undef; - } + if (rb_shape_too_complex_p(shape_id)) { + st_table * iv_table = rb_imemo_class_fields_complex_tbl(obj); + VALUE val; + if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { + return val; } else { - attr_index_t index = 0; - found = rb_shape_get_iv_index(shape_id, id, &index); - - if (found) { - ivar_list = RCLASS_PRIME_FIELDS(obj); - RUBY_ASSERT(ivar_list); - - val = ivar_list[index]; - } - else { - val = undef; - } + return undef; } } - if (found && - rb_is_instance_id(id) && - UNLIKELY(!rb_ractor_main_p()) && - !rb_ractor_shareable_p(val)) { - rb_raise(rb_eRactorIsolationError, - "can not get unshareable values from instance variables of classes/modules from non-main Ractors"); - } - return val; + RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + ivar_list = rb_imemo_class_fields_ptr(obj); + break; } case T_OBJECT: { @@ -1476,13 +1488,19 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) { rb_check_frozen(obj); - bool locked = false; - unsigned int lev = 0; VALUE val = undef; if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id); - RB_VM_LOCK_ENTER_LEV(&lev); - locked = true; + + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + RB_VM_LOCK_ENTER(); + { + val = rb_ivar_delete(fields_obj, id, undef); + } + RB_VM_LOCK_LEAVE(); + return val; + } } shape_id_t old_shape_id = rb_obj_shape_id(obj); @@ -1494,9 +1512,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id); if (next_shape_id == old_shape_id) { - if (locked) { - RB_VM_LOCK_LEAVE_LEV(&lev); - } return undef; } @@ -1511,7 +1526,11 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) switch(BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - fields = RCLASS_PRIME_FIELDS(obj); + rb_bug("Unreachable"); + break; + case T_IMEMO: + RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields)); + fields = rb_imemo_class_fields_ptr(obj); break; case T_OBJECT: fields = ROBJECT_FIELDS(obj); @@ -1546,10 +1565,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) } rb_obj_set_shape_id(obj, next_shape_id); - if (locked) { - RB_VM_LOCK_LEAVE_LEV(&lev); - } - return val; too_complex: @@ -1558,7 +1573,12 @@ too_complex: switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - table = RCLASS_WRITABLE_FIELDS_HASH(obj); + rb_bug("Unreachable"); + break; + + case T_IMEMO: + RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields)); + table = rb_imemo_class_fields_complex_tbl(obj); break; case T_OBJECT: @@ -1581,10 +1601,6 @@ too_complex: } } - if (locked) { - RB_VM_LOCK_LEAVE_LEV(&lev); - } - return val; } @@ -1597,6 +1613,11 @@ rb_attr_delete(VALUE obj, ID id) static shape_id_t obj_transition_too_complex(VALUE obj, st_table *table) { + if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { + RUBY_ASSERT(RCLASS_FIELDS_OBJ(obj)); + return obj_transition_too_complex(RCLASS_FIELDS_OBJ(obj), table); + } + RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); shape_id_t shape_id = rb_shape_transition_complex(obj); @@ -1612,9 +1633,7 @@ obj_transition_too_complex(VALUE obj, st_table *table) break; case T_CLASS: case T_MODULE: - old_fields = RCLASS_PRIME_FIELDS(obj); - RBASIC_SET_SHAPE_ID(obj, shape_id); - RCLASS_SET_FIELDS_HASH(obj, table); + rb_bug("Unreachable"); break; default: RB_VM_LOCKING() { @@ -2035,11 +2054,20 @@ rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val) bool rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id) { - if (rb_obj_shape_id(obj) == shape_id) { + shape_id_t old_shape_id = rb_obj_shape_id(obj); + if (old_shape_id == shape_id) { return false; } + if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) { + // Avoid creating the fields_obj just to freeze the class + if (!(shape_id == SPECIAL_CONST_SHAPE_ID && old_shape_id == ROOT_SHAPE_ID)) { + RBASIC_SET_SHAPE_ID(RCLASS_ENSURE_FIELDS_OBJ(obj), shape_id); + } + } + // FIXME: How to do multi-shape? RBASIC_SET_SHAPE_ID(obj, shape_id); + return true; } @@ -2131,7 +2159,12 @@ ivar_defined0(VALUE obj, ID id) switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - table = (st_table *)RCLASS_FIELDS_HASH(obj); + rb_bug("Unreachable"); + break; + + case T_IMEMO: + RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_class_fields)); + table = rb_imemo_class_fields_complex_tbl(obj); break; case T_OBJECT: @@ -2163,12 +2196,15 @@ rb_ivar_defined(VALUE obj, ID id) { if (SPECIAL_CONST_P(obj)) return Qfalse; - VALUE defined; + VALUE defined = Qfalse; switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: RB_VM_LOCKING() { - defined = ivar_defined0(obj, id); + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + defined = ivar_defined0(fields_obj, id); + } } break; default: @@ -2183,6 +2219,7 @@ struct iv_itr_data { struct gen_fields_tbl *fields_tbl; st_data_t arg; rb_ivar_foreach_callback_func *func; + VALUE *fields; bool ivar_only; }; @@ -2203,8 +2240,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data) break; case T_CLASS: case T_MODULE: + rb_bug("Unreachable"); + case T_IMEMO: + RUBY_ASSERT(IMEMO_TYPE_P(itr_data->obj, imemo_class_fields)); RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj)); - iv_list = RCLASS_PRIME_FIELDS(itr_data->obj); + + iv_list = rb_imemo_class_fields_ptr(itr_data->obj); break; default: iv_list = itr_data->fields_tbl->as.shape.fields; @@ -2247,6 +2288,7 @@ obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b rb_st_foreach(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data); } else { + itr_data.fields = ROBJECT_FIELDS(obj); iterate_over_shapes(shape_id, func, &itr_data); } } @@ -2270,27 +2312,29 @@ gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b rb_st_foreach(fields_tbl->as.complex.table, each_hash_iv, (st_data_t)&itr_data); } else { + itr_data.fields = fields_tbl->as.shape.fields; iterate_over_shapes(shape_id, func, &itr_data); } } static void -class_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only) +class_fields_each(VALUE fields_obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only) { - RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)); + IMEMO_TYPE_P(fields_obj, imemo_class_fields); struct iv_itr_data itr_data = { - .obj = obj, + .obj = fields_obj, .arg = arg, .func = func, .ivar_only = ivar_only, }; - shape_id_t shape_id = RBASIC_SHAPE_ID(obj); + shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); if (rb_shape_too_complex_p(shape_id)) { - rb_st_foreach(RCLASS_WRITABLE_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data); + rb_st_foreach(rb_imemo_class_fields_complex_tbl(fields_obj), each_hash_iv, (st_data_t)&itr_data); } else { + itr_data.fields = rb_imemo_class_fields_ptr(fields_obj); iterate_over_shapes(shape_id, func, &itr_data); } } @@ -2399,6 +2443,11 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, { if (SPECIAL_CONST_P(obj)) return; switch (BUILTIN_TYPE(obj)) { + case T_IMEMO: + if (IMEMO_TYPE_P(obj, imemo_class_fields)) { + class_fields_each(obj, func, arg, ivar_only); + } + break; case T_OBJECT: obj_fields_each(obj, func, arg, ivar_only); break; @@ -2406,11 +2455,14 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, case T_MODULE: IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0); RB_VM_LOCKING() { - class_fields_each(obj, func, arg, ivar_only); + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (fields_obj) { + class_fields_each(fields_obj, func, arg, ivar_only); + } } break; default: - if (FL_TEST(obj, FL_EXIVAR)) { + if (FL_TEST_RAW(obj, FL_EXIVAR)) { gen_fields_each(obj, func, arg, ivar_only); } break; @@ -2435,8 +2487,16 @@ rb_ivar_count(VALUE obj) break; case T_CLASS: case T_MODULE: - iv_count = RCLASS_FIELDS_COUNT(obj); - break; + { + VALUE fields_obj = RCLASS_FIELDS_OBJ(obj); + if (!fields_obj) { + return 0; + } + if (rb_shape_obj_too_complex_p(fields_obj)) { + return rb_st_table_size(rb_imemo_class_fields_complex_tbl(fields_obj)); + } + return RBASIC_FIELDS_COUNT(fields_obj); + } default: if (FL_TEST(obj, FL_EXIVAR)) { struct gen_fields_tbl *fields_tbl; @@ -4642,38 +4702,91 @@ rb_iv_set(VALUE obj, const char *name, VALUE val) return rb_ivar_set(obj, id, val); } -static VALUE * -class_ivar_set_shape_fields(VALUE obj, void *_data) +static int +class_ivar_set(VALUE obj, ID id, VALUE val) { - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + bool existing = true; + const VALUE original_fields_obj = RCLASS_FIELDS_OBJ(obj); + VALUE fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_class_fields_new(obj, 1); - return RCLASS_PRIME_FIELDS(obj); -} + shape_id_t next_shape_id = 0; + shape_id_t current_shape_id = RBASIC_SHAPE_ID(fields_obj); + if (UNLIKELY(rb_shape_too_complex_p(current_shape_id))) { + goto too_complex; + } -static void -class_ivar_set_shape_resize_fields(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *_data) -{ - REALLOC_N(RCLASS_PRIME_FIELDS(obj), VALUE, new_capa); -} + attr_index_t index; + if (!rb_shape_get_iv_index(current_shape_id, id, &index)) { + existing = false; -static void -class_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data) -{ - rb_obj_set_shape_id(obj, shape_id); -} + index = RSHAPE_LEN(current_shape_id); + if (index >= SHAPE_MAX_FIELDS) { + rb_raise(rb_eArgError, "too many instance variables"); + } -static shape_id_t -class_ivar_set_transition_too_complex(VALUE obj, void *_data) -{ - return rb_evict_fields_to_hash(obj); -} + next_shape_id = rb_shape_transition_add_ivar(fields_obj, id); + if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) { + attr_index_t current_len = RSHAPE_LEN(current_shape_id); + fields_obj = rb_imemo_class_fields_new_complex(obj, current_len + 1); + if (current_len) { + rb_obj_copy_fields_to_hash_table(original_fields_obj, rb_imemo_class_fields_complex_tbl(fields_obj)); + RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id); + } + goto too_complex; + } -static st_table * -class_ivar_set_too_complex_table(VALUE obj, void *_data) -{ - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); + attr_index_t next_capacity = RSHAPE_CAPACITY(next_shape_id); + attr_index_t current_capacity = RSHAPE_CAPACITY(current_shape_id); - return RCLASS_WRITABLE_FIELDS_HASH(obj); + if (UNLIKELY(next_capacity != current_capacity)) { + RUBY_ASSERT(next_capacity > current_capacity); + // We allocate a new fields_obj so that we're embedded as long as possible + fields_obj = rb_imemo_class_fields_new(obj, next_capacity); + if (original_fields_obj) { + MEMCPY(rb_imemo_class_fields_ptr(fields_obj), rb_imemo_class_fields_ptr(original_fields_obj), VALUE, RSHAPE_LEN(current_shape_id)); + } + } + + RUBY_ASSERT(RSHAPE(next_shape_id)->type == SHAPE_IVAR); + RUBY_ASSERT(index == (RSHAPE_LEN(next_shape_id) - 1)); + } + + VALUE *fields = rb_imemo_class_fields_ptr(fields_obj); + RB_OBJ_WRITE(fields_obj, &fields[index], val); + if (!existing) { + RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id); + } + + if (fields_obj != original_fields_obj) { + RCLASS_SET_FIELDS_OBJ(obj, fields_obj); + // TODO: What should we set as the T_CLASS shape_id? + // In most case we can replicate the single `fields_obj` shape + // but in namespaced case? + // Perhaps INVALID_SHAPE_ID? + RBASIC_SET_SHAPE_ID(obj, next_shape_id); + } + + RB_GC_GUARD(fields_obj); + return existing; + +too_complex: + { + st_table *table = rb_imemo_class_fields_complex_tbl(fields_obj); + existing = st_insert(table, (st_data_t)id, (st_data_t)val); + RB_OBJ_WRITTEN(fields_obj, Qundef, val); + + if (fields_obj != original_fields_obj) { + RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id); + RCLASS_SET_FIELDS_OBJ(obj, fields_obj); + // TODO: What should we set as the T_CLASS shape_id? + // In most case we can replicate the single `fields_obj` shape + // but in namespaced case? + // Perhaps INVALID_SHAPE_ID? + RBASIC_SET_SHAPE_ID(obj, next_shape_id); + } + } + RB_GC_GUARD(fields_obj); + return existing; } int @@ -4686,12 +4799,7 @@ rb_class_ivar_set(VALUE obj, ID id, VALUE val) rb_class_ensure_writable(obj); RB_VM_LOCKING() { - existing = general_ivar_set(obj, id, val, NULL, - class_ivar_set_shape_fields, - class_ivar_set_shape_resize_fields, - class_ivar_set_set_shape_id, - class_ivar_set_transition_too_complex, - class_ivar_set_too_complex_table).existing; + existing = class_ivar_set(obj, id, val); } return existing; @@ -4701,12 +4809,7 @@ static void class_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val) { RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)); - general_field_set(obj, target_shape_id, val, NULL, - class_ivar_set_shape_fields, - class_ivar_set_shape_resize_fields, - class_ivar_set_set_shape_id, - class_ivar_set_transition_too_complex, - class_ivar_set_too_complex_table); + obj_field_set(RCLASS_ENSURE_FIELDS_OBJ(obj), target_shape_id, val); } static int @@ -4722,9 +4825,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src) { RUBY_ASSERT(rb_type(dst) == rb_type(src)); RUBY_ASSERT(RB_TYPE_P(dst, T_CLASS) || RB_TYPE_P(dst, T_MODULE)); - RUBY_ASSERT(RSHAPE_TYPE_P(RBASIC_SHAPE_ID(dst), SHAPE_ROOT)); - RUBY_ASSERT(!RCLASS_PRIME_FIELDS(dst)); rb_ivar_foreach(src, tbl_copy_i, dst); } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index f09220a2ea..dbccc6bdbb 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1213,9 +1213,10 @@ ALWAYS_INLINE(static VALUE vm_getivar(VALUE, ID, const rb_iseq_t *, IVC, const s static inline VALUE vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, VALUE default_value) { + VALUE fields_obj; #if OPT_IC_FOR_IVAR VALUE val = Qundef; - VALUE * ivar_list; + VALUE *ivar_list; if (SPECIAL_CONST_P(obj)) { return default_value; @@ -1247,7 +1248,13 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } } - ivar_list = RCLASS_PRIME_FIELDS(obj); + fields_obj = RCLASS_FIELDS_OBJ(obj); + if (!fields_obj) { + return default_value; + } + ivar_list = rb_imemo_class_fields_ptr(fields_obj); + shape_id = rb_obj_shape_id(fields_obj); + break; } default: @@ -1318,7 +1325,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call switch (BUILTIN_TYPE(obj)) { case T_CLASS: case T_MODULE: - table = (st_table *)RCLASS_FIELDS_HASH(obj); + table = rb_imemo_class_fields_complex_tbl(fields_obj); break; case T_OBJECT: @@ -1374,6 +1381,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call RUBY_ASSERT(!UNDEF_P(val)); } + RB_GC_GUARD(fields_obj); return val; general_path: diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 23682ac63c..d92e12c38b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -409,6 +409,7 @@ pub const imemo_parser_strterm: imemo_type = 10; pub const imemo_callinfo: imemo_type = 11; pub const imemo_callcache: imemo_type = 12; pub const imemo_constcache: imemo_type = 13; +pub const imemo_class_fields: imemo_type = 14; pub type imemo_type = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 0447f46fd0..34f6ded80d 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -226,6 +226,7 @@ pub const imemo_parser_strterm: imemo_type = 10; pub const imemo_callinfo: imemo_type = 11; pub const imemo_callcache: imemo_type = 12; pub const imemo_constcache: imemo_type = 13; +pub const imemo_class_fields: imemo_type = 14; pub type imemo_type = u32; pub const METHOD_VISI_UNDEF: rb_method_visibility_t = 0; pub const METHOD_VISI_PUBLIC: rb_method_visibility_t = 1;