From 9eba45a72a63674e35d0e9dec6ddf73088ed39e8 Mon Sep 17 00:00:00 2001 From: ko1 Date: Tue, 23 Oct 2012 04:22:31 +0000 Subject: * vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME): add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'. This optimization makes all cfunc method calls `frameless', which is fster than ordinal cfunc method call. If `frame' is needed (for example, it calls another method with `rb_funcall()'), then build a frame. In other words, this optimization delays frame building. However, to delay the frame building, we need additional overheads: (1) Store the last call information. (2) Check the delayed frame buidling before the frame is needed. (3) Overhead to build a delayed frame. rb_thread_t::passed_ci is storage of delayed cfunc call information. (1) is lightweight because it is only 1 assignment to `passed_ci'. To achieve (2), we modify GET_THREAD() to check `passed_ci' every time. It causes 10% overhead on my envrionment. This optimization only works for cfunc methods which do not need their `frame'. After evaluation on my environment, this optimization does not effective every time. Because of this evaluation results, this optimization is disabled at default. * vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour of VM internals. I will extend this feature. * vm_method.c, method.h: change parameters of the `invoker' function. Receive `func' pointer as the first parameter. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 29 +++++++++ method.h | 2 +- vm.c | 1 + vm_core.h | 24 ++++++- vm_eval.c | 108 +++++++++++++++++++++++-------- vm_insnhelper.c | 196 +++++++++++++++++++++++++++++++++++++++++--------------- vm_method.c | 2 +- 7 files changed, 278 insertions(+), 84 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2fa0142f87..8fc021deff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +Tue Oct 23 12:57:29 2012 Koichi Sasada + + * vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME): + add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'. + This optimization makes all cfunc method calls `frameless', which + is fster than ordinal cfunc method call. + If `frame' is needed (for example, it calls another method with + `rb_funcall()'), then build a frame. In other words, this + optimization delays frame building. + However, to delay the frame building, we need additional overheads: + (1) Store the last call information. + (2) Check the delayed frame buidling before the frame is needed. + (3) Overhead to build a delayed frame. + rb_thread_t::passed_ci is storage of delayed cfunc call information. + (1) is lightweight because it is only 1 assignment to `passed_ci'. + To achieve (2), we modify GET_THREAD() to check `passed_ci' every + time. It causes 10% overhead on my envrionment. + This optimization only works for cfunc methods which do not need + their `frame'. + After evaluation on my environment, this optimization does not + effective every time. Because of this evaluation results, this + optimization is disabled at default. + + * vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour + of VM internals. I will extend this feature. + + * vm_method.c, method.h: change parameters of the `invoker' function. + Receive `func' pointer as the first parameter. + Tue Oct 23 06:21:05 2012 Aaron Patterson * ext/psych/parser.c: just get the constant defined in Ruby. diff --git a/method.h b/method.h index ba5899ca93..7f08ea261c 100644 --- a/method.h +++ b/method.h @@ -49,7 +49,7 @@ struct rb_call_info_struct; typedef struct rb_method_cfunc_struct { VALUE (*func)(ANYARGS); - VALUE (*invoker)(const struct rb_call_info_struct *ci, const VALUE *argv); + VALUE (*invoker)(VALUE (*func)(ANYARGS), const struct rb_call_info_struct *ci, const VALUE *argv); int argc; } rb_method_cfunc_t; diff --git a/vm.c b/vm.c index f8d59ba563..c47b5924f8 100644 --- a/vm.c +++ b/vm.c @@ -2210,6 +2210,7 @@ Init_VM(void) /* vm_backtrace.c */ Init_vm_backtrace(); + VM_PROFILE_ATEXIT(); } void diff --git a/vm_core.h b/vm_core.h index 5e68b8e9ab..e2d18c1aab 100644 --- a/vm_core.h +++ b/vm_core.h @@ -166,7 +166,7 @@ typedef struct rb_call_info_struct { int opt_pc; /* used by iseq */ long index; /* used by ivar */ int missing_reason; /* used by method_missing */ - VALUE (*func)(ANYARGS); /* used by cfunc */ + int inc_sp; /* used by cfunc */ } aux; VALUE (*call)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_call_info_struct *ci); @@ -478,6 +478,9 @@ typedef struct rb_thread_struct { /* for bmethod */ const rb_method_entry_t *passed_me; + /* for cfunc */ + rb_call_info_t *passed_ci; + /* for load(true) */ VALUE top_self; VALUE top_wrapper; @@ -816,7 +819,24 @@ extern rb_vm_t *ruby_current_vm; extern rb_event_flag_t ruby_vm_event_flags; #define GET_VM() ruby_current_vm -#define GET_THREAD() ruby_current_thread + +#ifndef OPT_CALL_CFUNC_WITHOUT_FRAME +#define OPT_CALL_CFUNC_WITHOUT_FRAME 0 +#endif + +static inline rb_thread_t * +GET_THREAD(void) +{ + rb_thread_t *th = ruby_current_thread; +#if OPT_CALL_CFUNC_WITHOUT_FRAME + if (UNLIKELY(th->passed_ci != 0)) { + void vm_call_cfunc_push_frame(rb_thread_t *th); + vm_call_cfunc_push_frame(th); + } +#endif + return th; +} + #define rb_thread_set_current_raw(th) (void)(ruby_current_thread = (th)) #define rb_thread_set_current(th) do { \ if ((th)->vm->running_thread != (th)) { \ diff --git a/vm_eval.c b/vm_eval.c index 0cf82b2020..f4208dee38 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -49,6 +49,84 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, return vm_call0_body(th, ci, argv); } +#if OPT_CALL_CFUNC_WITHOUT_FRAME +static VALUE +vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + VALUE val; + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); + { + rb_control_frame_t *reg_cfp = th->cfp; + const rb_method_entry_t *me = ci->me; + const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; + int len = cfunc->argc; + + if (len >= 0) rb_check_arity(ci->argc, len, len); + + th->passed_ci = ci; + ci->aux.inc_sp = 0; + VM_PROFILE_UP(2); + val = (*cfunc->invoker)(cfunc->func, ci, argv); + + if (reg_cfp == th->cfp) { + if (UNLIKELY(th->passed_ci != ci)) { + rb_bug("vm_call0_cfunc: passed_ci error (ci: %p, passed_ci: %p)", ci, th->passed_ci); + } + th->passed_ci = 0; + } + else { + if (reg_cfp != th->cfp + 1) { + rb_bug("vm_call0_cfunc: cfp consistency error"); + } + VM_PROFILE_UP(3); + vm_pop_frame(th); + } + } + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + + return val; +} +#else +static VALUE +vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + VALUE val; + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); + { + rb_control_frame_t *reg_cfp = th->cfp; + const rb_method_entry_t *me = ci->me; + const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; + int len = cfunc->argc; + + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, + ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), + 0, reg_cfp->sp, 1, ci->me); + + if (len >= 0) rb_check_arity(ci->argc, len, len); + + VM_PROFILE_UP(2); + val = (*cfunc->invoker)(cfunc->func, ci, argv); + + if (UNLIKELY(reg_cfp != th->cfp + 1)) { + rb_bug("vm_call0_cfunc_with_frame: cfp consistency error"); + } + VM_PROFILE_UP(3); + vm_pop_frame(th); + } + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + + return val; +} + +static VALUE +vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + return vm_call0_cfunc_with_frame(th, ci, argv); +} +#endif + /* `ci' should point temporal value (on stack value) */ static VALUE vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) @@ -84,35 +162,9 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) break; } case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_CFUNC: { - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); - { - rb_control_frame_t *reg_cfp = th->cfp; - rb_control_frame_t *cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, - ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), - 0, reg_cfp->sp, 1, ci->me); - - cfp->me = ci->me; - - { - const rb_method_entry_t *me = ci->me; - const rb_method_definition_t *def = me->def; - int len = def->body.cfunc.argc; - - if (len >= 0) rb_check_arity(ci->argc, len, len); - - ci->aux.func = def->body.cfunc.func; - val = (*def->body.cfunc.invoker)(ci, argv); - } - - if (reg_cfp != th->cfp + 1) { - rb_bug("cfp consistency error - call0"); - } - vm_pop_frame(th); - } - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + case VM_METHOD_TYPE_CFUNC: + val = vm_call0_cfunc(th, ci, argv); break; - } case VM_METHOD_TYPE_ATTRSET: { rb_check_arity(ci->argc, 1, 1); val = rb_ivar_set(ci->recv, ci->me->def->body.attr.id, argv[0]); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index ea5124c2bd..1ba1ce582b 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1294,137 +1294,152 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_in } static VALUE -call_cfunc_m2(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_m2(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, rb_ary_new4(ci->argc, argv)); + return (*func)(ci->recv, rb_ary_new4(ci->argc, argv)); } static VALUE -call_cfunc_m1(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_m1(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->argc, argv, ci->recv); + return (*func)(ci->argc, argv, ci->recv); } static VALUE -call_cfunc_0(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_0(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv); + return (*func)(ci->recv); } static VALUE -call_cfunc_1(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_1(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0]); + return (*func)(ci->recv, argv[0]); } static VALUE -call_cfunc_2(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_2(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1]); + return (*func)(ci->recv, argv[0], argv[1]); } static VALUE -call_cfunc_3(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_3(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2]); + return (*func)(ci->recv, argv[0], argv[1], argv[2]); } static VALUE -call_cfunc_4(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_4(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3]); } static VALUE -call_cfunc_5(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_5(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4]); } static VALUE -call_cfunc_6(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_6(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } static VALUE -call_cfunc_7(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_7(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } static VALUE -call_cfunc_8(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_8(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); } static VALUE -call_cfunc_9(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_9(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); } static VALUE -call_cfunc_10(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_10(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); } static VALUE -call_cfunc_11(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_11(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); } static VALUE -call_cfunc_12(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_12(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); } static VALUE -call_cfunc_13(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_13(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); } static VALUE -call_cfunc_14(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_14(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); } static VALUE -call_cfunc_15(const rb_call_info_t *ci, const VALUE *argv) +call_cfunc_15(VALUE (*func)(ANYARGS), const rb_call_info_t *ci, const VALUE *argv) { - return (ci->aux.func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); + return (*func)(ci->recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); } -static VALUE -vm_call_cfunc_call(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) -{ - reg_cfp->sp -= ci->argc + 1; - return (*ci->me->def->body.cfunc.invoker)(ci, reg_cfp->sp + 1); +#ifndef VM_PROFILE +#define VM_PROFILE 0 +#endif + +#if VM_PROFILE +static int vm_profile_counter[4]; +#define VM_PROFILE_UP(x) (vm_profile_counter[x]++) +#define VM_PROFILE_ATEXIT() atexit(vm_profile_show_result) +static void vm_profile_show_result(void) { + fprintf(stderr, "VM Profile results: \n"); + fprintf(stderr, "r->c call: %d\n", vm_profile_counter[0]); + fprintf(stderr, "r->c popf: %d\n", vm_profile_counter[1]); + fprintf(stderr, "c->c call: %d\n", vm_profile_counter[2]); + fprintf(stderr, "r->c popf: %d\n", vm_profile_counter[3]); } +#else +#define VM_PROFILE_UP(x) +#define VM_PROFILE_ATEXIT() +#endif static VALUE -vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { - volatile VALUE val = 0; + VALUE val; const rb_method_entry_t *me = ci->me; - const rb_method_definition_t *def = me->def; - int len = def->body.cfunc.argc; + const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; + int len = cfunc->argc; + VALUE recv = ci->recv; - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp, 1, me); if (len >= 0) rb_check_arity(ci->argc, len, len); - ci->aux.func = def->body.cfunc.func; - val = vm_call_cfunc_call(th, reg_cfp, ci); + reg_cfp->sp -= ci->argc + 1; + VM_PROFILE_UP(0); + val = (*cfunc->invoker)(cfunc->func, ci, reg_cfp->sp + 1); if (reg_cfp != th->cfp + 1) { rb_bug("vm_call_cfunc - cfp consistency error"); @@ -1432,11 +1447,89 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) vm_pop_frame(th); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); + + return val; +} + +#if OPT_CALL_CFUNC_WITHOUT_FRAME +static VALUE +vm_call_cfunc_latter(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + VALUE val; + int argc = ci->argc; + VALUE *argv = STACK_ADDR_FROM_TOP(argc); + const rb_method_cfunc_t *cfunc = &ci->me->def->body.cfunc; + + th->passed_ci = ci; + reg_cfp->sp -= argc + 1; + ci->aux.inc_sp = argc + 1; + VM_PROFILE_UP(0); + val = (*cfunc->invoker)(cfunc->func, ci, argv); + + /* check */ + if (reg_cfp == th->cfp) { /* no frame push */ + if (UNLIKELY(th->passed_ci != ci)) { + rb_bug("vm_call_cfunc_latter: passed_ci error (ci: %p, passed_ci: %p)", ci, th->passed_ci); + } + th->passed_ci = 0; + } + else { + if (UNLIKELY(reg_cfp != RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp))) { + rb_bug("vm_call_cfunc_latter: cfp consistency error (%p, %p)", reg_cfp, th->cfp+1); + } + vm_pop_frame(th); + VM_PROFILE_UP(1); + } + + return val; +} + +static VALUE +vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + VALUE val; + const rb_method_entry_t *me = ci->me; + int len = me->def->body.cfunc.argc; + VALUE recv = ci->recv; + + if (len >= 0) rb_check_arity(ci->argc, len, len); + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); + + if (!(ci->me->flag & NOEX_PROTECTED) && + !(ci->flag & VM_CALL_ARGS_SPLAT)) { + CI_SET_FASTPATH(ci, vm_call_cfunc_latter, 1); + } + val = vm_call_cfunc_latter(th, reg_cfp, ci); + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); return val; } +void +vm_call_cfunc_push_frame(rb_thread_t *th) +{ + rb_call_info_t *ci = th->passed_ci; + const rb_method_entry_t *me = ci->me; + th->passed_ci = 0; + + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp + ci->aux.inc_sp, 1, me); + + if (ci->call != vm_call_general) { + ci->call = vm_call_cfunc_with_frame; + } +} +#else /* OPT_CALL_CFUNC_WITHOUT_FRAME */ +static VALUE +vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + return vm_call_cfunc_with_frame(th, reg_cfp, ci); +} +#endif + static VALUE vm_call_ivar(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { @@ -1591,10 +1684,9 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) return vm_call_iseq_setup(th, cfp, ci); } case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_CFUNC:{ + case VM_METHOD_TYPE_CFUNC: CI_SET_FASTPATH(ci, vm_call_cfunc, enable_fastpath); return vm_call_cfunc(th, cfp, ci); - } case VM_METHOD_TYPE_ATTRSET:{ rb_check_arity(ci->argc, 0, 1); ci->aux.index = 0; diff --git a/vm_method.c b/vm_method.c index 6cec0ab1f8..8994fa2c2e 100644 --- a/vm_method.c +++ b/vm_method.c @@ -304,7 +304,7 @@ method_added(VALUE klass, ID mid) } static VALUE -(*call_cfunc_invoker_func(int argc))(const rb_call_info_t *, const VALUE *) +(*call_cfunc_invoker_func(int argc))(VALUE (*func)(ANYARGS), const rb_call_info_t *, const VALUE *) { switch (argc) { case -2: return call_cfunc_m2; -- cgit v1.2.3