2011-06-02 7 views
21

Estoy tratando de recoger un poco de x86. Estoy compilando en un mac de 64 bits con gcc -S-O0.¿Por qué% eax se pone a cero antes de una llamada a printf?

Código en C:

printf("%d", 1); 

Salida:

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

No entiendo por qué% eax se borra a 0 ante 'printf' se llama. Dado que printf devuelve el número de caracteres impresos a %eax mi mejor estimación es que se ha reducido a cero para prepararlo para printf, pero habría supuesto que printf tendría que ser responsable de prepararlo. Además, en cambio, si llamo a mi propia función int testproc(int p1), gcc no es necesario preparar %eax. Entonces me pregunto por qué gcc trata printf y testproc de manera diferente.

Respuesta

21

Desde el x86_64 System V ABI:

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

printf es una función con argumentos variables, y el número de registros vector usado es cero.

Tenga en cuenta que printf debe marcar solo %al, porque la persona que llama puede dejar basura en los bytes superiores de %rax. (Sin embargo, xor %eax,%eax es la forma más eficiente a cero %al)

Véase el this Q&A y la etiqueta wiki para más detalles, o para-para arriba-hasta la fecha enlaces ABI si el enlace anterior es rancio.

+0

'puts' está también poniendo a cero'% eax' justo antes de la llamada aunque solo toma un solo puntero. ¿Por qué es esto? – sh54

+0

Esto también ocurre antes de cualquier llamada a mi propia función 'void proc()' (incluso con -O2 establecido), pero no se pone a cero cuando se llama a una función 'void proc2 (int param)'. – sh54

+2

Para el registro, sucede antes de las llamadas a 'void proc()' porque esa firma en C en realidad no dice nada sobre el valor de proc, y podría ser una función variada, por lo que es necesario poner a cero el rax. 'void proc()' es diferente de 'void proc (void)'. Consulte http://stackoverflow.com/questions/693788/c-void-arguments – frangio

32

En el ABI x86_64, si una función tiene argumentos variables, entonces se espera que AL (que es parte de EAX) contenga el número de registros vectoriales utilizados para contener argumentos para esa función.

En su ejemplo:

printf("%d", 1); 

tiene un argumento entero así que no hay necesidad de un registro vectorial, por lo tanto, AL se establece en 0.

Por otro lado, si cambia de ejemplo para:

printf("%f", 1.0f); 

entonces el literal de coma flotante se almacena en un registro de vectores y, en consecuencia, AL se establece en 1:

movsd LC1(%rip), %xmm0 
leaq LC0(%rip), %rdi 
movl $1, %eax 
call _printf 

Como era de esperar:

printf("%f %f", 1.0f, 2.0f); 

hará que el compilador para configurar AL-2 ya que hay dos argumentos de coma flotante:

movsd LC0(%rip), %xmm0 
movapd %xmm0, %xmm1 
movsd LC2(%rip), %xmm0 
leaq LC1(%rip), %rdi 
movl $2, %eax 
call _printf 

En cuanto a sus otras preguntas:

puts también está poniendo a cero %eax justo antes de la llamada pesar de que sólo toma un solo puntero. ¿Por qué es esto?

No debería. Por ejemplo:

#include <stdio.h> 

void test(void) { 
    puts("foo"); 
} 

cuando se compila con gcc -c -O0 -S, salidas:

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

y %eax no se pone a cero. Sin embargo, si se quita #include <stdio.h> continuación, el conjunto resultante hace cero a cabo %eax justo antes de llamar puts():

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

La razón está relacionada con su segunda pregunta:

Esto también ocurre antes de cualquier llamada a mi propia void proc() función (incluso con -O2 establecido), pero no se pone a cero cuando se llama a una función void proc2 (int param).

Si el compilador no ve la declaración de una función, no hace suposiciones sobre sus parámetros, y la función podría aceptar argumentos variables. Lo mismo se aplica si especifica una lista de parámetros vacía (que no debería, y está marcada como una característica obsoleta C por ISO/IEC). Dado que el compilador no tiene suficiente información sobre los parámetros de la función, se pone a cero %eax antes de llamar a la función porque podría ser el caso de que la función se define como que tiene argumentos variables.

Por ejemplo:

#include <stdio.h> 

void function() { 
    puts("foo"); 
} 

void test(void) { 
    function(); 
} 

donde function() tiene una lista de parámetros vacía, da como resultado:

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

Sin embargo, si se sigue la práctica de especificar void recomiendan cuando la función no acepta parámetros, por ejemplo:

#include <stdio.h> 

void function(void) { 
    puts("foo"); 
} 

void test(void) { 
    function(); 
} 

la n el compilador sabe que function() no acepta argumentos - en particular, que no acepta argumentos variables - y por lo tanto no se borra %eax antes de llamar a esa función:

pushq %rbp 
movq %rsp, %rbp 
call _function 
leave 
ret 
+1

Nota de ABI: "Usamos el registro vectorial para referirnos a SSE o al registro AVX". –

+1

¿Cuál es la ventaja de pasar el conteo de vectores en '% rax'? ¿Es solo de rendimiento, para evitar guardar registros inútiles en "The Register Save Area"? –

Cuestiones relacionadas