Atualizado: 09/11/2025

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

Adição Vetorial com Instruções SSE e AVX em Assembly NASM

Para a soma de elementos correspondentes em vetores, são empregadas as seguintes instruções:

  • paddb / vpaddb: Soma de bytes em 16 pistas (SSE/AVX-128) ou 32 pistas (AVX-256).
  • paddw / vpaddw: Soma de words (16 bits) em 8 pistas (SSE/AVX-128) ou 16 pistas (AVX-256).
  • paddd / vpaddd: Soma de double words (32 bits) em 4 pistas (SSE/AVX-128) ou 8 pistas (AVX-256).
  • paddq / vpaddq: Soma de quad words (64 bits) em 2 pistas (SSE/AVX-128) ou 4 pistas (AVX-256).

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

paddb xmmdest, xmmsrc/mem128
vpaddb xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddb ymmdest, ymmsrc1, ymmsrc2/mem256

paddw xmmdest, xmmsrc/mem128
vpaddw xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddw ymmdest, ymmsrc1, ymmsrc2/mem256

paddd xmmdest, xmmsrc/mem128
vpaddd xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddd ymmdest, ymmsrc1, ymmsrc2/mem256

paddq xmmdest, xmmsrc/mem128
vpaddq xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddq ymmdest, ymmsrc1, ymmsrc2/mem256

As instruções com dois operandos somam as pistas correspondentes dos dois operandos e armazenam o resultado no primeiro. Já as instruções com três operandos somam o segundo e o terceiro operando, guardando o resultado no primeiro.

O exemplo a seguir para Linux demonstra uma soma simples.

global main

extern printf

section .data
    nums0 dd 1, 2, 4, 8
    nums1 dd 2, 3, 5, 9

    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    paddd xmm0, xmm1       ; XMM0 = XMM0 + XMM1

    movd esi, xmm0
    psrldq xmm0, 4         ; Desloca xmm0 para a direita para obter o próximo número
    movd edx, xmm0
    psrldq xmm0, 4
    movd ecx, xmm0
    psrldq xmm0, 4
    movd r8d, xmm0

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Neste caso, o registrador XMM0 conterá um vetor cujos elementos são a soma das pistas correspondentes dos dois registradores, resultando no vetor [3, 5, 9, 17]. Após a soma, o vetor é impresso no console para verificação.

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

$ nasm -f elf64 hello.asm -o hello.o
$ gcc -static hello.o -o hello
$ ./hello
3, 5, 9, 17

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

global main

extern printf

section .data
    nums0 dd 1, 2, 4, 8
    nums1 dd 2, 3, 5, 9
    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 40
    movaps xmm0, [rel nums0]
    movaps xmm1, [rel nums1]
    paddd xmm0, xmm1       ; XMM0 = XMM0 + XMM1

    movd edx, xmm0
    psrldq xmm0, 4
    movd r8d, xmm0
    psrldq xmm0, 4
    movd r9d, xmm0
    psrldq xmm0, 4
    movd eax, xmm0
    mov dword [rsp+32], eax

    mov rcx, format_str
    call printf

    add rsp, 40
    ret

O Problema do Overflow

Essas instruções não afetam nenhum dos flags de status e, portanto, não sinalizam a ocorrência de overflow (significa que o resultado excedeu a capacidade do tipo de dado). Se um overflow ocorre durante a adição, o valor excedente é simplesmente descartado (comportamento de wrap-around), o que pode levar a resultados incorretos.

global main

extern printf

section .data
    nums0 dw -32768, 0, 0, 0, 0, 0, 0, 0
    nums1 dw -10, 0, 0, 0, 0, 0, 0, 0

    format_str db "%d", 10, 0

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    paddw xmm0, xmm1       ; XMM0 = XMM0 + XMM1

    movd esi, xmm0
    movsx esi, si          ; Extensão de sinal do word de 16 bits para 32 bits

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

No código acima, o primeiro elemento do vetor nums0 (-32768) é somado com o primeiro de nums1 (-10). O resultado esperado seria -32778, mas como esse valor está fora do intervalo de um word com sinal (-32768 a 32767), ocorre um overflow, e o resultado se torna 32758.

Resultado da execução:

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

A responsabilidade de verificar se os operandos estão dentro de um intervalo válido recai sobre o programa. No entanto, as extensões SSE/AVX fornecem uma solução para este problema.

Adição com Aritmética de Saturação

A aritmética de saturação (saturation arithmetic) é uma alternativa que evita o wrap-around e é particularmente útil no processamento de multimídia (áudio, vídeo, imagens). As instruções a seguir implementam essa lógica:

  • paddsb / vpaddsb: Soma de bytes com sinal e saturação.
  • paddsw / vpaddsw: Soma de words com sinal e saturação.
  • paddusb / vpaddusb: Soma de bytes sem sinal e saturação.
  • paddusw / vpaddusw: Soma de words sem sinal e saturação.

Sua sintaxe é similar às instruções de adição padrão.

paddsb xmmdest, xmmsrc/mem128
vpaddsb xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddsb ymmdest, ymmsrc1, ymmsrc2/mem256

paddsw xmmdest, xmmsrc/mem128
vpaddsw xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddsw ymmdest, ymmsrc1, ymmsrc2/mem256

paddusb xmmdest, xmmsrc/mem128
vpaddusb xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddusb ymmdest, ymmsrc1, ymmsrc2/mem256

paddusw xmmdest, xmmsrc/mem128
vpaddusw xmmdest, xmmsrc1, xmmsrc2/mem128
vpaddusw ymmdest, ymmsrc1, ymmsrc2/mem256

Na adição sem sinal, se o resultado exceder o valor máximo para o tipo de dado, ele é "saturado" ou "fixado" nesse valor máximo. Por exemplo, se a soma de dois bytes (unsigned) ultrapassa 255 (0xFF), o resultado é fixado em 255. De forma análoga, em operações com sinal, o resultado é fixado nos limites máximo (positivo) ou mínimo (negativo) do intervalo (por exemplo, de -128 a 127 para bytes com sinal).

Modificando o exemplo anterior para usar adição com saturação:

global main

extern printf

section .data
    nums0 dw -32768, 2, -4, 8
    nums1 dw -10, -3, 5, -9

    format_str db "%d", 10, 0

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    paddsw xmm0, xmm1       ; XMM0 = XMM0 + XMM1
    ; XMM0 = [-32768, -1, 1, -1]

    movd esi, xmm0
    movsx esi, si           ; Extensão de sinal

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Neste caso, a soma dos primeiros elementos (-32768 + -10) ainda resulta em um overflow negativo. No entanto, com a aritmética de saturação, o resultado é fixado no valor mínimo possível para um word com sinal, que é -32768. Este comportamento é frequentemente mais previsível e desejável.

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

Adição Horizontal

As extensões SSE/AVX também fornecem instruções para "adição horizontal", que somam elementos adjacentes dentro do mesmo vetor.

  • (v)phaddw: Adição horizontal de números de 16 bits.
  • (v)phaddd: Adição horizontal de números de 32 bits.
  • (v)phaddsw: Adição horizontal de números de 16 bits com saturação.

A sintaxe é análoga às outras instruções de adição.

Essas instruções somam pares de elementos adjacentes (words ou dwords) de ambos os operandos e armazenam as somas no registrador de destino.

phaddw

A instrução phaddw opera da seguinte forma:

temp[0-15] = xmmdest[0-15] + xmmdest[16-31]
temp[16-31] = xmmdest[32-47] + xmmdest[48-63]
temp[32-47] = xmmdest[64-79] + xmmdest[80-95]
temp[48-63] = xmmdest[96-111] + xmmdest[112-127]
temp[64-79] = xmmsrc[0-15] + xmmsrc[16-31]
temp[80-95] = xmmsrc[32-47] + xmmsrc[48-63]
temp[96-111] = xmmsrc[64-79] + xmmsrc[80-95]
temp[112-127] = xmmsrc[96-111] + xmmsrc[112-127]
xmmdest = temp

As 4 words inferiores do resultado vêm da soma de pares do primeiro operando, e as 4 words superiores vêm da soma de pares do segundo operando.

vphaddw (128 bits)

A versão vphaddw de 128 bits com três operandos funciona de maneira semelhante, zerando os 128 bits superiores do registrador YMM de destino.

xmmdest[0-15] = xmmsrc1[0-15] + xmmsrc1[16-31]
xmmdest[16-31] = xmmsrc1[32-47] + xmmsrc1[48-63]
...
xmmdest[64-79] = xmmsrc2[0-15] + xmmsrc2[16-31]
...

phaddd

A instrução phaddd para dwords segue o mesmo padrão:

temp[0-31] = xmmdest[0-31] + xmmdest[32-63]
temp[32-63] = xmmdest[64-95] + xmmdest[96-127]
temp[64-95] = xmmsrc[0-31] + xmmsrc[32-63]
temp[96-127] = xmmsrc[64-95] + xmmsrc[96-127]
xmmdest = temp

As versões AVX (vphaddd) de 128 e 256 bits estendem essa lógica para mais elementos, operando de forma similar às suas contrapartes de 16 bits.

Se ocorrer um overflow durante a adição horizontal com (v)phaddw e (v)phaddd, ele é simplesmente ignorado (wrap-around). Com a instrução de saturação phaddsw, qualquer overflow positivo resulta em 0x7FFF, e qualquer overflow negativo resulta em 0x8000.


Resumo

  • As instruções padd realizam a soma paralela de elementos em vetores (bytes, words, dwords, qwords).
  • A adição padrão não sinaliza overflow, podendo levar a resultados incorretos.
  • A aritmética de saturação, com as instruções padd*s e padd*us, evita o wrap-around fixando o resultado nos valores mínimo ou máximo do tipo de dado.
  • A adição horizontal, com as instruções phadd, soma elementos adjacentes dentro do mesmo vetor, sendo útil para operações de redução como a soma de todos os elementos de um vetor.
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