Bug #12082 » 0001-compile.c-don-t-do-tail-call-optimization-if-covered.patch
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 = 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
|
||
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
|
||
:ok
|
||
end
|
||
EOF
|
||
assert_equal(:ok, test_rescue)
|
||
end;
|
||
end
|
||
class Bug10557
|
||
def [](_)
|
||
block_given?
|