Probably the single most important optimization performed by the ATS/Anairiats compiler is the translation of tail-recursive function calls into direct (local) jumps.
// [sum1] is recursive but not tail-recursive
fun sum1 (n: int): int = if n > 0 then n + sum1 (n-1) else 0
This function is recursive but not tail-recursive. The stack space it
consumes is proportional to the value of its argument. Essentially,
the ATS compiler translates the definition of
sum1 into the following C code:
int sum1 (int n) {
if (n > 1) return n + sum1 (n-1) ; else return 1 ;
}
When applied to an integer n,
the following defined function sum2 also sums up
integers from 1 to n.
fn sum2 (n: int): int = let // sum2 is non-recursive // [loop] is tail-recursive fun loop (n: int, res: int): int = if n > 0 then loop (n-1, res+n) else res // end of [loop] in loop (n, 0) end // end of [sum2]The inner function loop in the definition of sum2 is tail-recursive. The stack space consumed by loop is a constant independent of th value of the argument of sum2. Essentially, the ATS compiler translates the definition of sum2 into the following C code:
int sum2_loop (int n, int res) {
loop:
if (n > 1) {
res = res + n ; n = n - 1; goto loop;
} else {
// do nothing
}
return res ;
}
int sum2 (int n) { return sum2_loop (n, 0) ; }
// [fn*] indicates the need to combine two or more functions
// so as to translate tail-recursive calls into direct jumps
fn* even (n: int): bool = if n > 0 then odd (n-1) else true
and odd (n: int): bool = if n > 0 then even (n-1) else false
The keyword fn* is used to indicate to the ATS compiler
that the functions even and
odd need to be combined together so as to turn (mutually)
tail-recursive function calls into direct jumps. Essentially, the ATS
compiler emits the following C code after compiling this example:
bool even_odd (int tag, int n) {
bool res ;
switch (tag) {
0: goto even ;
1: goto odd ;
default : exit (1) ;
}
even: if (n > 0) { n = n - 1; goto odd; } else { res = true; goto done; }
odd: if (n > 0) { n = n - 1; goto even; } else { res = false; goto done; }
done: return res ;
} /* end of [even_odd] */
bool even (int n) { return even_odd (0, n) ; }
bool odd (int n) { return even_odd (1, n) ; }
Note that mutually recursive functions can be combined in such a manner
only if they all have the same return type. In the above case, both
even and odd have the same return type bool.
When translating C code involving embedded loops, we often encounter mutual tail-recursion. For instance, the following C code prints out ordered pairs of digits:
int main (int argc, char *argv[]) {
int i, j ;
for (i = 0; i <= 9; i += 1) {
for (j = i; j <= 9; j += 1) {
if (i < j) printf (", ") ; printf ("(%i, %i)", i, j) ;
} /* for */
printf ("\n") ;
} /* for */
return 0 ;
}
A straightforward translation of the C code into ATS (in functional style)
is given as follows:
implement main (argc, argv) = let
fn* loop1 {i:nat} (i: int i): void =
if i <= 9 then loop2 (i, i) else ()
// end of [loop1]
and loop2 {i,j:nat} (i: int i, j: int j): void =
if j <= 9 then begin
if i < j then begin
print ", "; printf ("(%i, %i)", @(i, j)); loop2 (i, j+1)
end // end of [if]
end else begin
print_newline (); loop1 (i+1)
end // end of [if]
// end of [loop2]
in
loop1 0
end // end of [main]
where the mutually tail-recursive funtions loop1 and loop2
correspond to the outer and inner loops in the C code, respectively.
The code used for illustration is available here.