I often hear people say that C doesn\'t perform tail call elimination. Even though it\'s not guaranteed by the standard, isn\'t it performed in practice by any decent implement
There are situations, where tail call optimisation would potentially break the ABI or at least be very difficult to implement in a semantic-preserving way. Think of position independent code in shared libraries for instance: Some platforms allow programs to link dynamically against libraries in order to save main memory when various different applications all depend on the same functionality. In such cases, the library is loaded once and mapped into each of the program’s virtual memory as if it was the only application on a system. On UNIX and also on some other systems, this is achieved by using position independent code for libraries, so that addressing is relative to an offset, rather than absolute to a fixed address space. On many platforms, however, position independent code must not be tail call optimised. The problem involved is that the offsets for navigating through the program have to be kept in registers; on Intel 32-bit, %ebx
is used which is a callee saved register; other platforms follow that notion. Unlike functions using normal calls, those deploying tail calls have to restore the callee saved registers before branching off to the subroutine, not when they return themselves. Normally, that is no problem, because at this point, the top most calling function does not care for the value stored in %ebx
, but the position independent code depends on this value upon each and every jump, call or branch command.
Other problems could be pending clean-ups in object-oriented languages (C++), meaning that the last call in a function isn't actually the last call - the clean-ups are. Hence, the compiler usually does not optimise, when this is the case.
Also setjmp
and longjmp
are problematic, of course, since this effectively means a function can finish execution more than once, before it actually finishes. Difficult or impossible to optimise at compile time!
There's probably more technical reasons one can think of. These are just some considerations.
Although modern compilers MAY do tail-call optimization if you turn on optimizations, your debug builds will probably run without it so that you can get stack traces and step in/out of code and wonderful things like that. In this situation, tail call optimization is not desired.
Since tail call optimization is not always desirable, it doesn't make sense to mandate it to compiler writers.