I am trying to pick up a little x86. I am compiling on a 64bit mac with gcc -S -O0.
Code in C:
printf("%d", 1);
Output:
movl $1, %esi
leaq LC0(%rip), %rdi
movl $0, %eax ; WHY?
call _printf
I do not understand why %eax is cleared to 0 before 'printf' is called. Since printf
returns the number of characters printed to %eax
my best guess it is zeroed out to prepare it for printf
but I would have assumed that printf
would have to be responsible for getting it ready. Also, in contrast, if I call my own function int testproc(int p1)
, gcc
sees no need to prepare %eax
. So I wonder why gcc
treats printf
and testproc
differently.
In the x86_64 ABI, if a function has variable arguments then AL
(which is part of EAX
) is expected to hold the number of vector registers used to hold arguments to that function.
In your example:
printf("%d", 1);
has an integer argument so there’s no need for a vector register, hence AL
is set to 0.
On the other hand, if you change your example to:
printf("%f", 1.0f);
then the floating-point literal is stored in a vector register and, correspondingly, AL
is set to 1
:
movsd LC1(%rip), %xmm0
leaq LC0(%rip), %rdi
movl $1, %eax
call _printf
As expected:
printf("%f %f", 1.0f, 2.0f);
will cause the compiler to set AL
to 2
since there are two floating-point arguments:
movsd LC0(%rip), %xmm0
movapd %xmm0, %xmm1
movsd LC2(%rip), %xmm0
leaq LC1(%rip), %rdi
movl $2, %eax
call _printf
As for your other questions:
puts
is also zeroing out%eax
right before the call though it only takes a single pointer. Why is this?
It shouldn’t. For instance:
#include <stdio.h>
void test(void) {
puts("foo");
}
when compiled with gcc -c -O0 -S
, outputs:
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
call _puts
leave
ret
and %eax
is not zeroed out. However, if you remove #include <stdio.h>
then the resulting assembly does zero out %eax
right before calling puts()
:
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
movl $0, %eax
call _puts
leave
ret
The reason is related to your second question:
This also happens before any call to my own void proc() function (even with -O2 set), but it is not zeroed when calling a void proc2(int param) function.
If the compiler doesn't see the declaration of a function then it makes no assumptions about its parameters, and the function could well accept variable arguments. The same applies if you specify an empty parameter list (which you shouldn’t, and it’s marked as an obsolescent C feature by ISO/IEC). Since the compiler doesn’t have enough information about the function parameters, it zeroes out %eax
before calling the function because it might be the case that the function is defined as having variable arguments.
For example:
#include <stdio.h>
void function() {
puts("foo");
}
void test(void) {
function();
}
where function()
has an empty parameter list, results in:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
call _function
leave
ret
However, if you follow the recommend practice of specifying void
when the function accepts no parameters, such as:
#include <stdio.h>
void function(void) {
puts("foo");
}
void test(void) {
function();
}
then the compiler knows that function()
doesn't accept arguments — in particular, it doesn’t accept variable arguments — and hence doesn’t clear %eax
before calling that function:
pushq %rbp
movq %rsp, %rbp
call _function
leave
ret