在 x86_64 ABI 中,如果一个函数具有可变参数,那么AL
(它是 的一部分EAX
)应该保存用于保存该函数参数的向量寄存器的数量。
在您的示例中:
printf("%d", 1);
有一个整数参数,因此不需要向量寄存器,因此AL
设置为 0。
另一方面,如果您将示例更改为:
printf("%f", 1.0f);
然后将浮点文字存储在向量寄存器中,并相应AL
地设置为1
:
movsd LC1(%rip), %xmm0
leaq LC0(%rip), %rdi
movl $1, %eax
call _printf
正如预期的那样:
printf("%f %f", 1.0f, 2.0f);
将导致编译器设置AL
为,2
因为有两个浮点参数:
movsd LC0(%rip), %xmm0
movapd %xmm0, %xmm1
movsd LC2(%rip), %xmm0
leaq LC1(%rip), %rdi
movl $2, %eax
call _printf
至于你的其他问题:
puts
%eax
尽管它只需要一个指针,但它也在调用之前归零。为什么是这样?
它不应该。例如:
#include <stdio.h>
void test(void) {
puts("foo");
}
使用 编译时gcc -c -O0 -S
,输出:
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
call _puts
leave
ret
并且%eax
没有归零。但是,如果您删除,则生成的程序集在调用之前#include <stdio.h>
会归零:%eax
puts()
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
movl $0, %eax
call _puts
leave
ret
原因与您的第二个问题有关:
这也发生在对我自己的 void proc() 函数的任何调用之前(即使设置了 -O2 ),但在调用 void proc2(int param) 函数时它不会归零。
如果编译器没有看到函数的声明,那么它不会对其参数做任何假设,并且该函数可以很好地接受可变参数。如果您指定一个空参数列表(您不应该这样做,并且它被 ISO/IEC 标记为过时的 C 功能),这同样适用。由于编译器没有关于函数参数的足够信息,它%eax
会在调用函数之前清零,因为函数可能被定义为具有可变参数。
例如:
#include <stdio.h>
void function() {
puts("foo");
}
void test(void) {
function();
}
wherefunction()
有一个空的参数列表,结果是:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
call _function
leave
ret
但是,如果您遵循指定void
函数何时不接受参数的推荐做法,例如:
#include <stdio.h>
void function(void) {
puts("foo");
}
void test(void) {
function();
}
然后编译器知道它function()
不接受参数 - 特别是,它不接受可变参数 - 因此%eax
在调用该函数之前不会清除:
pushq %rbp
movq %rsp, %rbp
call _function
leave
ret