Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在编译过程中,什么时候删除这些无关紧要的代码(没有效果的代码)? < >上面的C++代码似乎在英特尔汇编中产生以下代码: volatile int num = 0; num = num + 10; P>按照C++中的“IF”规则,一个实现可以自由地做任何它想要的,只要可观察的行为符合标准。具体来说,在C++17,4.6/1中(作为一个示例):_C++_Gcc_Assembly_X86 - Fatal编程技术网

在编译过程中,什么时候删除这些无关紧要的代码(没有效果的代码)? < >上面的C++代码似乎在英特尔汇编中产生以下代码: volatile int num = 0; num = num + 10; P>按照C++中的“IF”规则,一个实现可以自由地做任何它想要的,只要可观察的行为符合标准。具体来说,在C++17,4.6/1中(作为一个示例):

在编译过程中,什么时候删除这些无关紧要的代码(没有效果的代码)? < >上面的C++代码似乎在英特尔汇编中产生以下代码: volatile int num = 0; num = num + 10; P>按照C++中的“IF”规则,一个实现可以自由地做任何它想要的,只要可观察的行为符合标准。具体来说,在C++17,4.6/1中(作为一个示例):,c++,gcc,assembly,x86,C++,Gcc,Assembly,X86,为什么编译器不会生成汇编代码: volatile int num = 0; num = num + 0; 这种琐碎的代码在编译过程的哪一部分被删除。是否有任何编译器标志会使GCC编译器不进行此类优化 > P>按照C++中的“IF”规则,一个实现可以自由地做任何它想要的,只要可观察的行为符合标准。具体来说,在C++17,4.6/1中(作为一个示例): 。。。一致性实现需要模拟(仅)抽象机器的可观察行为,如下所述 这一规定有时被称为“仿佛”规则,因为实施可以自由地忽略本国际标准的任何要求,只要其

为什么编译器不会生成汇编代码:

volatile int num = 0;
num = num + 0;

这种琐碎的代码在编译过程的哪一部分被删除。是否有任何编译器标志会使GCC编译器不进行此类优化

> P>按照C++中的“IF”规则,一个实现可以自由地做任何它想要的,只要可观察的行为符合标准。具体来说,在
C++17,4.6/1中(作为一个示例):

。。。一致性实现需要模拟(仅)抽象机器的可观察行为,如下所述

这一规定有时被称为“仿佛”规则,因为实施可以自由地忽略本国际标准的任何要求,只要其结果是符合要求,只要可以从程序的可观察行为中确定

例如,如果一个实际的实现可以推断出它的值没有被使用,并且不会产生影响程序的可观察行为的副作用,那么它就不需要计算表达式的一部分


至于如何控制
gcc
,我的第一个建议是使用
-O0
标志关闭所有优化。您可以通过使用各种
-f
选项获得更精细的控制,但
-O0
应该是一个好的开始。

叮当声将在
-O0
处发出
添加eax、0
,但gcc、ICC和MSVC都不会发出。见下文


gcc-O0
并不意味着“没有优化”。gcc没有一种“脑死的直译”模式,它尝试将每个C表达式的每个组件直接音译到asm指令

GCC的
-O0
并不打算完全未优化。它的目的是“快速编译”,并使调试产生预期的结果(即使使用调试器修改C变量,或跳转到函数中的另一行)。因此,它会溢出/重新加载每个C语句周围的所有内容,假设在此类块之前停止的调试器可以异步修改内存。(有趣的后果示例和更详细的解释:)


对于
gcc-O0
来说,没有太多的需求来生成更慢的代码(例如,忘记了
0
是加法标识),因此没有人实现这一选项。如果这种行为是可选的,它甚至可能会使gcc变慢。(或者可能有这样一个选项,但它在默认情况下即使在
-O0
,也会打开,因为它很快,不会影响调试,而且很有用。通常,当调试构建运行得足够快,可以使用时,人们会喜欢它,特别是对于大型或实时项目。)

正如@Basile Starynkevitch在中所解释的,gcc总是在生成可执行文件的过程中通过其内部表示进行转换。只要这样做,就会产生一些优化

例如,gcc的“除以常数”算法使用or移位(2的幂)代替
idiv
指令。但是
clang-O0
将使用
idiv
表示
x/=2


在这种情况下,Clang的
-O0
优化程度也低于gcc:

mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-4]
add eax, 0
mov DWORD PTR [rbp-4], eax

正如您所说,gcc省略了无用的
addeax,0
。ICC17多次存储/重新加载。在调试模式下,MSVC通常非常直白,但即使它也避免发出
addeax,0


Clang也是Godbolt上4个x86编译器中唯一一个将
idiv
用于
返回x/2的编译器。其他的都是SAR+CMOV或其他实现C符号划分语义的工具。

@vu1p3n0x:我想kris是在问为什么gcc只加载
num
并再次存储它,而不使用
addeax,0
指令(因为gcc即使在
-O0
上也会优化该部分)。相关的,并回答了以下问题:。gcc没有“完全愚蠢”的模式。在生成可执行文件的过程中,它总是通过其内部表示进行转换。有人猜测,它从未“被删除”,但实际上从未被添加。编译器不必花那么多功夫来实现
x+0
不需要代码。如果您只是想在机器代码中保留立即字节以供进一步修补,那么不清楚为什么不使用
10
版本,并在默认情况下将其修补为
0
,或者根据需要修补为其他
-128..+127
值。(还请注意,如果您的目标是二进制修补,您可能希望使用一些32b或64b常量来获得足够大的立即数编码的
add
,因为
-128..+127
范围中的值将只使用imm8编码(值为单字节)…至少对于任何通用汇编程序(由gcc使用))。有趣的是,您没有明确回答“在编译过程的哪个部分”的问题,尽管隐式地说,当解析的文本转换为“gcc用于生成最终机器代码的内容”的内部表示时,它显然是作为内部步骤之一发生的。甚至不能命名它,诱惑使用“C++抽象机”,但是GCC所使用的内部表示肯定比纯C++抽象机具有更多的属性和特征,并且我不知道GCC开发者是否有它的名字。@ Po7G:GCC的内部表示之一称为GIMPLE。另一个是寄存器转移语言(RTL),我想。IDK什么时候发生的。你可以在不同的步骤中甩掉内部代表,但我不是这方面的专家。
void foo(void) {
    volatile int num = 0;
    num = num + 0;
}
    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