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



假设我在全局作用域中声明了:

const int a =0x93191;

在main函数中,我有以下条件:

if(a>0)
    do_something

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

but如果我写:

if(*(&a)>0)
    do_something

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


相比之下,GCC确实优化了(-O1-O2-O3):

#include <stdio.h>
const a = 3333;
int main()
{
    if (a >333)
        printf("firstn");
return 0;
}

用- 3编译:

(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("firstn");
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("firstn");
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

编译器要找出"这是我能算出的实际值是多少"的复杂程度,并不是没有界限的。如果你写了一个足够复杂的语句,编译器会简单地说"我不知道这个值是什么,我将生成代码来计算它"。

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

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

说了这么多,这应该是相当微不足道的(并且根据注释,编译器应该将*(&a)视为与a相同),因此它没有摆脱比较似乎很奇怪。

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

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

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

中填充值的情况要复杂一些。

相关内容

  • 没有找到相关文章

最新更新