gccで末尾再帰の最適化

フリーソフトウェア徹底活用講座(21)を見ていたら、gccでも-foptimize-sibling-callsをつけてコンパイルすれば末尾再帰が最適化されるようです。階乗を求めるサンプルプログラムで最適化の様子を確認してみました。
ファイル fact.c

#include <stdio.h>
#include <stdlib.h>

int fact(int n)
{
    if (n == 0)
	return 1;
    else
	return n * fact(n - 1);
}

int fact_tail(int n, int x)
{
    if (n == 0)
	return x;
    else
	return fact_tail(n - 1, n * x);
}

int
main(int argc, char *argv[])
{
    int n = atoi(argv[1]);
    
    printf("%d %d\n", fact(n), fact_tail(n, 1));
    
    return 0;
}

以下のようにコンパイルします。

$ gcc -S -O0 -foptimize-sibling-calls fact.c

出力されたアセンブラは以下の通りです。

...
fact:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        cmpl    $0, 8(%ebp)
        jne     .L8
        movl    $1, -4(%ebp)
        jmp     .L7
.L8:
        subl    $12, %esp
        movl    8(%ebp), %eax
        decl    %eax
        pushl   %eax
        call    fact → 再帰呼出しになっている。
        addl    $16, %esp
        imull   8(%ebp), %eax
        movl    %eax, -4(%ebp)
.L7:
        movl    -4(%ebp), %eax
        leave
        ret
...
fact_tail:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        cmpl    $0, 8(%ebp)
        jne     .L11
        movl    12(%ebp), %eax
        movl    %eax, -4(%ebp)
        jmp     .L10
.L11:
        movl    8(%ebp), %eax
        imull   12(%ebp), %eax
        movl    %eax, 12(%ebp)
        movl    8(%ebp), %eax
        decl    %eax
        movl    %eax, 8(%ebp)
        leave
        jmp     fact_tail → 末尾再帰が最適化されている。
.L10:
        movl    -4(%ebp), %eax
        leave
        ret
...

んん、確かに関数fact_tailは、末尾再帰がjmp命令になっていますね。