[前][次][番号順一覧][スレッド一覧]

ruby-changes:25241

From: ko1 <ko1@a...>
Date: Tue, 23 Oct 2012 13:22:44 +0900 (JST)
Subject: [ruby-changes:25241] ko1:r37293 (trunk): * vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):

ko1	2012-10-23 13:22:31 +0900 (Tue, 23 Oct 2012)

  New Revision: 37293

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37293

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/method.h
    trunk/vm.c
    trunk/vm_core.h
    trunk/vm_eval.c
    trunk/vm_insnhelper.c
    trunk/vm_method.c

Index: method.h
===================================================================
--- method.h	(revision 37292)
+++ method.h	(revision 37293)
@@ -49,7 +49,7 @@
 
 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;
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 37292)
+++ ChangeLog	(revision 37293)
@@ -1,3 +1,32 @@
+Tue Oct 23 12:57:29 2012  Koichi Sasada  <ko1@a...>
+
+	* 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 <aaron@t...>
 
 	* ext/psych/parser.c: just get the constant defined in Ruby.
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 37292)
+++ vm_core.h	(revision 37293)
@@ -166,7 +166,7 @@
 	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 @@
     /* 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_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)) { \
Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 37292)
+++ vm_eval.c	(revision 37293)
@@ -49,6 +49,84 @@
     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 @@
 	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]);
Index: vm_method.c
===================================================================
--- vm_method.c	(revision 37292)
+++ vm_method.c	(revision 37293)
@@ -304,7 +304,7 @@
 }
 
 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;
Index: vm.c
===================================================================
--- vm.c	(revision 37292)
+++ vm.c	(revision 37293)
@@ -2210,6 +2210,7 @@
 
     /* vm_backtrace.c */
     Init_vm_backtrace();
+    VM_PROFILE_ATEXIT();
 }
 
 void
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 37292)
+++ vm_insnhelper.c	(revision 37293)
@@ -1294,137 +1294,152 @@
 }
 
 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,12 +1447,90 @@
 
     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)
 {
     VALUE val = vm_getivar(ci->recv, ci->me->def->body.attr.id, 0, ci, 1);
@@ -1591,10 +1684,9 @@
 		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;

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]