зеркало из https://github.com/github/ruby.git
* 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:
Родитель
0dc5b8ce8c
Коммит
745c23b2d9
40
ChangeLog
40
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
|
||||
|
|
22
insns.def
22
insns.def
|
@ -1092,28 +1092,20 @@ 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");
|
||||
#else
|
||||
th->cfp++;
|
||||
vm_pop_frame(th);
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
vm_pop_frame(th);
|
||||
RESTORE_REGS();
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
/* deal with control flow 3: exception */
|
||||
|
|
50
vm.c
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)
|
||||
|
|
21
vm_dump.c
21
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
15
vm_exec.c
15
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");
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче