Atualizado: 09/11/2025

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

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

Para a subtração de elementos correspondentes em vetores, as extensões SSE e AVX oferecem um conjunto de instruções análogo ao da adição:

  • psubb / vpsubb: Subtração de bytes em 16 pistas (SSE/AVX-128) ou 32 pistas (AVX-256).
  • psubw / vpsubw: Subtração de words (16 bits) em 8 pistas (SSE/AVX-128) ou 16 pistas (AVX-256).
  • psubd / vpsubd: Subtração de double words (32 bits) em 4 pistas (SSE/AVX-128) ou 8 pistas (AVX-256).
  • psubq / vpsubq: Subtração 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:

psubb xmmdest, xmmsrc/mem128
vpsubb xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubb ymmdest, ymmsrc1, ymmsrc2/mem256

psubw xmmdest, xmmsrc/mem128
vpsubw xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubw ymmdest, ymmsrc1, ymmsrc2/mem256

psubd xmmdest, xmmsrc/mem128
vpsubd xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubd ymmdest, ymmsrc1, ymmsrc2/mem256

psubq xmmdest, xmmsrc/mem128
vpsubq xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubq ymmdest, ymmsrc1, ymmsrc2/mem256

As instruções com dois operandos subtraem as pistas do segundo operando das pistas correspondentes do primeiro, armazenando o resultado no primeiro operando (dest = dest - src). As instruções com três operandos subtraem o terceiro do segundo e guardam o resultado no primeiro (dest = src1 - src2).

O exemplo a seguir para Linux demonstra uma subtração simples.

global main

extern printf

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

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

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    psubd xmm0, xmm1       ; XMM0 = XMM0 - XMM1
    ; Resultado em XMM0: [-1, 1, -2, 3]

    ; Exibe os dados no console
    movd esi, xmm0
    psrldq xmm0, 4
    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

Resultado da execução do programa:

$ nasm -f elf64 hello.asm -o hello.o
$ gcc -static hello.o -o hello
$ ./hello
-1, 1, -2, 3

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

global main

extern printf

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

section .text
main:
    sub rsp, 40
    movaps xmm0, [rel nums0]
    movaps xmm1, [rel nums1]
    psubd xmm0, xmm1       ; XMM0 = XMM0 - XMM1
    ; Resultado em XMM0: [-1, 1, -2, 3]

    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

Assim como nas instruções de adição, as instruções de subtração não afetam nenhum flag de status. Qualquer informação sobre borrow, overflow ou underflow é perdida, o que pode levar a resultados incorretos.

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

De forma análoga à adição, as extensões SSE/AVX fornecem instruções para subtração com saturação, que evitam o comportamento de wrap-around.

  • psubsb / vpsubsb: Subtração de bytes com sinal e saturação.
  • psubsw / vpsubsw: Subtração de words com sinal e saturação.
  • psubusb / vpsubusb: Subtração de bytes sem sinal e saturação.
  • psubusw / vpsubusw: Subtração de words sem sinal e saturação.

A sintaxe dessas instruções é idêntica à de suas contrapartes sem saturação.

psubsb xmmdest, xmmsrc/mem128
vpsubsb xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubsb ymmdest, ymmsrc1, ymmsrc2/mem256

psubsw xmmdest, xmmsrc/mem128
vpsubsw xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubsw ymmdest, ymmsrc1, ymmsrc2/mem256

psubusb xmmdest, xmmsrc/mem128
vpsubusb xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubusb ymmdest, ymmsrc1, ymmsrc2/mem256

psubusw xmmdest, xmmsrc/mem128
vpsubusw xmmdest, xmmsrc1, xmmsrc2/mem128
vpsubusw ymmdest, ymmsrc1, ymmsrc2/mem256

Para operandos com sinal, o resultado é "saturado" ou fixado nos limites do tipo de dado:

  • Bytes (8 bits): Satura em +127 (0x7F) para overflow positivo e -128 (0x80) para overflow negativo.
  • Words (16 bits): Satura em +32767 (0x7FFF) e -32768 (0x8000).

Para operandos sem sinal, a saturação ocorre no valor máximo do tipo de dado para overflow positivo e em 0 para overflow negativo.

Vejamos como a saturação afeta o resultado. Primeiro, um exemplo com subtração padrão que causa overflow.

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]
    psubw xmm0, xmm1       ; XMM0 = XMM0 - XMM1

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

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Neste código, a operação -32768 - 10 é realizada. Matematicamente, o resultado seria -32778, mas este valor está fora do intervalo de um word com sinal.

Resultado da execução:

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

O valor 32758 é claramente incorreto. Agora, vamos aplicar a subtração com saturação.

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]
    psubsw xmm0, xmm1       ; Subtração com saturação

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

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Resultado da execução com saturação:

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

O resultado -32768, embora não seja matematicamente exato, é o valor mínimo possível para o tipo de dado. Ele está muito mais próximo do resultado desejado e, em muitos cenários, como no processamento de imagens ou áudio, é um resultado aceitável e previsível.


Resumo

  • As instruções psub realizam a subtração paralela de elementos em vetores (bytes, words, dwords, qwords).
  • A subtração padrão não gerencia overflow ou underflow, o que pode levar a resultados incorretos.
  • A aritmética de saturação, implementada pelas instruções psub*s e psub*us, evita o wrap-around fixando o resultado nos valores mínimo ou máximo permitidos pelo tipo de dado.
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