Atualizado: 09/11/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Operações Lógicas em Vetores com SSE e AVX em Assembly NASM

As extensões SSE e AVX oferecem um conjunto de instruções para a execução de operações lógicas bit a bit sobre vetores de dados.

  • andpd: Executa uma operação AND bit a bit (dest = dest AND source) em operandos de 128 bits.
  • vandpd: Realiza uma operação AND bit a bit (dest = source1 AND source2) em operandos de 128 ou 256 bits.
  • andnpd: Executa uma operação AND NOT bit a bit (dest = (~dest) AND source) em operandos de 128 bits.
  • vandnpd: Realiza uma operação AND NOT bit a bit (dest = (~source1) AND source2) em operandos de 128 ou 256 bits.
  • orpd: Executa uma operação OR bit a bit (dest = dest OR source) em operandos de 128 bits.
  • vorpd: Realiza uma operação OR bit a bit (dest = source1 OR source2) em operandos de 128 ou 256 bits.
  • xorpd: Executa uma operação XOR bit a bit (dest = dest XOR source) em operandos de 128 bits.
  • vxorpd: Realiza uma operação XOR bit a bit (dest = source1 XOR source2) em operandos de 128 ou 256 bits.

A sintaxe geral para essas instruções é a seguinte:

andpd xmmdest, xmmsrc/mem128
vandpd xmmdest, xmmsrc1, xmmsrc2/mem128
vandpd ymmdest, ymmsrc1, ymmsrc2/mem256

andnpd xmmdest, xmmsrc/mem128
vandnpd xmmdest, xmmsrc1, xmmsrc2/mem128
vandnpd ymmdest, ymmsrc1, ymmsrc2/mem256

orpd xmmdest, xmmsrc/mem128
vorpd xmmdest, xmmsrc1, xmmsrc2/mem128
vorpd ymmdest, ymmsrc1, ymmsrc2/mem256

xorpd xmmdest, xmmsrc/mem128
vxorpd xmmdest, xmmsrc1, xmmsrc2/mem128
vxorpd ymmdest, ymmsrc1, ymmsrc2/mem256

As instruções SSE (sem o prefixo v) não alteram os bits superiores do registrador YMM de destino. Por outro lado, as instruções AVX (com o prefixo v) que operam em 128 bits preenchem os 128 bits superiores do registrador YMM de destino com zeros. Quando um operando é uma variável na memória, ele deve estar alinhado a uma fronteira apropriada (16 bytes para mem128 e 32 bytes para mem256) para evitar erros de alinhamento em tempo de execução.

Exemplo de Operação AND

O código a seguir demonstra a aplicação da operação AND.

global _start

section .data
    nums0 dd 0, 1, 0, 1
    nums1 dd 0, 1, 1, 0

section .text
_start:
    movaps xmm0, [nums0]     ; Carrega o vetor nums0 no registrador xmm0
    movaps xmm1, [nums1]     ; Carrega o vetor nums1 no registrador xmm1
    andpd xmm0, xmm1         ; XMM0 = 0, 1, 0, 0

    mov rax, 60
    syscall

Neste exemplo, os vetores nums0 e nums1 são carregados nos registradores xmm0 e xmm1, respectivamente. A instrução andpd aplica a operação lógica AND entre os elementos correspondentes dos dois vetores. A operação AND retorna 1 somente se os bits em ambos os operandos forem 1.

O cálculo realizado é o seguinte:

  [0, 1, 0, 1]
AND
  [0, 1, 1, 0]
=
  [0, 1, 0, 0]

Como resultado, o registrador xmm0 conterá o vetor [0, 1, 0, 0].

Para verificar o resultado, podemos exibir os valores no console, como mostra o exemplo para Linux a seguir.

global main

extern printf

section .data
    nums0 dd 0, 1, 0, 1
    nums1 dd 0, 1, 1, 0
    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 8
    movaps xmm0, [nums0]     ; Carrega o vetor nums0 no registrador xmm0
    movaps xmm1, [nums1]     ; Carrega o vetor nums1 no registrador xmm1
    andpd xmm0, xmm1         ; XMM0 = 0, 1, 0, 0

    movd esi, xmm0           ; Move o primeiro número para esi
    psrldq xmm0, 4           ; Desloca 4 bytes para a direita para acessar o próximo número
    movd edx, xmm0           ; Move o segundo número para edx
    psrldq xmm0, 4           ; Desloca novamente
    movd ecx, xmm0           ; Move o terceiro número para ecx
    psrldq xmm0, 4           ; Desloca novamente
    movd r8d, xmm0           ; Move o quarto número para r8d

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Neste programa, a função printf da biblioteca C é usada para exibir os valores. Após a operação AND, cada elemento do vetor resultante em xmm0 é extraído e movido para os registradores apropriados (esi, edx, ecx, r8d) para serem passados como argumentos para a printf.

Compilação e execução do programa:

nasm -f elf64 hello.asm -o hello.o
gcc -static  hello.o -o hello
./hello
0, 1, 0, 0

A seguir, a versão análoga do programa para Windows.

global main

extern printf

section .data
    nums0 dd 0, 1, 0, 1
    nums1 dd 0, 1, 1, 0
    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 40
    movaps xmm0, [rel nums0]     ; Carrega o vetor nums0 em xmm0
    movaps xmm1, [rel nums1]     ; Carrega o vetor nums1 em xmm1
    andpd xmm0, xmm1             ; XMM0 = 0, 1, 0, 0

    movd edx, xmm0               ; Move o primeiro número para edx
    psrldq xmm0, 4               ; Desloca 4 bytes para a direita
    movd r8d, xmm0               ; Move o segundo número para r8d
    psrldq xmm0, 4               ; Desloca novamente
    movd r9d, xmm0               ; Move o terceiro número para r9d
    psrldq xmm0, 4               ; Desloca novamente
    movd eax, xmm0
    mov dword [rsp+32], eax      ; Move o quarto número para a pilha

    mov rcx, format_str
    call printf

    add rsp, 40
    ret

Exemplo de Manipulação de Strings

Outro exemplo prático é a manipulação de strings. O código a seguir, para Linux, converte uma string para letras minúsculas.

global main

extern printf

section .data
    message db "HeLLo",0
    maxlen equ ($-message-1)
align 16
mask1:  times maxlen db 0x20
        times (16-maxlen) db 0

mask2:  times maxlen db 0xDF
        times (16-maxlen) db 0

    format_str db "%s", 10, 0

section .text
main:
    sub rsp, 8

    movdqa xmm0, [message]     ; Copia a string de message para o registrador xmm0
    orpd xmm0, [mask1]         ; Converte para minúsculas
    ; andpd xmm0, [mask2]        ; Converte para maiúsculas (exemplo)

    movdqa [message], xmm0     ; Copia a string de xmm0 de volta para message

    mov rdi, format_str
    mov rsi, message
    xor rax, rax
    call printf

    add rsp, 8
    ret

Na tabela ASCII, as letras minúsculas diferem das maiúsculas pelo estado do bit 5 (contando a partir do zero). Por exemplo, "A" é 01000001 e "a" é 01100001. Para converter uma letra para minúscula, basta definir o bit 5 como 1. Para isso, uma máscara é definida.

A máscara mask1 contém uma sequência de bytes com o valor 0x20 (binário 00100000), que possui apenas o bit 5 definido. A string é carregada no registrador xmm0 e, com a instrução orpd xmm0, [mask1], o bit 5 de cada caractere da string é ativado através da operação OR, convertendo os caracteres para minúsculo.

Por fim, a string modificada é copiada de volta para a memória e exibida no console. Este é um exemplo simplificado, pois não trata caracteres não alfabéticos ou strings maiores que 16 bytes. No entanto, ilustra como as extensões SSE/AVX podem simplificar operações em strings.

Resultado da execução do programa:

$ nasm -f elf64 hello.asm -o hello.o
$ gcc -static  hello.o -o hello
$ ./hello
hello

De forma análoga, para converter uma letra para maiúscula, é necessário zerar o bit 5. Isso pode ser feito com uma operação AND e a máscara mask2, que contém o valor 0xDF (binário 11011111).

andpd xmm0, [mask2]

Resumo

  • As extensões SSE/AVX incluem instruções para operações lógicas bit a bit (AND, OR, XOR, AND NOT) em vetores.
  • As versões AVX (prefixo v) geralmente operam com três operandos e gerenciam os bits superiores dos registradores YMM de forma diferente das versões SSE.
  • Essas operações são eficientes para processar múltiplos dados simultaneamente, como na verificação de condições ou na manipulação de strings.
  • É crucial garantir o alinhamento de memória ao usar operandos que estão na memória para evitar falhas de segmentação.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com