鉴于此代码:
int x;
int a (int b) {
b = a (b);
b += x;
return b;
}
为什么GCC会返回此输出(英特尔语法):http://goo.gl/8D32F1 - Godbolt的GCC Explorer
a:
sub rsp, 8
call a
mov edx, DWORD PTR x[rip]
add rsp, 8
lea eax, [rax+rdx*8]
add eax, edx
ret
和Clang返回此输出(AT& T语法):http://goo.gl/Zz2rKA - Godbolt的Clang Explorer
a: # @a
pushq %rax
callq a
addl x(%rip), %eax
popq %rdx
ret
部分代码显然无法访问?由于函数的第一个语句是
b = a (b);
该函数将永远以递归方式调用自身(直到堆栈溢出并出现段错误)。这意味着您永远不会超越该行,因此,其余代码无法访问。可达性优化理论上应该删除代码,对吗?
两个编译器都在x64上运行,并带有以下标志
-O3
- 最高优化-march=native
- [不必要]尽可能使用机器特定的优化-x c
- 假设输入语言为C 我当时认为他们应该返回更多内容(双关语):
GCC(英特尔语法):
a:
.L1:
jmp .L1
Clang(AT& T语法):
a:
.LBB0_1:
jmp .LBB0_1
注意:这些样本是从以前观察的记忆中手工编写的,可能不正确。
总的来说,为什么没有任何一个编译器将函数折叠成单个递归跳转,因为其余代码无法访问?
以下代码:
int j (int x) {
while (1) {};
x++;
return x;
}
GCC返回:http://goo.gl/CYSUW2
j:
.L2:
jmp .L2
Clang回归:
j: # @j
.LBB0_1: # =>This Inner Loop Header: Depth=1
jmp .LBB0_1
对于此代码:
int r (int x) {
return r (x);
}
GCC生成递归跳转:http://goo.gl/eWo2Nb
r:
.L2:
jmp .L2
Clang早早回复:http://goo.gl/CVJKiZ
r: # @r
ret
答案 0 :(得分:4)
您使用的编译器可能仅在单个功能框架内的块级别实现数据流分析,而不考虑递归。 (或许,只有有趣的递归,即尾递归。)由于递归调用不是尾调用,因此从优化的角度来看并不感兴趣。
你的函数有一个问题:编译它的方式,它会炸毁堆栈。它是这样编译的,因为调用不是尾调用;把它当作一个合法的优化并不合理。
可以将呼叫视为"伪尾呼叫"理由是调用之后的代码永远不会被调用,所以如果我们删除那个代码,那么递归调用就是函数做的最后一件事。然后我们可以将堆栈代码减少到仅仅是无限循环。但是,这实际上不能称为优化;它是用不同的bug表现来代替一个bug表现的。