O conceito de “variáveis locais”, como estamos habituados em linguagens de programação de mais alto nível, não tem equivalência direta em assembly.
IMPORTANTE: Todos os códigos em assembly compartilhados nessa série omitem verificações e, para fins de simplicidade, não estão otimizados.
Na prática, o desafio é determinar, dentre as alternativas disponíveis, a forma mais eficiente para armazenar e recuperar, de alguma forma, dados na memória tornando-os acessíveis para processamento pelo código. Escolhas infelizes geralmente implicam em performance mais pobre.
#include <iostream>
int summarize(const int value)
{
auto result = 0;
for (auto i = 1; i <= value; i++)
{
result += i;
}
return result;
}
int main() {
std::cout << "summarize(10) = " << summarize(10) << std::endl;
}
O mais eficiente costuma ser utilizar os registradores do processador. Embora eles tenham pouca capacidade, estão montados fisicamente junto ao processador o que torna sua velocidade de acesso imbatível.
Os compiladores geralmente utilizam os registradores para, por exemplo, armazenar valores de contadores e acumuladores em loops.
.model flat,c
.code
summarize_ proc
push ebp
mov ebp, esp
push ebx
xor eax, eax ; result = 0;
mov ebx, 1
mov ecx,[ebp+8] ; ecx = value
jmp check_for_is_complete
for_body:
add eax, ebx
inc ebx
check_for_is_complete:
cmp ebx, ecx
jg finish
jmp for_body
finish:
pop ebx
pop ebp
ret
summarize_ endp
end
Importante que lembremos que os registradores que utilizamos em nossas funções são os mesmos disponíveis para todo o código. Dependendo da convenção adotada, alguns registradores podem ser voláteis ou não-voláteis. Registradores não-voláteis devem ter seu valor restaurado sempre antes da função retornar (como ebx e ebp no exemplo). Registradores voláteis podem ter seus valores modificados livremente.
Outra alternativa comum é alocar espaço na stack para acomodar o valor das variáveis.
#include <iostream>
int summarize(const int a, const int b)
{
const auto max = (a > b) ? a : b;
const auto min = (a < b) ? a : b;
return (max * (max + 1) - (min - 1) * min) / 2;
}
int main() {
std::cout << "summarize(10) = " << summarize(100, 1) << std::endl;
}
Quando utilizamos a stack para armazenar os valores de variáveis locais, é comum “reservar” espaço logo no início da função (trecho de código conhecido como prólogo) e garantir que o espaço alocado seja liberado no final da função (trecho de código conhecido como epílogo).

A prática comum é colocar os valores das variáveis locais logo após o registrador ebp. Dessa forma, parâmetros para as funções são acessados com deslocamentos positivos e variáveis são acessadas com deslocamentos negativos.
.model flat,c
.code
summarize_ proc
push ebp
mov ebp, esp
sub esp, 8 ; allocating space on the stack for two integers
mov eax,[ebp+8] ; eax = 'a'
mov ecx,[ebp+12] ; ecx = 'b'
cmp eax, ecx
jle aIsMax
mov [ebp - 8], eax ; max = a
jmp resume_1
aIsMax:
mov [ebp - 8], ecx ; max = b
resume_1:
cmp eax, ecx
jge aIsMin
mov [ebp - 4], eax ; min = a
jmp resume_2
aIsMin:
mov [ebp - 4], ecx ; min = b
resume_2:
mov eax, [ebp - 8] ; eax = max
add eax, 1
imul eax, [ebp - 8]
mov ecx, [ebp - 4] ; ecx = min
sub ecx, 1
imul ecx, [ebp - 4]
sub eax, ecx
cdq
sar eax, 1
mov esp, ebp ; releasing local storage space
pop ebp
ret
summarize_ endp
end
Obviamente, nada disso é relevante quando estamos escrevendo código em Java, C++ ou C#. Por sorte, os compiladores fazem um ótimo trabalho escondendo esses “detalhes” do nosso dia a dia.
Objetos complexos, armazenados na heap, tem apenas seus endereço armazenado localmente (também em registradores ou na stack).