summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog21
-rw-r--r--compile.c50
-rw-r--r--insns.def67
-rw-r--r--iseq.c17
-rw-r--r--test/ruby/test_regexp.rb39
-rw-r--r--vm.c31
-rw-r--r--vm_core.h16
-rw-r--r--vm_insnhelper.c37
8 files changed, 187 insertions, 91 deletions
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 <ko1@atdot.net>
+
+ * 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 <ko1@atdot.net>
* 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++;
-
- ADD_INSN2(ret, line, onceinlinecache, lend, INT2FIX(ic_index));
- ADD_INSN(ret, line, pop);
+ 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);
- 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
@@ -1198,35 +1198,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
@j インラインキャッシュの値を設定する。
*/
@@ -1246,6 +1217,44 @@ setinlinecache
/**
@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
@j case 文で、可能なら表引きでジャンプする。
*/
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("<ic:%"PRIdPTRDIFF">", (struct iseq_inline_cache_entry *)op - iseq->ic_entries);
+ ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (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)
{
- 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;
+ 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];
- if (cfp == 0) {
- rb_bug("m_core_set_postexe: unreachable");
+ REWIND_CFP({
+ 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;
+}