blob: b00f71853d77d129a8eda5cd857a1dc9148c963c [file] [log] [blame]
"use strict";
// factorial calls aux that tail-calls back into factorial.
// It is not OK to turn that tail call into a jump back to the top of the function, because the call to aux is not a tail call.
function factorial(n) {
function aux(n) {
if (n == 1)
return 1;
return factorial(n - 1);
}
return n * aux(n);
}
// Same, but this time with an irrelevant tail call in factorial.
// This exercises a different code path because the recursive tail call optimization aborts early if the op_enter block is not split, and it is split by the detection of a tail call.
function factorial2(n) {
function aux2(n) {
if (n == 1)
return 1;
return factorial2(n - 1);
}
function id(n) {
return n;
}
return id(n * aux2(n));
}
// This time, aux is tail-calling itself: the optimization is valid but must jump to the start of aux3, not of factorial3.
function factorial3(n) {
function aux3(n, acc) {
if (n == 1)
return acc;
return aux3 (n-1, n*acc);
}
return n * aux3(n-1, 1);
}
// In this case, it is valid to jump all the way to the top of factorial4, because all calls are tail calls.
function factorial4(n, acc) {
function aux4(n, acc) {
if (n == 1)
return acc;
return factorial4(n-1, n*acc);
}
if (acc)
return aux4(n, acc);
return aux4(n, 1);
}
// This function is used to test that recursing with a different number of arguments doesn't mess up with the stack.
// The first two tail calls should be optimized, but not the last one (no place on the stack for the third argument).
function foo(a, b) {
if (a == 0)
return 42;
if (a == 1)
return foo(a - 1);
if (a == 2)
return foo(b - 1, a);
return foo (b - 1, a, 43);
}
// Same deal as foo, just with an inlining thrown into the mix.
// Even the first tail call should not be optimized in this case, because some code in the compiler may constant-fold the number of arguments in that case.
function bar(x, y) {
function auxBar(a, b) {
if (a == 0)
return 42;
if (a == 1)
return auxBar(a - 1);
if (a == 2)
return auxBar(b - 1, a);
return auxBar(b - 1, a, 43);
}
return auxBar(x, y);
}
function test(result, expected, name) {
if (result != expected)
throw "Wrong result for " + name + ": " + result + " instead of " + expected;
}
for (var i = 0; i < 10000; ++i) {
test(factorial(20), 2432902008176640000, "factorial");
test(factorial2(20), 2432902008176640000, "factorial2");
test(factorial3(20), 2432902008176640000, "factorial3");
test(factorial4(20), 2432902008176640000, "factorial4");
test(foo(10, 10), 42, "foo");
test(bar(10, 10), 42, "bar");
}