首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么某些编译器优化if(a>0)而不是if(*(&a)>0)?

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

Stack Overflow用户
提问于 2013-06-20 17:10:05
回答 2查看 376关注 0票数 8

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

代码语言:javascript
复制
const int a =0x93191;

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

代码语言:javascript
复制
if(a>0)
    do_something

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

如果我写:但是

代码语言:javascript
复制
if(*(&a)>0)
    do_something

if (cmpbranch)将位于已编译的对象文件中。

相反,GCC确实使用(-O1-O2-O3)对两者进行优化:

代码语言:javascript
复制
#include <stdio.h>
const a = 3333;

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

return 0;
}

-O3:编译的

代码语言:javascript
复制
(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.

而为了

代码语言:javascript
复制
#include <stdio.h>
const a = 3333;

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

return 0;
}

将给予:

代码语言:javascript
复制
(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没有:

代码语言:javascript
复制
#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编译器版本中可能有一些bug。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-06-20 19:14:45

优化是编译器的实现细节。实现它们需要时间和精力,编译器编写人员通常关注于语言的共同用途(例如,极不频繁的优化代码的投资回报几乎为零)。

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

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

票数 4
EN

Stack Overflow用户

发布于 2013-06-20 17:18:17

编译器需要经过多大的复杂程度才能找到“这是我能找出的实际值的东西”,这并不是无限的。如果您编写了一个足够复杂的语句,编译器就会简单地说:“我不知道值是什么,我会生成代码来计算它”。

对于编译器来说,这是完全有可能的,因为它不会改变。但是,也有可能一些编译器在这个过程中“放弃”--这也可能取决于编译链中的这个分析是在哪里完成的。

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

话虽如此,但这应该是相当微不足道的(而且按照注释,编译器应该与a一样同意a),所以它不排除比较似乎很奇怪。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17219697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档