diff --git a/benchmark/vm2_poly_same_method.yml b/benchmark/vm2_poly_same_method.yml new file mode 100644 index 00000000000000..867c433cf83aa3 --- /dev/null +++ b/benchmark/vm2_poly_same_method.yml @@ -0,0 +1,25 @@ +prelude: | + module AR; end + class AR::Base + def create_or_update + nil + end + def save + create_or_update + end + end + class Foo < AR::Base; end + class Bar < AR::Base; end + o1 = Foo.new + o2 = Bar.new +benchmark: + vm2_poly_same_method: | + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; +loop_count: 6000000 diff --git a/insns.def b/insns.def index e3edc5e29faa88..f365106e2c8961 100644 --- a/insns.def +++ b/insns.def @@ -911,7 +911,7 @@ invokeblock // attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci); { static struct rb_call_cache cc = { - 0, 0, NULL, vm_invokeblock_i, + 0, 0, NULL, NULL, vm_invokeblock_i, }; VALUE bh = VM_BLOCK_HANDLER_NONE; diff --git a/internal.h b/internal.h index 3dbe9ad38e6316..2e8d23b08596f1 100644 --- a/internal.h +++ b/internal.h @@ -2328,6 +2328,7 @@ enum method_missing_reason { MISSING_NONE = 0x40 }; struct rb_callable_method_entry_struct; +struct rb_method_definition_struct; struct rb_execution_context_struct; struct rb_control_frame_struct; struct rb_calling_info; @@ -2339,6 +2340,7 @@ struct rb_call_cache { /* inline cache: values */ const struct rb_callable_method_entry_struct *me; + const struct rb_method_definition_struct *def; VALUE (*call)(struct rb_execution_context_struct *ec, struct rb_control_frame_struct *cfp, diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 352d38fe455a68..66c50cdf599054 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1374,16 +1374,41 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag) static VALUE vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc); +#ifdef __has_attribute +#if __has_attribute(artificial) +__attribute__((__artificial__)) +#endif +#endif +static inline vm_call_handler +calccall(const struct rb_call_cache *cc, const rb_callable_method_entry_t *me) +{ + if (UNLIKELY(!me)) { + return vm_call_general; /* vm_call_method_nome() situation */ + } + else if (LIKELY(cc->me != me)) { + return vm_call_general; /* normal cases */ + } + else if (UNLIKELY(cc->def != me->def)) { + return vm_call_general; /* cc->me was refined elsewhere */ + } + else { + return cc->call; + } +} + MJIT_FUNC_EXPORTED void rb_vm_search_method_slowpath(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass) { - cc->me = rb_callable_method_entry(klass, ci->mid); + const rb_callable_method_entry_t *me = + rb_callable_method_entry(klass, ci->mid); + *cc = (struct rb_call_cache) { + GET_GLOBAL_METHOD_STATE(), + RCLASS_SERIAL(klass), + me, + me ? me->def : NULL, + calccall(cc, me), + }; VM_ASSERT(callable_method_entry_p(cc->me)); - cc->call = vm_call_general; -#if OPT_INLINE_METHOD_CACHE - cc->method_state = GET_GLOBAL_METHOD_STATE(); - cc->class_serial = RCLASS_SERIAL(klass); -#endif } static void