Build: push vs movl

I have C code that I compiled with gcc:

int main() {
    int x = 1;
    printf("%d\n",x);
    return 0;
}

      

I ran it through gdb 7.9.1 and came up with this assembler code for main:

0x0000000100000f40 <+0>:    push   %rbp                   # save original frame pointer
0x0000000100000f41 <+1>:    mov    %rsp,%rbp              # stack pointer is new frame pointer
0x0000000100000f44 <+4>:    sub    $0x10,%rsp             # make room for vars
0x0000000100000f48 <+8>:    lea    0x47(%rip),%rdi        # 0x100000f96
0x0000000100000f4f <+15>:   movl   $0x0,-0x4(%rbp)        # put 0 on the stack
0x0000000100000f56 <+22>:   movl   $0x1,-0x8(%rbp)        # put 1 on the stack
0x0000000100000f5d <+29>:   mov    -0x8(%rbp),%esi
0x0000000100000f60 <+32>:   mov    $0x0,%al
0x0000000100000f62 <+34>:   callq  0x100000f74
0x0000000100000f67 <+39>:   xor    %esi,%esi              # set %esi to 0
0x0000000100000f69 <+41>:   mov    %eax,-0xc(%rbp)
0x0000000100000f6c <+44>:   mov    %esi,%eax
0x0000000100000f6e <+46>:   add    $0x10,%rsp             # move stack pointer to original location
0x0000000100000f72 <+50>:   pop    %rbp                   # reclaim original frame pointer
0x0000000100000f73 <+51>:   retq  

      

As I understand it push %rpb

pushes the pointer onto the stack, so we can get it later with pop %rbp

. Then it sub $0x10,%rsp

removes 10 bytes of the room on the stack so we can stack stuff on it.

Later interactions with the stack push variables directly onto the stack via memory addressing rather than push

pushing them onto the stack:

movl $0x0, -0x4(%rbp)
movl $0x1, -0x8(%rbp)

      

Why does the compiler use movl instead of push to get this information onto the stack?

Does the register in a register after the memory address match that value in that register?

+3


source to share


3 answers


Very often, modern compilers move the stack pointer once at the beginning of a function and move it back to the end. This allows for more efficient indexing since it can treat the memory space as a memory mapped area rather than a simple stack. For example, values ​​that are suddenly useless (possibly due to an optimized shortcutted operator) may be ignored rather than force them to be removed from the stack.



Perhaps in simpler days there was a reason for using push. There is no advantage with modern processors, so there is no reason to make special cases in the compiler to use push / pop whenever possible. It doesn't look like compiler-written assembly code, readable!

+4


source


While Cort is correct, there is another important reason for this practice, apparently to allocate stack space. According to the ABI, function calls must be aligned to 16 bytes. Instead of fiddling with the stack every time a call needs to be made from within a function, it is usually easier and more efficient to first adjust the stack to align correctly and then change values ​​that might otherwise have been pushed onto it.



So, the stack is absolutely set up for local variable space, but it is also set up to ensure that the stack aligns correctly for calls to the standard library.

+2


source


I'm not an authority on builders or compilers, but I played around with MASM that day and spent a whole bunch of time with WinDbg while debugging C ++ releases.

I think the answer to your question is because it is easier.

push / pop instructions write and pop off the stack, but they also modify the stack as they are processed. The C / C ++ compiler uses a stack for all of its local variables. It does this by shifting the stack pointer by the exact number of bytes it needs to store all local variables, and does it correctly when you enter the function.

After this read and write, all these variables can be done from anywhere in the function, and also as many times as you want, simply by using the mov commands. If you look at a clean assembly, you might ask why you need to create a hole in the stack to copy two values ​​into that space using mov, when you could do two push commands.

But look at it from the point of view of the compiler author. The process of entering a function and allocating a stack for local variables is coded separately and completely separate from the process of reading / writing these variables.

0


source







All Articles