Atualizado: 18/10/2025

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

Cópia de Dados com Instruções SIMD em Assembly NASM

A arquitetura x86-64 oferece diversas instruções para cópia de dados, que permitem mover informações entre registradores (SSE ou AVX), carregar valores de registradores de uso geral ou variáveis, e gravar novamente esses valores em variáveis ou outros registradores.


Cópia de um único valor

As extensões SSE incluem as instruções movd (para cópia de números de 32 bits) e movq (para números de 64 bits):

movd xmm, reg32/mem32
movq xmm, reg64/mem64

A instrução movd copia um valor de um registrador de 32 bits ou de uma variável para os 32 bits inferiores do registrador XMM. A instrução movq faz o mesmo para valores de 64 bits, copiando-os para os 64 bits inferiores do registrador XMM.

Essas instruções também realizam a operação inversa — ou seja, transferem dados de um registrador XMM para um registrador de uso geral ou para uma variável:

movd reg32/mem32, xmm
movq reg64/mem64, xmm

A instrução movq também permite copiar os 64 bits inferiores de um registrador XMM para outro:

movq xmm, xmm

As extensões AVX apresentam versões equivalentes dessas instruções, com o prefixo v:

vmovd xmm, reg32/mem32
vmovq xmm, reg64/mem64
vmovd reg32/mem32, xmm
vmovq reg64/mem64, xmm

Essas instruções servem tanto para inteiros quanto para números de ponto flutuante.

Exemplo em Linux

global _start

section .data
num dw 22

section .text
_start:
    mov rax, 7
    ; para XMM
    movq xmm0, rax      ; XMM0 = 7
    movd xmm1, [num]    ; XMM1 = 22

    ; de XMM
    movq rdi, xmm1      ; RDI = 22
    movd [num], xmm0    ; num = 7

    ; resultado de verificação
    add rdi, [num]      ; RDI = 22 + 7 = 29

    mov rax, 60
    syscall

Exemplo em Windows

global _start

section .data
num dw 22

section .text
_start:
    mov rax, 7
    movq xmm0, rax           ; XMM0 = 7
    movd xmm1, [rel num]     ; XMM1 = 22

    movq rax, xmm1           ; RAX = 22
    movd [rel num], xmm0     ; num = 7

    add rax, [rel num]       ; RAX = 29
    ret

O mesmo princípio é aplicado a números de ponto flutuante.

global _start

section .data
num0 dq 2.13
num1 dw 3.14

section .text
_start:
    movq xmm0, [num0]    ; XMM0 = 2.13
    movd xmm1, [num1]    ; XMM1 = 3.14

    mov rax, 60
    syscall

Cópia de números de ponto flutuante

As instruções movss e movsd permitem copiar valores de ponto flutuante individuais.

movss xmmn, mem32
movss mem32, xmmn
movss xmmsrc, xmmdest

movsd xmmn, mem64
movsd mem64, xmmn
movsd xmmsrc, xmmdest

Essas instruções copiam dados de variáveis para registradores XMM ou de registradores para variáveis. Para maior desempenho, as variáveis usadas em movss devem estar alinhadas em endereços múltiplos de 4 bytes, e as usadas em movsd devem estar alinhadas em endereços múltiplos de 8 bytes.


Cópia de vetores de dados

Para copiar vetores inteiros de dados, a arquitetura oferece instruções que distinguem entre cópia alinhada e não alinhada.


Cópia de dados alinhados

As extensões SSE fornecem:

  • movaps: cópia de conjuntos de números de 32 bits.
  • movapd: cópia de conjuntos de números de 64 bits.
  • movdqa: cópia de valores inteiros de 128 bits.

Essas instruções transferem 16 bytes entre variáveis e registradores XMM ou entre dois registradores XMM.

As extensões AVX oferecem versões equivalentes com prefixo v, que transferem 16 ou 32 bytes entre variáveis e registradores XMM/YMM, ou entre dois registradores XMM/YMM. Durante a cópia em registradores XMM, os bits superiores dos registradores YMM correspondentes são zerados.

Principais variações:

movaps mem128, xmm
vmovaps mem128, xmm
vmovaps mem256, ymm
movaps xmm, mem128
vmovaps xmm, mem128
vmovaps ymm, mem256
movaps xmm, xmm
vmovaps xmm, xmm
vmovaps ymm, ymm
movapd mem128, xmm
vmovapd mem128, xmm
vmovapd mem256, ymm
movapd xmm, mem128
vmovapd xmm, mem128
vmovapd ymm, mem256
movdqa mem128, xmm
vmovdqa mem128, xmm
vmovdqa mem256, ymm
movdqa xmm, mem128
vmovdqa xmm, mem128
vmovdqa ymm, mem256

Essas instruções exigem que os dados estejam alinhados em fronteiras de 16 ou 32 bytes. Caso contrário, ocorre uma exceção de acesso não alinhado. A letra “a” em seus nomes deriva de aligned (alinhado).

Exemplo em Linux

global _start

section .data
align 16
source dd 12, 13, 14, 15

section .bss
align 16
dest resd 4

section .text
_start:
    movaps xmm0, [source]
    movaps [dest], xmm0
    mov edi, [dest]    ; RDI = 12

    mov rax, 60
    syscall

Exemplo em Windows

global _start

section .data
align 16
source dd 12, 13, 14, 15

section .bss
align 16
dest resd 4

section .text
_start:
    movaps xmm0, [rel source]
    movaps [rel dest], xmm0
    mov eax, [rel dest]
    ret

Nesse exemplo, os vetores source e dest são alinhados a 16 bytes. Os quatro números de source são copiados para o registrador XMM0, e então transferidos para dest.

Quando um vetor contém mais dados do que o registrador comporta (por exemplo, sete números de 32 bits), apenas os quatro primeiros serão copiados — o tamanho máximo que cabe em um registrador de 128 bits.


Cópia de strings

global _start

section .data
align 16
source db "Hello PROGRAMICIO.COM", 10, 0
len equ $ - source

section .bss
align 16
dest resb len

section .text
_start:
    vmovapd ymm0, [source]
    vmovapd [dest], ymm0

    mov rdi, 1
    mov rsi, dest
    mov rdx, len
    mov rax, 1
    syscall

    mov rax, 60
    syscall

Neste caso, a string source é copiada para o registrador YMM0 e depois para dest. O alinhamento em 16 bytes garante o funcionamento correto das instruções vmovapd.

Exemplo em Windows

global _start

extern GetStdHandle
extern WriteFile

section .data
align 16
source db "Hello PROGRAMICIO.COM", 10, 0
len equ $ - source

section .bss
align 16
dest resb len

section .text
_start:
    sub rsp, 40
    vmovapd ymm0, [rel source]
    vmovapd [rel dest], ymm0

    mov rcx, -11
    call GetStdHandle
    mov rcx, rax
    mov rdx, dest
    mov r8d, len
    xor r9, r9
    mov qword [rsp + 32], 0
    call WriteFile
    add rsp, 40
    ret

É importante lembrar que essas instruções exigem alinhamento. Por exemplo, se antes de um vetor houver uma variável de 1 byte, ele deixará de estar alinhado e poderá causar falhas:

section .data
temp1 db 1
source dd 12, 13, 14, 15

Para garantir alinhamento:

section .data
temp1 db 1
align 16
source dd 12, 13, 14, 15

Como alternativa, se possível, a ordem das variáveis pode ser alterada para evitar necessidade de realinhamento:

section .data
source dd 12, 13, 14, 15
dest resd 4
temp1 db 1

Cópia de dados não alinhados

Quando o alinhamento não pode ser garantido, é possível usar instruções equivalentes que funcionam com dados não alinhados:

  • (v)movups: cópia de valores de 4 bytes.
  • (v)movupd: cópia de valores de 8 bytes.
  • (v)movdqu: cópia de blocos de 16 bytes.

A letra “u” vem de unaligned (não alinhado). Essas instruções operam da mesma forma, mas toleram endereços não múltiplos de 16 ou 32 bytes, embora geralmente apresentem desempenho inferior.

Exemplo em Linux

global _start

section .data
source dq 22, 23, 24, 25

section .bss
dest resd 8

section .text
_start:
    movupd xmm0, [source]
    movupd [dest], xmm0

    mov rdi, [dest]    ; RDI = 22

    mov rax, 60
    syscall

Essas instruções são úteis quando não é possível garantir o alinhamento da memória, mas ainda assim é necessário copiar blocos de dados vetoriais.


Resumo

  • movd e movq realizam cópias simples entre registradores e memória.
  • movss e movsd copiam valores individuais de ponto flutuante.
  • movaps, movapd e movdqa copiam vetores alinhados.
  • movups, movupd e movdqu copiam vetores não alinhados.
  • O alinhamento em memória é essencial para o desempenho e a segurança das instruções SIMD.
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