为什么有些编译器优化if(a> 0)而不是if(*(& a)> 0)?

时间:2013-06-20 17:10:05

标签: c optimization compiler-construction rvds

假设我已在全球范围内声明:

const int a =0x93191;

在主要功能中我有以下条件:

if(a>0)
    do_something

我注意到的一个尴尬是RVDS编译器将删除if语句,并且目标文件中没有分支/ jmp。

如果我写:

if(*(&a)>0)
    do_something

if(cmpbranch)将在编译的目标文件中。


相比之下,GCC会使用(-O1-O2-O3优化两者:

#include <stdio.h>
const a = 3333;

int main()
{
    if (a >333)
        printf("first\n");

return 0;
}

使用-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.

#include <stdio.h>
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>:    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.

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.

我使用的RVDS编译器版本可能存在一些错误。

2 个答案:

答案 0 :(得分:11)

编译器将通过查找“这是我可以弄清楚实际值是什么”的复杂程度,并非无限制。如果你写了一个足够复杂的语句,编译器会简单地说“我不知道它的值是什么,我会生成代码来计算它”。

编译器完全有可能发现它不会改变。但也有可能一些编译器在这个过程中“放弃” - 它也可能取决于编译链中这个分析的位置。

这可能是“as-if”规则的一个相当典型的例子 - 允许编译器执行任何优化,生成结果“as-if”执行。

说了这么多,这应该是相当微不足道的(根据评论,编译器应该*(&a)a相同,所以它似乎很奇怪它然后不会摆脱比较。

答案 1 :(得分:4)

优化是编译器的实现细节。实现它们需要花费时间和精力,编译器编写者通常关注语言的常见用法(即,非常罕见的优化代码的投资回报几乎为零)。

话虽如此,两段代码都有一个重要区别,在第一种情况下a没有使用odr,只用作rvalue,这意味着它可以作为编译时常量处理。也就是说,当直接使用a时(没有地址,没有绑定它的引用)编译器会立即替换该值。编译器必须知道该值而不访问变量,因为它可以用于需要常量表达式的上下文(即定义数组的大小)。

在第二种情况下a使用了odr,地址被采用并且读取该位置的值。在将结果传递给优化器之前,编译器必须生成执行这些步骤的代码。反过来,优化器可以检测到它是一个常量并用值替换整个操作,但这比前一个编译器本身填充值的情况要多一些。