Search code examples
cassemblycompiler-constructioncompiler-optimization

Why does main initialize stack frame when there are no variables


why does this code:

#include "stdio.h"
int main(void) {
    puts("Hello, World!");
}

decide to initialize a stack frame? Here is the assembly code:

.LC0:
        .string "Hello, World!"
main:
        push    rbp
        mov     rbp, rsp
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        mov     eax, 0
        pop     rbp
        ret

Why does the compiler initialize a stack frame only for it to be destroyed later, withoput it ever being used? This surely wont cause any errors on the outside of the main function because I never use the stack, so I wont cause any errors. Why is it compiled this way?


Solution

  • Having these steps in every compiled function is the "baseline" for the compiler, unoptimized. It looks clean in disassembly, and makes sense. However, the compiler can optimize the output to reduce overhead from code that has no real effect. You can see this by compiling with different optimization levels.

    What you got is like this:

    .LC0:
      .string "Hello, World!"
    main:
      push rbp
      mov rbp, rsp
      mov edi, OFFSET FLAT:.LC0
      call puts
      mov eax, 0
      pop rbp
      ret
    

    That's compiled in GCC with no optimization.

    Adding the flag -O4 gives this output:

    .LC0:
      .string "Hello, World!"
    main:
      sub rsp, 8
      mov edi, OFFSET FLAT:.LC0
      call puts
      xor eax, eax
      add rsp, 8
      ret
    

    You'll notice that this still moves the stack pointer, but it skips changing the base pointer, and avoid the time-consuming memory access associated with that.

    The stack is assumed to be aligned on a 16-byte boundary. With the return address having been pushed, this leaves another 8 bytes to be subtracted to get to the boundary before the function call.