Skip to content

Commit 4ff2c58

Browse files
wanabeko1
authored andcommitted
retry tailcall optimization (#2529)
Sorry, f62f903 is push miss.
1 parent 58b363b commit 4ff2c58

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

test/ruby/test_optimization.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,21 @@ def run(current, final)
451451
}
452452
end
453453

454+
def test_tailcall_not_to_grow_stack
455+
bug16161 = '[ruby-core:94881]'
456+
457+
tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
458+
begin;
459+
def foo(n)
460+
return :ok if n < 1
461+
foo(n - 1)
462+
end
463+
end;
464+
assert_nothing_raised(SystemStackError, bug16161) do
465+
assert_equal(:ok, foo(1_000_000), bug16161)
466+
end
467+
end
468+
454469
class Bug10557
455470
def [](_)
456471
block_given?

vm_insnhelper.c

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,30 @@ vm_call_iseq_setup_normal_opt_start(rb_execution_context_t *ec, rb_control_frame
18711871
return vm_call_iseq_setup_normal(ec, cfp, calling, cc->me, opt_pc, param - delta, local);
18721872
}
18731873

1874+
static VALUE
1875+
vm_call_iseq_setup_tailcall_opt_start(rb_execution_context_t *ec, rb_control_frame_t *cfp,
1876+
struct rb_calling_info *calling,
1877+
const struct rb_call_info *ci, struct rb_call_cache *cc)
1878+
{
1879+
const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
1880+
const int lead_num = iseq->body->param.lead_num;
1881+
const int opt = calling->argc - lead_num;
1882+
const int opt_pc = (int)iseq->body->param.opt_table[opt];
1883+
1884+
RB_DEBUG_COUNTER_INC(ccf_iseq_opt);
1885+
1886+
#if USE_OPT_HIST
1887+
if (opt_pc < OPT_HIST_MAX) {
1888+
opt_hist[opt]++;
1889+
}
1890+
else {
1891+
opt_hist[OPT_HIST_MAX]++;
1892+
}
1893+
#endif
1894+
1895+
return vm_call_iseq_setup_tailcall(ec, cfp, calling, ci, cc, opt_pc);
1896+
}
1897+
18741898
static void
18751899
args_setup_kw_parameters(rb_execution_context_t *const ec, const rb_iseq_t *const iseq,
18761900
VALUE *const passed_values, const int passed_keyword_len, const VALUE *const passed_keywords,
@@ -1957,9 +1981,16 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
19571981
argument_arity_error(ec, iseq, argc, lead_num, lead_num + opt_num);
19581982
}
19591983

1960-
CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
1961-
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
1962-
!(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED));
1984+
if (LIKELY(!(ci->flag & VM_CALL_TAILCALL))) {
1985+
CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
1986+
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
1987+
!(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED));
1988+
}
1989+
else {
1990+
CC_SET_FASTPATH(cc, vm_call_iseq_setup_tailcall_opt_start,
1991+
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
1992+
!(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED));
1993+
}
19631994

19641995
/* initialize opt vars for self-references */
19651996
VM_ASSERT((int)iseq->body->param.size == lead_num + opt_num);

0 commit comments

Comments
 (0)