summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/vm_cvar.yml20
-rw-r--r--class.c6
-rw-r--r--common.mk1
-rw-r--r--compile.c10
-rw-r--r--debug_counter.h6
-rw-r--r--gc.c36
-rw-r--r--id_table.c6
-rw-r--r--include/ruby/internal/intern/variable.h1
-rw-r--r--insns.def10
-rw-r--r--internal/class.h8
-rw-r--r--variable.c92
-rw-r--r--vm.c5
-rw-r--r--vm_core.h5
-rw-r--r--vm_insnhelper.c84
-rw-r--r--vm_insnhelper.h3
15 files changed, 271 insertions, 22 deletions
diff --git a/benchmark/vm_cvar.yml b/benchmark/vm_cvar.yml
new file mode 100644
index 0000000000..1d0e161829
--- /dev/null
+++ b/benchmark/vm_cvar.yml
@@ -0,0 +1,20 @@
+prelude: |
+ class A
+ @@foo = 1
+
+ def self.foo
+ @@foo
+ end
+
+ ("A".."Z").each do |module_name|
+ eval <<-EOM
+ module #{module_name}
+ end
+
+ include #{module_name}
+ EOM
+ end
+ end
+benchmark:
+ vm_cvar: A.foo
+loop_count: 600000
diff --git a/class.c b/class.c
index 9ac2b3ff47..ef3db6dab3 100644
--- a/class.c
+++ b/class.c
@@ -27,6 +27,7 @@
#include <ctype.h>
#include "constant.h"
+#include "debug_counter.h"
#include "id_table.h"
#include "internal.h"
#include "internal/class.h"
@@ -43,6 +44,8 @@
#define METACLASS_OF(k) RBASIC(k)->klass
#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
+RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
+
void
rb_class_subclass_add(VALUE super, VALUE klass)
{
@@ -957,6 +960,7 @@ rb_include_class_new(VALUE module, VALUE super)
RCLASS_CONST_TBL(module) = rb_id_table_create(0);
}
RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
+ RCLASS_CVC_TBL(klass) = RCLASS_CVC_TBL(module);
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
RCLASS_SET_SUPER(klass, super);
@@ -1085,6 +1089,8 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
VALUE super_class = RCLASS_SUPER(c);
// invalidate inline method cache
+ RB_DEBUG_COUNTER_INC(cvar_include_invalidate);
+ ruby_vm_global_cvar_state++;
tbl = RCLASS_M_TBL(module);
if (tbl && rb_id_table_size(tbl)) {
if (search_super) { // include
diff --git a/common.mk b/common.mk
index 30380e0a4d..feb14b9a0d 100644
--- a/common.mk
+++ b/common.mk
@@ -2460,6 +2460,7 @@ class.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
class.$(OBJEXT): {$(VPATH)}class.c
class.$(OBJEXT): {$(VPATH)}config.h
class.$(OBJEXT): {$(VPATH)}constant.h
+class.$(OBJEXT): {$(VPATH)}debug_counter.h
class.$(OBJEXT): {$(VPATH)}defines.h
class.$(OBJEXT): {$(VPATH)}encoding.h
class.$(OBJEXT): {$(VPATH)}id.h
diff --git a/compile.c b/compile.c
index c3aa8f3600..c541711ff9 100644
--- a/compile.c
+++ b/compile.c
@@ -8066,8 +8066,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
if (!popped) {
ADD_INSN(ret, line_node, dup);
}
- ADD_INSN1(ret, line_node, setclassvariable,
- ID2SYM(node->nd_vid));
+ ADD_INSN2(ret, line_node, setclassvariable,
+ ID2SYM(node->nd_vid),
+ get_ivar_ic_value(iseq,node->nd_vid));
break;
}
case NODE_OP_ASGN1: {
@@ -8690,8 +8691,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
}
case NODE_CVAR:{
if (!popped) {
- ADD_INSN1(ret, line_node, getclassvariable,
- ID2SYM(node->nd_vid));
+ ADD_INSN2(ret, line_node, getclassvariable,
+ ID2SYM(node->nd_vid),
+ get_ivar_ic_value(iseq,node->nd_vid));
}
break;
}
diff --git a/debug_counter.h b/debug_counter.h
index 9452f4c737..3cf80cc188 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -24,6 +24,12 @@ RB_DEBUG_COUNTER(mc_inline_miss_same_cme) // IMC miss, but same CME
RB_DEBUG_COUNTER(mc_inline_miss_same_def) // IMC miss, but same definition
RB_DEBUG_COUNTER(mc_inline_miss_diff) // IMC miss, different methods
+RB_DEBUG_COUNTER(cvar_write_inline_hit) // cvar cache hit on write
+RB_DEBUG_COUNTER(cvar_read_inline_hit) // cvar cache hit on read
+RB_DEBUG_COUNTER(cvar_inline_miss) // miss inline cache
+RB_DEBUG_COUNTER(cvar_class_invalidate) // invalidate cvar cache when define a cvar that's defined on a subclass
+RB_DEBUG_COUNTER(cvar_include_invalidate) // invalidate cvar cache on module include or prepend
+
RB_DEBUG_COUNTER(mc_cme_complement) // number of acquiring complement CME
RB_DEBUG_COUNTER(mc_cme_complement_hit) // number of cache hit for complemented CME
diff --git a/gc.c b/gc.c
index 0ad090affb..09420deac2 100644
--- a/gc.c
+++ b/gc.c
@@ -2998,6 +2998,13 @@ cc_table_free(rb_objspace_t *objspace, VALUE klass, bool alive)
}
}
+static enum rb_id_table_iterator_result
+cvar_table_free_i(VALUE value, void * ctx)
+{
+ xfree((void *) value);
+ return ID_TABLE_CONTINUE;
+}
+
void
rb_cc_table_free(VALUE klass)
{
@@ -3109,6 +3116,10 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
if (RCLASS_IV_INDEX_TBL(obj)) {
iv_index_tbl_free(RCLASS_IV_INDEX_TBL(obj));
}
+ if (RCLASS_CVC_TBL(obj)) {
+ rb_id_table_foreach_values(RCLASS_CVC_TBL(obj), cvar_table_free_i, NULL);
+ rb_id_table_free(RCLASS_CVC_TBL(obj));
+ }
if (RCLASS_SUBCLASSES(obj)) {
if (BUILTIN_TYPE(obj) == T_MODULE) {
rb_class_detach_module_subclasses(obj);
@@ -4552,6 +4563,9 @@ obj_memsize_of(VALUE obj, int use_all_types)
if (RCLASS_IV_TBL(obj)) {
size += st_memsize(RCLASS_IV_TBL(obj));
}
+ if (RCLASS_CVC_TBL(obj)) {
+ size += rb_id_table_memsize(RCLASS_CVC_TBL(obj));
+ }
if (RCLASS_IV_INDEX_TBL(obj)) {
// TODO: more correct value
size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
@@ -9750,6 +9764,27 @@ update_cc_tbl(rb_objspace_t *objspace, VALUE klass)
}
static enum rb_id_table_iterator_result
+update_cvc_tbl_i(ID id, VALUE cvc_entry, void *data)
+{
+ struct rb_cvar_class_tbl_entry *entry;
+
+ entry = (struct rb_cvar_class_tbl_entry *)cvc_entry;
+
+ entry->class_value = rb_gc_location(entry->class_value);
+
+ return ID_TABLE_CONTINUE;
+}
+
+static void
+update_cvc_tbl(rb_objspace_t *objspace, VALUE klass)
+{
+ struct rb_id_table *tbl = RCLASS_CVC_TBL(klass);
+ if (tbl) {
+ rb_id_table_foreach_with_replace(tbl, update_cvc_tbl_i, 0, objspace);
+ }
+}
+
+static enum rb_id_table_iterator_result
update_const_table(VALUE value, void *data)
{
rb_const_entry_t *ce = (rb_const_entry_t *)value;
@@ -9820,6 +9855,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
if (!RCLASS_EXT(obj)) break;
update_m_tbl(objspace, RCLASS_M_TBL(obj));
update_cc_tbl(objspace, obj);
+ update_cvc_tbl(objspace, obj);
gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj));
diff --git a/id_table.c b/id_table.c
index 840ab46ee3..149f52cd88 100644
--- a/id_table.c
+++ b/id_table.c
@@ -92,7 +92,7 @@ rb_id_table_init(struct rb_id_table *tbl, int capa)
return tbl;
}
-struct rb_id_table *
+MJIT_FUNC_EXPORTED struct rb_id_table *
rb_id_table_create(size_t capa)
{
struct rb_id_table *tbl = ALLOC(struct rb_id_table);
@@ -223,7 +223,7 @@ hash_table_show(struct rb_id_table *tbl)
}
#endif
-int
+MJIT_FUNC_EXPORTED int
rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
{
id_key_t key = id2key(id);
@@ -253,7 +253,7 @@ rb_id_table_insert_key(struct rb_id_table *tbl, const id_key_t key, const VALUE
return TRUE;
}
-int
+MJIT_FUNC_EXPORTED int
rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val)
{
return rb_id_table_insert_key(tbl, id2key(id), val);
diff --git a/include/ruby/internal/intern/variable.h b/include/ruby/internal/intern/variable.h
index 8210662fa0..faa0cc004f 100644
--- a/include/ruby/internal/intern/variable.h
+++ b/include/ruby/internal/intern/variable.h
@@ -72,6 +72,7 @@ VALUE rb_mod_const_missing(VALUE,VALUE);
VALUE rb_cvar_defined(VALUE, ID);
void rb_cvar_set(VALUE, ID, VALUE);
VALUE rb_cvar_get(VALUE, ID);
+VALUE rb_cvar_find(VALUE, ID, VALUE*);
void rb_cv_set(VALUE, const char*, VALUE);
VALUE rb_cv_get(VALUE, const char*);
void rb_define_class_variable(VALUE, const char*, VALUE);
diff --git a/insns.def b/insns.def
index c6ebf7401b..0b741af504 100644
--- a/insns.def
+++ b/insns.def
@@ -230,26 +230,28 @@ setinstancevariable
/* Get value of class variable id of klass as val. */
DEFINE_INSN
getclassvariable
-(ID id)
+(ID id, IVC ic)
()
(VALUE val)
/* "class variable access from toplevel" warning can be hooked. */
// attr bool leaf = false; /* has rb_warning() */
{
- val = rb_cvar_get(vm_get_cvar_base(vm_get_cref(GET_EP()), GET_CFP(), 1), id);
+ rb_cref_t * cref = vm_get_cref(GET_EP());
+ rb_control_frame_t *cfp = GET_CFP();
+ val = vm_getclassvariable(GET_ISEQ(), cref, cfp, id, (ICVARC)ic);
}
/* Set value of class variable id of klass as val. */
DEFINE_INSN
setclassvariable
-(ID id)
+(ID id, IVC ic)
(VALUE val)
()
/* "class variable access from toplevel" warning can be hooked. */
// attr bool leaf = false; /* has rb_warning() */
{
vm_ensure_not_refinement_module(GET_SELF());
- rb_cvar_set(vm_get_cvar_base(vm_get_cref(GET_EP()), GET_CFP(), 1), id, val);
+ vm_setclassvariable(GET_ISEQ(), vm_get_cref(GET_EP()), GET_CFP(), id, val, (ICVARC)ic);
}
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
diff --git a/internal/class.h b/internal/class.h
index 6c03a31a4e..1d25d9e7eb 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -31,6 +31,12 @@ struct rb_iv_index_tbl_entry {
VALUE class_value;
};
+struct rb_cvar_class_tbl_entry {
+ uint32_t index;
+ rb_serial_t global_cvar_state;
+ VALUE class_value;
+};
+
struct rb_classext_struct {
struct st_table *iv_index_tbl; // ID -> struct rb_iv_index_tbl_entry
struct st_table *iv_tbl;
@@ -40,6 +46,7 @@ struct rb_classext_struct {
struct rb_id_table *const_tbl;
struct rb_id_table *callable_m_tbl;
struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */
+ struct rb_id_table *cvc_tbl;
struct rb_subclass_entry *subclasses;
struct rb_subclass_entry **parent_subclasses;
/**
@@ -83,6 +90,7 @@ typedef struct rb_classext_struct rb_classext_t;
#endif
#define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl)
#define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl)
+#define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl)
#define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl)
#define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_)
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
diff --git a/variable.c b/variable.c
index 5e5f5c4bf4..778778c866 100644
--- a/variable.c
+++ b/variable.c
@@ -39,6 +39,9 @@
#include "ractor_core.h"
#include "vm_sync.h"
+RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
+#define GET_GLOBAL_CVAR_STATE() (ruby_vm_global_cvar_state)
+
typedef void rb_gvar_compact_t(void *var);
static struct rb_id_table *rb_global_tbl;
@@ -3325,6 +3328,30 @@ cvar_overtaken(VALUE front, VALUE target, ID id)
}
}
+static VALUE
+find_cvar(VALUE klass, VALUE * front, VALUE * target, ID id)
+{
+ VALUE v = Qundef;
+ CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR();
+ if (cvar_lookup_at(klass, id, (&v))) {
+ if (!*front) {
+ *front = klass;
+ }
+ *target = klass;
+ }
+
+ for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) {
+ if (cvar_lookup_at(klass, id, (&v))) {
+ if (!*front) {
+ *front = klass;
+ }
+ *target = klass;
+ }
+ }
+
+ return v;
+}
+
#define CVAR_FOREACH_ANCESTORS(klass, v, r) \
for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { \
if (cvar_lookup_at(klass, id, (v))) { \
@@ -3338,6 +3365,20 @@ cvar_overtaken(VALUE front, VALUE target, ID id)
CVAR_FOREACH_ANCESTORS(klass, v, r);\
} while(0)
+static void
+check_for_cvar_table(VALUE subclass, VALUE key)
+{
+ st_table *tbl = RCLASS_IV_TBL(subclass);
+
+ if (tbl && st_lookup(tbl, key, NULL)) {
+ RB_DEBUG_COUNTER_INC(cvar_class_invalidate);
+ ruby_vm_global_cvar_state++;
+ return;
+ }
+
+ rb_class_foreach_subclass(subclass, check_for_cvar_table, key);
+}
+
void
rb_cvar_set(VALUE klass, ID id, VALUE val)
{
@@ -3357,26 +3398,61 @@ rb_cvar_set(VALUE klass, ID id, VALUE val)
}
check_before_mod_set(target, id, val, "class variable");
- rb_class_ivar_set(target, id, val);
+ int result = rb_class_ivar_set(target, id, val);
+
+ struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(target);
+
+ if (!rb_cvc_tbl) {
+ rb_cvc_tbl = RCLASS_CVC_TBL(target) = rb_id_table_create(2);
+ }
+
+ struct rb_cvar_class_tbl_entry *ent;
+
+ if (!rb_id_table_lookup(rb_cvc_tbl, id, (VALUE*)&ent)) {
+ ent = ALLOC(struct rb_cvar_class_tbl_entry);
+ ent->class_value = target;
+ ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
+ rb_id_table_insert(rb_cvc_tbl, id, (VALUE)ent);
+ RB_DEBUG_COUNTER_INC(cvar_inline_miss);
+ } else {
+ ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
+ }
+
+ // Break the cvar cache if this is a new class variable
+ // and target is a module or a subclass with the same
+ // cvar in this lookup.
+ if (result == 0) {
+ if (RB_TYPE_P(target, T_CLASS)) {
+ if (RCLASS_SUBCLASSES(target)) {
+ rb_class_foreach_subclass(target, check_for_cvar_table, id);
+ }
+ }
+ }
}
VALUE
-rb_cvar_get(VALUE klass, ID id)
+rb_cvar_find(VALUE klass, ID id, VALUE *front)
{
- VALUE tmp, front = 0, target = 0;
- st_data_t value;
+ VALUE target = 0;
+ VALUE value;
- tmp = klass;
- CVAR_LOOKUP(&value, {if (!front) front = klass; target = klass;});
+ value = find_cvar(klass, front, &target, id);
if (!target) {
rb_name_err_raise("uninitialized class variable %1$s in %2$s",
- tmp, ID2SYM(id));
+ klass, ID2SYM(id));
}
- cvar_overtaken(front, target, id);
+ cvar_overtaken(*front, target, id);
return (VALUE)value;
}
VALUE
+rb_cvar_get(VALUE klass, ID id)
+{
+ VALUE front = 0;
+ return rb_cvar_find(klass, id, &front);
+}
+
+VALUE
rb_cvar_defined(VALUE klass, ID id)
{
if (!klass) return Qfalse;
diff --git a/vm.c b/vm.c
index 6140bcf861..3963d67168 100644
--- a/vm.c
+++ b/vm.c
@@ -405,6 +405,7 @@ unsigned int ruby_vm_event_local_num;
rb_serial_t ruby_vm_global_constant_state = 1;
rb_serial_t ruby_vm_class_serial = 1;
+rb_serial_t ruby_vm_global_cvar_state = 1;
static const struct rb_callcache vm_empty_cc = {
.flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
@@ -484,7 +485,7 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
static VALUE
vm_stat(int argc, VALUE *argv, VALUE self)
{
- static VALUE sym_global_constant_state, sym_class_serial;
+ static VALUE sym_global_constant_state, sym_class_serial, sym_global_cvar_state;
VALUE arg = Qnil;
VALUE hash = Qnil, key = Qnil;
@@ -505,6 +506,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
S(global_constant_state);
S(class_serial);
+ S(global_cvar_state);
#undef S
}
@@ -516,6 +518,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
SET(global_constant_state, ruby_vm_global_constant_state);
SET(class_serial, ruby_vm_class_serial);
+ SET(global_cvar_state, ruby_vm_global_cvar_state);
#undef SET
if (!NIL_P(key)) { /* matched key should return above */
diff --git a/vm_core.h b/vm_core.h
index 7eda31f11e..59b788e178 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -241,6 +241,10 @@ struct iseq_inline_iv_cache_entry {
struct rb_iv_index_tbl_entry *entry;
};
+struct iseq_inline_cvar_cache_entry {
+ struct rb_cvar_class_tbl_entry *entry;
+};
+
union iseq_inline_storage_entry {
struct {
struct rb_thread_struct *running_thread;
@@ -1157,6 +1161,7 @@ enum vm_svar_index {
/* inline cache */
typedef struct iseq_inline_constant_cache *IC;
typedef struct iseq_inline_iv_cache_entry *IVC;
+typedef struct iseq_inline_cvar_cache_entry *ICVARC;
typedef union iseq_inline_storage_entry *ISE;
typedef const struct rb_callinfo *CALL_INFO;
typedef const struct rb_callcache *CALL_CACHE;
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 171b0f4273..72c90a8a5c 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -951,7 +951,7 @@ vm_ensure_not_refinement_module(VALUE self)
}
static inline VALUE
-vm_get_iclass(rb_control_frame_t *cfp, VALUE klass)
+vm_get_iclass(const rb_control_frame_t *cfp, VALUE klass)
{
return klass;
}
@@ -1041,7 +1041,7 @@ vm_get_ev_const(rb_execution_context_t *ec, VALUE orig_klass, ID id, bool allow_
}
static inline VALUE
-vm_get_cvar_base(const rb_cref_t *cref, rb_control_frame_t *cfp, int top_level_raise)
+vm_get_cvar_base(const rb_cref_t *cref, const rb_control_frame_t *cfp, int top_level_raise)
{
VALUE klass;
@@ -1282,6 +1282,86 @@ vm_setivar(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, const str
}
static inline VALUE
+vm_getclassvariable(const rb_iseq_t *iseq, const rb_cref_t *cref, const rb_control_frame_t *cfp, ID id, ICVARC ic)
+{
+ if (ic->entry && ic->entry->global_cvar_state == GET_GLOBAL_CVAR_STATE()) {
+ VALUE v = Qundef;
+ RB_DEBUG_COUNTER_INC(cvar_read_inline_hit);
+
+ if (st_lookup(RCLASS_IV_TBL(ic->entry->class_value), (st_data_t)id, &v)) {
+ return v;
+ }
+ }
+
+ VALUE klass = vm_get_cvar_base(cref, cfp, 1);
+ VALUE defined_class = 0;
+
+ VALUE cvar_value = rb_cvar_find(klass, id, &defined_class);
+
+ if (RB_TYPE_P(defined_class, T_ICLASS)) {
+ defined_class = RBASIC(defined_class)->klass;
+ }
+
+ struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(defined_class);
+
+ if (!rb_cvc_tbl) {
+ rb_cvc_tbl = RCLASS_CVC_TBL(defined_class) = rb_id_table_create(2);
+ }
+
+ struct rb_cvar_class_tbl_entry *ent;
+
+ if (!rb_id_table_lookup(rb_cvc_tbl, id, (VALUE*)&ent)) {
+ rb_bug("should have cvar cache entry");
+ } else {
+ ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
+ }
+
+ ic->entry = ent;
+ RB_OBJ_WRITTEN(iseq, Qundef, ent->class_value);
+
+ return cvar_value;
+}
+
+static inline void
+vm_setclassvariable(const rb_iseq_t *iseq, const rb_cref_t *cref, const rb_control_frame_t *cfp, ID id, VALUE val, ICVARC ic)
+{
+ if (ic->entry && ic->entry->global_cvar_state == GET_GLOBAL_CVAR_STATE()) {
+ RB_DEBUG_COUNTER_INC(cvar_write_inline_hit);
+
+ rb_class_ivar_set(ic->entry->class_value, id, val);
+ return;
+ }
+
+ VALUE klass = vm_get_cvar_base(cref, cfp, 1);
+
+ rb_cvar_set(klass, id, val);
+
+ VALUE defined_class = 0;
+ rb_cvar_find(klass, id, &defined_class);
+
+ if (RB_TYPE_P(defined_class, T_ICLASS)) {
+ defined_class = RBASIC(defined_class)->klass;
+ }
+
+ struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(defined_class);
+
+ if (!rb_cvc_tbl) {
+ rb_bug("the cvc table should be set");
+ }
+
+ struct rb_cvar_class_tbl_entry *ent;
+
+ if (!rb_id_table_lookup(rb_cvc_tbl, id, (VALUE*)&ent)) {
+ rb_bug("should have cvar cache entry");
+ } else {
+ ent->global_cvar_state = GET_GLOBAL_CVAR_STATE();
+ }
+
+ ic->entry = ent;
+ RB_OBJ_WRITTEN(iseq, Qundef, ent->class_value);
+}
+
+static inline VALUE
vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic)
{
return vm_getivar(obj, id, iseq, ic, NULL, FALSE);
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index e254e709e1..0d90eb9434 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -16,6 +16,7 @@ MJIT_SYMBOL_EXPORT_BEGIN
RUBY_EXTERN VALUE ruby_vm_const_missing_count;
RUBY_EXTERN rb_serial_t ruby_vm_global_constant_state;
RUBY_EXTERN rb_serial_t ruby_vm_class_serial;
+RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
MJIT_SYMBOL_EXPORT_END
@@ -179,6 +180,8 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable
#define NEXT_CLASS_SERIAL() (++ruby_vm_class_serial)
#define GET_GLOBAL_CONSTANT_STATE() (ruby_vm_global_constant_state)
#define INC_GLOBAL_CONSTANT_STATE() (++ruby_vm_global_constant_state)
+#define GET_GLOBAL_CVAR_STATE() (ruby_vm_global_cvar_state)
+#define INC_GLOBAL_CVAR_STATE() (++ruby_vm_global_cvar_state)
static inline struct vm_throw_data *
THROW_DATA_NEW(VALUE val, const rb_control_frame_t *cf, int st)