Even more strange, but still not a real compiler bug:
If you pass the -32
value as 7th or later variadic argument, it may be expanded to 64bit:
#include <stdio.h>
#include <stdarg.h>
/* long long int is a 64bit datatype on both Unix and Windows */
void setter(long long int arg, ...)
{
va_list args;
va_start(args, arg);
while(arg != 0) {
printf("0x%016llx %lld\n", arg, arg);
arg = va_arg(args, long long int);
}
va_end(args);
}
int main()
{
setter(-32, -32, -32, -32, -32, -32, -32, -32, 0);
return 0;
}
And the output on x64 Linux is:
0xffffffffffffffe0 -32
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0xffffffffffffffe0 -32
0xffffffffffffffe0 -32
However, the output on x64 Windows is something like:
0xffffffffffffffe0 -32
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00040800ffffffe0 1134700294832096
0x178bfbffffffffe0 1696726761565323232
0x00007ff6ffffffe0 140698833649632
0x00007ff6ffffffe0 140698833649632
The reason here is that in the 64bit x86 calling conventions, a number of leading arguments (6 in System V AMD64 ABI, 4 in Microsoft x64) is passed via CPU registers, and only subsequent arguments are passed via the stack.
As the 64bit registers provide separate access to their lower 32bit, the compiler does set their lower 32bits only, so there is no value expansion to 64bit.
For the subsequent arguments, it depends on the compiler:
- gcc does push expanded 64bit values onto the stack
- MSVC cl.exe does write each lower 32bits onto the stack, leaving each upper 32bits uninitialized there
- not sure about other compilers
In any case, there are 64bits reserved for each argument on the stack.