mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Speedup block.call
[Feature #14330]
* insns.def (getblockparamproxy): introduce new instruction to return the `rb_block_param_proxy` object if possible. This object responds to `call` method and invoke given block (completely similar to `yield`). * method.h (OPTIMIZED_METHOD_TYPE_BLOCK_CALL): add new optimized call type which is for `rb_block_param_proxy.cal`. * vm_insnhelper.c (vm_call_method_each_type): ditto. * vm_insnhelper.c (vm_call_opt_block_call): ditto. * vm_core.h (BOP_CALL, PROC_REDEFINED_OP_FLAG): add check for `Proc#call` redefinition. * compile.c (iseq_compile_each0): compile to use new insn `getblockparamproxy` for method call. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61659 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
bb8f6ac0fe
commit
7fd1183467
7 changed files with 128 additions and 6 deletions
27
compile.c
27
compile.c
|
@ -1399,6 +1399,21 @@ iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int l
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
|
||||
{
|
||||
int level, ls;
|
||||
int idx = get_dyna_var_idx(iseq, id, &level, &ls);
|
||||
if (iseq_local_block_param_p(iseq, ls - idx, level)) {
|
||||
*pidx = ls - idx;
|
||||
*plevel = level;
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
|
||||
{
|
||||
|
@ -6159,7 +6174,17 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
#endif
|
||||
/* receiver */
|
||||
if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
|
||||
CHECK(COMPILE(recv, "recv", node->nd_recv));
|
||||
int idx, level;
|
||||
|
||||
if (mid == idCall &&
|
||||
nd_type(node->nd_recv) == NODE_LVAR &&
|
||||
iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) {
|
||||
ADD_INSN2(recv, nd_line(node->nd_recv), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
|
||||
}
|
||||
else {
|
||||
CHECK(COMPILE(recv, "recv", node->nd_recv));
|
||||
}
|
||||
|
||||
if (type == NODE_QCALL) {
|
||||
else_label = NEW_LABEL(line);
|
||||
end_label = NEW_LABEL(line);
|
||||
|
|
48
insns.def
48
insns.def
|
@ -127,6 +127,54 @@ setblockparam
|
|||
VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
@c variable
|
||||
@e Get special proxy object which only responds to `call` method if the block parameter
|
||||
represents a iseq/ifunc block. Otherwise, same as `getblockparam`.
|
||||
@j ブロックパラメータが iseq/ifunc ブロックであれば、特殊なプロキシオブジェクトを取得する。
|
||||
*/
|
||||
DEFINE_INSN
|
||||
getblockparamproxy
|
||||
(lindex_t idx, rb_num_t level)
|
||||
()
|
||||
(VALUE val)
|
||||
{
|
||||
const VALUE *ep = vm_get_ep(GET_EP(), level);
|
||||
VM_ASSERT(VM_ENV_LOCAL_P(ep));
|
||||
|
||||
if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) {
|
||||
VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
|
||||
|
||||
if (block_handler) {
|
||||
switch (vm_block_handler_type(block_handler)) {
|
||||
case block_handler_type_iseq:
|
||||
case block_handler_type_ifunc:
|
||||
val = rb_block_param_proxy;
|
||||
break;
|
||||
case block_handler_type_symbol:
|
||||
val = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
|
||||
goto INSN_LABEL(set);
|
||||
case block_handler_type_proc:
|
||||
val = VM_BH_TO_PROC(block_handler);
|
||||
goto INSN_LABEL(set);
|
||||
default:
|
||||
VM_UNREACHABLE(getblockparamproxy);
|
||||
}
|
||||
}
|
||||
else {
|
||||
val = Qnil;
|
||||
INSN_LABEL(set):
|
||||
vm_env_write(ep, -(int)idx, val);
|
||||
VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM);
|
||||
}
|
||||
}
|
||||
else {
|
||||
val = *(ep - idx);
|
||||
RB_DEBUG_COUNTER_INC(lvar_get);
|
||||
(void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@c variable
|
||||
@e Get value of special local variable ($~, $_, ..).
|
||||
|
|
1
method.h
1
method.h
|
@ -154,6 +154,7 @@ typedef struct rb_method_refined_struct {
|
|||
enum method_optimized_type {
|
||||
OPTIMIZED_METHOD_TYPE_SEND,
|
||||
OPTIMIZED_METHOD_TYPE_CALL,
|
||||
OPTIMIZED_METHOD_TYPE_BLOCK_CALL,
|
||||
OPTIMIZED_METHOD_TYPE__MAX
|
||||
};
|
||||
|
||||
|
|
3
proc.c
3
proc.c
|
@ -2329,6 +2329,9 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
|
|||
case OPTIMIZED_METHOD_TYPE_CALL:
|
||||
*max = UNLIMITED_ARGUMENTS;
|
||||
return 0;
|
||||
case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
|
||||
*max = UNLIMITED_ARGUMENTS;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
13
vm.c
13
vm.c
|
@ -295,6 +295,8 @@ static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_fra
|
|||
static VALUE vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
|
||||
static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
|
||||
|
||||
static VALUE rb_block_param_proxy;
|
||||
|
||||
#include "vm_insnhelper.h"
|
||||
#include "vm_exec.h"
|
||||
#include "vm_insnhelper.c"
|
||||
|
@ -1494,6 +1496,7 @@ vm_redefinition_check_flag(VALUE klass)
|
|||
if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG;
|
||||
if (klass == rb_cTrueClass) return TRUE_REDEFINED_OP_FLAG;
|
||||
if (klass == rb_cFalseClass) return FALSE_REDEFINED_OP_FLAG;
|
||||
if (klass == rb_cProc) return PROC_REDEFINED_OP_FLAG;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1537,7 +1540,9 @@ add_opt_method(VALUE klass, ID mid, VALUE bop)
|
|||
{
|
||||
const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
|
||||
|
||||
if (me && me->def->type == VM_METHOD_TYPE_CFUNC) {
|
||||
if (me &&
|
||||
(me->def->type == VM_METHOD_TYPE_CFUNC ||
|
||||
me->def->type == VM_METHOD_TYPE_OPTIMIZED)) {
|
||||
st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
|
||||
}
|
||||
else {
|
||||
|
@ -3054,6 +3059,12 @@ Init_VM(void)
|
|||
}
|
||||
vm_init_redefined_flag();
|
||||
|
||||
rb_block_param_proxy = rb_obj_alloc(rb_cObject);
|
||||
rb_add_method(rb_singleton_class(rb_block_param_proxy), rb_intern("call"), VM_METHOD_TYPE_OPTIMIZED,
|
||||
(void *)OPTIMIZED_METHOD_TYPE_BLOCK_CALL, METHOD_VISI_PUBLIC);
|
||||
rb_obj_freeze(rb_block_param_proxy);
|
||||
rb_gc_register_mark_object(rb_block_param_proxy);
|
||||
|
||||
/* vm_backtrace.c */
|
||||
Init_vm_backtrace();
|
||||
VM_PROFILE_ATEXIT();
|
||||
|
|
|
@ -483,6 +483,7 @@ enum ruby_basic_operators {
|
|||
BOP_UMINUS,
|
||||
BOP_MAX,
|
||||
BOP_MIN,
|
||||
BOP_CALL,
|
||||
|
||||
BOP_LAST_
|
||||
};
|
||||
|
@ -619,6 +620,7 @@ typedef struct rb_vm_struct {
|
|||
#define NIL_REDEFINED_OP_FLAG (1 << 9)
|
||||
#define TRUE_REDEFINED_OP_FLAG (1 << 10)
|
||||
#define FALSE_REDEFINED_OP_FLAG (1 << 11)
|
||||
#define PROC_REDEFINED_OP_FLAG (1 << 12)
|
||||
|
||||
#define BASIC_OP_UNREDEFINED_P(op, klass) (LIKELY((GET_VM()->redefined_flag[(op)]&(klass)) == 0))
|
||||
|
||||
|
|
|
@ -2047,7 +2047,18 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
|
|||
return vm_call_method(ec, reg_cfp, calling, ci, cc);
|
||||
}
|
||||
|
||||
static VALUE vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler);
|
||||
static inline VALUE vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler);
|
||||
|
||||
NOINLINE(static VALUE
|
||||
vm_invoke_block_noinline(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
|
||||
struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler));
|
||||
|
||||
static VALUE
|
||||
vm_invoke_block_noinline(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
|
||||
struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler)
|
||||
{
|
||||
return vm_invoke_block(ec, reg_cfp, calling, ci, block_handler);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_opt_call(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)
|
||||
|
@ -2059,7 +2070,24 @@ vm_call_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
|
|||
if (argc > 0) MEMMOVE(&TOPN(argc), &TOPN(argc-1), VALUE, argc);
|
||||
DEC_SP(1);
|
||||
|
||||
return vm_invoke_block(ec, reg_cfp, calling, ci, VM_BH_FROM_PROC(procval));
|
||||
return vm_invoke_block_noinline(ec, reg_cfp, calling, ci, VM_BH_FROM_PROC(procval));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_opt_block_call(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)
|
||||
{
|
||||
int argc = calling->argc;
|
||||
VALUE block_handler = VM_ENV_BLOCK_HANDLER(reg_cfp->ep);
|
||||
|
||||
if (BASIC_OP_UNREDEFINED_P(BOP_CALL, PROC_REDEFINED_OP_FLAG)) {
|
||||
if (argc > 0) MEMMOVE(&TOPN(argc), &TOPN(argc-1), VALUE, argc);
|
||||
DEC_SP(1);
|
||||
return vm_invoke_block_noinline(ec, reg_cfp, calling, ci, block_handler);
|
||||
}
|
||||
else {
|
||||
calling->recv = rb_vm_bh_to_procval(ec, block_handler);
|
||||
return vm_call_general(ec, reg_cfp, calling, ci, cc);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -2264,6 +2292,9 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st
|
|||
case OPTIMIZED_METHOD_TYPE_CALL:
|
||||
CI_SET_FASTPATH(cc, vm_call_opt_call, TRUE);
|
||||
return vm_call_opt_call(ec, cfp, calling, ci, cc);
|
||||
case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
|
||||
CI_SET_FASTPATH(cc, vm_call_opt_block_call, TRUE);
|
||||
return vm_call_opt_block_call(ec, cfp, calling, ci, cc);
|
||||
default:
|
||||
rb_bug("vm_call_method: unsupported optimized method type (%d)",
|
||||
cc->me->def->body.optimize_type);
|
||||
|
@ -2689,8 +2720,9 @@ vm_proc_to_block_handler(VALUE procval)
|
|||
return Qundef;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler)
|
||||
static inline VALUE
|
||||
vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
|
||||
struct rb_calling_info *calling, const struct rb_call_info *ci, VALUE block_handler)
|
||||
{
|
||||
int is_lambda = FALSE;
|
||||
|
||||
|
|
Loading…
Reference in a new issue