Estrutura do Processador x86
Vamos conhecer a estrutura interna de um processador x86-64 moderno, focando no que é visível para o programador em assembly.
Registradores de Uso Geral (GPRs)
O x86-64 tem 16 registradores de uso geral de 64 bits:
| 64 bits | 32 bits | 16 bits | 8 bits (baixo) | 8 bits (alto)* | Função típica |
|---|---|---|---|---|---|
| RAX | EAX | AX | AL | AH | Acumulador / retorno de função |
| RBX | EBX | BX | BL | BH | Base (preservado entre chamadas) |
| RCX | ECX | CX | CL | CH | Contador (loops) |
| RDX | EDX | DX | DL | DH | Dados / extensão de RAX |
| RSI | ESI | SI | SIL | — | Source Index (origem em operações de string) |
| RDI | EDI | DI | DIL | — | Destination Index (destino) |
| RBP | EBP | BP | BPL | — | Base Pointer (frame pointer) |
| RSP | ESP | SP | SPL | — | Stack Pointer |
| R8 | R8D | R8W | R8B | — | Uso geral (adicionados no AMD64) |
| R9 | R9D | R9W | R9B | — | Uso geral |
| R10 | R10D | R10W | R10B | — | Uso geral |
| R11 | R11D | R11W | R11B | — | Uso geral |
| R12 | R12D | R12W | R12B | — | Uso geral (preservado) |
| R13 | R13D | R13W | R13B | — | Uso geral (preservado) |
| R14 | R14D | R14W | R14B | — | Uso geral (preservado) |
| R15 | R15D | R15W | R15B | — | Uso geral (preservado) |
* Os nomes AH, BH, CH, DH acessam o byte alto (bits 15-8) dos registradores AX, BX, CX, DX. Não estão disponíveis para R8-R15.
Acessar partes menores de um registrador não zera os bits superiores, com exceção de operações de 32 bits (que zeram automaticamente os 32 bits superiores do registrador de 64 bits correspondente).
Registradores Especiais
-
RIP (Instruction Pointer): aponta para a próxima instrução a ser executada. Não é acessível diretamente (não pode fazer
mov rax, rip). - RFLAGS: registrador de flags com bits de estado (Zero, Carry, Sign, Overflow, etc.) e controle (Interrupt, Direction, etc.).
- Registradores de segmento (CS, DS, SS, ES, FS, GS): remanescentes da era 8086. Em modo 64 bits, apenas FS e GS são usados na prática (para thread-local storage e acesso a estruturas do sistema).
Modos de Operação
O x86-64 pode operar em vários modos, herdados de sua evolução:
| Modo | Bits | Descrição |
|---|---|---|
| Real mode | 16 | Compatível com 8086. Como o processador inicia. |
| Protected mode | 32 | Modo com paginação, proteção, multitarefa. |
| Long mode | 64 | Modo nativo de 64 bits (AMD64). |
| Compatibility mode | 32 | Submodo do long mode para rodar código 32 bits. |
Processadores modernos iniciam em real mode (16 bits) por compatibilidade. O bootloader/UEFI faz a transição para protected mode, depois para long mode. Como programadores de aplicação em Linux, nosso código sempre roda em long mode (64 bits).
Registradores SIMD
Além dos GPRs, o x86 moderno tem conjuntos de registradores para operações SIMD (uma instrução, múltiplos dados):
| Conjunto | Registradores | Largura | Introdução |
|---|---|---|---|
| MMX | mm0–mm7 | 64 bits | Pentium MMX |
| SSE / XMM | xmm0–xmm15 | 128 bits | Pentium III / AMD64 |
| AVX / YMM | ymm0–ymm15 | 256 bits | Sandy Bridge |
| AVX-512 / ZMM | até zmm0–zmm31 | 512 bits | Skylake-X |
Os registradores MMX são mapeados nos registradores da FPU (x87), então não podem ser usados simultaneamente. Os XMM/YMM/ZMM são independentes e podem conter múltiplos inteiros ou floats. AVX-512 não está presente em todos os processadores x86 modernos; quando disponível e habilitado pelo sistema operacional em modo 64 bits, pode expor até 32 registradores ZMM.
Pipeline e Microarquitetura
Internamente, processadores x86 modernos são muito diferentes do que as instruções sugerem:
- Busca (fetch): instruções x86 de tamanho variável são lidas da memória
- Decodificação: instruções x86 complexas são quebradas em micro-operações (µops), que são operações simples no estilo RISC
- Renomeação de registradores: os poucos registradores x86 são mapeados para um arquivo de registradores físico muito maior
- Execução fora de ordem: µops são reordenadas para maximizar paralelismo
- Execução especulativa: branch predictor decide qual caminho executar antes da condição ser resolvida
- Aposentadoria (retirement): µops são confirmadas em ordem
Para o programador assembly, essa complexidade é invisível — você escreve instruções x86 e o processador faz a “mágica” internamente.
Paginação e Memória Virtual
Em long mode, o x86-64 usa paginação de 4 níveis (recentemente 5 níveis, com extensão LA57):
- Cada processo vê um espaço de endereço virtual privado
- O hardware traduz endereços virtuais para físicos via tabelas de página
- Tamanho de página típico: 4 KB (também suporta 2 MB e 1 GB com huge pages)
- Endereço virtual em 64 bits: na prática, apenas 48 bits são usados (256 TB), com extensão para 57 bits
Como programador de aplicação em Linux, você não precisa gerenciar paginação manualmente — o kernel faz isso. Mas é útil saber que seu programa não acessa a memória física diretamente.
Convenção de Chamada (Linux x86-64)
A System V AMD64 ABI (usada no Linux) define:
- Argumentos: RDI, RSI, RDX, RCX, R8, R9 (os demais na stack)
- Retorno: RAX (e RDX se precisar de 128 bits)
- Preservados pela função chamada: RBX, RBP, R12–R15
- Stack deve ser alinhada em 16 bytes antes de cada
call - Red zone: 128 bytes abaixo de RSP que podem ser usados sem ajustar RSP (apenas em leaf functions)
Essa convenção é a que usamos em todo o tutorial!