perchè usa il movl invece di push?

prestare attenzione a questo codice:

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

dopo di che :

 gcc -S ac 

quel comando mostra il nostro codice sorgente in assembly.

ora possiamo vedere nella funzione principale, non usiamo mai il comando “push” per spingere gli argomenti di una funzione nello stack. e ha usato “movel” invece di quello

 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 

perché succede? qual è la differenza tra loro?

    Ecco cosa dice il manuale di gcc al riguardo:

     -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. 

    Apparentemente -maccumulate-outgoing-args è abilitato di default, sovrascrivendo -mpush-args . -mno-accumulate-outgoing-args esplicitamente con -mno-accumulate-outgoing-args ripristina il metodo PUSH , qui.

    Quel codice sta semplicemente mettendo le costanti (1, 2, 3) alle posizioni di offset dal puntatore dello stack (aggiornato) (esp). Il compilatore sta scegliendo di fare il “push” manualmente con lo stesso risultato.

    “push” imposta entrambi i dati e aggiorna il puntatore dello stack. In questo caso, il compilatore lo sta riducendo a un solo aggiornamento del puntatore dello stack (rispetto a tre). Un esperimento interessante sarebbe provare a cambiare la funzione “a” per prendere solo un argomento e vedere se il modello di istruzioni cambia.

    gcc esegue tutti i tipi di ottimizzazioni, inclusa la selezione delle istruzioni in base alla velocità di esecuzione della particolare CPU ottimizzata per. Noterai che cose come x *= n sono spesso sostituite da un mix di SHL, ADD e / o SUB, specialmente quando n è una costante; mentre MUL è usato solo quando il runtime medio (e cache / etc. footprints) della combinazione di SHL-ADD-SUB supera quello di MUL, o n non è una costante (e quindi l’uso di cicli con shl-add-sub sarebbe vieni più costoso).

    In caso di argomenti della funzione: MOV può essere parallelizzato dall’hardware, mentre PUSH no. (Il secondo PUSH deve attendere il completamento del primo PUSH a causa dell’aggiornamento del registro esp.) In caso di argomenti di funzione, i MOV possono essere eseguiti in parallelo.

    È su OS X per caso? Ho letto da qualche parte che richiede che il puntatore dello stack sia allineato ai limiti di 16 byte. Questo potrebbe spiegare questo tipo di generazione di codice.

    Ho trovato l’articolo: http://blogs.embarcadero.com/eboling/2009/05/20/5607

    Il set di istruzioni Pentium non ha un’istruzione per spingere una costante nello stack. Quindi usare push sarebbe lento: il programma dovrebbe mettere la costante in un registro e premere il registro:

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

    Quindi il compilatore rileva che l’utilizzo di movl è più veloce. Immagino che puoi provare a chiamare la tua funzione con una variabile anziché una costante:

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