简体   繁体   English

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

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

I have write a very simple program to test gcc -O2 option, it will generate a wrong behavior. 我已经编写了一个非常简单的程序来测试gcc -O2选项,它将生成错误的行为。 Is it a bug of gcc? 这是gcc的错误吗?

My C program: 我的C程序:

#include <stdio.h>

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

When I compile it with -O1, everything is right, i is added 1 in each loop. 当我用-O1编译它时,一切都正确,我在每个循环中加1。 But when I compile it with -O2, i is ignored, "hello" is output in each loop. 但是,当我使用-O2进行编译时,将被忽略,在每个循环中都会输出“ hello”。 Why -O2 will cause this wrong behavior? 为什么-O2会导致这种错误行为? Is obvious bug of gcc? 是gcc的明显错误吗? My gcc version is 4.8.3 for cygwin, and I have tried a new one, it also has the same problem. 我的gcc版本是cygwin的4.8.3,我尝试了一个新版本,它也有同样的问题。

Compile with -O1, the assembly code is: 用-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

Compile with -O2, the assembly code is: 用-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

This optimization is correct. 此优化是正确的。

At -O1 , the compiler is essentially rewriting your function as: -O1 ,编译器实质上将您的函数重写为:

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

At -O2 , the empty loop is being optimized out. -O2 ,正在优化空循环。

Doesn't sound like a bug to me. 对我来说听起来不像是个虫子。 Why should the compiler generate code that will waste time when the observable behavior of the program is to output "hello\\n" in an endless loop. 当程序的可观察行为是在无限循环中输出“ hello \\ n”时,为什么编译器为什么要生成会浪费时间的代码。 You don't need to increment a useless variable. 您不需要增加无用的变量。

It notices that you're not doing anything else in the loops, and so optimizes them away. 它会注意到您在循环中没有执行任何其他操作,因此可以优化它们。 You still get the same output. 您仍然会得到相同的输出。 That's the whole point of optimizations: it gets rid of useless stuff going on inside as long as the same output is generated. 这就是优化的全部要点:只要生成相同的输出,它就会消除内部无用的内容。

Edit: As mentioned in this post , if you want to keep an empty loop from being optimized away, you can include asm(""); 编辑:作为中提到的这篇文章 ,如果你想保持一个空的循环被优化掉,可以包括asm(""); , an empty assembly command, inside the body of the while loop. 是一个空的汇编命令,位于while循环的主体内部。 This keeps GCC from optimizing it away. 这使GCC无法对其进行优化。

What behavior would you expect? 您会期望什么行为? Basically the i++ > 0x1FFFFFFFUL condition will be true infinite number of times, and each time it will be true it will print "hello". 基本上, i++ > 0x1FFFFFFFUL条件将是无数次的真实值,并且每次满足时它将打印“ hello”。

The externally observable behavior is to print "hello" infinite number of times. 外部可观察到的行为是无限次打印“ hello”。 It's rather overly clever optimization from the compiler to just produce that. 编译器仅产生这种优化就太聪明了。

If you want an empty loop to actually being looped you should probably not tell the compiler to optimize that hard, or better make sure that you have externally observable behavior each run of the loop. 如果您希望实际循环一个空循环,则可能不应该告诉编译器进行困难的优化,或者最好确保循环的每次运行都具有外部可观察到的行为。 By definition this can be done by volatile declaring i : 根据定义,这可以通过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 declaration basically means that the compiler have to produce code that actually performs every write to that variable. volatile声明基本上意味着编译器必须生成实际执行对该变量的每次写入的代码。

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

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