1

I have write a very simple program to test gcc -O2 option, it will generate a wrong behavior. Is it a bug of gcc?

My C program:

#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. But when I compile it with -O2, i is ignored, "hello" is output in each loop. Why -O2 will cause this wrong behavior? Is obvious bug of gcc? My gcc version is 4.8.3 for cygwin, and I have tried a new one, it also has the same problem.

Compile with -O1, the assembly code is:

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:

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
undur_gongor
  • 15,657
  • 5
  • 63
  • 75
Ivan
  • 191
  • 5
  • 2
    The variable isn't `volatile` and it is not used by your program beyond creating an endless loop. I'd call it a bug if it _wasn't_ optimized away. – Lundin Mar 01 '16 at 07:54
  • 1
    @Ivan: Please avoid changing/extending your question. Create a new one (maybe referencing this question). I rolled back the last addition. – undur_gongor Mar 01 '16 at 09:30
  • OK. I have add my new question to [here](http://stackoverflow.com/questions/35720731/how-exactly-the-gcc-do-the-optimization) – Ivan Mar 01 '16 at 10:44

4 Answers4

6

This optimization is correct.

At -O1, the compiler is essentially rewriting your function as:

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

At -O2, the empty loop is being optimized out.

5

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. You don't need to increment a useless variable.

Art
  • 19,807
  • 1
  • 34
  • 60
2

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("");, an empty assembly command, inside the body of the while loop. This keeps GCC from optimizing it away.

Community
  • 1
  • 1
Chris
  • 1,613
  • 17
  • 24
1

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".

The externally observable behavior is to print "hello" infinite number of times. 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:

#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.

skyking
  • 13,817
  • 1
  • 35
  • 57