Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

send-pop optimisation #2100

Open
wants to merge 20 commits into
base: master
from
Open
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

precalc INSN_CALLER_RETVAL_POPPED_P()

This macro is expanded inside of send-ish instructions, which are
super-duper hot paths.  By statically analysing this into the call
info, we can optimise situations where return values _do_ get used.
  • Loading branch information...
shyouhei committed Feb 26, 2019
commit a6df4a267edc93f05c74cbab3bf4ea5ce12e0ffc
@@ -1152,6 +1152,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal
ci->mid = mid;
ci->flag = flag;
ci->orig_argc = argc;
ci->compiled_frame_bits = 0;

if (kw_arg) {
ci->flag |= VM_CALL_KWARG;
@@ -3536,7 +3537,7 @@ iseq_sendpop_optimization_phase1(const LINK_ANCHOR *anchor)
continue;
}
else {
ci->flag |= VM_CALL_POPIT;
ci->compiled_frame_bits |= VM_FRAME_FLAG_POPIT;
}
}
}
@@ -3549,7 +3550,7 @@ iseq_sendpop_optimization_phase2(INSN *iobj)
if (! ci) {
return COMPILE_OK;
}
else if (! (ci->flag & VM_CALL_POPIT)) {
else if (! (ci->compiled_frame_bits & VM_FRAME_FLAG_POPIT)) {
return COMPILE_OK;
}
else switch (iobj->insn_id) {
@@ -3604,28 +3605,48 @@ iseq_sendpop_optimization_phase3(const LINK_ANCHOR *anchor, int do_si)
if (! ci) {
continue;
}
else if (! (ci->flag & VM_CALL_POPIT)) {
else if (! (ci->compiled_frame_bits & VM_FRAME_FLAG_POPIT)) {
continue;
}
else if (! IS_INSN(e->next)) {
continue;
}
else if (! LIKELY(do_si)) {
/* We don't optimize. Be tidy. */
ci->flag &= ~(unsigned int)VM_CALL_POPIT;
ci->compiled_frame_bits &= ~VM_FRAME_FLAG_POPIT;
}
else if (! LIKELY(IS_INSN_ID(e->next, pop))) {
/* Don't know if we reach here but the flag shall be
* wiped because we do use the return value this case.
*/
ci->flag &= ~(unsigned int)VM_CALL_POPIT;
ci->compiled_frame_bits &= ~VM_FRAME_FLAG_POPIT;
}
else {
ELEM_REMOVE(e->next);
}
}
}

static void
iseq_construct_compiled_frame_bits(const LINK_ANCHOR *anchor)
{
for (const LINK_ELEMENT *e = FIRST_ELEMENT(anchor); e->next; e = e->next) {
struct rb_call_info *ci = elem2ci(e);

if (ci) {
const LINK_ELEMENT *i = get_next_insn((INSN *)e);

if (i && IS_INSN_ID(i, pop)) {
ci->compiled_frame_bits |= VM_FRAME_FLAG_POPPED;
}
else if (ci->compiled_frame_bits & VM_FRAME_FLAG_POPIT) {
/* POPIT implies POPED */
ci->compiled_frame_bits |= VM_FRAME_FLAG_POPPED;
}
}
}
}

static int
iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
{
@@ -3648,7 +3669,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
if (do_si) {
iseq_specialized_instruction(iseq, (INSN *)list);
iseq_sendpop_optimization_phase2((INSN *)list);
iseq_sendpop_optimization_phase2((INSN *)list);
}
if (do_ou) {
insn_operands_unification((INSN *)list);
@@ -3670,6 +3691,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)

iseq_sendpop_optimization_phase3(anchor, do_si);
iseq_insert_bailouts(iseq, anchor);
iseq_construct_compiled_frame_bits(anchor);
return COMPILE_OK;
}

@@ -8002,6 +8024,8 @@ insn_data_to_s_detail(INSN *iobj)
struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, j);
rb_str_cat2(str, "<callinfo:");
if (ci->mid) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(ci->mid));
if (ci->compiled_frame_bits)
rb_str_catf(str, "(%#x)", ci->compiled_frame_bits);
rb_str_catf(str, ", %d>", ci->orig_argc);
break;
}
@@ -777,8 +777,7 @@ send
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
{
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), ci, blockiseq, false);
int popped = INSN_CALLER_RETVAL_POPPED_P();
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, popped, vm_search_method_wrap);
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_method_wrap);

if (val == Qundef) {
RESTORE_REGS();
@@ -797,8 +796,7 @@ opt_send_without_block
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
{
VALUE bh = VM_BLOCK_HANDLER_NONE;
int popped = INSN_CALLER_RETVAL_POPPED_P();
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, popped, vm_search_method_wrap);
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_method_wrap);

if (val == Qundef) {
RESTORE_REGS();
@@ -873,8 +871,7 @@ invokesuper
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
{
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), ci, blockiseq, true);
int popped = INSN_CALLER_RETVAL_POPPED_P();
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, popped, vm_search_super_method);
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_super_method);

if (val == Qundef) {
RESTORE_REGS();
@@ -897,8 +894,7 @@ invokeblock
};

VALUE bh = VM_BLOCK_HANDLER_NONE;
int popped = INSN_CALLER_RETVAL_POPPED_P();
val = vm_sendish(ec, GET_CFP(), ci, &cc, bh, popped, vm_search_invokeblock);
val = vm_sendish(ec, GET_CFP(), ci, &cc, bh, vm_search_invokeblock);

if (val == Qundef) {
RESTORE_REGS();
13 iseq.c
@@ -1864,9 +1864,20 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
CALL_FLAG(KWARG);
CALL_FLAG(KW_SPLAT);
CALL_FLAG(OPT_SEND); /* maybe not reachable */
CALL_FLAG(POPIT);
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
}

if (ci->compiled_frame_bits) {
VALUE str = Qundef;
if (ci->compiled_frame_bits & VM_FRAME_FLAG_POPIT) {
str = rb_str_new_cstr("[POPIT]");
}
else if (ci->compiled_frame_bits & VM_FRAME_FLAG_POPPED) {
str = rb_str_new_cstr("[POPED]");
}
assert(str != Qundef);
rb_ary_push(ary, str);
}
ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
}
break;
@@ -54,7 +54,7 @@
% end
fprintf(f, " calling.argc = %d;\n", ci->orig_argc);
fprintf(f, " calling.recv = stack[%d];\n", b->stack_size - 1 - argc);
fprintf(f, " calling.popped = false;\n");
fprintf(f, " calling.compiled_frame_bits = 0;\n");

% # JIT: Special CALL_METHOD. Bypass cc_copy->call and inline vm_call_iseq_setup_normal for vm_call_iseq_setup_func FASTPATH.
fprintf(f, " {\n");
@@ -24,7 +24,7 @@ sp_inc_of_sendish(const struct rb_call_info *ci)
const int argc = ci->orig_argc;
const int recv = 1;
const int retn = 1;
const int popn = (ci->flag & VM_CALL_POPIT) ? 1 : 0;
const int popn = (ci->compiled_frame_bits & VM_FRAME_FLAG_POPIT) ? 1 : 0;

/* 1. 2. 3. 5. 6. */
return 0 - argb - argc - recv + retn - popn;
@@ -247,6 +247,7 @@ struct rb_call_info {
ID mid;
unsigned int flag;
int orig_argc;
unsigned int compiled_frame_bits;
};

struct rb_call_info_kw_arg {
@@ -263,7 +264,7 @@ struct rb_calling_info {
VALUE block_handler;
VALUE recv;
int argc;
int popped;
unsigned int compiled_frame_bits;
};

struct rb_call_cache;
@@ -1078,7 +1079,6 @@ enum vm_call_flag_bits {
VM_CALL_SUPER_bit, /* super */
VM_CALL_ZSUPER_bit, /* zsuper */
VM_CALL_OPT_SEND_bit, /* internal flag */
VM_CALL_POPIT_bit, /* ;m(...); */
VM_CALL__END
};

@@ -1094,7 +1094,6 @@ enum vm_call_flag_bits {
#define VM_CALL_SUPER (0x01 << VM_CALL_SUPER_bit)
#define VM_CALL_ZSUPER (0x01 << VM_CALL_ZSUPER_bit)
#define VM_CALL_OPT_SEND (0x01 << VM_CALL_OPT_SEND_bit)
#define VM_CALL_POPIT (0x01 << VM_CALL_POPIT_bit)

enum vm_special_object_type {
VM_SPECIAL_OBJECT_VMCORE = 1,
@@ -56,7 +56,7 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE

calling->recv = recv;
calling->argc = argc;
calling->popped = false;
calling->compiled_frame_bits = false;

return vm_call0_body(ec, calling, &ci_entry, &cc_entry, argv);
}
@@ -1768,13 +1768,14 @@ static inline VALUE
vm_call_iseq_setup_normal(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me,
int opt_pc, int param_size, int local_size)
{
int popped = calling->popped;
unsigned long flags = VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL |
calling->compiled_frame_bits;
const rb_iseq_t *iseq = def_iseq_ptr(me->def);
VALUE *argv = cfp->sp - calling->argc;
VALUE *sp = argv + param_size;
cfp->sp = argv - 1 /* recv */;

vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | popped, calling->recv,
vm_push_frame(ec, iseq, flags, calling->recv,
calling->block_handler, (VALUE)me,
iseq->body->iseq_encoded + opt_pc, sp,
local_size - param_size,
@@ -1792,8 +1793,10 @@ vm_call_iseq_setup_tailcall(rb_execution_context_t *ec, rb_control_frame_t *cfp,
const rb_iseq_t *iseq = def_iseq_ptr(me->def);
VALUE *src_argv = argv;
VALUE *sp_orig, *sp;
VALUE finish_flag = VM_FRAME_FINISHED_P(cfp) ? VM_FRAME_FLAG_FINISH : 0;
unsigned long popped = VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_POPPED);
unsigned long flags =
VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL |
VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_POPPED) |
(VM_FRAME_FINISHED_P(cfp) ? VM_FRAME_FLAG_FINISH : 0);

if (VM_BH_FROM_CFP_P(calling->block_handler, cfp)) {
struct rb_captured_block *dst_captured = VM_CFP_TO_CAPTURED_BLOCK(RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
@@ -1821,7 +1824,7 @@ vm_call_iseq_setup_tailcall(rb_execution_context_t *ec, rb_control_frame_t *cfp,
*sp++ = src_argv[i];
}

vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | finish_flag | popped,
vm_push_frame(ec, iseq, flags,
calling->recv, calling->block_handler, (VALUE)me,
iseq->body->iseq_encoded + opt_pc, sp,
iseq->body->local_table_size - iseq->body->param.size,
@@ -1997,12 +2000,14 @@ vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp
VALUE recv = calling->recv;
VALUE block_handler = calling->block_handler;
int argc = calling->argc;
int popped = calling->popped;
unsigned long flags =
VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL |
calling->compiled_frame_bits;

RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);

vm_push_frame(ec, NULL, VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL | popped, recv,
vm_push_frame(ec, NULL, flags, recv,
block_handler, (VALUE)me,
0, ec->cfp->sp, 0, 0);

@@ -2839,7 +2844,7 @@ vm_yield_setup_args(rb_execution_context_t *ec, const rb_iseq_t *iseq, const int
calling = &calling_entry;
calling->argc = argc;
calling->block_handler = block_handler;
calling->popped = 0;
calling->compiled_frame_bits = 0;

ci_entry.flag = 0;
ci = &ci_entry;
@@ -2858,12 +2863,14 @@ vm_invoke_iseq_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
const int arg_size = iseq->body->param.size;
VALUE * const rsp = GET_SP() - calling->argc;
int opt_pc = vm_callee_setup_block_arg(ec, calling, ci, iseq, rsp, is_lambda ? arg_setup_method : arg_setup_block);
int popped = calling->popped;
unsigned long flags =
VM_FRAME_MAGIC_BLOCK |
(is_lambda ? VM_FRAME_FLAG_LAMBDA : 0) |
calling->compiled_frame_bits;

SET_SP(rsp);

vm_push_frame(ec, iseq,
VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0) | popped,
vm_push_frame(ec, iseq, flags,
captured->self,
VM_GUARDED_PREV_EP(captured->ep), 0,
iseq->body->iseq_encoded + opt_pc,
@@ -3443,7 +3450,6 @@ vm_sendish(
struct rb_call_info *ci,
struct rb_call_cache *cc,
VALUE block_handler,
int popped,
void (*method_explorer)(
const struct rb_control_frame_struct *reg_cfp,
struct rb_call_info *ci,
@@ -3457,7 +3463,7 @@ vm_sendish(
.block_handler = block_handler,
.recv = recv,
.argc = argc,
.popped = popped,
.compiled_frame_bits = ci->compiled_frame_bits,
};

method_explorer(GET_CFP(), ci, cc, recv);
@@ -199,23 +199,6 @@ enum vm_regan_acttype {
# define INC_GLOBAL_TIMESTAMP() (++ruby_vm_global_timestamp)
#endif

#ifdef MJIT_HEADER
#define INSN_CALLER_RETVAL_POPPED_P() false
#else
/* INSN_CLASS changes from place to place. That prevents us from
* writing `#if INSN_CLASS == ...` right here. */
#define INSN_CALLER_RETVAL_POPPED_P() (int)( \
(INSN_CLASS == RUBYVM_SENDPOP_INSTRUCTIONS) ? \
(VM_FRAME_FLAG_POPPED | VM_FRAME_FLAG_POPIT) : \
CURRENT_INSN_IS(pop) ? \
VM_FRAME_FLAG_POPPED : \
CURRENT_INSN_IS(leave) ? \
VM_ENV_FLAGS(GET_EP(), VM_FRAME_FLAG_POPPED) : \
CURRENT_INSN_IS(opt_bailout) ? \
VM_ENV_FLAGS(GET_EP(), VM_FRAME_FLAG_POPPED) : \
0)
#endif

static inline struct vm_throw_data *
THROW_DATA_NEW(VALUE val, const rb_control_frame_t *cf, VALUE st)
{
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.