Bug #12082 » 0001-compile.c-don-t-do-tail-call-optimization-if-covered-v2.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; | ||
| /* 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? | ||
- « Previous
- 1
- 2
- Next »