我有一个简单的程序:
#include <cmath>
int main()
{
for (int i = 0; i < 50; ++i)
std::sqrt(i);
}
Clang 3.8 在 -O3
下进行了优化,但gcc 6.1 没有。它生成了以下汇编代码:
## Annotations in comments added after the question was answered,
## for the benefit of future readers.
main:
pushq %rbx
xorl %ebx, %ebx
jmp .L2
.L4:
pxor %xmm0, %xmm0 # break cvtsi2sd's false dep on the old value of xmm0
pxor %xmm1, %xmm1 # xmm1 = 0.0
cvtsi2sd %ebx, %xmm0 # xmm0 = (double)i
ucomisd %xmm0, %xmm1 # scalar double comparison, setting flags
ja .L7 # if (0.0 > (double)i) sqrt(i); // The `a` = above. AT&T syntax reverses the order, but it's jump if xmm1 above xmm0
.L2:
addl $1, %ebx # i++
cmpl $50, %ebx
jne .L4 # i != 50
xorl %eax, %eax
popq %rbx
ret # return 0
.L7:
call sqrt # only executed on i < 0. Otherwise gcc knows std::sqrt has no side effects.
jmp .L2
如果我正确理解了“as-if”规则,编译器可以优化掉不改变程序可观察行为的代码,包括I/O写入等。我丢弃了
std::sqrt
的结果并且没有进行任何I/O操作。此外,我的程序中没有#pragma STDC FENV_ACCESS
。那么,std::sqrt
是否具有可观察的副作用,或者GCC不优化调用的原因是其他的呢?
std::sqrt
是实现的一部分,因此编译器可以知道它的作用。 - Baum mit Augensqrt
可能有高度优化的汇编实现,这使得 gcc 难以证明它没有副作用。 - nwp-fno-math-errno
是一个编译器选项,意为禁止在浮点运算中设置errno变量。 - Marc Glisse