В ABI x86_64, если функция имеет переменные аргументы, то 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();
}
, где function()
имеет пустой список параметров, в результате:
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