9

I have the following piece of code that I wrote in C. Its fairly simple as it just right bit-shifts x for every loop of for.

int main() {
   int x = 1;
   for (int i = 0; i > -2; i++) {
      x >> 2;
   }
}

Now the strange thing that is happening is that when I just compile it without any optimizations or with first level optimization (-O), it runs just fine (I am timing the executable and its about 1.4s with -O and 5.4s without any optimizations.

Now when I add -O2 or -O3 switch for compilation and time the resulting executable, it doesn't stop (I have tested for up to 60s).

Any ideas on what might be causing this?

16
  • 4
    How is your loop supposed to stop when i will always be greater than -2 Commented Aug 19, 2011 at 15:46
  • 9
    Overflow is technically Undefined Behaviour, so anything may happen (regardless of optimizations setting). Commented Aug 19, 2011 at 15:47
  • 3
    Actually pmg is right. And no, Undefined Behavior is not the thing you do in C. And "technically" it shouldn't work Commented Aug 19, 2011 at 15:51
  • 3
    @mtahmed: as you noticed, the result can be different for different optimization settings. It can also be different for different compilers on different architectures, or for different moon phases ... :) Commented Aug 19, 2011 at 15:51
  • 1
    @pmg: Curse those moon phases! Commented Aug 19, 2011 at 15:52

3 Answers 3

18

The optimized loop is producing an infinite loop which is a result of you depending on signed integer overflow. Signed integer overflow is undefined behavior in C and should not be depended on. Not only can it confuse developers it may also be optimized out by the compiler.

Assembly (no optimizations): gcc -std=c99 -S -O0 main.c

_main:
LFB2:
    pushq   %rbp
LCFI0:
    movq    %rsp, %rbp
LCFI1:
    movl    $1, -4(%rbp)
    movl    $0, -8(%rbp)
    jmp L2
L3:
    incl    -8(%rbp)
L2:
    cmpl    $-2, -8(%rbp)
    jg  L3
    movl    $0, %eax
    leave
    ret


Assembly (optimized level 3): gcc -std=c99 -S -O3 main.c

_main:
LFB2:
    pushq   %rbp
LCFI0:
    movq    %rsp, %rbp
LCFI1:
L2:
    jmp L2  #<- infinite loop
Sign up to request clarification or add additional context in comments.

2 Comments

Out of curiosity, is the behavior of integer overflow undefined in C?
@tskuzzy, signed integer overflow is UB. unsigned integers behave well and just wrap around. So OP's loop would work quite well when transposed to an unsigned setting.
7

You will get the definitive answer by looking at the binary that's produced (using objdump or something).

But as others have noted, this is probably because you're relying on undefined behaviour. One possible explanation is that the compiler is free to assume that i will never be less than -2, and so will eliminate the conditional entirely, and convert this into an infinite loop.

Also, your code has no observable side effects, so the compiler is also free to optimise the entire program away to nothing, if it likes.

Comments

2

Additional information about why integer overflows are undefined can be found here:

http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html

Search for the paragraph "Signed integer overflow".

2 Comments

There are also two more blogposts: blog.llvm.org/2011/05/… and blog.llvm.org/2011/05/…
I really can't say enough how good those articles are. Among other things, the articles really made clear and concrete exactly why certain mysterious optimizations might remove code that appears to be necessary. The reasons for and consequences of UB are not very well understood in the C community, and those articles are about the best reading on the subject I can recall. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.