diff --git a/ChangeLog b/ChangeLog index a83b06a8de..5f572485d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Wed Aug 21 02:32:32 2013 Koichi Sasada + + * insns.def: fix regexp's once option behavior. + fix [ruby-trunk - Bug #6701] + + * insns.def: remove `onceinlinecache' and introduce `once' instruction. + `once' doesn't use `setinlinecache' insn any more. + + * vm_core.h: `union iseq_inline_storage_entry' to store once data. + + * compile.c: catch up above changes. + + * iseq.c: ditto. + + * vm.c, vm_insnhelper.c: ditto. fix `m_core_set_postexe()' which + is depend on `onceinlinecache' insn. + + * test/ruby/test_regexp.rb: add tests. + + * iseq.c: ISEQ_MINOR_VERSION to 1 (should increment major?) + Wed Aug 21 02:30:15 2013 Koichi Sasada * gc.c (rb_gcdebug_print_obj_condition): add printing information. diff --git a/compile.c b/compile.c index 09841fc931..e8d180eb47 100644 --- a/compile.c +++ b/compile.c @@ -1432,8 +1432,8 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) /* make instruction sequence */ generated_iseq = ALLOC_N(VALUE, pos); line_info_table = ALLOC_N(struct iseq_line_info_entry, k); - iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size); - MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size); + iseq->is_entries = ALLOC_N(union iseq_inline_storage_entry, iseq->is_size); + MEMZERO(iseq->is_entries, union iseq_inline_storage_entry, iseq->is_size); iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size); /* MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); */ @@ -1531,9 +1531,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) case TS_IC: /* inline cache */ { int ic_index = FIX2INT(operands[j]); - IC ic = &iseq->ic_entries[ic_index]; - if (UNLIKELY(ic_index >= iseq->ic_size)) { - rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->ic_size); + IC ic = (IC)&iseq->is_entries[ic_index]; + if (UNLIKELY(ic_index >= iseq->is_size)) { + rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->is_size); } generated_iseq[pos + 1 + j] = (VALUE)ic; break; @@ -3923,7 +3923,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) ADD_INSN(ret, line, dup); } ADD_INSN2(ret, line, setinstancevariable, - ID2SYM(node->nd_vid), INT2FIX(iseq->ic_size++)); + ID2SYM(node->nd_vid), INT2FIX(iseq->is_size++)); break; } case NODE_CDECL:{ @@ -4662,7 +4662,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) debugi("nd_vid", node->nd_vid); if (!poped) { ADD_INSN2(ret, line, getinstancevariable, - ID2SYM(node->nd_vid), INT2FIX(iseq->ic_size++)); + ID2SYM(node->nd_vid), INT2FIX(iseq->is_size++)); } break; } @@ -4671,7 +4671,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) if (iseq->compile_data->option->inline_const_cache) { LABEL *lend = NEW_LABEL(line); - int ic_index = iseq->ic_size++; + int ic_index = iseq->is_size++; ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index)); ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_vid)); @@ -4824,16 +4824,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } case NODE_DREGX_ONCE:{ /* TODO: once? */ - LABEL *lend = NEW_LABEL(line); - int ic_index = iseq->ic_size++; + int ic_index = iseq->is_size++; + NODE *dregx_node = NEW_NODE(NODE_DREGX, node->u1.value, node->u2.value, node->u3.value); + NODE *block_node = NEW_NODE(NODE_SCOPE, 0, dregx_node, 0); + VALUE block_iseq = NEW_CHILD_ISEQVAL(block_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); - ADD_INSN2(ret, line, onceinlinecache, lend, INT2FIX(ic_index)); - ADD_INSN(ret, line, pop); - - compile_dregx(iseq, ret, node); - - ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index)); - ADD_LABEL(ret, lend); + ADD_INSN2(ret, line, once, block_iseq, INT2FIX(ic_index)); if (poped) { ADD_INSN(ret, line, pop); @@ -5010,7 +5006,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) if (rb_is_const_id(node->nd_mid)) { /* constant */ LABEL *lend = NEW_LABEL(line); - int ic_index = iseq->ic_size++; + int ic_index = iseq->is_size++; DECL_ANCHOR(pref); DECL_ANCHOR(body); @@ -5052,7 +5048,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } case NODE_COLON3:{ LABEL *lend = NEW_LABEL(line); - int ic_index = iseq->ic_size++; + int ic_index = iseq->is_size++; debugi("colon3#nd_mid", node->nd_mid); @@ -5201,19 +5197,13 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) break; } case NODE_POSTEXE:{ - LABEL *lend = NEW_LABEL(line); VALUE block = NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); - int ic_index = iseq->ic_size++; - - ADD_INSN2(ret, line, onceinlinecache, lend, INT2FIX(ic_index)); - ADD_INSN(ret, line, pop); + int is_index = iseq->is_size++; ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); ADD_INSN1(ret, line, putiseq, block); - ADD_SEND (ret, line, ID2SYM(id_core_set_postexe), INT2FIX(1)); - - ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index)); - ADD_LABEL(ret, lend); + ADD_INSN1(ret, line, putobject, INT2FIX(is_index)); + ADD_SEND (ret, line, ID2SYM(id_core_set_postexe), INT2FIX(2)); if (poped) { ADD_INSN(ret, line, pop); @@ -5711,8 +5701,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, break; case TS_IC: argv[j] = op; - if (NUM2INT(op) >= iseq->ic_size) { - iseq->ic_size = NUM2INT(op) + 1; + if (NUM2INT(op) >= iseq->is_size) { + iseq->is_size = NUM2INT(op) + 1; } break; case TS_CALLINFO: diff --git a/insns.def b/insns.def index b5ca35c44b..ff622fa987 100644 --- a/insns.def +++ b/insns.def @@ -1196,35 +1196,6 @@ getinlinecache } } -/** - @c optimize - @e inline cache (once) - @j once を実現する。 - */ -DEFINE_INSN -onceinlinecache -(OFFSET dst, IC ic) -() -(VALUE val) -{ - retry: - if (ic->ic_vmstat) { - val = ic->ic_value.value; - JUMP(dst); - } - else if (ic->ic_value.value == Qundef) - { - RUBY_VM_CHECK_INTS(th); - rb_thread_schedule(); - goto retry; - } - else { - /* none */ - ic->ic_value.value = Qundef; - val = Qnil; - } -} - /** @c optimize @e set inline cache @@ -1244,6 +1215,44 @@ setinlinecache ruby_vm_const_missing_count = 0; } +/** + @c optimize + @e once + @j once を実現する。 + */ +DEFINE_INSN +once +(ISEQ iseq, IC ic) +() +(VALUE val) +{ + union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic; + + retry: + if (is->once.done == Qfalse) { + if (is->once.running_thread == NULL) { + is->once.running_thread = th; + val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is); + /* is->once.running_thread is cleared by vm_once_clear() */ + is->once.done = Qtrue; + rb_iseq_add_mark_object(GET_ISEQ(), val); + } + else if (is->once.running_thread == th) { + /* recursive once */ + val = vm_once_exec(iseq); + } + else { + /* waiting for finish */ + RUBY_VM_CHECK_INTS(th); + rb_thread_schedule(); + goto retry; + } + } + else { + val = is->once.value; + } +} + /** @c optimize @e case dispatcher diff --git a/iseq.c b/iseq.c index 198295a479..e600a4de6e 100644 --- a/iseq.c +++ b/iseq.c @@ -22,7 +22,7 @@ #include "insns_info.inc" #define ISEQ_MAJOR_VERSION 2 -#define ISEQ_MINOR_VERSION 0 +#define ISEQ_MINOR_VERSION 1 VALUE rb_cISeq; @@ -81,7 +81,7 @@ iseq_free(void *ptr) RUBY_FREE_UNLESS_NULL(iseq->iseq); RUBY_FREE_UNLESS_NULL(iseq->line_info_table); RUBY_FREE_UNLESS_NULL(iseq->local_table); - RUBY_FREE_UNLESS_NULL(iseq->ic_entries); + RUBY_FREE_UNLESS_NULL(iseq->is_entries); RUBY_FREE_UNLESS_NULL(iseq->callinfo_entries); RUBY_FREE_UNLESS_NULL(iseq->catch_table); RUBY_FREE_UNLESS_NULL(iseq->arg_opt_table); @@ -142,7 +142,7 @@ iseq_memsize(const void *ptr) size += iseq->local_table_size * sizeof(ID); size += iseq->catch_table_size * sizeof(struct iseq_catch_table_entry); size += iseq->arg_opts * sizeof(VALUE); - size += iseq->ic_size * sizeof(struct iseq_inline_cache_entry); + size += iseq->is_size * sizeof(union iseq_inline_storage_entry); size += iseq->callinfo_size * sizeof(rb_call_info_t); if (iseq->compile_data) { @@ -1200,7 +1200,7 @@ rb_insn_operand_intern(rb_iseq_t *iseq, break; case TS_IC: - ret = rb_sprintf("", (struct iseq_inline_cache_entry *)op - iseq->ic_entries); + ret = rb_sprintf("", (union iseq_inline_storage_entry *)op - iseq->is_entries); break; case TS_CALLINFO: @@ -1773,10 +1773,11 @@ iseq_data_to_ary(rb_iseq_t *iseq) rb_ary_push(ary, ID2SYM(entry->id)); } break; - case TS_IC: { - struct iseq_inline_cache_entry *ic = (struct iseq_inline_cache_entry *)*seq; - rb_ary_push(ary, INT2FIX(ic - iseq->ic_entries)); - } + case TS_IC: + { + union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq; + rb_ary_push(ary, INT2FIX(is - iseq->is_entries)); + } break; case TS_CALLINFO: { diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index c32b48fe4c..5982d23e1f 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -966,6 +966,45 @@ class TestRegexp < Test::Unit::TestCase } end + def test_once + pr1 = proc{|i| /#{i}/o} + assert_equal(/0/, pr1.call(0)) + assert_equal(/0/, pr1.call(1)) + assert_equal(/0/, pr1.call(2)) + + # recursive + pr2 = proc{|i| + if i > 0 + /#{pr2.call(i-1).to_s}#{i}/ + else + // + end + } + assert_equal(/(?-mix:(?-mix:(?-mix:)1)2)3/, pr2.call(3)) + + # multi-thread + m = Mutex.new + pr3 = proc{|i| + /#{m.unlock; sleep 0.5; i}/o + } + ary = [] + n = 0 + th1 = Thread.new{m.lock; ary << pr3.call(n+=1)} + th2 = Thread.new{m.lock; ary << pr3.call(n+=1)} + th1.join; th2.join + assert_equal([/1/, /1/], ary) + + # escape + pr4 = proc{|i| + catch(:xyzzy){ + /#{throw :xyzzy, i}/o + :ng + } + } + assert_equal(0, pr4.call(0)) + assert_equal(1, pr4.call(1)) + end + # This assertion is for porting x2() tests in testpy.py of Onigmo. def assert_match_at(re, str, positions, msg = nil) re = Regexp.new(re) unless re.is_a?(Regexp) diff --git a/vm.c b/vm.c index 8dd71e2b3d..142fc91d2e 100644 --- a/vm.c +++ b/vm.c @@ -2128,27 +2128,20 @@ m_core_undef_method(VALUE self, VALUE cbase, VALUE sym) } static VALUE -m_core_set_postexe(VALUE self, VALUE iseqval) +m_core_set_postexe(VALUE self, VALUE block_iseqval, VALUE is_index_val) { + int is_index = FIX2INT(is_index_val); + rb_thread_t *th = GET_THREAD(); + rb_iseq_t *iseq = rb_vm_get_ruby_level_next_cfp(th, th->cfp)->iseq; + union iseq_inline_storage_entry *is = &iseq->is_entries[is_index]; + REWIND_CFP({ - rb_iseq_t *blockiseq; - rb_block_t *blockptr; - rb_thread_t *th = GET_THREAD(); - rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); - VALUE proc; - - if (cfp == 0) { - rb_bug("m_core_set_postexe: unreachable"); + if (is->once.done != Qtrue) { + rb_iseq_t *block_iseq; + GetISeqPtr(block_iseqval, block_iseq); + rb_set_end_proc(rb_call_end_proc, vm_make_proc_with_iseq(block_iseq)); + is->once.done = Qtrue; } - - GetISeqPtr(iseqval, blockiseq); - - blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); - blockptr->iseq = blockiseq; - blockptr->proc = 0; - - proc = rb_vm_make_proc(th, blockptr, rb_cProc); - rb_set_end_proc(rb_call_end_proc, proc); }); return Qnil; } @@ -2291,7 +2284,7 @@ Init_VM(void) rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2); rb_define_method_id(klass, id_core_define_method, m_core_define_method, 3); rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3); - rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 1); + rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 2); rb_define_method_id(klass, id_core_hash_from_ary, m_core_hash_from_ary, 1); rb_define_method_id(klass, id_core_hash_merge_ary, m_core_hash_merge_ary, 2); rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1); diff --git a/vm_core.h b/vm_core.h index bba13168dc..fd0fa0bf3b 100644 --- a/vm_core.h +++ b/vm_core.h @@ -125,15 +125,25 @@ struct iseq_compile_data_ensure_node_stack; typedef struct rb_compile_option_struct rb_compile_option_t; + struct iseq_inline_cache_entry { VALUE ic_vmstat; VALUE ic_class; union { + size_t index; VALUE value; - long index; } ic_value; }; +union iseq_inline_storage_entry { + struct { + struct rb_thread_struct *running_thread; + VALUE value; + VALUE done; + } once; + struct iseq_inline_cache_entry cache; +}; + /* to avoid warning */ struct rb_thread_struct; struct rb_control_frame_struct; @@ -224,8 +234,8 @@ struct rb_iseq_struct { /* sizeof(vars) + 1 */ int local_size; - struct iseq_inline_cache_entry *ic_entries; - int ic_size; + union iseq_inline_storage_entry *is_entries; + int is_size; rb_call_info_t *callinfo_entries; int callinfo_size; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index c3a243f815..bb72b88b82 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -512,7 +512,7 @@ vm_getivar(VALUE obj, ID id, IC ic, rb_call_info_t *ci, int is_attr) if (LIKELY((!is_attr && (ic->ic_class == klass && ic->ic_vmstat == GET_VM_STATE_VERSION())) || (is_attr && ci->aux.index > 0))) { - long index = !is_attr ? ic->ic_value.index : ci->aux.index - 1; + long index = !is_attr ? (long)ic->ic_value.index : ci->aux.index - 1; long len = ROBJECT_NUMIV(obj); VALUE *ptr = ROBJECT_IVPTR(obj); @@ -568,7 +568,7 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, rb_call_info_t *ci, int is_attr) if (LIKELY( (!is_attr && ic->ic_class == klass && ic->ic_vmstat == GET_VM_STATE_VERSION()) || (is_attr && ci->aux.index > 0))) { - long index = !is_attr ? ic->ic_value.index : ci->aux.index-1; + long index = !is_attr ? (long)ic->ic_value.index : ci->aux.index-1; long len = ROBJECT_NUMIV(obj); VALUE *ptr = ROBJECT_IVPTR(obj); @@ -2351,3 +2351,36 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci return val; } } + +static VALUE +vm_make_proc_with_iseq(rb_iseq_t *blockiseq) +{ + rb_block_t *blockptr; + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); + + if (cfp == 0) { + rb_bug("m_core_set_postexe: unreachable"); + } + + blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); + blockptr->iseq = blockiseq; + blockptr->proc = 0; + + return rb_vm_make_proc(th, blockptr, rb_cProc); +} + +static VALUE +vm_once_exec(rb_iseq_t *iseq) +{ + VALUE proc = vm_make_proc_with_iseq(iseq); + return rb_proc_call_with_block(proc, 0, 0, Qnil); +} + +static VALUE +vm_once_clear(VALUE data) +{ + union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)data; + is->once.running_thread = NULL; + return Qnil; +}