我如何知道gcc(更具体地说是g++)是否正在优化特定函数中的尾部递归? (因为它出现了几次:我一般不想测试gcc是否可以优化尾部递归。我想知道它是否可以优化我的尾部递归函数。)
如果您的答案是“查看生成的汇编程序”,那么我想确切地知道我在寻找什么,是否可以编写一个简单的程序来检查汇编程序以查看是否有优化。
PS。我知道这是5个月前出现的问题Which, if any, C++ compilers do tail-recursion optimization?的一部分。但是,我认为该问题的这一部分没有得到令人满意的回答。 (答案是“检查编译器是否进行了优化(我知道)的最简单方法是执行调用,否则将导致堆栈溢出–或查看程序集输出。”)
最佳答案
让我们使用the example code from the other question。编译它,但是告诉gcc不要汇编:
gcc -std=c99 -S -O2 test.c
Now let's look at the _atoi
function in the resultant test.s file (gcc 4.0.1 on Mac OS 10.5):
.text
.align 4,0x90
_atoi:
pushl %ebp
testl %eax, %eax
movl %esp, %ebp
movl %eax, %ecx
je L3
.align 4,0x90
L5:
movzbl (%ecx), %eax
testb %al, %al
je L3
leal (%edx,%edx,4), %edx
movsbl %al,%eax
incl %ecx
leal -48(%eax,%edx,2), %edx
jne L5
.align 4,0x90
L3:
leave
movl %edx, %eax
ret
编译器已对此函数执行了尾调用优化。我们可以知道,因为该代码中没有
call
指令,而原始的C代码显然有一个函数调用。此外,我们可以看到jne L5
指令,该指令在函数中向后跳转,表示在C代码中显然没有循环时会出现循环。如果在关闭优化的情况下进行重新编译,则会看到一行显示call _atoi
的信息,并且也不会看到任何向后跳转。是否可以自动执行此操作是另一回事。汇编代码的细节将取决于您正在编译的代码。
我想,您可以通过编程方式发现它。使函数打印出堆栈指针的当前值(在x86上注册ESP)。如果该函数在第一个调用中打印的值与递归调用中打印的值相同,则说明编译器已执行了尾部调用优化。这个想法需要修改您希望观察的功能,但这可能会影响编译器选择优化功能的方式。如果测试成功(两次都打印相同的ESP值),那么我认为可以合理地假设优化也将在没有您的仪器的情况下执行,但是如果测试失败,我们将不知道失败是否是由于仪器代码的添加。