Entendendo a “Stack” em sua forma mais primitiva (em Assembly)

Conhecer assembly, geralmente, não é fundamental para o dia a dia de um programador. Entretanto, entender como assembly funciona ajuda a valorizar determinadas características de linguagens de programação de nível mais alto e, até mesmo, deixar mais confortável cenários mais complexos de depuração ou otimização.

Um dos conceitos centrais de programação em assembly que ajudam a entender o comportamento do código, em uma linguagem como Java ou C#, é a stack

O que é a Stack?

Trata-se de uma área contínua de memória que nossos programas utilizam para manipular dados, principalmente primitivos, e é intensamente utilizada na “comunicação” durante chamadas e retornos de funções.

Os dados são “empilhados” na stack usando, em assembly, a instrução push e são desempilhados usando a instrução pop. O endereço de memória correspondente ao “topo” da stack é mantindo no registrador esp.

O topo da pilha “cresce negativamente”, de high-memory para low-memory. Assim, sempre que um valor é empilhado na stack, o valor de esp é decrescido. De maneira análoga, sempre que um valor é “desempilhado” da stack, o valor de esp é acrescido.

Qual a relação entre a Stack e a execução de funções?

De maneira geral, sempre que uma função é chamada, os dados necessários para sua execução são dispostos na stack obedecendo uma determinada convenção. Voltando ao código do post anterior,  podemos perceber no “header” da função addInAsm a convenção que deveria ser respeitada (cdecl).

#include <iostream>

extern "C" int addInAsm(int a, int b);

int main() {
	int a = 2;
	int b = 3;

	int result = addInAsm(a, b);

	std::cout << a << " + " << b << " = " << result << std::endl;
	return 0;
}

O código em assembly que escrevemos utiliza “empiricamente” o que está acordado na convenção para acessar os dados.

    .model flat, c
    .code

addInAsm proc 

; Initialize a stack frame pointer  
    push ebp
    mov  ebp, esp

; load the paramaters 
    mov  eax, [ebp + 8]    ; eax = a
    mov  ecx, [ebp + 12]   ; ecx = b

;
    add  eax, ecx         

; restore the stack frame
    pop  ebp
    ret

addInAsm endp

    end

Segundo as convenções, a stack foi atualizada, na chamada, para conter, os parâmetros da função e o endereço de retorno para quando a função encerrar.

Perceba que códigos em assembly não empregam sistemas sofisticados de tipos. Ou seja, não há abstrações com relação a valores em memória – tudo são bytes que ganham significado conforme a intenção do código e são acessados através dos deslocamentos impostos pelo “tamanho” de cada dado.

O endereço de retorno é capturado pela instrução ret, diretamente da stack, para saber onde está a próxima instrução da função chamadora (main) a ser procesada.

Qual a relação entre a Stack e a parâmetros “byref”?

Quando passamos parâmetros “por referência”, mandamos na Stack, no lugar dos valores, os endereços de memória correspondendo as variáveis que desejamos atualizar.

#include <iostream>

extern "C" void addMul(int a, int b, int* sum, int* prod);

int main() {
	int a = 2;
	int b = 5;
	int sum = 0;
	int prod = 0;

	addMul(a, b, &sum, &prod);

	std::cout <<
		"The sum of "
		<< a <<
		" and "
		<< b <<
		" is "
		<< sum << " and the product is " << prod
		<< std::endl;
}

No exemplo, os parâmetros prod e sum são passados como referência, mas a estrutura na stack é praticamente inalterada.

addMul proc

        push ebp
        mov ebp,esp

        push edx
        push ecx
        push eax

        mov ecx,[ebp+8]                     ;ecx = 'a'
        mov eax, ecx                        ;eax = 'a'
        mov edx,[ebp+12]                    ;edx = 'b'

        imul ecx,edx                        ;edx = 'a' * 'b'
        mov ebx,[ebp+20]                    ;ebx = 'prod'
        mov [ebx],ecx                       ;save product

        add eax, edx                        ;eax = 'a' + 'b'
        mov ebx,[ebp+16]                    ;ebx = 'sum'
        mov [ebx],eax                       ;save sum

        pop eax
        pop ecx
        pop edx
        
        pop ebp

        ret
addMul endp
        end

A mudança na stack fica apenas nas duas novas posições necessárias para acomodar os novos valores.

No código em assembly, repare que utilizemos os “endereços” contidos nos parâmetros e não seus valores diretamente (como fazemos para as variáveis a e b).

O que acontece se a memória destinada para a Stack for esgotada?

O resultado depende do ambiente operacional onde estamos trabalhando. Em C#, por exemplo, uma StackOverflowException irá ser disparada e o programa se encerrará.

Importante indicar que o “esgotamento” da Stack geralmente é causado por execuções recursivas em demasia. Afinal, a stack é utilizada para fazer “o caminho de volta” na execução de diversas funções.

Eventualmente, podemos escrever código que ajuda o compilador a entender que “não será necessário” voltar para a função quando houver um retorno.

int factorial(int n, int b = 1) {
    if (n == 0) {
        return b;
        }
    return factorial(n - 1, b * n);
}

Alguns compiladores conseguem identificar esses cenários e não criar um registro na stack para cada chamada.

Concluindo

A stack é um dos conceitos fundamentais para execução de programas de computador em qualquer ambiente moderno. Em seu formato mais “bruto” trata-se apenas de um espaço contínuo de memória que é atualizado seguindo algumas convenções muito simples.

O conhecimento sobre como a Stack é manipulada, em seu estado mais fundamental, ajuda programadores a apreciar o bom trabalho dos compiladores e entender oportunidades de otimização.

Compartilhe este insight:

Comentários

Participe deixando seu comentário sobre este artigo a seguir:

Subscribe
Notify of
guest
1 Comentário
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Sérgio Trovatti Uetanabaro
Sérgio Trovatti Uetanabaro
3 anos atrás

Bem didático esse artigo mas tem uma observação:

imul ecx,edx ;edx = ‘a’ * ‘b’

ECX receberia o acumulado, não EDX: ecx = ‘a’ * ‘b’

AUTOR

Elemar Júnior
Fundador e CEO da EximiaCo atua como tech trusted advisor ajudando empresas e profissionais a gerar mais resultados através da tecnologia.

NOVOS HORIZONTES PARA O SEU NEGÓCIO

Nosso time está preparado para superar junto com você grandes desafios tecnológicos.

Entre em contato e vamos juntos utilizar a tecnologia do jeito certo para gerar mais resultados.

Insights EximiaCo

Confira os conteúdos de negócios e tecnologia desenvolvidos pelos nossos consultores:

Arquivo

Pós-pandemia, trabalho remoto e a retenção dos profissionais de TI

CTO Consulting e Especialista em Execução em TI
EximiaCo 2024 - Todos os direitos reservados
1
0
Queremos saber a sua opinião, deixe seu comentáriox
()
x
Oferta de pré-venda!

Mentoria em
Arquitetura de Software

Práticas, padrões & técnicas para Arquitetura de Software, de maneira efetiva, com base em cenários reais para profissionais envolvidos no projeto e implantação de software.

Muito obrigado!

Deu tudo certo com seu envio!
Logo entraremos em contato

Entendendo a “Stack” em sua forma mais primitiva (em Assembly)

Para se candidatar nesta turma aberta, preencha o formulário a seguir:

Entendendo a “Stack” em sua forma mais primitiva (em Assembly)

Para se candidatar nesta turma aberta, preencha o formulário a seguir:

Condição especial de pré-venda: R$ 14.000,00 - contratando a mentoria até até 31/01/2023 e R$ 15.000,00 - contratando a mentoria a partir de 01/02/2023, em até 12x com taxas.

Tenho interesse nessa capacitação

Para solicitar mais informações sobre essa capacitação para a sua empresa, preencha o formulário a seguir:

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

O seu insight foi excluído com sucesso!

O seu insight foi excluído e não está mais disponível.

O seu insight foi salvo com sucesso!

Ele está na fila de espera, aguardando ser revisado para ter sua publicação programada.

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse nessa solução

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse neste serviço

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

× Precisa de ajuda?