volatile int num = 0;
num = num + 10;
上面的C++代码似乎在intel汇编中产生以下代码:
mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-4]
add eax, 10
mov DWORD PTR [rbp-4], eax
如果我将C++代码更改为
volatile int num = 0;
num = num + 0;
为什么编译器不生成汇编代码,如下所示:
mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-4]
add eax, 0
mov DWORD PTR [rbp-4], eax
gcc7.2 -O0
leaves out the add eax, 0
, but all the other instructions are the same (Godbolt)。这类琐碎的代码在编译过程的哪一部分被删除。是否有任何编译器标志会使GCC编译器不进行此类优化。
最佳答案
clang将在add eax, 0
处发出-O0
,但是gcc,ICC或MSVC都不会发出。见下文。gcc -O0
并不意味着“没有优化”。 gcc没有尝试将每个C表达式的每个组件直接音译为asm指令的“braindead文字翻译”模式。
GCC的-O0
并非旨在完全未优化。它旨在“快速编译”,并使调试产生预期的结果(即使您使用调试器修改C变量,或跳转到函数中的另一行)。因此,它会溢出/重新加载每个C语句周围的所有内容,假设可以通过在此类块之前停止的调试器来异步修改内存。 (结果的有趣示例,以及更详细的解释:Why does integer division by -1 (negative one) result in FPE?)
不需要gcc -O0
来制作甚至更慢的代码(例如,忘记0
是可加性标识),因此没有人为此实现选项。如果该行为是可选的,甚至可能会使gcc变慢。 (或者也许有这样的选项,但是即使在-O0
上,它也是默认启用的,因为它速度快,不会损害调试且有用。通常人们喜欢它的调试版本可以足够快地运行以使其可用,特别是对于大型或实际的情况时间项目。)
正如@Basile Starynkevitch在Disable all optimization options in GCC中解释的那样,gcc在生成可执行文件的过程中始终会转换其内部表示形式。完全执行此操作会导致某些优化。
例如,gcc的“除以常数”算法even at -O0
使用a fixed-point multiplicative inverse或移位(对于2的幂),而不是idiv
指令。但是clang -O0
将idiv
使用x /= 2
。
在这种情况下,Clang的-O0
的优化效果也低于gcc:
void foo(void) {
volatile int num = 0;
num = num + 0;
}
asm output on Godbolt for x86-64
push rbp
mov rbp, rsp
# your asm block from the question, but with 0 instead of 10
mov dword ptr [rbp - 4], 0
mov eax, dword ptr [rbp - 4]
add eax, 0
mov dword ptr [rbp - 4], eax
pop rbp
ret
如您所说,gcc忽略了无用的
add eax,0
。 ICC17多次存储/重新加载。 MSVC在 Debug模式下通常是非常文字的,但是即使避免发出add eax,0
,它也是如此。Clang也是Godbolt上唯一将
idiv
用作return x/2;
的4个x86编译器之一。其余全部为SAR + CMOV或任何实现C的带符号划分语义的东西。