繁体   English   中英

gcc -O2优化错误行为,这是一个错误吗?

[英]gcc -O2 optimize to wrong behavior, is it a bug?

我已经编写了一个非常简单的程序来测试gcc -O2选项,它将生成错误的行为。 这是gcc的错误吗?

我的C程序:

#include <stdio.h>

int main(int argc, char **argv)
{
    unsigned long i = 0;
    while (1) {
        if (++i > 0x1fffffffUL) {
            printf("hello\n");
            i = 0;
        }
    }
}

当我用-O1编译它时,一切都正确,我在每个循环中加1。 但是,当我使用-O2进行编译时,将被忽略,在每个循环中都会输出“ hello”。 为什么-O2会导致这种错误行为? 是gcc的明显错误吗? 我的gcc版本是cygwin的4.8.3,我尝试了一个新版本,它也有同样的问题。

用-O1编译,汇编代码为:

Disassembly of section .text:

    00000000 <_main>:
    #include <stdio.h>

    int main(int argc, char **argv)
    {
       0:   55                      push   %ebp
       1:   89 e5                   mov    %esp,%ebp
       3:   83 e4 f0                and    $0xfffffff0,%esp
       6:   83 ec 10                sub    $0x10,%esp
       9:   e8 00 00 00 00          call   e <_main+0xe>
       e:   b8 00 00 00 20          mov    $0x20000000,%eax
        unsigned long i = 0;
        while (1) {
            if (++i > 0x1fffffffUL) {
      13:   83 e8 01                sub    $0x1,%eax
      16:   75 fb                   jne    13 <_main+0x13>
                printf("hello\n");
      18:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
      1f:   e8 00 00 00 00          call   24 <_main+0x24>
      24:   eb e8                   jmp    e <_main+0xe>
      26:   90                      nop
      27:   90                      nop

用-O2编译,汇编代码为:

Disassembly of section .text.startup:

00000000 <_main>:
#include <stdio.h>

int main(int argc, char **argv)
{
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   83 ec 10                sub    $0x10,%esp
   9:   e8 00 00 00 00          call   e <_main+0xe>
   e:   66 90                   xchg   %ax,%ax
  10:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  17:   e8 00 00 00 00          call   1c <_main+0x1c>
  1c:   eb f2                   jmp    10 <_main+0x10>
  1e:   90                      nop
  1f:   90                      nop

此优化是正确的。

-O1 ,编译器实质上将您的函数重写为:

for (;;) {
    for (int i = 0; i < 0x20000000; i++) {
    }
    printf("hello\n");
}

-O2 ,正在优化空循环。

对我来说听起来不像是个虫子。 当程序的可观察行为是在无限循环中输出“ hello \\ n”时,为什么编译器为什么要生成会浪费时间的代码。 您不需要增加无用的变量。

它会注意到您在循环中没有执行任何其他操作,因此可以优化它们。 您仍然会得到相同的输出。 这就是优化的全部要点:只要生成相同的输出,它就会消除内部无用的内容。

编辑:作为中提到的这篇文章 ,如果你想保持一个空的循环被优化掉,可以包括asm(""); 是一个空的汇编命令,位于while循环的主体内部。 这使GCC无法对其进行优化。

您会期望什么行为? 基本上, i++ > 0x1FFFFFFFUL条件将是无数次的真实值,并且每次满足时它将打印“ hello”。

外部可观察到的行为是无限次打印“ hello”。 编译器仅产生这种优化就太聪明了。

如果您希望实际循环一个空循环,则可能不应该告诉编译器进行困难的优化,或者最好确保循环的每次运行都具有外部可观察到的行为。 根据定义,这可以通过volatile声明i来完成:

#include <stdio.h>

int main(int argc, char **argv)
{
    static volatile unsigned long i = 0;
    while (1) {
        if (++i > 0x1fffffffUL) {
            printf("hello\n");
            i = 0;
        }
    }
}

volatile声明基本上意味着编译器必须生成实际执行对该变量的每次写入的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM