【问题标题】:Why some compilers optimize if(a>0) and not if(*(&a)>0)?为什么有些编译器会优化 if(a>0) 而不是 if(*(&a)>0)?
【发布时间】:2013-06-17 16:01:03
【问题描述】:

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

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&gt;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 编译器版本可能存在一些错误。

【问题讨论】:

  • 什么编译器和什么编译器选项?
  • @0x90 armvct 我以前没听说过那个编译器?您可以发布链接以便我们更好地模拟它们吗?
  • GCC 4.5.3 优化了-O1 和更高版本的分支。
  • 为什么不发布 ARM 程序集转储? RVDS 的输出在哪里?除了 RVDS 这个词之外,这似乎与 ARM 无关。
  • 另一个模块可以取const的地址并改变它。在 C++ 中,没有全局 const,但在 C 中有。即,编译器不必为变量分配空间。例如,const 硬件寄存器可能意味着它对软件是只读的; extern const 的行为更有意义。有些人可能会通过阅读标准得出结论,编译器应该同样对待这些。由于编译器需要针对所有这些情况进行优化,(*(&amp;a)) 的信息可能无法通过优化器阶段,尤其是当您提供变量全局范围时。

标签: c optimization compiler-construction rvds


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    编译器完全有可能确定它不会改变。但是也有可能一些编译器在这个过程中“放弃”——这也可能取决于编译链中的哪个位置进行了这种分析。

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

    说了这么多,这应该是相当微不足道的(根据 cmets,编译器应该考虑 *(&amp;a)a 相同),所以它没有摆脱比较似乎很奇怪。

    【讨论】:

    • 毫无疑问,这是编译器未能识别优化机会,但这并不像在编译器中跨越某个复杂度阈值那么简单。 *(&amp;a) 不仅是一个非常简单的表达式,而且在 C 标准的脚注中明确标注为等于 a(1999 标准中的注释 83,1999 TC2 草案 n1124 中的 84,2011 中的 102)。我们知道这个编译器优化了a &gt; 0 给定a 的可见值。因此,它未能优化 *(&amp;a) &gt; 0 的事实表明它错过了 C 语义的一个清晰明确的方面。
    • 感谢您的澄清,埃里克!
    • 我对这篇文章投了反对票,因为还有其他事情发生。没有合理的编译器会退出。
    • @EricPostpischil:在 C++ 中并不完全相同。 C 和 C++ 中的整数常量略有不同。例如,在 C++ 的情况下,a 不构成常量 aodr-use,而 *&amp;aodr-use。虽然在示例代码中这无关紧要(符号 is 已定义),但它可能对静态成员很重要。
    • @Mikhail 一个 reasonable 编译器可能会放弃,因为变量不是 static
    猜你喜欢
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 2017-03-12
    • 1970-01-01
    • 2016-01-13
    • 2014-08-12
    • 2011-03-30
    • 1970-01-01
    相关资源
    最近更新 更多