summaryrefslogtreecommitdiff
path: root/vm_eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm_eval.c')
-rw-r--r--vm_eval.c288
1 files changed, 219 insertions, 69 deletions
diff --git a/vm_eval.c b/vm_eval.c
index 8eb1d5c84c..c75e7d76d3 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -48,7 +48,22 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
struct rb_calling_info calling = {
.ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
.cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme),
- .block_handler = VM_BLOCK_HANDLER_NONE,
+ .block_handler = vm_passed_block_handler(ec),
+ .recv = recv,
+ .argc = argc,
+ .kw_splat = kw_splat,
+ };
+
+ return vm_call0_body(ec, &calling, argv);
+}
+
+static inline VALUE
+vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callcache *cc, int kw_splat)
+{
+ struct rb_calling_info calling = {
+ .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
+ .cc = cc,
+ .block_handler = vm_passed_block_handler(ec),
.recv = recv,
.argc = argc,
.kw_splat = kw_splat,
@@ -58,12 +73,37 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
}
static VALUE
+vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme)
+{
+ calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme);
+ return vm_call0_body(ec, calling, argv);
+}
+
+static VALUE
+vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, VALUE klass, enum method_missing_reason ex)
+{
+ ID mid = vm_ci_mid(calling->ci);
+ klass = RCLASS_SUPER(klass);
+
+ if (klass) {
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
+
+ if (cme) {
+ RUBY_VM_CHECK_INTS(ec);
+ return vm_call0_cme(ec, calling, argv, cme);
+ }
+ }
+
+ vm_passed_block_handler_set(ec, calling->block_handler);
+ return method_missing(ec, calling->recv, mid, calling->argc, argv, ex, calling->kw_splat);
+}
+
+static VALUE
vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv)
{
const struct rb_callinfo *ci = calling->ci;
- const struct rb_callcache *cc = calling->cc;
VALUE val;
- const rb_callable_method_entry_t *me = vm_cc_cme(cc);
+ const rb_callable_method_entry_t *me = vm_cc_cme(calling->cc);
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
int len = cfunc->argc;
VALUE recv = calling->recv;
@@ -115,12 +155,8 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
{
const struct rb_callinfo *ci = calling->ci;
const struct rb_callcache *cc = calling->cc;
-
VALUE ret;
- calling->block_handler = vm_passed_block_handler(ec);
-
- again:
switch (vm_cc_cme(cc)->def->type) {
case VM_METHOD_TYPE_ISEQ:
{
@@ -169,38 +205,27 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
ret = vm_call_bmethod_body(ec, calling, argv);
goto success;
case VM_METHOD_TYPE_ZSUPER:
+ {
+ VALUE klass = RCLASS_ORIGIN(vm_cc_cme(cc)->defined_class);
+ return vm_call0_super(ec, calling, argv, klass, MISSING_SUPER);
+ }
case VM_METHOD_TYPE_REFINED:
{
- const rb_method_type_t type = vm_cc_cme(cc)->def->type;
- VALUE super_class = vm_cc_cme(cc)->defined_class;
-
- if (type == VM_METHOD_TYPE_ZSUPER) {
- super_class = RCLASS_ORIGIN(super_class);
- }
- else if (vm_cc_cme(cc)->def->body.refined.orig_me) {
- vm_cc_cme_set(cc, refined_method_callable_without_refinement(vm_cc_cme(cc)));
- goto again;
- }
+ const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
- super_class = RCLASS_SUPER(super_class);
- if (super_class) {
- vm_cc_cme_set(cc, rb_callable_method_entry(super_class, vm_ci_mid(ci)));
- if (vm_cc_cme(cc)) {
- RUBY_VM_CHECK_INTS(ec);
- goto again;
- }
+ if (cme->def->body.refined.orig_me) {
+ const rb_callable_method_entry_t *orig_cme = refined_method_callable_without_refinement(cme);
+ return vm_call0_cme(ec, calling, argv, orig_cme);
}
- enum method_missing_reason ex = (type == VM_METHOD_TYPE_ZSUPER) ? MISSING_SUPER : 0;
- ret = method_missing(ec, calling->recv, vm_ci_mid(ci), calling->argc, argv, ex, calling->kw_splat);
- goto success;
+ VALUE klass = cme->defined_class;
+ return vm_call0_super(ec, calling, argv, klass, 0);
}
case VM_METHOD_TYPE_ALIAS:
- vm_cc_cme_set(cc, aliased_callable_method_entry(vm_cc_cme(cc)));
- goto again;
+ return vm_call0_cme(ec, calling, argv, aliased_callable_method_entry(vm_cc_cme(cc)));
case VM_METHOD_TYPE_MISSING:
{
- vm_passed_block_handler_set(ec, calling->block_handler);
+ vm_passed_block_handler_set(ec, calling->block_handler);
return method_missing(ec, calling->recv, vm_ci_mid(ci), calling->argc,
argv, MISSING_NOENTRY, calling->kw_splat);
}
@@ -312,9 +337,106 @@ rb_check_stack_overflow(void)
if (ec) stack_check(ec);
}
+NORETURN(static void uncallable_object(VALUE recv, ID mid));
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
+static const struct rb_callcache *
+cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme)
+{
+ const struct rb_callcache *cc;
+
+ RB_VM_LOCK_ENTER();
+ {
+ struct rb_class_cc_entries *ccs;
+ struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
+
+ if (rb_id_table_lookup(cc_tbl, mid, (VALUE*)&ccs)) {
+ // ok
+ }
+ else {
+ ccs = vm_ccs_create(klass, cme);
+ rb_id_table_insert(cc_tbl, mid, (VALUE)ccs);
+ }
+
+ if (ccs->len > 0) {
+ cc = ccs->entries[0].cc;
+ }
+ else {
+ const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, false); // TODO: proper ci
+ cc = vm_cc_new(klass, cme, vm_call_general);
+ METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
+ vm_ccs_push(klass, ccs, ci, cc);
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+
+ return cc;
+}
+
+static VALUE
+gccct_hash(VALUE klass, ID mid)
+{
+ return (klass >> 3) ^ (VALUE)mid;
+}
+
+NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index));
+
+static const struct rb_callcache *
+gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)
+{
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
+ const struct rb_callcache *cc;
+
+ if (cme != NULL) {
+ cc = cc_new(klass, mid, argc, cme);
+ }
+ else {
+ cc = NULL;
+ }
+
+ return vm->global_cc_cache_table[index] = cc;
+}
+
+static inline const struct rb_callcache *
+gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
+{
+ VALUE klass;
+
+ if (!SPECIAL_CONST_P(recv)) {
+ klass = RBASIC_CLASS(recv);
+ if (UNLIKELY(!klass)) uncallable_object(recv, mid);
+ }
+ else {
+ klass = CLASS_OF(recv);
+ }
+
+ // search global method cache
+ unsigned int index = (unsigned int)(gccct_hash(klass, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE);
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
+ const struct rb_callcache *cc = vm->global_cc_cache_table[index];
+
+ if (LIKELY(cc)) {
+ if (LIKELY(vm_cc_class_check(cc, klass))) {
+ const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
+ if (LIKELY(!METHOD_ENTRY_INVALIDATED(cme) &&
+ cme->called_id == mid)) {
+
+ VM_ASSERT(vm_cc_cme(cc) == rb_callable_method_entry(klass, mid));
+ RB_DEBUG_COUNTER_INC(gccct_hit);
+
+ return cc;
+ }
+ }
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(gccct_null);
+ }
+
+ RB_DEBUG_COUNTER_INC(gccct_miss);
+ return gccct_method_search_slowpath(vm, klass, mid, argc, index);
+}
+
/*!
* \internal
* calls the specified method.
@@ -336,7 +458,6 @@ rb_call0(rb_execution_context_t *ec,
VALUE recv, ID mid, int argc, const VALUE *argv,
call_type call_scope, VALUE self)
{
- const rb_callable_method_entry_t *me;
enum method_missing_reason call_status;
call_type scope = call_scope;
int kw_splat = RB_NO_KEYWORDS;
@@ -354,21 +475,34 @@ rb_call0(rb_execution_context_t *ec,
break;
}
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+
if (scope == CALL_PUBLIC) {
RB_DEBUG_COUNTER_INC(call0_public);
- me = rb_callable_method_entry_with_refinements(CLASS_OF(recv), mid, NULL);
+
+ const rb_callable_method_entry_t *cc_cme = cc ? vm_cc_cme(cc) : NULL;
+ const rb_callable_method_entry_t *cme = callable_method_entry_refeinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
+ call_status = rb_method_call_status(ec, cme, scope, self);
+
+ if (UNLIKELY(call_status != MISSING_NONE)) {
+ return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ }
+ else if (UNLIKELY(cc_cme != cme)) { // refinement is solved
+ stack_check(ec);
+ return rb_vm_call_kw(ec, recv, mid, argc, argv, cme, kw_splat);
+ }
}
else {
RB_DEBUG_COUNTER_INC(call0_other);
- me = rb_search_method_entry(recv, mid);
- }
- call_status = rb_method_call_status(ec, me, scope, self);
+ call_status = rb_method_call_status(ec, cc ? vm_cc_cme(cc) : NULL, scope, self);
- if (call_status != MISSING_NONE) {
- return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ if (UNLIKELY(call_status != MISSING_NONE)) {
+ return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ }
}
+
stack_check(ec);
- return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat);
+ return vm_call0_cc(ec, recv, mid, argc, argv, cc, kw_splat);
}
struct rescue_funcall_args {
@@ -586,7 +720,6 @@ rb_type_str(enum ruby_value_type type)
return NULL;
}
-NORETURN(static void uncallable_object(VALUE recv, ID mid));
static void
uncallable_object(VALUE recv, ID mid)
{
@@ -860,6 +993,45 @@ method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VAL
#ifndef MJIT_HEADER
+static inline VALUE
+rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
+{
+ rb_execution_context_t *ec = GET_EC();
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+ VALUE self = ec->cfp->self;
+
+ if (LIKELY(cc) &&
+ LIKELY(rb_method_call_status(ec, vm_cc_cme(cc), scope, self) == MISSING_NONE)) {
+ // fastpath
+ return vm_call0_cc(ec, recv, mid, argc, argv, cc, false);
+ }
+ else {
+ return rb_call0(ec, recv, mid, argc, argv, scope, self);
+ }
+}
+
+#ifdef rb_funcallv
+#undef rb_funcallv
+#endif
+/*!
+ * Calls a method
+ * \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(VALUE recv, ID mid, int argc, const VALUE *argv)
+{
+ return rb_funcallv_scope(recv, mid, argc, argv, CALL_FCALL);
+}
+
+VALUE
+rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
+{
+ return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
+}
+
/*!
* Calls a method
* \param recv receiver of the method
@@ -885,7 +1057,8 @@ rb_apply(VALUE recv, ID mid, VALUE args)
}
argv = ALLOCA_N(VALUE, argc);
MEMCPY(argv, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, argc);
- return rb_call(recv, mid, argc, argv, CALL_FCALL);
+
+ return rb_funcallv(recv, mid, argc, argv);
}
#ifdef rb_funcall
@@ -921,29 +1094,7 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
else {
argv = 0;
}
- 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
- * \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(VALUE recv, ID mid, int argc, const VALUE *argv)
-{
- return rb_call(recv, mid, argc, argv, CALL_FCALL);
-}
-
-VALUE
-rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
-{
- return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
+ return rb_funcallv(recv, mid, n, argv);
}
/*!
@@ -973,7 +1124,6 @@ rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VA
return Qundef;
}
-
/*!
* Calls a method.
*
@@ -986,7 +1136,7 @@ rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VA
VALUE
rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
{
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_scope(recv, mid, argc, argv, CALL_PUBLIC);
}
VALUE
@@ -999,7 +1149,7 @@ VALUE
rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
{
PASS_PASSED_BLOCK_HANDLER();
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_public(recv, mid, argc, argv);
}
VALUE
@@ -1016,7 +1166,7 @@ rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE pas
vm_passed_block_handler_set(GET_EC(), passed_procval);
}
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_public(recv, mid, argc, argv);
}
VALUE
@@ -1191,10 +1341,10 @@ VALUE
rb_yield(VALUE val)
{
if (val == Qundef) {
- return rb_yield_0(0, 0);
+ return rb_yield_0(0, NULL);
}
else {
- return rb_yield_1(val);
+ return rb_yield_0(1, &val);
}
}