Por que a memória da pilha é alocada quando não é usada?

14

Considere o seguinte exemplo:

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

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

O código de montagem gerado para vector::empty(por clang, com otimizações):

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

Por que alocar espaço na pilha? Não é usado de todo. O pushe poppode ser omitido. Compilações otimizadas de MSVC e gcc também usam espaço de pilha para esta função (veja no godbolt ), portanto, deve haver uma razão.

Dr. Gut
fonte
7
Você considerou o thisparâmetro implícito ?
dan04 7/01
11
@ Bob__: Não. Por que devo? vector::size()não está definido no exemplo para simular que não está embutido.
Dr. Gut
11
Então, como um compilador pode otimizar algo que não sabe?
Bob__
11
@ Bob__: Eu acho que conhecer a implementação de vector::size()não é relevante para alocar ou não alocar um quadro de pilha vector::empty(). Em empty()que é simplesmente chamado, seja ele qual for.
Dr. Gut
11
Bem, você está chamando uma função que retorna algo, precisa de espaço para isso (se não souber de nada).
Bob__

Respostas:

11

Ele aloca espaço na pilha, para que a pilha seja alinhada por 16 bytes. É necessário, porque o endereço de retorno ocupa 8 bytes; portanto, é necessário um espaço adicional de 8 bytes para manter a pilha de 16 bytes alinhada.

O alinhamento de quadros de pilha pode ser configurado com argumentos de linha de comando para alguns compiladores.

  • MSVC : A documentação diz que a pilha está sempre alinhada por 16 bytes. Nenhum argumento de linha de comando pode mudar isso. O exemplo do godbolt mostra que 40 bytes são subtraídos rspno início da função, o que significa que algo mais também afeta isso.
  • clang : a -mstack-alignmentopção especifica o alinhamento da pilha. Parece que o padrão é 16, embora não esteja documentado. Se você defini-lo como 8, a alocação da pilha ( pushe pop) desaparece do código de montagem gerado.
  • gcc : a -mpreferred-stack-boundaryopção especifica o alinhamento da pilha. Se o valor fornecido for N, significa 2 ^ N bytes de alinhamento. O valor padrão é 4, o que significa 16 bytes. Se você defini-lo como 3 (ou seja, 8 bytes), a alocação da pilha ( sube addpara rsp) desaparece do código de montagem gerado.

Confira no godbolt .

geza
fonte
É por isso que os especialistas em c ++ sempre avisaram: coloque os membros struct / class na ordem do tamanho maior / maior para o menor ... apenas dessa maneira seria corretamente eficiente
nonock
@geza: Obrigado. Eu fiz algumas pesquisas para os outros dois compiladores e escrevi na sua resposta. Você gosta disso?
Dr. Gut
11
@ Dr.Gut: obrigado, você fez a resposta muito melhor e completa. Observe que esse alinhamento de pilha geralmente é documentado na ABI do sistema (por exemplo, para alguns sistemas, aqui estão os documentos: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
geza 8/01
@geza: Obrigado.
Dr. Gut