diff --git a/benchmark/mjit_send_cfunc.yml b/benchmark/mjit_send_cfunc.yml new file mode 100644 index 0000000000..b5f9c897ec --- /dev/null +++ b/benchmark/mjit_send_cfunc.yml @@ -0,0 +1,6 @@ +prelude: | + def mjit_send_cfunc + self.class + end +benchmark: mjit_send_cfunc +loop_count: 100000000 diff --git a/tool/ruby_vm/views/_mjit_compile_send.erb b/tool/ruby_vm/views/_mjit_compile_send.erb index 0e033a4ebe..255648bc2d 100644 --- a/tool/ruby_vm/views/_mjit_compile_send.erb +++ b/tool/ruby_vm/views/_mjit_compile_send.erb @@ -15,15 +15,18 @@ % # compiler: Use captured cc to avoid race condition const struct rb_callcache *captured_cc = captured_cc_entries(status)[call_data_index(cd, body)]; % - const rb_iseq_t *iseq; +% # compiler: Inline send insn where some supported fastpath is used. + const rb_iseq_t *iseq = NULL; const CALL_INFO ci = cd->ci; - if (!status->compile_info->disable_send_cache && has_valid_method_type(captured_cc) - // CC_SET_FASTPATH in vm_callee_setup_arg - && !(vm_ci_flag(ci) & VM_CALL_TAILCALL) // inlining non-tailcall path - && vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_ISEQ - && fastpath_applied_iseq_p(ci, captured_cc, iseq = def_iseq_ptr(vm_cc_cme(captured_cc)->def))) { - - int param_size = iseq->body->param.size; + if (!status->compile_info->disable_send_cache && has_valid_method_type(captured_cc) && ( +% # `CC_SET_FASTPATH(cc, vm_call_cfunc, TRUE)` in `vm_call_method_each_type` + vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC +% # `CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(...), vm_call_iseq_optimizable_p(...))` in `vm_callee_setup_arg`, +% # and support only non-VM_CALL_TAILCALL path inside it + || (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_ISEQ + && fastpath_applied_iseq_p(ci, captured_cc, iseq = def_iseq_ptr(vm_cc_cme(captured_cc)->def)) + && !(vm_ci_flag(ci) & VM_CALL_TAILCALL)) + )) { int sp_inc = (int)sp_inc_of_sendish(ci); fprintf(f, "{\n"); @@ -40,7 +43,7 @@ <%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> % # JIT: If ISeq is inlinable, call the inlined method without pushing a frame. - if (status->inlined_iseqs != NULL && status->inlined_iseqs[pos] == iseq->body) { + if (iseq && status->inlined_iseqs != NULL && iseq->body == status->inlined_iseqs[pos]) { fprintf(f, " {\n"); fprintf(f, " VALUE orig_self = reg_cfp->self;\n"); fprintf(f, " reg_cfp->self = stack[%d];\n", b->stack_size + sp_inc - 1); @@ -49,7 +52,7 @@ fprintf(f, " }\n"); } else { -% # JIT: Forked `vm_sendish` to inline various things +% # JIT: Forked `vm_sendish` (except method_explorer = vm_search_method_wrap) to inline various things fprintf(f, " {\n"); fprintf(f, " VALUE val;\n"); fprintf(f, " struct rb_calling_info calling;\n"); @@ -58,20 +61,28 @@ % else fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"); % end - fprintf(f, " calling.argc = %d;\n", vm_ci_argc(ci)); + fprintf(f, " calling.kw_splat = %d;\n", IS_ARGS_KW_SPLAT(ci) > 0); fprintf(f, " calling.recv = stack[%d];\n", b->stack_size + sp_inc - 1); + fprintf(f, " calling.argc = %d;\n", vm_ci_argc(ci)); -% # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE - fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, cc_cme, 0, %d, %d);\n", param_size, iseq->body->local_table_size); - if (iseq->body->catch_except_p) { - fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); - fprintf(f, " val = vm_exec(ec, TRUE);\n"); + if (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC) { +% # TODO: optimize this more + fprintf(f, " CALL_DATA cd = (CALL_DATA)0x%"PRIxVALUE";\n", operands[0]); + fprintf(f, " val = vm_call_cfunc(ec, reg_cfp, &calling, cd);\n"); } - else { - fprintf(f, " if ((val = mjit_exec(ec)) == Qundef) {\n"); - fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); // This is vm_call0_body's code after vm_call_iseq_setup - fprintf(f, " val = vm_exec(ec, FALSE);\n"); - fprintf(f, " }\n"); + else { // VM_METHOD_TYPE_ISEQ +% # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE + fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, cc_cme, 0, %d, %d);\n", iseq->body->param.size, iseq->body->local_table_size); + if (iseq->body->catch_except_p) { + fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); + fprintf(f, " val = vm_exec(ec, TRUE);\n"); + } + else { + fprintf(f, " if ((val = mjit_exec(ec)) == Qundef) {\n"); + fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); // This is vm_call0_body's code after vm_call_iseq_setup + fprintf(f, " val = vm_exec(ec, FALSE);\n"); + fprintf(f, " }\n"); + } } fprintf(f, " stack[%d] = val;\n", b->stack_size + sp_inc - 1); fprintf(f, " }\n");