11

So I have the following C code I wrote:

#include <stdio.h>


int main() {
    int i = 1;

    while(i) {
        printf("in loop\n");
        i++;

        if(i == 10) {
            break;
        }
    }

    return 0;
}

Compiled with gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2 it disassembles to this:

   0x000000000040051c <+0>: push   %rbp
   0x000000000040051d <+1>: mov    %rsp,%rbp
   0x0000000000400520 <+4>: sub    $0x10,%rsp
   0x0000000000400524 <+8>: movl   $0x1,-0x4(%rbp)
   0x000000000040052b <+15>:    jmp    0x400541 <main+37>
   0x000000000040052d <+17>:    mov    $0x400604,%edi
   0x0000000000400532 <+22>:    callq  0x4003f0 <puts@plt>
   0x0000000000400537 <+27>:    addl   $0x1,-0x4(%rbp)
   0x000000000040053b <+31>:    cmpl   $0xa,-0x4(%rbp)
   0x000000000040053f <+35>:    je     0x400549 <main+45>
   0x0000000000400541 <+37>:    cmpl   $0x0,-0x4(%rbp)
   0x0000000000400545 <+41>:    jne    0x40052d <main+17>
   0x0000000000400547 <+43>:    jmp    0x40054a <main+46>
   0x0000000000400549 <+45>:    nop
   0x000000000040054a <+46>:    mov    $0x0,%eax
   0x000000000040054f <+51>:    leaveq 
   0x0000000000400550 <+52>:    retq  

Why is there a nop on +45? And why does not je on +35 just jump right to +46?

15

It might be for function alignment. As it is now it returns on 0x400550, which can be divided by 8. If it returned on 0x40054f it isn't aligned. Just a speculation, though.

3
  • 2
    I Googled function alignment and found this post on SO, and it seems to answer my question in some more detail. Thank you for your help. – pyCtrl_ Oct 4 '13 at 15:32
  • 3
    Sorry, I'm not convinced this is function alignment. This only applies to function starts, not ends. It's not -falign-labels either (there is a nop inserted as per description of falign-labels but the adjusted address is not used). I rather think this is caused by the compiler reserving some bytes for longer representation of opcodes and not cleaned up. – Jongware Oct 13 '13 at 14:07
  • 4
    Yes, this just appears to be unoptimized code. Try comparing the code generated by -O3 or -Os to the code generated by -O0. – microtherion Oct 13 '13 at 16:44
3

Most microprocessors fetch code in aligned 16-byte or 32-byte blocks. If an important subroutine entry or jump label happens to be near the end of a 16-byte block then the microprocessor will only get a few useful bytes of code when fetching that block of code. It may have to fetch the next 16 bytes too before it can decode the first instructions after the label. This can be avoided by aligning important subroutine entries and loop entries by 16. Aligning by 8 will assure that at least 8 bytes of code can be loaded with the first instruction fetch, which may be sufficient if the instructions are small.

via Optimizing subroutines in assembly language by Agner Fog. PDF

1
  • Can you point out this better alignment in the OP's disassembly? I don't get why the nops place would be an improvement. – Jongware Oct 19 '13 at 0:47
0

Another reason for NOP insertion is due to pipeline scheduling. If it takes a cycle for branch prediction to determine whether it was correct or not (and if not to flush the pipe), then you'd need a cycle delay before results are committed to registers.

Regarding the specific example where the jump equal goes to a NOP, it appears to me that the processor needs a cycle to determine whether it got the right answer or not and adjust the pipe as necessary.

Great job digging in to the code and understanding what is going on. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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