1
0
Fork 0
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:
ko1 2018-01-07 19:18:49 +00:00
parent bb8f6ac0fe
commit 7fd1183467
7 changed files with 128 additions and 6 deletions

View file

@ -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);

View file

@ -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 ($~, $_, ..).

View file

@ -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
View file

@ -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
View file

@ -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();

View file

@ -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))

View file

@ -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;