I have been looking at some simple C code and the different output from GCC using different optimization levels.

C code

#include <stdio.h>

int main() {
    int i = 0;

    while(i<10) {

    i = 0;

    while(i<10) {
            printf("i: %d\n", i);


When I compile the code using -Os or -O2 the first loop works a bit differently. It it decrements instead of incrementing, and it is in two different ways. I am wondering why it decrements instead of incrementing like in the code, and the the small difference between -Os and -O2.

-Os compiled

0x400486 <main+6>       mov     edi,0x40068c
0x40048b <main+11>      call    0x400450 <puts@plt>
0x400490 <main+16>      dec     ebx
0x400492 <main+18>      jne     0x400486 <main+6>

-O2 compiled

0x400490 <main+16>      mov    edi,0x40069c
0x400495 <main+21>      call   0x400450 <puts@plt>
0x40049a <main+26>      sub    ebx,0x1
0x40049d <main+29>      jne    0x400490 <main+16> 

2 Answers 2


By decrementing, compiler can make use of jne (jump if not equal/zero) which does the comparison (to zero) and jump in a single instruction. In case of incrementing, it would have to do a cmp/test (with 10) and then a conditional jump like jnz/jne. I believe it is a part of optimization.

-Os flag optimizes to bring down the code size. The code generated using -Os uses dec ebx instead of sub ebx, 0x1, since dec ebx is a 2-byte instruction while sub ebx, 0x1 is a 3-byte instruction (Note the address at which the next instruction begins). That explains the small difference.

  • This does explain the difference in one direction, but not the other; it seems that there must be an advantage of sub over dec otherwise O2 would not have chosen it.
    – PlasmaHH
    Mar 11, 2014 at 13:03
  • 1
    With a little research I think I found the reason why O2 uses sub instead of dec. Intel® 64 and IA-32 Architectures Optimization Reference Manual In table C-19 in appendix C add/sub has a lower latency. Mar 13, 2014 at 12:37

Because I can't comment, I'll try to fix some inaccuracies in pnak4j's answer.

dec ebx is really a 1-byte instruction (I don't know why it appears to be 2-bytes). DEC sets the ZF flag accordingly to the result of (ebx-1) when: zero or not zero. Then, JNE does the jump if not zero (JNE/JNZ are the same). JMP is not a conditional jump, therefore it would not make much sense after CMP/TEST.

  • By jmp, I meant a conditional jump. An unconditional jump doesn't make much sense after a compare. It has been edited. Thanks.
    – pank4j
    Mar 11, 2014 at 7:20
  • 1
    dec ebx is 0xff 0xcb
    – PlasmaHH
    Mar 11, 2014 at 13:07

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.