Search code examples
assemblyx86-64

Why does eax get cleared before `leave`?


I've generated a few simple programs using the compiler explorer. Why does eax always get set to zero before the stack pops rbp? Is it standard to clear the registers before exiting?

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 1
        mov     DWORD PTR [rbp-8], 1
        add     DWORD PTR [rbp-4], 1
        mov     eax, DWORD PTR [rbp-4]
        mov     DWORD PTR [rbp-8], eax
        mov     eax, 0 <--------------------------?
        pop     rbp
        ret

For reference, I am using x86-64 gcc 12.1


Solution

  • No, it's not standard. You don't "clear" registers per-se, you always set them to some value. Sometimes that value is 0.

    In C++ and C99, there's an implicit return 0; at the bottom of main(). EAX is the return-value register in all mainstream x86 calling conventions, including your x86-64 System V. Zeroing EAX implements that return 0;, and wouldn't be there if the function wasn't called main.

    (Or if your source includes an explicit return 0; of course; you forgot to include that in your question, or even link it as part of your Godbolt link; use the "share" button to make a shortlink or better a full link.)


    In C++, falling off the end of a non-void function without a return statement is undefined behaviour. The implicit return 0 at the end of main counts, so renaming it would make it UB for that path of execution to be reached, i.e. for the function to be called.

    In C, it's only UB if the caller uses a return value from a function that fell off the end, because of C history: it existed for a while before void was added, so there was historic code that had functions declared like foo(int x) with an implicit int return value, but actually they didn't return anything and the caller didn't read the return value.

    So anyway, renaming an int main() to int foo() without adding a return statement will give your function undefined behaviour in C++, and with optimization enabled GCC won't emit any instructions at all for it, not even a ret.

    (It will of course also print a warning, at least with -Wall. You should definitely enable -Wall while playing around with compiler output, as things the compiler thinks are worth warning about are often related to weird asm. And on Godbolt, look for any red in the bottom left of the asm output pane, showing the number of warnings. Click on it to open a new pane with that.)