Dlaczego pamięć stosu jest przydzielana, gdy nie jest używana?

14

Rozważ następujący przykład:

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

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

Wygenerowany kod zestawu dla vector::empty(według clang, z optymalizacjami):

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

Dlaczego przydziela miejsce na stosie? W ogóle nie jest używany. pushI popmoże być pominięty. Zoptymalizowane kompilacje MSVC i gcc również używają przestrzeni stosu dla tej funkcji (patrz na godbolt ), więc musi być jakiś powód.

Dr Gut
źródło
7
Czy uwzględniłeś niejawny thisparametr?
dan04
1
@Bob__: Nie. Dlaczego powinienem? vector::size()nie jest zdefiniowany w tym przykładzie, aby zasymulować, że nie jest wstawiony.
Dr Gut
1
Jak więc kompilator może zoptymalizować coś, czego nie zna?
Bob__
1
@Bob__: Myślę, że znajomość implementacji vector::size()nie ma znaczenia dla przydzielania lub nieprzydzielania ramki stosu vector::empty(). W empty()to właśnie nazywa, cokolwiek to jest.
Dr Gut
1
Cóż, wywołujesz funkcję, która coś zwraca , potrzebujesz na to miejsca (jeśli nie znasz nic lepszego).
Bob__

Odpowiedzi:

11

Przydziela przestrzeń stosu, więc stos jest wyrównany do 16 bajtów. Jest to konieczne, ponieważ adres zwrotny zajmuje 8 bajtów, więc potrzebna jest dodatkowa 8-bajtowa przestrzeń, aby utrzymać stos 16-bajtowy.

Wyrównanie ramek stosu można skonfigurować za pomocą argumentów wiersza poleceń dla niektórych kompilatorów.

  • MSVC : Dokumentacja mówi, że stos jest zawsze wyrównany do 16 bajtów. Żaden argument wiersza poleceń nie może tego zmienić. Przykład godbolta pokazuje, że od rsppoczątku funkcji odejmuje się 40 bajtów , co oznacza, że ​​coś innego również na to wpływa.
  • clang : -mstack-alignmentOpcja określa wyrównanie stosu. Wygląda na to, że domyślnie jest to 16, choć nie zostało to udokumentowane. Jeśli ustawisz go na 8, alokacja stosu ( pushi pop) zniknie z wygenerowanego kodu zestawu.
  • gcc : -mpreferred-stack-boundaryOpcja określa wyrównanie stosu. Jeśli podana wartość to N, oznacza to 2 ^ N bajtów wyrównania. Wartość domyślna to 4, co oznacza 16 bajtów. Jeśli ustawisz go na 3 (tj. 8 bajtów), przydział stosu ( subi adddla rsp) zniknie z wygenerowanego kodu zestawu.

Sprawdź godbolt .

geza
źródło
Właśnie dlatego guru c ++, eksperci zawsze ostrzegali: posortuj członków struct / class w kolejności od najdłuższego / największego rozmiaru do najmniejszego ... tylko w ten sposób będzie to poprawnie
działało
@geza: Dziękuję. Zrobiłem badania dla pozostałych dwóch kompilatorów i napisałem to do twojej odpowiedzi. Czy lubisz to?
Dr Gut
1
@ Dr.Gut: dzięki, odpowiedź była znacznie lepsza i kompletna. Zauważ, że wyrównanie stosu jest zwykle udokumentowane w ABI dla systemu (na przykład dla niektórych systemów, oto dokumenty: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
geza
@geza: Dziękuję.
Dr Gut