我一直在研究GCC中的一些神奇功能O3
(实际上我正在使用Clang编译,但是它与GCC相同,我猜优化器的很大部分是从GCC移植到Clang的)。
考虑这个C程序:
int foo(int n) {
if (n == 0) return 1;
return n * foo(n-1);
}
int main() {
return foo(10);
}
我第一次感到非常惊讶的是(这也在这个问题中引起了注意 - https://dev59.com/EXRC5IYBdhLWcg3wG9Xo#414774),int foo(int)
(一个基本阶乘函数)如何编译成一个紧凑的循环。这是它的ARM汇编代码:
.globl _foo
.align 2
.code 16
.thumb_func _foo
_foo:
mov r1, r0
movs r0, #1
cbz r1, LBB0_2
LBB0_1:
muls r0, r1, r0
subs r1, #1
bne LBB0_1
LBB0_2:
bx lr
哇,我想了一下。这很有趣!完全使用紧密循环来计算阶乘。哇。它并不是尾调用优化,因为它不是尾调用。但它似乎进行了类似的优化。
现在看看 main
:
.globl _main
.align 2
.code 16
.thumb_func _main
_main:
movw r0, #24320
movt r0, #55
bx lr
说实话,这让我大开眼界。它完全绕过了foo
函数,并返回了10!
的值3628800
。
这让我真正意识到编译器通常可以比你更好地优化代码。但是,它引出了一个问题,它是如何做到如此出色的工作的?所以,有人能解释一下(可能通过链接相关代码)以下优化是如何工作的吗:
将初始的
foo
优化为紧凑循环。优化
main
直接返回结果而不是实际执行foo
函数。
此外,这个问题的另一个有趣的副作用是展示GCC/Clang可以进行的一些更有趣的优化。