* 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
This commit is contained in:
ko1 2012-06-15 10:22:34 +00:00
Родитель 0dc5b8ce8c
Коммит 745c23b2d9
8 изменённых файлов: 76 добавлений и 89 удалений

Просмотреть файл

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

Просмотреть файл

@ -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();
}
}
/**********************************************************/

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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