usar tag qué que primero posicionarse negocio manager instalar hacer google fragmento cómo contenedor configurar como aparezca aparecer c assembly stack

c - tag - ¿Por qué usa el movl en lugar de push?



posicionarse en google maps (5)

Preste atención a este código:

#include <stdio.h> void a(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } int main() { a(1,2,3); }

después de esto :

gcc -S a.c

Ese comando muestra nuestro código fuente en ensamblador.

Ahora podemos ver en la función principal, nunca usamos el comando "push" para empujar los argumentos de la función en la pila. y usó "movel" en lugar de eso

main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $3, 8(%esp) movl $2, 4(%esp) movl $1, (%esp) call a leave

¿Por que sucede? ¿Qué diferencia hay entre ellos?


¿Es esto en OS X por casualidad? Leí en algún lugar que requiere que el puntero de pila esté alineado en los límites de 16 bytes. Eso podría explicar este tipo de generación de código.

Encontré el artículo: http://blogs.embarcadero.com/eboling/2009/05/20/5607


El conjunto de instrucciones Pentium no tiene una instrucción para empujar una constante en la pila. Por lo tanto, utilizar push sería lento: el programa tendría que poner la constante en un registro y empujar el registro:

... movl $1, %eax pushl %eax ...

Así que el compilador detecta que usar movl es más rápido. Supongo que puedes intentar llamar a tu función con una variable en lugar de una constante:

int x; scanf("%d", &x); // make sure x is not a constant a(x, x, x);


Ese código simplemente pone las constantes (1, 2, 3) directamente en las posiciones de desplazamiento desde el puntero de pila (actualizado) (esp). El compilador está eligiendo hacer el "empuje" manualmente con el mismo resultado.

"push" establece los datos y actualiza el puntero de pila. En este caso, el compilador está reduciendo eso a solo una actualización del puntero de pila (vs. tres). Un experimento interesante sería intentar cambiar la función "a" para tomar solo un argumento y ver si el patrón de instrucción cambia.


Esto es lo que el manual de gcc tiene que decir al respecto:

-mpush-args -mno-push-args Use PUSH operations to store outgoing parameters. This method is shorter and usually equally fast as method using SUB/MOV operations and is enabled by default. In some cases disabling it may improve performance because of improved scheduling and reduced dependencies. -maccumulate-outgoing-args If enabled, the maximum amount of space required for outgoing arguments will be computed in the function prologue. This is faster on most modern CPUs because of reduced dependencies, improved scheduling and reduced stack usage when preferred stack boundary is not equal to 2. The drawback is a notable increase in code size. This switch implies -mno-push-args.

Al parecer, -maccumulate-outgoing-args está habilitado de forma predeterminada, anulando -mpush-args . La compilación explícita con -mno-accumulate-outgoing-args vuelve al método PUSH , aquí.


gcc realiza todo tipo de optimizaciones, incluida la selección de instrucciones basadas en la velocidad de ejecución de la CPU en particular que se está optimizando. Notará que cosas como x *= n menudo se reemplazan por una mezcla de SHL, ADD y / o SUB, especialmente cuando n es una constante; mientras que MUL solo se usa cuando el tiempo de ejecución promedio (y las huellas de caché / etc.) de la combinación de SHL-ADD-SUB excedería el de MUL, o n no es una constante (y, por lo tanto, usar bucles con shl-add-sub) venir más costoso).

En caso de argumentos de función: MOV puede ser paralelizado por hardware, mientras que PUSH no puede. (El segundo PUSH debe esperar a que termine el primer PUSH debido a la actualización del registro esp). En el caso de los argumentos de función, los MOV pueden ejecutarse en paralelo.