diff options
-rw-r--r-- | benchmark/vm_call_bmethod.yml | 37 | ||||
-rw-r--r-- | test/ruby/test_call.rb | 13 | ||||
-rw-r--r-- | vm_insnhelper.c | 79 |
3 files changed, 124 insertions, 5 deletions
diff --git a/benchmark/vm_call_bmethod.yml b/benchmark/vm_call_bmethod.yml new file mode 100644 index 0000000000..40136e5aa4 --- /dev/null +++ b/benchmark/vm_call_bmethod.yml @@ -0,0 +1,37 @@ +prelude: | + define_method(:a0){} + define_method(:a1){|a| a} + define_method(:s){|*a| a} + define_method(:b){|kw: 1| kw} + + t0 = 0.times.to_a + t1 = 1.times.to_a + t10 = 10.times.to_a + t100 = 100.times.to_a + kw = {kw: 2} +benchmark: + bmethod_simple_0: | + a0 + bmethod_simple_1: | + a1(1) + bmethod_simple_0_splat: | + a0(*t0) + bmethod_simple_1_splat: | + a1(*t1) + bmethod_no_splat: | + s + bmethod_0_splat: | + s(*t0) + bmethod_1_splat: | + s(*t1) + bmethod_10_splat: | + s(*t10) + bmethod_100_splat: | + s(*t100) + bmethod_kw: | + b(kw: 1) + bmethod_no_kw: | + b + bmethod_kw_splat: | + b(**kw) +loop_count: 6000000 diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb index 4d5a387f42..05a362e72f 100644 --- a/test/ruby/test_call.rb +++ b/test/ruby/test_call.rb @@ -100,6 +100,19 @@ class TestCall < Test::Unit::TestCase } end + def test_call_bmethod_proc + pr = proc{|sym| sym} + define_singleton_method(:a, &pr) + ary = [10] + assert_equal(10, a(*ary)) + + pr = proc{|*sym| sym} + define_singleton_method(:a, &pr) + ary = [10] + assert_equal([10], a(*ary)) + assert_equal([10], a(10)) + end + def test_call_splat_order bug12860 = '[ruby-core:77701] [Bug# 12860]' ary = [1, 2] diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 0bbae89498..264dedecb5 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3555,16 +3555,61 @@ vm_call_bmethod_body(rb_execution_context_t *ec, struct rb_calling_info *calling return val; } +static int vm_callee_setup_block_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_callinfo *ci, const rb_iseq_t *iseq, VALUE *argv, const enum arg_setup_type arg_setup_type); +static VALUE invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc); + static VALUE -vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) +vm_call_iseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { - RB_DEBUG_COUNTER_INC(ccf_bmethod); + RB_DEBUG_COUNTER_INC(ccf_iseq_bmethod); + + const struct rb_callcache *cc = calling->cc; + const rb_callable_method_entry_t *cme = vm_cc_cme(cc); + VALUE procv = cme->def->body.bmethod.proc; + + if (!RB_OBJ_SHAREABLE_P(procv) && + cme->def->body.bmethod.defined_ractor != rb_ractor_self(rb_ec_ractor_ptr(ec))) { + rb_raise(rb_eRuntimeError, "defined with an un-shareable Proc in a different Ractor"); + } + + rb_proc_t *proc; + GetProcPtr(procv, proc); + const struct rb_block *block = &proc->block; + + while (vm_block_type(block) == block_type_proc) { + block = vm_proc_block(block->as.proc); + } + VM_ASSERT(vm_block_type(block) == block_type_iseq); + + const struct rb_captured_block *captured = &block->as.captured; + const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq); + int i, opt_pc; + + VALUE *sp = cfp->sp - calling->argc - 1; + for (i = 0; i < calling->argc; i++) { + sp[i] = sp[i+1]; + } + + if (vm_ci_flag(calling->ci) & VM_CALL_ARGS_SIMPLE) { + opt_pc = vm_callee_setup_block_arg(ec, calling, calling->ci, iseq, sp, arg_setup_method); + } + else { + opt_pc = setup_parameters_complex(ec, iseq, calling, calling->ci, sp, arg_setup_method); + } + + cfp->sp = sp; + return invoke_bmethod(ec, iseq, calling->recv, captured, cme, + VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_LAMBDA, opt_pc); +} + +static VALUE +vm_call_noniseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) +{ + RB_DEBUG_COUNTER_INC(ccf_noniseq_bmethod); VALUE *argv; int argc; - const struct rb_callinfo *ci = calling->ci; - - CALLER_SETUP_ARG(cfp, calling, ci, ALLOW_HEAP_ARGV); + CALLER_SETUP_ARG(cfp, calling, calling->ci, ALLOW_HEAP_ARGV); if (UNLIKELY(calling->heap_argv)) { argv = RARRAY_PTR(calling->heap_argv); cfp->sp -= 2; @@ -3579,6 +3624,30 @@ vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_c return vm_call_bmethod_body(ec, calling, argv); } +static VALUE +vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) +{ + RB_DEBUG_COUNTER_INC(ccf_bmethod); + + const struct rb_callcache *cc = calling->cc; + const rb_callable_method_entry_t *cme = vm_cc_cme(cc); + VALUE procv = cme->def->body.bmethod.proc; + rb_proc_t *proc; + GetProcPtr(procv, proc); + const struct rb_block *block = &proc->block; + + while (vm_block_type(block) == block_type_proc) { + block = vm_proc_block(block->as.proc); + } + if (vm_block_type(block) == block_type_iseq) { + CC_SET_FASTPATH(cc, vm_call_iseq_bmethod, TRUE); + return vm_call_iseq_bmethod(ec, cfp, calling); + } + + CC_SET_FASTPATH(cc, vm_call_noniseq_bmethod, TRUE); + return vm_call_noniseq_bmethod(ec, cfp, calling); +} + VALUE rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner) { |