summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/vm_call_bmethod.yml37
-rw-r--r--test/ruby/test_call.rb13
-rw-r--r--vm_insnhelper.c79
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)
{