diff --git a/ChangeLog b/ChangeLog index ed06937658..068fe4438d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +Fri Jun 15 19:22:13 2012 Koichi Sasada + + * 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 * lib/test/unit.rb (Test::Unit::Runner#puke): always add skipped diff --git a/insns.def b/insns.def index 379dbfba03..e256a83758 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 7a7de59f3a..5aa0e392f4 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 d2e519bbb6..47ae7f1dee 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 aad5e1c310..c0c9ac2ba4 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 d38f114b86..68b380b49c 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 = ""; } @@ -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 31511b986f..457143c2bc 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 fdfaa29823..e72bc487df 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"); }