Warum wird Stapelspeicher zugewiesen, wenn er nicht verwendet wird?

14

Betrachten Sie das folgende Beispiel:

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

Der generierte Assemblycode für vector::empty(durch Klirren, mit Optimierungen):

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

Warum wird Stapelspeicher zugewiesen? Es wird überhaupt nicht verwendet. Das pushund popkönnte weggelassen werden. Optimierte Builds von MSVC und gcc verwenden für diese Funktion auch Stapelspeicher (siehe Godbolt ), daher muss es einen Grund geben.

Dr. Gut
quelle
7
Haben Sie den impliziten thisParameter berücksichtigt ?
dan04
1
@ Bob__: Nein. Warum sollte ich? vector::size()wird im Beispiel nicht definiert, um zu simulieren, dass es nicht inline ist.
Dr. Gut
1
Wie kann ein Compiler etwas optimieren, das er nicht kennt?
Bob__
1
@ Bob__: Ich denke, dass das Wissen um die Implementierung von vector::size()nicht relevant für das Zuweisen oder Nichtzuweisen eines Stapelrahmens für ist vector::empty(). Darin empty()heißt es einfach, was auch immer es ist.
Dr. Gut
1
Nun, Sie rufen eine Funktion auf, die etwas zurückgibt , dafür benötigen Sie Platz (wenn Sie es nicht besser wissen).
Bob__

Antworten:

11

Es weist Stapelspeicherplatz zu, sodass der Stapel 16-Byte-ausgerichtet ist. Dies ist erforderlich, da die Rücksprungadresse 8 Byte benötigt. Daher wird ein zusätzlicher 8-Byte-Speicherplatz benötigt, um den Stapel mit 16 Byte ausgerichtet zu halten.

Die Ausrichtung von Stapelrahmen kann für einige Compiler mit Befehlszeilenargumenten konfiguriert werden.

  • MSVC : Die Dokumentation besagt, dass der Stapel immer 16-Byte-ausgerichtet ist. Kein Befehlszeilenargument kann dies ändern. Das Godbolt-Beispiel zeigt, dass 40 Bytes von subtrahiert werdenrsp zu Beginn der Funktion werden, was bedeutet, dass dies auch von etwas anderem beeinflusst wird.
  • clang : Die -mstack-alignmentOption gibt die Stapelausrichtung an. Es scheint, dass der Standardwert 16 ist, obwohl er nicht dokumentiert ist. Wenn Sie 8 festlegen, wird die Stapelzuordnung ( pushund pop) aus dem generierten Assemblycode entfernt.
  • gcc : Die-mpreferred-stack-boundary Option gibt die Stapelausrichtung an. Wenn der angegebene Wert N ist, bedeutet dies 2 ^ N Bytes Ausrichtung. Der Standardwert ist 4, was 16 Bytes bedeutet. Wenn Sie 3 (dh 8 Byte) festlegen, verschwindet die Stapelzuordnung ( subund addfür rsp) aus dem generierten Assemblycode.

Schauen Sie sich Godbolt an .

geza
quelle
Aus diesem Grund haben C ++ - Gurus, Experten, immer gewarnt: Struktur- / Klassenmitglieder in der Reihenfolge der längsten / größten Größe auf die kleinste setzen ... nur so wäre es richtig effizient
nonock
@geza: Danke. Ich habe einige Nachforschungen für die beiden anderen Compiler angestellt und diese auf Ihre Antwort geschrieben. Gefällt es dir
Dr. Gut
1
@ Dr.Gut: Danke, du hast die Antwort viel besser und vollständiger gemacht. Beachten Sie, dass die Stapelausrichtung normalerweise im ABI für das System dokumentiert ist (für einige Systeme sind beispielsweise die folgenden Dokumente aufgeführt: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
Geza
@geza: Danke.
Dr. Gut