Почему% eax обнуляется перед вызовом printf?

Я пытаюсь подобрать немного x86. Я компилирую на 64-битном mac с gcc -S -O0.

Код в C:

printf("%d", 1); 

Вывод:

 movl $1, %esi leaq LC0(%rip), %rdi movl $0, %eax ; WHY? call _printf 

Я не понимаю, почему% eax очищается до 0, пока не вызывается «printf». Поскольку printf возвращает количество символов, напечатанных в %eax я думаю, что он будет обнулен, чтобы подготовить его к printf но я бы предположил, что printf должен будет нести ответственность за его готовность. Кроме того, наоборот, если я вызываю свою собственную функцию int testproc(int p1) , gcc видит необходимости готовить %eax . Поэтому мне интересно, почему gcc обрабатывает printf и testproc разному.

Из системы x86_64 V ABI :

 Register Usage %rax temporary register; with variable arguments passes information about the number of vector registers used; 1st return register ... 

printf – это функция с переменными аргументами, а число используемых векторных регистров равно нулю.

Обратите внимание, что printf должен проверять только %al , поскольку вызывающему абоненту разрешено оставлять мусор в более высоких байтах %rax . (Тем не менее, xor %eax,%eax – самый эффективный путь к нулю %al )

Более подробную информацию см. В этой статье Q & A и x86 tag wiki, или для современных ссылок ABI, если вышеуказанная ссылка устарела.

В 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  void test(void) { puts("foo"); } 

когда скомпилировано с gcc -c -O0 -S , выходы:

 pushq %rbp movq %rsp, %rbp leaq LC0(%rip), %rdi call _puts leave ret 

и %eax не обнуляется. Однако, если вы удалите #include то итоговая assembly обнуляет %eax прямо перед вызовом puts() :

 pushq %rbp movq %rsp, %rbp leaq LC0(%rip), %rdi movl $0, %eax call _puts leave ret 

Причина связана с вашим вторым вопросом:

Это также случается перед любым вызовом моей собственной функции void proc () (даже с -O2 set), но она не обнуляется при вызове функции void proc2 (int param).

Если компилятор не видит объявления функции, то он не делает никаких предположений о своих параметрах, и функция может принимать переменные аргументы. То же самое применяется, если вы укажете пустой список параметров (который вам не следует, и он помечен как устаревшая функция C по ISO / IEC). Поскольку у компилятора недостаточно информации о параметрах функции, он обнуляет %eax перед вызовом функции, потому что это может быть случай, когда функция определена как имеющая переменные аргументы.

Например:

 #include  void function() { puts("foo"); } void test(void) { function(); } 

где function() имеет пустой список параметров, приводит к:

 pushq %rbp movq %rsp, %rbp movl $0, %eax call _function leave ret 

Однако, если вы следуете рекомендациям практики определения void когда функция не принимает никаких параметров, таких как:

 #include  void function(void) { puts("foo"); } void test(void) { function(); } 

то компилятор знает, что function() не принимает аргументы – в частности, она не принимает переменные аргументы – и, следовательно, не очищает %eax до вызова этой функции:

 pushq %rbp movq %rsp, %rbp call _function leave ret 
Interesting Posts
Давайте будем гением компьютера.