1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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?)



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42637 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2013-08-20 17:41:13 +00:00
parent a8fbb064a7
commit 30b1947df2
8 changed files with 187 additions and 91 deletions

View file

@ -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.

View file

@ -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:

View file

@ -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

15
iseq.c
View file

@ -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,9 +1773,10 @@ 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:

View file

@ -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)

29
vm.c
View file

@ -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;
int is_index = FIX2INT(is_index_val);
rb_thread_t *th = GET_THREAD();
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
VALUE proc;
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);

View file

@ -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;

View file

@ -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;
}