Я пытаюсь подобрать немного x86. Я компилирую на 64-битном mac с gcc -S -O0.
Я не понимаю, почему% eax очищается до 0 до вызова printf. Поскольку printf
возвращает количество символов, напечатанных на %eax
, моя лучшая догадка обнуляется, чтобы подготовить его для printf
, но я предположил бы, что printf
должен будет нести ответственность за его готовность. Кроме того, напротив, если я вызываю свою собственную функцию int testproc(int p1)
, gcc
не видит необходимости готовить %eax
. Поэтому мне интересно, почему gcc
обрабатывает printf
и testproc
по-разному.
Ответ 2
В 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) она не обнуляется.
Если компилятор не видит объявления функции, то он не делает никаких предположений о своих параметрах, и функция может принимать переменные аргументы. То же самое применяется, если вы укажете пустой список параметров (который вы не должны, и его обозначили как устаревшую функцию C по ISO/IEC). Поскольку компилятор не имеет достаточной информации о параметрах функции, перед вызовом функции он обнуляет %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