diff options
-rw-r--r-- | benchmark/vm_cvar.yml | 20 | ||||
-rw-r--r-- | class.c | 6 | ||||
-rw-r--r-- | common.mk | 1 | ||||
-rw-r--r-- | compile.c | 10 | ||||
-rw-r--r-- | debug_counter.h | 6 | ||||
-rw-r--r-- | gc.c | 36 | ||||
-rw-r--r-- | id_table.c | 6 | ||||
-rw-r--r-- | include/ruby/internal/intern/variable.h | 1 | ||||
-rw-r--r-- | insns.def | 10 | ||||
-rw-r--r-- | internal/class.h | 8 | ||||
-rw-r--r-- | variable.c | 92 | ||||
-rw-r--r-- | vm.c | 5 | ||||
-rw-r--r-- | vm_core.h | 5 | ||||
-rw-r--r-- | vm_insnhelper.c | 84 | ||||
-rw-r--r-- | vm_insnhelper.h | 3 |
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 @@ -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 @@ -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 @@ -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 @@ -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); @@ -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; @@ -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 */ @@ -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) |