Pourquoi la mémoire de pile est-elle allouée lorsqu'elle n'est pas utilisée?

14

Prenons l'exemple suivant:

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

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

Le code d'assembly généré pour vector::empty(par clang, avec optimisations):

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

Pourquoi alloue-t-il de l'espace de pile? Il n'est pas utilisé du tout. Le pushet poppourrait être omis. Les versions optimisées de MSVC et gcc utilisent également l'espace de pile pour cette fonction (voir sur godbolt ), donc il doit y avoir une raison.

Dr. Gut
la source
7
Avez-vous pris en compte le thisparamètre implicite ?
dan04
1
@Bob__: Non. Pourquoi le devrais-je? vector::size()n'est pas défini dans l'exemple pour simuler qu'il n'est pas en ligne.
Dr Gut
1
Alors, comment un compilateur pourrait-il optimiser quelque chose qu'il ne connaît pas?
Bob__
1
@Bob__: Je pense que connaître l'implémentation de vector::size()n'est pas pertinent pour allouer ou ne pas allouer un cadre de pile pour vector::empty(). En empty()elle est simplement appelée, quelle qu'elle soit.
Dr Gut
1
Eh bien, vous appelez une fonction qui renvoie quelque chose, vous avez besoin d'espace pour cela (si vous ne savez pas mieux).
Bob__

Réponses:

11

Il alloue l'espace de pile, donc la pile est alignée sur 16 octets. Il est nécessaire, car l'adresse de retour prend 8 octets, donc un espace supplémentaire de 8 octets est nécessaire pour garder la pile de 16 octets alignée.

L'alignement des cadres de pile peut être configuré avec des arguments de ligne de commande pour certains compilateurs.

  • MSVC : La documentation indique que la pile est toujours alignée sur 16 octets. Aucun argument de ligne de commande ne peut changer cela. L'exemple du godbolt montre que 40 octets sont soustraits rspau début de la fonction, ce qui signifie que quelque chose d'autre affecte également cela.
  • clang : l' -mstack-alignmentoption spécifie l'alignement de la pile. Il semble que la valeur par défaut soit 16, bien que non documentée. Si vous le définissez sur 8, l'allocation de pile ( pushet pop) disparaît du code d'assembly généré.
  • gcc : L' -mpreferred-stack-boundaryoption spécifie l'alignement de la pile. Si la valeur donnée est N, cela signifie 2 ^ N octets d'alignement. La valeur par défaut est 4, ce qui signifie 16 octets. Si vous le définissez sur 3 (c'est-à-dire 8 octets), l'allocation de pile ( subet addpour rsp) disparaît du code d'assembly généré.

Découvrez Godbolt .

geza
la source
C'est pourquoi les gourous du c ++, les experts ont toujours mis en garde: mettez les membres struct / class par ordre de la taille la plus longue / la plus grande au plus petit ... seulement de cette façon, ce serait correctement efficace
nonock
@geza: Merci. J'ai fait des recherches pour les deux autres compilateurs et je l'ai écrit dans votre réponse. Aimes-tu?
Dr Gut
1
@ Dr.Gut: merci, vous avez rendu la réponse bien meilleure et complète. Notez que l'alignement de la pile est généralement documenté dans l'ABI du système (par exemple, pour certains systèmes, voici les documents: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
geza
@geza: Merci.
Dr.Gut