Tail Recursion

Tel telford@eng.uts.edu.au
Fri Oct 2 00:09:00 GMT 1998


I'm from the guile (scheme) mailing list and the argument has come
up over tail calls. Some test code of mine seems to show a bug in
egcs tail recursion:

----------------------------------------------------------------tail.c
typedef int returnable_thing;
extern int i;
extern void silly_side_effect( void );
extern returnable_thing tail_final( void );

returnable_thing tail_eater( void )
{
    silly_side_effect();
    if( i-- )
    {
        return( tail_eater());
    }
    return( tail_final());
}
----------------------------------------------------------------------

Compiled with: gcc -O -S -fomit-frame-pointer tail.c
to give:

----------------------------------------------------------------tail.s
        .file   "tail.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .align 4
.globl tail_eater
        .type    tail_eater,@function
tail_eater:
.L3:
        call silly_side_effect
        decl i
        cmpl $-1,i
        jne .L3
        call tail_final
        ret
.Lfe1:
        .size    tail_eater,.Lfe1-tail_eater
        .ident  "GCC: (GNU) egcs-2.91.57 19980901 (egcs-1.1 release)"
----------------------------------------------------------------------

All looks good here, the tail recursion of tail_eater() into itself
has become a loop and doesn't blow the stack. However, changing the
first line to:

typedef void returnable_thing;

will give this result:
----------------------------------------------------------------tail.s
        .file   "tail.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .align 4
.globl tail_eater
        .type    tail_eater,@function
tail_eater:
        call silly_side_effect
        decl i
        cmpl $-1,i
        je .L2
        call tail_eater
        ret
        .p2align 4,,7
.L2:
        call tail_final
        ret
.Lfe1:
        .size    tail_eater,.Lfe1-tail_eater
        .ident  "GCC: (GNU) egcs-2.91.57 19980901 (egcs-1.1 release)"
----------------------------------------------------------------------

The tail recursion is spoiled but why shouldn't I get tail recursion
from a function returning void? It seems strange that it would be handled
differently to other functions, am I missing the obvious?

One further question... why can't ``call tail_final'' also be optimised to a
jump? It seems to me that ANY call immediately followed by a return
should be optimised to a jump. I understand that the frame pointer
would normally be between the call and the ret but when I tell the
compiler -fomit-frame-pointer then I'm saying that I don't give a toss
about debugging and what I want is the best optimisation. This would
allow mutual recursion and state machines and other nice things.

Does this depend on processor architecture? Is so, why?

	- Tel




More information about the Gcc-bugs mailing list