toge's diary

コンピュータ関連の趣味をつらつらと。

gcc 4.3.0 評価3

gcc 4.3.0 評価1 gcc 4.3.0 評価2

すごいっ。とうとうgccがやってくれたっ。

static int foo(int count) {
  if (count <= 0)
    return 1;
  return count * foo(count - 1);
}

int main(int argc, char* argv[]) {
  printf("%d\n", foo(10));
  return 0;
}

gcc 4.2.3 -O3の場合、こんな簡単な関数でもやっぱり再帰呼び出しなのです。
ちなみにinlineつけようが__attribute__((pure))つけようが結果はちっとも変わりません。

_Z3fooi:
	pushl	%ebp
	movl	$1, %eax
	movl	%esp, %ebp
	subl	$40, %esp
	movl	%ebx, -12(%ebp)
	movl	8(%ebp), %ebx
	movl	%esi, -8(%ebp)
	movl	%edi, -4(%ebp)
	testl	%ebx, %ebx
	jle	.L4
(省略)
	movl	-4(%ebp), %edi
	movl	%ebp, %esp
	popl	%ebp
	ret
.L31:
	leal	-9(%ebx), %eax
	movl	%eax, (%esp)
	call	_Z3fooi          # 悲しきかな再帰呼び出し
	imull	-16(%ebp), %eax
	jmp	.L28
	
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$20, %esp
	movl	$9, (%esp)
	call	_Z3fooi
	movl	$.LC0, (%esp)
	leal	(%eax,%eax,4), %eax
	addl	%eax, %eax
	movl	%eax, 4(%esp)
	call	printf
	addl	$20, %esp
	xorl	%eax, %eax
	popl	%ecx
	popl	%ebp
	leal	-4(%ecx), %esp
	ret

で、gcc-4.3.0 RC1 -O3の場合。

main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$20, %esp
	movl	$3628800, 4(%esp)
	movl	$.LC0, (%esp)
	call	printf
	addl	$20, %esp
	xorl	%eax, %eax
	popl	%ecx
	popl	%ebp
	leal	-4(%ecx), %esp
	ret

やった! とうとう再帰呼び出しを無くせましたっ。それどころか即値になってるよ。おめでとう!ありがとう!

ちなみにfoo関数の引数をvolatileな変数に変えると、mainj関数内のループに変換してくれます。
末尾再帰がループに変換された瞬間です。素敵すぎだgcc 4.3.0!

static int foo(int count) {
  if (count <= 0)
    return 1;
  return count * foo(count - 1);
}

int main(int argc, char* argv[]) {
  volatile int i = 10;
  printf("%lld\n", foo(i));
  return 0;
}
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	movl	$1, %ecx
	subl	$36, %esp
	movl	$10, -8(%ebp)
	movl	-8(%ebp), %eax
	testl	%eax, %eax
	jg	.L4
	jmp	.L3
.L7:
	movl	%edx, %eax
.L4:
	leal	-1(%eax), %edx
	imull	%eax, %ecx
	testl	%edx, %edx
	jg	.L7
.L3:
	movl	%ecx, 4(%esp)
	movl	$.LC0, (%esp)
	call	printf
	addl	$36, %esp
	xorl	%eax, %eax
	popl	%ecx
	popl	%ebp
	leal	-4(%ecx), %esp
	ret