0x9*_*x90 8 c compiler-construction optimization rvds
假设我已在全球范围内宣布:
const int a =0x93191;
Run Code Online (Sandbox Code Playgroud)
在主要功能中我有以下条件:
if(a>0)
do_something
Run Code Online (Sandbox Code Playgroud)
我注意到的一个尴尬是RVDS编译器将删除该if语句,并且目标文件中没有branch/jmp.
但如果我写:
if(*(&a)>0)
do_something
Run Code Online (Sandbox Code Playgroud)
if(cmp和branch)将在编译的目标文件中.
相反,GCC 用(-O1或-O2或-O3)优化两者:
#include <stdio.h>
const a = 3333;
int main()
{
if (a >333)
printf("first\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用-O3编译:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>: push %rbp
0x0000000100000f11 <main+1>: mov %rsp,%rbp
0x0000000100000f14 <main+4>: lea 0x3d(%rip),%rdi # 0x100000f58
0x0000000100000f1b <main+11>: callq 0x100000f2a <dyld_stub_puts>
0x0000000100000f20 <main+16>: xor %eax,%eax
0x0000000100000f22 <main+18>: pop %rbp
0x0000000100000f23 <main+19>: retq
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
并为
#include <stdio.h>
const a = 3333;
int main()
{
if (*(&a) >333)
printf("first\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
会给:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>: push %rbp
0x0000000100000f11 <main+1>: mov %rsp,%rbp
0x0000000100000f14 <main+4>: lea 0x3d(%rip),%rdi # 0x100000f58
0x0000000100000f1b <main+11>: callq 0x100000f2a <dyld_stub_puts>
0x0000000100000f20 <main+16>: xor %eax,%eax
0x0000000100000f22 <main+18>: pop %rbp
0x0000000100000f23 <main+19>: retq
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
GCC将两者视为相同(应该如此)而RVDS不同?
我试图检查使用的影响,volatile并且在RVDS中它确实降低了if(a>333)但是gcc没有:
#include <stdio.h>
volatile const a = 3333;
int main()
{
if (a >333)
printf("first\n");
return 0;
}
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000100000f10 <main+0>: push %rbp
0x0000000100000f11 <main+1>: mov %rsp,%rbp
0x0000000100000f14 <main+4>: cmpl $0x14e,0x12a(%rip) # 0x100001048 <a>
0x0000000100000f1e <main+14>: jl 0x100000f2c <main+28>
0x0000000100000f20 <main+16>: lea 0x39(%rip),%rdi # 0x100000f60
0x0000000100000f27 <main+23>: callq 0x100000f36 <dyld_stub_puts>
0x0000000100000f2c <main+28>: xor %eax,%eax
0x0000000100000f2e <main+30>: pop %rbp
0x0000000100000f2f <main+31>: retq
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
可能在我使用的RVDS编译器版本中存在一些错误.
Mat*_*son 11
编译器将通过找出"这是我可以弄清楚实际值是什么"的复杂程度,并非无限制.如果你写了一个足够复杂的语句,编译器会简单地说"我不知道它的值是什么,我将生成代码来计算它".
对于编译器来说,完全可以确定它不会改变.但也有可能一些编译器在这个过程中"放弃" - 它也可能取决于编译链中这个分析的位置.
这可能是"as-if"规则的一个相当典型的例子 - 允许编译器执行任何优化,生成结果"as-if"执行.
说了这么多,这应该是相当简单的(根据评论,编译器应该*(&a)相同a),所以它似乎没有摆脱比较似乎很奇怪.
优化是编译器的实现细节。实现它们需要时间和精力,并且编译器编写者通常关注该语言的常见用途(即优化极不常见的代码的投资回报几乎为零)。
话虽这么说,这两段代码有一个重要的区别,在第一种情况下,a不使用 odr,仅用作右值,这意味着它可以作为编译时常量进行处理。也就是说,当a直接使用(没有地址,没有绑定到它的引用)时,编译器立即替换该值。编译器必须知道该值,而无需访问该变量,因为它可以在常量表达式所在的上下文中使用必需的(即定义数组的大小)。
第二种情况a是使用 odr,获取地址并读取该位置的值。在将结果传递给优化器之前,编译器必须生成执行这些步骤的代码。优化器反过来可以检测到它是一个常量,并用该值替换整个操作,但这比之前编译器自己填充值的情况要复杂一些。