user541686
user541686

Reputation: 210445

Why does the compiler generate this code?

I disassembled an object file (most likely generated using the Visual C++ compiler) using DumpBin and saw the following piece of code:

...         ...
mov         dword ptr [ebp-4],eax       // Why save EAX?
push        dword ptr [ebp+14h]
push        dword ptr [ebp+10h]
push        dword ptr [ebp+0Ch]
push        dword ptr [ebp+8]
mov         eax,dword ptr [ebp-4]       // Why restore EAX? Did it change at all?
call        <function>
...         ...

Could someone please explain why the EAX register is being saved and restored across these 4 push instructions?

Upvotes: 11

Views: 358

Answers (3)

FrankH.
FrankH.

Reputation: 18217

volatile or not, the only technical reason why EAX would have to be initialized directly before making a function call on Windows were if that function is declared __syscall, i.e. using the Windows CS_SYSCALL calling convention. Conceptually, this is a bit similar to the UN*X x86_64 convention where %al contains the number of floating point type args passed in %xmm registers.

The syscall calling convention on Windows is identical to __cdecl, i.e. function args on stack in reverse order, but with the addition that AL contains a count of the number of arguments; this is done so that the kernel code which is usually at the final end of this knows how much data to read from the user stack onto the kernel stack to retrieve the args.

EAX is a scratch register for all calling conventions on 32bit Windows, its value is never preserved over function calls, initializing it directly before making a call is redundant. Even if the variable it holds were volatile - because a simple re-load isn't a memory barrier and doesn't "commit" a previous store. In addition, the location [EBP - 4] is within the stack, so the variable is local (and a volatile qualifier makes little sense).

If it's not a missed optimization then it could be an invocation of a __syscall function(...) with different numbers of arguments, like, hypothetically,

__syscall printf_syscall_conv(char *fmt, ...);

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3)
{
    if (*strchr('%', fmt) == '\0')    // if no "%" in fmt, pass no args
        printf_syscall_conv(fmt);
    else
        printf_syscall_conv(fmt, val1, val2, val3);
}

This could conceivably create assembly output like yours.

Upvotes: 2

Matteo Italia
Matteo Italia

Reputation: 126787

Also, maybe it's compiled in release mode, but that variable has been marked as volatile, which tells the compiler that such variable may change without it knowing, so it is forced to continuously write/restore it on/from the stack

Upvotes: 10

Crashworks
Crashworks

Reputation: 41384

Was this built in debug mode? If so, the compiler stores every local variable on the stack so that the debugger can find them in a consistent way.

The elision of such unnecessary stores and reloads is one of the optimizations that constitutes "release" mode.

Upvotes: 6

Related Questions