From 3adc77d2bbb7ab35375659a658052fa1831f2745 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 26 May 2017 13:44:00 -0700 Subject: [PATCH] Optimize instance variable access if $VERBOSE is not true when compiling This patch optimizes instance variable lookup in the case the $VERBOSE is not true when compiling. If $VERBOSE is not true when compiling code, it makes the instance variable access use an optimized VM instruction that does not check $VERBOSE at runtime. This does not change the behavior if $VERBOSE is not changed at runtime, only when $VERBOSE is not true when compiling the code, but is true when running it. In the case where $VERBOSE is not true when compiling and true at runtime, this patch makes ruby no longer emit the warning message. --- compile.c | 8 +++++++- insns.def | 14 ++++++++++++++ test/ruby/test_exception.rb | 2 +- test/ruby/test_module.rb | 4 +--- vm_insnhelper.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/compile.c b/compile.c index 3939ea0360..84d29b38de 100644 --- a/compile.c +++ b/compile.c @@ -5685,9 +5685,15 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp case NODE_IVAR:{ debugi("nd_vid", node->nd_vid); if (!popped) { - ADD_INSN2(ret, line, getinstancevariable, + if (RTEST(ruby_verbose)) { + ADD_INSN2(ret, line, getinstancevariable, ID2SYM(node->nd_vid), get_ivar_ic_value(iseq,node->nd_vid)); + } else { + ADD_INSN2(ret, line, getinstancevariablefast, + ID2SYM(node->nd_vid), + get_ivar_ic_value(iseq,node->nd_vid)); + } } break; } diff --git a/insns.def b/insns.def index fa82d80850..b83febe792 100644 --- a/insns.def +++ b/insns.def @@ -121,6 +121,20 @@ getinstancevariable /** @c variable + @e Get value of instance variable id of self (optimized version + used when $VERBOSE is not true). + */ +DEFINE_INSN +getinstancevariablefast +(ID id, IC ic) +() +(VALUE val) +{ + val = vm_getinstancevariablefast(GET_SELF(), id, ic); +} + +/** + @c variable @e Set value of instance variable id of self to val. If is_local is not 0, set value of class local variable. @j self のインスタンス変数 id を val にする。 diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 6955118de7..6566a65e7c 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -995,7 +995,7 @@ def capture_warning_warn end def test_warning_warn - warning = capture_warning_warn {@a} + warning = capture_warning_warn {eval '@a'} assert_match(/instance variable @a not initialized/, warning[0]) assert_equal(["a\nz\n"], capture_warning_warn {warn "a\n", "z"}) diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index d1c5d53822..debdf581f4 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -1970,14 +1970,12 @@ class << self attr_accessor :cattr end attr_accessor :iattr - def ivar - @ivar - end end def test_uninitialized_instance_variable a = AttrTest.new assert_warning(/instance variable @ivar not initialized/) do + eval 'def a.ivar; @ivar end' assert_nil(a.ivar) end a.instance_variable_set(:@ivar, 42) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 4c25ba6e8d..fcf41dda90 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1010,6 +1010,52 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_ } static inline VALUE +vm_getinstancevariablefast(VALUE obj, ID id, IC ic) +{ +#if USE_IC_FOR_IVAR + if (LIKELY(RB_TYPE_P(obj, T_OBJECT))) { + VALUE val = Qnil; + if (RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial, + ic->ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass))) { + st_index_t index = ic->ic_value.index; + if (LIKELY(index < ROBJECT_NUMIV(obj))) { + val = ROBJECT_IVPTR(obj)[index]; + if (UNLIKELY(val == Qundef)) { + val = Qnil; + } + } + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); + return val; + } + else { + st_data_t index; + struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); + + if (iv_index_tbl) { + if (st_lookup(iv_index_tbl, id, &index)) { + if (index < ROBJECT_NUMIV(obj)) { + val = ROBJECT_IVPTR(obj)[index]; + if (UNLIKELY(val == Qundef)) { + val = Qnil; + } + } + ic->ic_value.index = index; + ic->ic_serial = RCLASS_SERIAL(RBASIC(obj)->klass); + } + } + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); + return val; + } + } + else { + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_noobject); + } +#endif + RB_DEBUG_COUNTER_INC(ivar_get_ic_miss); + return rb_attr_get(obj, id); +} + +static inline VALUE vm_getinstancevariable(VALUE obj, ID id, IC ic) { return vm_getivar(obj, id, ic, 0, 0); -- 2.11.0