Take the 2-minute tour ×
Reverse Engineering Stack Exchange is a question and answer site for researchers and developers who explore the principles of a system through analysis of its structure, function, and operation. It's 100% free, no registration required.

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) {
            printf("Hello\n");
            i++;
    }

    i = 0;

    while(i<10) {
            printf("i: %d\n", i);
            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> 
share|improve this question

2 Answers 2

up vote 6 down vote accepted

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.

share|improve this answer
    
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 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. –  pyCtrl_ Mar 13 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.

share|improve this answer
    
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 at 7:20
1  
dec ebx is 0xff 0xcb –  PlasmaHH Mar 11 at 13:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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