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命令になっていますね。