Extensões SSE e AVX/AVX2 em Assembly NASM
As instruções SIMD (Single Instruction, Multiple Data — “uma instrução, múltiplos dados”) representam um conjunto especial de instruções que permitem o processamento paralelo de dados. Isso significa que grupos de valores podem ser processados simultaneamente, o que aumenta o desempenho e acelera a execução do programa.
Na arquitetura x86-64, as instruções SIMD são implementadas através de extensões de diferentes gerações:
- SSE/SSE2 (Streaming SIMD Extensions): fornecem dezesseis registradores de 128 bits (XMM0–XMM15), capazes de trabalhar com tipos inteiros e de ponto flutuante.
- AVX/AVX2 (Advanced Vector Extensions): suportam dezesseis registradores de 256 bits (YMM0–YMM15).
- AVX-512: amplia o conjunto para trinta e dois registradores de 512 bits (ZMM0–ZMM31).
Essas arquiteturas lidam com dois tipos principais de dados: valores escalares (números isolados) e vetores (conjuntos de valores). Os valores escalares representam inteiros ou números de ponto flutuante de precisão simples ou dupla. Os vetores contêm vários valores numéricos dispostos em sequência, variando de 2 a 32 elementos, conforme o tipo de dado e o tamanho do registrador (byte, word, dword, qword).
Os registradores XMM (XMM0–XMM15) armazenam um valor de ponto flutuante de 32 bits (float) ou quatro valores de 32 bits (vetor com 4 floats). Os registradores YMM (YMM0–YMM15) armazenam oito valores de ponto flutuante de precisão simples (32 bits cada).

Os registradores XMM também podem conter um número de ponto flutuante de precisão dupla (.double) ou um vetor com dois números double. Os registradores YMM armazenam vetores com quatro números double.

Além de números de ponto flutuante, os registradores podem armazenar inteiros: o XMM contém 16 bytes, 8 words, 4 dwords ou 2 qwords, enquanto o YMM contém o dobro dessa capacidade.

Os elementos de um vetor também são chamados de lanes (pistas) dos registradores XMM e YMM. Por exemplo, um registrador XMM de 128 bits pode conter um vetor de 16 bytes. Os bits de 0 a 7 formam a lane 0, de 8 a 15 a lane 1, de 16 a 23 a lane 2 e assim por diante. Os bits de 120 a 127 formam a lane 15. Um registrador YMM de 256 bits possui 32 lanes de 1 byte, e um registrador ZMM de 512 bits possui 64 lanes.
De forma semelhante, um registrador XMM contém 8 lanes para o tipo word, o YMM possui 16 e o ZMM, 32. Para tipos dword ou long, o XMM possui 4 lanes, o YMM possui 8 e o ZMM possui 16.
Verificação de suporte às extensões SIMD
Os processadores diferem quanto ao suporte às extensões SIMD. Versões mais novas geralmente suportam instruções mais recentes. As extensões SSE4.2 foram lançadas em 2006, e é altamente provável que qualquer computador moderno as suporte. As extensões AVX foram apresentadas em 2008, e os primeiros processadores com suporte — a série Sandy Bridge — surgiram em 2011. As extensões AVX2 apareceram em 2013 nos processadores Haswell. As AVX-512 foram propostas em 2013 e lançadas nas famílias Xeon Phi x200 e Skylake-X entre 2015 e 2016.
A instrução cpuid permite identificar os recursos disponíveis no processador.
Ela recebe um parâmetro em EAX, e o resultado é retornado em registradores como EAX, EBX, ECX e EDX, dependendo da informação solicitada.
Para verificar o suporte a extensões SIMD, define-se EAX = 1 e examina-se o conteúdo de ECX. Cada bit de ECX indica a presença de um recurso específico:
| Bit | Recurso |
|---|---|
| 0 | SSE3 disponível |
| 1 | PCLMULQDQ disponível |
| 9 | SSSE3 disponível |
| 19 | SSE4.1 disponível |
| 20 | SSE4.2 disponível |
| 28 | AVX disponível |
Exemplo de programa Linux para exibir o conteúdo de ECX em hexadecimal:
global _start
section .data
hexstr db "0x123456789ABCDEFG",10,0
len equ $-hexstr
hexmap db 48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70
section .text
_start:
mov eax, 1
cpuid
mov edi, ecx
call print_reg
mov rax, 60
syscall
print_reg:
mov rbx, hexstr
mov rsi, hexmap
add rbx, 17
mov rcx, 16
forloop:
mov rax, rdi
and rax, 0x0f
mov al, byte [rsi + rax]
mov byte [rbx], al
sub rbx, 1
shr rdi, 4
sub rcx, 1
jne forloop
mov rax, 1
mov rdi, 1
mov rsi, hexstr
mov rdx, len
syscall
retNo exemplo acima, o valor retornado em ECX indica os recursos suportados.
Por exemplo, um valor 0x00000000FEDAB223 em binário corresponde a 11111110110110101011001000100011, o que permite identificar os bits ativos e, portanto, as extensões disponíveis.
Para informações adicionais, define-se EAX = 7 e ECX = 0. Neste caso, os bits de EBX indicam o suporte a extensões mais recentes:
| Bit | Recurso |
|---|---|
| 3 | BMI1 (Bit Manipulation Instructions 1) |
| 5 | AVX2 disponível |
| 8 | BMI2 disponível |
Exemplo de programa para verificar suporte a SSE4.2, AVX e AVX2:
global _start
section .data
SSE42Support equ 0x00180000
AVXSupport equ 0x10000000
AVX2Support equ 0x20
section .text
_start:
mov rax, 7
xor ecx, ecx
cpuid
and ebx, AVX2Support
jnz AVX2
mov rax, 1
cpuid
and ecx, AVXSupport
jnz AVX
and ecx, SSE42Support
jnz SSE42
mov rdi, 40
jmp exit
AVX2:
mov rdi, 52
jmp exit
AVX:
mov rdi, 50
jmp exit
SSE42:
mov rdi, 42
exit:
mov rax, 60
syscallNesse código, as máscaras binárias são aplicadas aos registradores de saída da instrução cpuid para identificar a extensão presente no processador.
Registro de controle MXCSR
O SSE possui um registrador de controle e status chamado MXCSR, com 32 bits, que define o comportamento das operações de ponto flutuante e as condições de exceção.
Os primeiros 16 bits têm significados específicos:
| Bit | Nome | Descrição |
|---|---|---|
| 0 | IE | Exceção de operação inválida |
| 1 | DE | Exceção de denormalização |
| 2 | ZE | Exceção de divisão por zero |
| 3 | OE | Exceção de overflow |
| 4 | UE | Exceção de underflow |
| 5 | PE | Perda de precisão |
| 6 | DAZ | Trata valores denormalizados como zero |
| 7–12 | Máscaras de exceção correspondentes (IM, DM, ZM, OM, UM, PM) | |
| 13–14 | Controle de arredondamento (00 = mais próximo, 01 = -∞, 10 = +∞, 11 = truncamento) | |
| 15 | FTZ | Define underflow para zero |
Os bits de 16 a 31 são reservados.
O acesso ao registrador MXCSR é feito com as instruções:
ldmxcsr mem32
stmxcsr mem32A primeira carrega o registrador a partir de uma variável de 32 bits, enquanto a segunda armazena o seu conteúdo em memória. Essas instruções são geralmente empregadas para configurar o modo de arredondamento.
Resumo
- As instruções SIMD permitem o processamento paralelo de dados.
- SSE e AVX introduzem registradores XMM, YMM e ZMM com 128, 256 e 512 bits.
- A instrução
cpuididentifica quais extensões estão disponíveis no processador. - O registrador MXCSR controla exceções e modos de arredondamento das operações SSE.