Atualizado: 09/11/2025

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

Multiplicação Vetorial com Instruções SSE e AVX

As extensões SSE e AVX fornecem um conjunto de instruções para a multiplicação de elementos em vetores.

Multiplicação com Resultado Truncado

A principal complexidade da multiplicação vetorial é que o produto de dois números de n bits pode gerar um resultado de até 2n bits. No entanto, a pista de destino no registrador SIMD tem apenas n bits. Para lidar com isso, a maioria das instruções de multiplicação armazena apenas uma parte do resultado (a parte inferior ou a superior).

  • pmullw / vpmullw: Multiplica words (16 bits) e armazena os 16 bits inferiores (low word) do resultado.
  • pmulhuw / vpmulhuw: Multiplica words sem sinal e armazena os 16 bits superiores (high word) do resultado.
  • pmulhw / vpmulhw: Multiplica words com sinal e armazena os 16 bits superiores (high word) do resultado.
  • pmulld / vpmulld: Multiplica dwords (32 bits) e armazena os 32 bits inferiores (low dword) do resultado.
  • vpmullq: Multiplica qwords (64 bits) e armazena os 64 bits inferiores (low qword) do resultado.

A operação de vpmulld de 128 bits, por exemplo, pode ser visualizada da seguinte forma:

// Multiplica os 4 pares de dwords de 32 bits
Temp0[63:0] := SRC1[31:0] * SRC2[31:0]
Temp1[63:0] := SRC1[63:32] * SRC2[63:32]
Temp2[63:0] := SRC1[95:64] * SRC2[95:64]
Temp3[63:0] := SRC1[127:96] * SRC2[127:96]

// Armazena os 32 bits inferiores de cada resultado temporário no destino
DEST[31:0]   := Temp0[31:0]
DEST[63:32]  := Temp1[31:0]
DEST[95:64]  := Temp2[31:0]
DEST[127:96] := Temp3[31:0]

As versões de 256 bits estendem essa lógica para 8 pistas.

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

pmullw xmmdest, xmmsrc/mem128
vpmullw xmmdest, xmmsrc, xmm/mem128
vpmullw ymmdest, ymmsrc, ymm/mem256

pmulhuw xmmdest, xmmsrc/mem128
vpmulhuw xmmdest, xmmsrc, xmm/mem128
vpmulhuw ymmdest, ymmsrc, ymm/mem256

pmulhw xmmdest, xmmsrc/mem128
vpmulhw xmmdest, xmmsrc, xmm/mem128
vpmulhw ymmdest, ymmsrc, ymm/mem256

pmulld xmmdest, xmmsrc/mem128
vpmulld xmmdest, xmmsrc, xmm/mem128
vpmulld ymmdest, ymmsrc, ymm/mem256

vpmullq xmmdest, xmmsrc, xmm/mem128
vpmullq ymmdest, ymmsrc, ymm/mem256

O exemplo a seguir para Linux demonstra a multiplicação de dwords.

global main

extern printf

section .data
    nums0 dd 5, 6, 7, 8
    nums1 dd 4, 5, 6, 7
    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    vpmulld xmm0, xmm0, xmm1       ; XMM0 = XMM0 * XMM1
    ; Resultado em XMM0: [20, 30, 42, 56]

    ; 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

Neste código, as pistas de 32 bits dos registradores xmm0 e xmm1 são multiplicadas.

Resultado da execução do programa:

$ nasm -f elf64 hello.asm -o hello.o
$ gcc -static hello.o -o hello
$ ./hello
20, 30, 42, 56

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

global main

extern printf

section .data
    nums0 dd 5, 6, 7, 8
    nums1 dd 4, 5, 6, 7
    format_str db "%d, %d, %d, %d", 10, 0

section .text
main:
    sub rsp, 40
    movaps xmm0, [rel nums0]
    movaps xmm1, [rel nums1]
    vpmulld xmm0, xmm0, xmm1       ; XMM0 = XMM0 * XMM1
    ; Resultado em XMM0: [20, 30, 42, 56]

    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

Multiplicação com Resultado Completo

Existem instruções específicas que contornam a limitação do tamanho do resultado, armazenando o produto completo em uma pista de tamanho maior.

  • pmuldq / vpmuldq: Multiplica os dwords (32 bits) com sinal das pistas 0 e 2, e armazena os resultados completos de 64 bits nas pistas de qword 0 e 1 do registrador de destino.
  • pmuludq / vpmuludq: Similar à anterior, mas opera com dwords sem sinal.

A sintaxe dessas instruções é a seguinte:

pmuldq xmmdest, xmmsrc/mem128
vpmuldq xmmdest, xmmsrc1, xmm/mem128
vpmuldq ymmdest, ymmsrc1, ymm/mem256

pmuludq xmmdest, xmmsrc/mem128
vpmuludq xmmdest, xmmsrc1, xmm/mem128
vpmuludq ymmdest, ymmsrc1, ymm/mem256

Outra instrução notável é a pclmulqdq (e vpclmulqdq), que realiza multiplicação carry-less de qwords (64 bits) e armazena o resultado de 128 bits.

pclmulqdq xmmdest, xmmsrc/mem128, imm8
vpclmulqdq xmmdest, xmmsrc1, xmmsrc2/mem128, imm8

O operando imediato imm8 seleciona quais qwords dos operandos de origem serão multiplicados.

imm8Operação (vpclmulqdq)
0x00dest = src1[0-63] * src2[0-63]
0x01dest = src1[64-127] * src2[0-63]
0x10dest = src1[0-63] * src2[64-127]
0x11dest = src1[64-127] * src2[64-127]

O exemplo a seguir para Linux demonstra a multiplicação de qwords de 64 bits.

global main

extern printf

section .data
    nums0 dq 5, 6
    nums1 dq 3, 4
    format_str db "%ld", 10, 0

section .text
main:
    sub rsp, 8

    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    vpclmulqdq xmm0, xmm0, xmm1, 0x00
    ; Resultado em XMM0[0:63]: 15

    ; Exibe os dados no console
    movq rsi, xmm0

    mov rdi, format_str
    xor rax, rax
    call printf

    add rsp, 8
    ret

Resumo

  • A multiplicação SIMD precisa lidar com o fato de que o produto de n bits pode ter 2n bits.
  • Instruções como pmullw e pmulld armazenam a parte inferior do resultado, descartando a parte superior.
  • Instruções como pmulhw e pmulhuw armazenam a parte superior do resultado, descartando a inferior.
  • Instruções especializadas como pmuldq e pclmulqdq foram criadas para armazenar o resultado completo da multiplicação em pistas maiores.
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