summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-15 10:22:34 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-15 10:22:34 +0000
commit745c23b2d981e23024f044e934967bc6dae66d80 (patch)
tree9fd909ad9129bf5b11bbe1cbf77a11b6d9fc37b8
parent0dc5b8ce8c11d196ed44d333c4bfae8c7f2d0bac (diff)
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type).
Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog40
-rw-r--r--insns.def26
-rw-r--r--vm.c50
-rw-r--r--vm_backtrace.c3
-rw-r--r--vm_core.h3
-rw-r--r--vm_dump.c21
-rw-r--r--vm_eval.c7
-rw-r--r--vm_exec.c15
8 files changed, 76 insertions, 89 deletions
diff --git a/ChangeLog b/ChangeLog
index ed06937..068fe44 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+Fri Jun 15 19:22:13 2012 Koichi Sasada <ko1@atdot.net>
+
+ * vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type).
+ Before this commit:
+ `finish frame' was place holder which indicates that VM loop
+ needs to return function.
+ If a C method calls a Ruby methods (a method written by Ruby),
+ then VM loop will be (re-)invoked. When the Ruby method returns,
+ then also VM loop should be escaped. `finish frame' has only
+ one instruction `finish', which returns VM loop function.
+ VM loop function executes `finish' instruction, then VM loop
+ function returns itself.
+ With such mechanism, `leave' instruction (which returns one
+ frame from current scope) doesn't need to check that this `leave'
+ should also return from VM loop function.
+ Strictly, one branch can be removed from `leave' instructon.
+ Consideration:
+ However, pushing the `finish frame' needs costs because
+ it needs several memory accesses. The number of pushing
+ `finish frame' is greater than I had assumed. Of course,
+ pushing `finish frame' consumes additional control frame.
+ Moreover, recent processors has good branch prediction,
+ with which we can ignore such trivial checking.
+ After this commit:
+ Finally, I decide to remove `finish frame' and `finish'
+ instruction. Some parts of VM depend on `finish frame',
+ so the new frame flag VM_FRAME_FLAG_FINISH is introduced.
+ If this frame should escape from VM function loop, then
+ the result of VM_FRAME_TYPE_FINISH_P(cfp) is true.
+ `leave' instruction checks this flag every time.
+ I measured performance on it. However on my environments,
+ it improves some benchmarks and slows some benchmarks down.
+ Maybe it is because of C compiler optimization parameters.
+ I'll re-visit here if this cause problems.
+
+ * insns.def (leave, finish): remove finish instruction.
+
+ * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c:
+ apply above changes.
+
Fri Jun 15 19:11:23 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* lib/test/unit.rb (Test::Unit::Runner#puke): always add skipped
diff --git a/insns.def b/insns.def
index 379dbfb..e256a83 100644
--- a/insns.def
+++ b/insns.def
@@ -1092,27 +1092,19 @@ leave
}
RUBY_VM_CHECK_INTS();
- vm_pop_frame(th);
- RESTORE_REGS();
-}
-/**
- @c method/iterator
- @e return from this vm loop
- @j VM loop から抜ける。
- */
-DEFINE_INSN
-finish
-()
-(VALUE val)
-(VALUE val)
-{
+ if (UNLIKELY(VM_FRAME_TYPE_FINISH_P(GET_CFP()))) {
#if OPT_CALL_THREADED_CODE
- rb_bug("unused instruction on OPT_CALL_THREADED_CODE");
+ rb_bug("unused instruction on OPT_CALL_THREADED_CODE");
#else
- th->cfp++;
- return val;
+ vm_pop_frame(th);
+ return val;
#endif
+ }
+ else {
+ vm_pop_frame(th);
+ RESTORE_REGS();
+ }
}
/**********************************************************/
diff --git a/vm.c b/vm.c
index 7a7de59..5aa0e39 100644
--- a/vm.c
+++ b/vm.c
@@ -123,16 +123,6 @@ rb_vm_inc_const_missing_count(void)
/* control stack frame */
-static inline VALUE
-rb_vm_set_finish_env(rb_thread_t * th)
-{
- vm_push_frame(th, 0, VM_FRAME_MAGIC_FINISH,
- Qnil, VM_ENVVAL_BLOCK_PTR(VM_CF_BLOCK_PTR(th->cfp)), 0,
- th->cfp->sp, 1);
- th->cfp->pc = (VALUE *)&finish_insn_seq[0];
- return Qtrue;
-}
-
static void
vm_set_top_stack(rb_thread_t * th, VALUE iseqval)
{
@@ -144,10 +134,8 @@ vm_set_top_stack(rb_thread_t * th, VALUE iseqval)
}
/* for return */
- rb_vm_set_finish_env(th);
-
CHECK_STACK_OVERFLOW(th->cfp, iseq->local_size + iseq->stack_max);
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP,
+ vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH,
th->top_self, VM_ENVVAL_BLOCK_PTR(0), iseq->iseq_encoded,
th->cfp->sp, iseq->local_size);
}
@@ -159,11 +147,8 @@ vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref)
rb_block_t * const block = th->base_block;
GetISeqPtr(iseqval, iseq);
- /* for return */
- rb_vm_set_finish_env(th);
-
CHECK_STACK_OVERFLOW(th->cfp, iseq->local_size + iseq->stack_max);
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL, block->self,
+ vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH, block->self,
VM_ENVVAL_PREV_EP_PTR(block->ep), iseq->iseq_encoded,
th->cfp->sp, iseq->local_size);
@@ -490,11 +475,6 @@ rb_vm_make_env_object(rb_thread_t * th, rb_control_frame_t *cfp)
{
VALUE envval;
- if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
- /* for method_missing */
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- }
-
envval = vm_make_env_each(th, cfp, cfp->ep, VM_CF_LEP(cfp));
if (PROCDEBUG) {
@@ -609,8 +589,6 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
int type = block_proc_is_lambda(block->proc) ?
VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK;
- rb_vm_set_finish_env(th);
-
cfp = th->cfp;
CHECK_STACK_OVERFLOW(cfp, argc + iseq->stack_max);
@@ -621,7 +599,7 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
type == VM_FRAME_MAGIC_LAMBDA);
- ncfp = vm_push_frame(th, iseq, type,
+ ncfp = vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH,
self, VM_ENVVAL_PREV_EP_PTR(block->ep),
iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, iseq->local_size - arg_size);
ncfp->me = th->passed_me;
@@ -1027,7 +1005,6 @@ vm_frametype_name(const rb_control_frame_t *cfp)
case VM_FRAME_MAGIC_BLOCK: return "block";
case VM_FRAME_MAGIC_CLASS: return "class";
case VM_FRAME_MAGIC_TOP: return "top";
- case VM_FRAME_MAGIC_FINISH: return "finish";
case VM_FRAME_MAGIC_CFUNC: return "cfunc";
case VM_FRAME_MAGIC_PROC: return "proc";
case VM_FRAME_MAGIC_IFUNC: return "ifunc";
@@ -1186,7 +1163,7 @@ vm_exec(rb_thread_t *th)
if (cfp->ep == escape_ep) {
if (state == TAG_RETURN) {
- if ((cfp + 1)->pc != &finish_insn_seq[0]) {
+ if (!VM_FRAME_TYPE_FINISH_P(cfp)) {
SET_THROWOBJ_CATCH_POINT(err, (VALUE)(cfp + 1)->ep);
SET_THROWOBJ_STATE(err, state = TAG_BREAK);
}
@@ -1205,7 +1182,7 @@ vm_exec(rb_thread_t *th)
if (!catch_iseqval) {
result = GET_THROWOBJ_VAL(err);
th->errinfo = Qnil;
- th->cfp += 2;
+ vm_pop_frame(th);
goto finish_vme;
}
}
@@ -1349,17 +1326,16 @@ vm_exec(rb_thread_t *th)
break;
}
- th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
-
- if (VM_FRAME_TYPE(th->cfp) != VM_FRAME_MAGIC_FINISH) {
- goto exception_handler;
- }
- else {
+ if (VM_FRAME_TYPE_FINISH_P(th->cfp)) {
vm_pop_frame(th);
th->errinfo = err;
TH_POP_TAG2();
JUMP_TAG(state);
}
+ else {
+ th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
+ goto exception_handler;
+ }
}
}
finish_vme:
@@ -1464,7 +1440,7 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg,
volatile VALUE iseqval = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
VALUE val;
- vm_push_frame(th, DATA_PTR(iseqval), VM_FRAME_MAGIC_TOP,
+ vm_push_frame(th, DATA_PTR(iseqval), VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH,
recv, VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1);
val = (*func)(arg);
@@ -1808,8 +1784,8 @@ th_init(rb_thread_t *th, VALUE self)
th->cfp = (void *)(th->stack + th->stack_size);
- vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_TOP, Qnil /* dummy self */,
- VM_ENVVAL_BLOCK_PTR(0), 0 /* dummy pc */, th->stack, 1);
+ vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH,
+ Qnil /* dummy self */, VM_ENVVAL_BLOCK_PTR(0), 0 /* dummy pc */, th->stack, 1);
th->status = THREAD_RUNNABLE;
th->errinfo = Qnil;
diff --git a/vm_backtrace.c b/vm_backtrace.c
index d2e519b..47ae7f1 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -379,8 +379,7 @@ backtrace_each(rb_thread_t *th,
start_cfp =
RUBY_VM_NEXT_CONTROL_FRAME(
- RUBY_VM_NEXT_CONTROL_FRAME(
- RUBY_VM_NEXT_CONTROL_FRAME(start_cfp))); /* skip top frames */
+ RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
if (start_cfp < last_cfp) {
size = 0;
diff --git a/vm_core.h b/vm_core.h
index aad5e1c..c0c9ac2 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -596,7 +596,6 @@ enum vm_special_object_type {
#define VM_FRAME_MAGIC_BLOCK 0x21
#define VM_FRAME_MAGIC_CLASS 0x31
#define VM_FRAME_MAGIC_TOP 0x41
-#define VM_FRAME_MAGIC_FINISH 0x51
#define VM_FRAME_MAGIC_CFUNC 0x61
#define VM_FRAME_MAGIC_PROC 0x71
#define VM_FRAME_MAGIC_IFUNC 0x81
@@ -609,6 +608,8 @@ enum vm_special_object_type {
/* other frame flag */
#define VM_FRAME_FLAG_PASSED 0x0100
+#define VM_FRAME_FLAG_FINISH 0x0200
+#define VM_FRAME_TYPE_FINISH_P(cfp) ((cfp)->flag & VM_FRAME_FLAG_FINISH)
#define RUBYVM_CFUNC_FRAME_P(cfp) \
(VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC)
diff --git a/vm_dump.c b/vm_dump.c
index d38f114..68b380b 100644
--- a/vm_dump.c
+++ b/vm_dump.c
@@ -32,7 +32,6 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
char ep_in_heap = ' ';
char posbuf[MAX_POSBUF+1];
int line = 0;
- int nopos = 0;
const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-";
VALUE tmp;
@@ -62,10 +61,6 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
case VM_FRAME_MAGIC_BLOCK:
magic = "BLOCK";
break;
- case VM_FRAME_MAGIC_FINISH:
- magic = "FINISH";
- nopos = 1;
- break;
case VM_FRAME_MAGIC_CFUNC:
magic = "CFUNC";
break;
@@ -97,10 +92,7 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
selfstr = "";
}
- if (nopos) {
- /* no name */
- }
- else if (cfp->iseq != 0) {
+ if (cfp->iseq != 0) {
if (RUBY_VM_IFUNC_P(cfp->iseq)) {
iseq_name = "<ifunc>";
}
@@ -130,9 +122,12 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
fprintf(stderr, "s:%04"PRIdPTRDIFF" b:%04"PRIdPTRDIFF" ", (cfp->sp - th->stack), bp);
fprintf(stderr, ep_in_heap == ' ' ? "e:%06"PRIdPTRDIFF" " : "e:%06"PRIxPTRDIFF" ", ep % 10000);
fprintf(stderr, "%-6s", magic);
- if (line && !nopos) {
+ if (line) {
fprintf(stderr, " %s", posbuf);
}
+ if (VM_FRAME_TYPE_FINISH_P(cfp)) {
+ fprintf(stderr, " [FINISH]");
+ }
if (0) {
fprintf(stderr, " \t");
fprintf(stderr, "iseq: %-24s ", iseq_name);
@@ -302,8 +297,8 @@ vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
(ptr - th->stack));
}
}
- else if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
- if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 2)) {
+ else if (VM_FRAME_TYPE_FINISH_P(VM_FRAME_TYPE(cfp))) {
+ if ((th)->stack + (th)->stack_size > (VALUE *)(cfp + 1)) {
vm_stack_dump_each(th, cfp + 1);
}
else {
@@ -350,7 +345,7 @@ rb_vmdebug_debug_print_pre(rb_thread_t *th, rb_control_frame_t *cfp)
{
rb_iseq_t *iseq = cfp->iseq;
- if (iseq != 0 && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_FINISH) {
+ if (iseq != 0 && !VM_FRAME_TYPE_FINISH_P(cfp)) {
VALUE *seq = iseq->iseq;
ptrdiff_t pc = cfp->pc - iseq->iseq_encoded;
int i;
diff --git a/vm_eval.c b/vm_eval.c
index 31511b9..457143c 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -12,7 +12,6 @@
**********************************************************************/
static inline VALUE method_missing(VALUE obj, ID id, int argc, const VALUE *argv, int call_status);
-static inline VALUE rb_vm_set_finish_env(rb_thread_t * th);
static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const NODE *cref);
static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv);
static NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr);
@@ -50,12 +49,9 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
again:
switch (def->type) {
case VM_METHOD_TYPE_ISEQ: {
- rb_control_frame_t *reg_cfp;
+ rb_control_frame_t *reg_cfp = th->cfp;
int i;
- rb_vm_set_finish_env(th);
- reg_cfp = th->cfp;
-
CHECK_STACK_OVERFLOW(reg_cfp, argc + 1);
*reg_cfp->sp++ = recv;
@@ -64,6 +60,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
}
vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */, me);
+ th->cfp->flag |= VM_FRAME_FLAG_FINISH;
val = vm_exec(th);
break;
}
diff --git a/vm_exec.c b/vm_exec.c
index fdfaa29..e72bc48 100644
--- a/vm_exec.c
+++ b/vm_exec.c
@@ -25,14 +25,6 @@
#endif
/* #define DECL_SC_REG(r, reg) VALUE reg_##r */
-#if OPT_STACK_CACHING
-static VALUE finish_insn_seq[1] = { BIN(finish_SC_ax_ax) };
-#elif OPT_CALL_THREADED_CODE
-static VALUE const finish_insn_seq[1] = { 0 };
-#else
-static VALUE finish_insn_seq[1] = { BIN(finish) };
-#endif
-
#if !OPT_CALL_THREADED_CODE
static VALUE
vm_exec_core(rb_thread_t *th, VALUE initial)
@@ -84,11 +76,6 @@ vm_exec_core(rb_thread_t *th, VALUE initial)
#if OPT_TOKEN_THREADED_CODE || OPT_DIRECT_THREADED_CODE
#include "vmtc.inc"
if (UNLIKELY(th == 0)) {
-#if OPT_STACK_CACHING
- finish_insn_seq[0] = (VALUE)&&LABEL (finish_SC_ax_ax);
-#else
- finish_insn_seq[0] = (VALUE)&&LABEL (finish);
-#endif
return (VALUE)insns_address_table;
}
#endif
@@ -145,7 +132,7 @@ vm_exec_core(rb_thread_t *th, VALUE initial)
}
}
- if (VM_FRAME_TYPE(th->cfp) != VM_FRAME_MAGIC_FINISH) {
+ if (VM_FRAME_TYPE_FINISH_P(th->cfp)) {
rb_bug("cfp consistency error");
}