1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

add rb_funcallv_with_cc()

Why not cache the method entry at each caller site.  The void**
is in fact a method entry, but this struct is hidden from ruby.h
so intentionally left opaque.
This commit is contained in:
Urabe, Shyouhei 2018-10-11 11:47:49 +09:00 committed by 卜部昌平
parent ccad34f453
commit 2a6457b5b7
Notes: git 2019-09-04 14:09:03 +09:00
2 changed files with 64 additions and 1 deletions

View file

@ -1892,6 +1892,10 @@ VALUE rb_funcallv_public(VALUE, ID, int, const VALUE*);
#define rb_funcall3 rb_funcallv_public
VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE);
#if GCC_VERSION_SINCE(3, 3, 0)
__attribute__((__nonnull__ (1))) /* TODO: should check using configure */
#endif
VALUE rb_funcallv_with_cc(void**, VALUE, ID, int, const VALUE*);
int rb_scan_args(int, const VALUE*, const char*, ...);
VALUE rb_call_super(int, const VALUE*);
VALUE rb_current_receiver(void);
@ -2607,10 +2611,19 @@ __extension__({ \
const VALUE rb_funcall_args[] = {__VA_ARGS__}; \
const int rb_funcall_nargs = \
(int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \
rb_funcallv(recv, mid, \
static void *rb_funcall_opaque_cc = NULL; \
rb_funcallv_with_cc(&rb_funcall_opaque_cc, \
recv, mid, \
rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \
rb_funcall_nargs ? rb_funcall_args : NULL); \
})
# define rb_funcallv(recv, mid, argc, argv) \
__extension__({ \
static void *rb_funcallv_opaque_cc = NULL; \
rb_funcallv_with_cc(&rb_funcallv_opaque_cc, \
recv, mid, argc,argv); \
})
#endif
#ifndef RUBY_DONT_SUBST

View file

@ -800,7 +800,9 @@ rb_apply(VALUE recv, ID mid, VALUE args)
return rb_call(recv, mid, argc, argv, CALL_FCALL);
}
#ifdef rb_funcall
#undef rb_funcall
#endif
/*!
* Calls a method
* \param recv receiver of the method
@ -834,6 +836,9 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
return rb_call(recv, mid, n, argv, CALL_FCALL);
}
#ifdef rb_funcallv
#undef rb_funcallv
#endif
/*!
* Calls a method
* \param recv receiver of the method
@ -862,6 +867,51 @@ rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
}
/*!
* Calls a method
* \param ptr opaque call cache
* \param recv receiver of the method
* \param mid an ID that represents the name of the method
* \param argc the number of arguments
* \param argv pointer to an array of method arguments
*/
VALUE
rb_funcallv_with_cc(void **ptr, VALUE recv, ID mid, int argc, const VALUE *argv)
{
VM_ASSERT(ptr);
const VALUE klass = CLASS_OF(recv);
VM_ASSERT(klass != Qfalse);
VM_ASSERT(RBASIC_CLASS(klass) == 0 || rb_obj_is_kind_of(klass, rb_cClass));
rb_serial_t now = GET_GLOBAL_METHOD_STATE();
rb_serial_t serial = RCLASS_SERIAL(klass);
struct opaque {
const rb_callable_method_entry_t *me;
rb_serial_t updated_at;
rb_serial_t class_at;
ID mid;
} *cc;
if (UNLIKELY(! *ptr)) {
*ptr = ZALLOC(struct opaque);
}
cc = *ptr;
if (cc->updated_at != now ||
cc->class_at != serial ||
cc->mid != mid) {
*cc = (struct opaque) {
rb_callable_method_entry(klass, mid), now, serial, mid,
};
}
if (UNLIKELY(UNDEFINED_METHOD_ENTRY_P(cc->me))) {
/* :FIXME: this path can be made faster */
return rb_funcallv(recv, mid, argc, argv);
}
else {
return rb_vm_call0(GET_EC(), recv, mid, argc, argv, cc->me);
}
}
VALUE
rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
{