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.