Warum verwendet gcc movl anstelle von Push-to-Pass-Funktionsargumenten?

Lesezeit: 3 Minuten

Benutzer-Avatar
Puuh

achte auf diesen Code:

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

int main()
{
    a(1,2,3); 
}

danach :

gcc -S a.c

Dieser Befehl zeigt unseren Quellcode in Assembly.

Jetzt können wir in der Hauptfunktion sehen, dass wir niemals den Befehl “push” verwenden, um die Argumente der a-Funktion in den Stapel zu schieben. und es wurde stattdessen “movel” verwendet

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

Warum passiert das? Was ist der Unterschied zwischen ihnen?

Benutzer-Avatar
Narr

Hier ist was das gcc-Handbuch muss dazu sagen:

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

Offenbar -maccumulate-outgoing-args ist standardmäßig aktiviert und überschreibt -mpush-args. Explizit kompilieren mit -mno-accumulate-outgoing-args kehrt zu zurück PUSH Methode, hier.


2019-Aktualisierung: Moderne CPUs haben seit ungefähr Pentium M einen effizienten Push / Pop.
-mno-accumulate-outgoing-args (und die Verwendung von Push) wurde schließlich zum Standard für -mtune=generic im Januar 2014.

  • Eine viel bessere Frage wäre, warum diese aufblähende Option -maccumulate-outgoing-args wird nicht automatisch durch deaktiviert -Os.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    28. Dezember 2010 um 4:32 Uhr

  • @R.. Also weißt du warum?

    – Toni

    25. März 2015 um 12:49 Uhr

  • @ Tony: Offensichtlich, denn bei der Entscheidung, welche der vielen (~ 200) Optimierungs-Flags für jede spezifische -O-Option aktiviert / deaktiviert werden sollen, rutschen manchmal Dinge durch die Ritzen.

    – ninjalj

    27. Juli 2015 um 21:06 Uhr

  • Aktualisieren: -maccumulate-outgoing-args war standardmäßig deaktiviert -mtune=generic im Januar 2014, jetzt, wo CPUs ohne Stack-Engines sehr selten sind. (Wahrscheinlich hätte man das früher machen sollen).

    – Peter Cordes

    30. August 2016 um 23:44 Uhr


Benutzer-Avatar
Ben Zotto

Dieser Code setzt die Konstanten (1, 2, 3) einfach direkt an Offset-Positionen vom (aktualisierten) Stapelzeiger (esp). Der Compiler entscheidet sich dafür, den “Push” manuell mit dem gleichen Ergebnis durchzuführen.

“push” setzt die Daten und aktualisiert den Stapelzeiger. In diesem Fall reduziert der Compiler dies auf nur eine Aktualisierung des Stapelzeigers (gegenüber drei). Ein interessantes Experiment wäre es, zu versuchen, die Funktion “a” zu ändern, um nur ein Argument zu nehmen, und zu sehen, ob sich das Befehlsmuster ändert.

  • Warum müssen Sie die Konstante zuerst in ein Register schreiben? x86 unterstützt das Pushen von unmittelbaren Konstanten

    – Nekrolis

    26. Dezember 2010 um 19:50 Uhr

gcc führt alle Arten von Optimierungen durch, einschließlich der Auswahl von Anweisungen basierend auf der Ausführungsgeschwindigkeit der jeweiligen CPU, für die optimiert wird. Sie werden feststellen, dass Dinge wie x *= n wird oft durch eine Mischung aus SHL, ADD und/oder SUB ersetzt, besonders wenn n eine Konstante ist; während MUL nur verwendet wird, wenn die durchschnittliche Laufzeit (und Cache-/etc.-Footprints) der Kombination von SHL-ADD-SUB die von MUL überschreiten würde, oder n ist keine Konstante (und daher wäre die Verwendung von Schleifen mit shl-add-sub teurer).

Im Falle von Funktionsargumenten: MOV kann durch Hardware parallelisiert werden, während PUSH dies nicht kann. (Der zweite PUSH muss wegen der Aktualisierung des esp-Registers auf das Ende des ersten PUSH warten.) Im Fall von Funktionsargumenten können MOVs parallel ausgeführt werden.

Ist das zufällig unter OS X? Ich habe irgendwo gelesen, dass der Stapelzeiger an 16-Byte-Grenzen ausgerichtet sein muss. Das könnte möglicherweise diese Art der Codegenerierung erklären.

Ich habe den Artikel gefunden: http://blogs.embarcadero.com/eboling/2009/05/20/5607

1175090cookie-checkWarum verwendet gcc movl anstelle von Push-to-Pass-Funktionsargumenten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy