Project

General

Profile

Bug #12082 » 0001-compile.c-don-t-do-tail-call-optimization-if-covered-v2.patch

rhenium (Kazuki Yamaguchi), 03/09/2016 09:17 AM

View differences:

compile.c
static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_optimize_tailcall(rb_iseq_t *iseq);
static int iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl);
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
......
debugs("[compile step 4.3 (set_optargs_table)] \n");
if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
/* dirty hack: it have to look through catch table */
if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization &&
ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization)
iseq_optimize_tailcall(iseq);
debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
......
}
static int
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list)
{
INSN *iobj = (INSN *)list;
again:
......
}
}
if (do_tailcallopt &&
(iobj->insn_id == BIN(send) ||
iobj->insn_id == BIN(opt_aref_with) ||
iobj->insn_id == BIN(opt_aset_with) ||
iobj->insn_id == BIN(invokesuper))) {
/*
* send ...
* leave
* =>
* send ..., ... | VM_CALL_TAILCALL, ...
* leave # unreachable
*/
INSN *piobj = NULL;
if (iobj->link.next) {
LINK_ELEMENT *next = iobj->link.next;
do {
if (next->type != ISEQ_ELEMENT_INSN) {
next = next->next;
continue;
}
switch (INSN_OF(next)) {
case BIN(nop):
/*case BIN(trace):*/
next = next->next;
break;
case BIN(leave):
piobj = iobj;
default:
next = NULL;
break;
}
} while (next);
}
if (piobj) {
struct rb_call_info *ci = (struct rb_call_info *)piobj->operands[0];
if (piobj->insn_id == BIN(send) || piobj->insn_id == BIN(invokesuper)) {
if (piobj->operands[2] == 0) { /* no blockiseq */
ci->flag |= VM_CALL_TAILCALL;
}
}
else {
ci->flag |= VM_CALL_TAILCALL;
}
}
}
return COMPILE_OK;
}
......
{
LINK_ELEMENT *list;
const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
const int do_tailcallopt = ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
list = FIRST_ELEMENT(anchor);
......
while (list) {
if (list->type == ISEQ_ELEMENT_INSN) {
if (do_peepholeopt) {
iseq_peephole_optimize(iseq, list, do_tailcallopt);
iseq_peephole_optimize(iseq, list);
}
if (do_si) {
iseq_specialized_instruction(iseq, (INSN *)list);
......
return Qnil;
}
static int
iseq_optimize_tailcall(rb_iseq_t *iseq)
{
unsigned int i;
VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
const struct iseq_catch_table *table = iseq->body->catch_table;
char *cmap;
/* rescue block can't tail call because of errinfo */
if (iseq->body->type == ISEQ_TYPE_RESCUE || iseq->body->type == ISEQ_TYPE_ENSURE)
return COMPILE_OK;
cmap = xcalloc(1, iseq->body->iseq_size);
if (table) {
for (i = table->size; i-- > 0; ) {
const struct iseq_catch_table_entry *entry = &table->entries[i];
memset(cmap + entry->start, 1, entry->end - entry->start);
}
}
/*
* send ...
* leave
* =>
* send ..., ... | VM_CALL_TAILCALL, ...
* leave # unreachable
*/
for (i = 0; i < iseq->body->iseq_size; ) {
int insn = (int)encoded[i];
int len = insn_len(insn);
if (i + len >= iseq->body->iseq_size) break;
if (!cmap[i] && BIN(leave) == (int)encoded[i + len]) {
struct rb_call_info *ci = (struct rb_call_info *)encoded[i + 1];
switch (insn) {
case BIN(invokesuper):
case BIN(send):
if (encoded[i + 3]) break; /* with block */
case BIN(opt_aref_with):
case BIN(opt_aset_with):
case BIN(opt_send_without_block):
case BIN(opt_length):
case BIN(opt_size):
case BIN(opt_empty_p):
case BIN(opt_succ):
case BIN(opt_not):
case BIN(opt_plus):
case BIN(opt_minus):
case BIN(opt_mult):
case BIN(opt_div):
case BIN(opt_mod):
case BIN(opt_eq):
case BIN(opt_neq):
case BIN(opt_lt):
case BIN(opt_le):
case BIN(opt_gt):
case BIN(opt_ge):
case BIN(opt_ltlt):
case BIN(opt_aref):
case BIN(opt_aset):
ci->flag |= VM_CALL_TAILCALL;
}
}
i += len;
}
xfree(cmap);
return COMPILE_OK;
}
/**
compile each node
......
ADD_INSNL(ret, line, jump, label_miss);
ADD_LABEL(ret, label_hit);
COMPILE(ret, "resbody body", resq->nd_body);
if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
ADD_INSN(ret, line, nop);
}
ADD_INSN(ret, line, leave);
ADD_LABEL(ret, label_miss);
resq = resq->nd_head;
test/ruby/test_optimization.rb
end;
end
def test_tailcall_rescue
assert_separately([], <<~'end;')
def do_raise
raise "must be rescued"
end
def errinfo
$!
end
options = {
tailcall_optimization: true,
trace_instruction: false,
}
RubyVM::InstructionSequence.compile(<<~EOF, __FILE__, __FILE__, __LINE__, options).eval
def test_rescue
return do_raise if true # suppress unreachable warning
1 + 2
rescue
errinfo
end
EOF
err = test_rescue
assert(err)
assert_equal(RuntimeError, err.class)
end;
end
class Bug10557
def [](_)
block_given?
(2-2/2)