Qual é o objective do registrador de pointers de quadro EBP?

Eu sou um iniciante em linguagem assembly e notei que o código x86 emitido pelos compiladores geralmente mantém o ponteiro do quadro em volta mesmo no modo release / optimized quando ele poderia usar o registrador EBP para outra coisa.

Eu entendo porque o ponteiro do quadro pode tornar o código mais fácil de depurar, e pode ser necessário se alloca() for chamado dentro de uma function. No entanto, o x86 tem muito poucos registradores e usa dois deles para manter a localização do quadro de pilha quando um seria suficiente não faz sentido para mim. Por que omitir o ponteiro do quadro é considerado uma má idéia mesmo em versões otimizadas / liberadas?

Ponteiro de quadro é um ponteiro de referência que permite que um depurador saiba onde a variável local ou um argumento está com um único deslocamento constante. Embora o valor do ESP mude ao longo da execução, o EBP permanece o mesmo, tornando possível alcançar a mesma variável no mesmo offset (como o primeiro parâmetro estará sempre em EBP + 8 enquanto os offsets do ESP podem mudar significativamente desde que você estará pressionando / estalando coisas)

Por que os compiladores não jogam fora o ponteiro do quadro? Como com o ponteiro de frameworks, o depurador pode descobrir onde as variables ​​locais e os argumentos estão usando a tabela de símbolos, uma vez que eles têm a garantia de estarem em um deslocamento constante para o EBP. Caso contrário, não há uma maneira fácil de descobrir onde uma variável local está em qualquer ponto no código.

Como Greg mencionou, também ajuda a desvelar o empilhamento de um depurador, já que o EBP fornece uma lista de frameworks de empilhamento invertida, permitindo ao depurador descobrir o tamanho do quadro de pilha (variables ​​locais + argumentos) da function.

A maioria dos compiladores fornece uma opção para omitir pointers de frameworks, embora torne a debugging realmente difícil. Essa opção nunca deve ser usada globalmente, mesmo no código de lançamento. Você não sabe quando precisará depurar a falha de um usuário.

Apenas adicionando meus dois centavos a boas respostas já.

Faz parte de uma boa arquitetura de linguagem ter uma cadeia de frameworks de pilha. A BP aponta para o quadro atual, onde as variables ​​locais da sub-rotina são armazenadas. (Os locais estão em offsets negativos e os argumentos estão em offsets positivos.)

A ideia de que está impedindo que um registro perfeitamente bom seja usado na otimização levanta a questão: quando e onde a otimização realmente vale a pena?

A otimização só vale a pena em loops apertados que 1) não chamam funções, 2) onde o contador do programa gasta uma fração significativa de seu tempo, e 3) no código que o compilador realmente verá (isto é, funções não-biblioteca). Isso geralmente é uma fração muito pequena do código geral, especialmente em sistemas grandes.

Outro código pode ser torcido e comprimido para se livrar de ciclos, e isso simplesmente não importará, porque o contador de programa praticamente nunca está lá.

Eu sei que você não fez isso, mas na minha experiência, 99% dos problemas de desempenho não têm nada a ver com a otimização do compilador. Eles têm tudo a ver com o excesso de design.

Depende do compilador, certamente. Eu vi código otimizado emitido por compiladores x86 que usa livremente o registrador EBP como um registrador de propósito geral. (Eu não me lembro de qual compilador eu notei com isso, no entanto.)

Os compiladores também podem optar por manter o registro EBP para auxiliar no desenrolar da pilha durante o tratamento de exceções, mas isso depende novamente da implementação precisa do compilador.

No entanto, x86 tem muito poucos registradores

Isso é verdade apenas no sentido de que os opcodes só podem endereçar 8 registros. O processador em si terá, na verdade, muito mais registros do que isso e usará a renomeação de registradores, o pipelining, a execução especulativa e outras palavras-chave do processador para contornar esse limite. A Wikipedia tem um bom parágrafo introdutório sobre o que um processador x86 pode fazer para superar o limite de registro: http://en.wikipedia.org/wiki/X86#Current_implementations .

Usando frameworks de pilha ficou incrivelmente barato em qualquer hardware, mesmo remotamente moderno. Se você tem frameworks de pilha baratos, salvar alguns registros não é tão importante. Tenho certeza de que os frames de stack rápidos versus os mais registros foram um trade-off de engenharia, e os frames de stack rápidos venceram.

Quanto você está economizando em registrar puro? Vale a pena?