Instruções de movimentação de dados

Listando algumas instruções de movimentação de dados do SSE.

MOVAP(S|D)/MOVUP(S|D) | Move Aligned/Unaligned Packed (Single|Double)-precision floating-point

MOVAPS xmm(n), xmm(n)
MOVAPS xmm(n), float(4)
MOVAPS float(4), xmm(n)

MOVUPS xmm(n), xmm(n)
MOVUPS xmm(n), float(4)
MOVUPS float(4), xmm(n)


MOVAPD xmm(n), xmm(n)
MOVAPD xmm(n), double(2)
MOVAPD double(2), xmm(n)

MOVUPD xmm(n), xmm(n)
MOVUPD xmm(n), double(2)
MOVUPD double(2), xmm(n)

As instruções MOVAPS e MOVUPS fazem a mesma coisa: Movem 4 valores float empacotados entre registradores XMM ou de/para memória principal. MOVAPD e MOVUPD porém lida com 2 valores double.

A diferença é que a instrução MOVAPS/MOVAPD espera que o endereço do valor na memória esteja alinhado a um valor de 16 bytes, caso não esteja a instrução dispara uma exceção #GP (General Protection ou "segmentation fault" como é conhecido no Linux). O motivo dessa instrução exigir isso é que acessar o endereço alinhado é muito mais performático.

Já a instrução MOVUPS/MOVUPD pode acessar um endereço de memória desalinhado (unaligned) sem ocorrer nenhum erro, porém ela é menos performática.

Um exemplo de uso da MOVAPS na nossa PoC:

#include <stdio.h>

void assembly(float *array);

int main(void)
{
  float array[4];
  assembly(array);

  printf("%f, %f, %f, %f\n", array[0], array[1], array[2], array[3]);
  return 0;
}

Sem entrar em detalhes ainda sobre a convenção de chamada, o ponteiro recebido como argumento pela função assembly() está no registrador RDI.

Sobre o atributo align=16 usado na seção .rodata ele serve para fazer exatamente o que o nome sugere: Alinhar o endereço inicial da seção em um múltiplo de 16, que é uma exigência da instrução MOVAPS.

Um detalhe interessante que vale citar é que apesar da instrução ter sido feita para lidar com um determinado tipo de dado nada impede de nós carregarmos outros dados nos registradores XMM. No exemplo abaixo usei a instrução MOVUPS para mover uma string de 16 bytes com apenas duas instruções:

#include <stdio.h>

void assembly(char *array);

int main(void)
{
  char text[16];
  assembly(text);

  printf("Resultado: %s\n", text);
  return 0;
}

MOVS(S|D) | Move Scalar (Single|Double)-precision floating-point

MOVSS xmm(n), xmm(n)
MOVSS xmm(n), float(1)
MOVSS float(1), xmm(n)


MOVSD xmm(n), xmm(n)
MOVSD xmm(n), double(1)
MOVSD double(1), xmm(n)

Move um único float/double entre registradores XMM, onde o valor estaria contido na double word (4 bytes) ou quadword (8 bytes) menos significativo do registrador. E também é possível mover de/para memória principal.

MOVLP(S|D) | Move Low Packed (Single|Double)-precision floating-point

MOVLPS xmm(n), float(2)
MOVLPS float(2), xmm(n)


MOVLPD xmm(n), double(1)
MOVLPD double(1), xmm(n)

A instrução MOVLPS instrução é semelhante à MOVUPS porém carrega/escreve apenas dois floats. No registrador os dois floats ficam armazenados no quadword (8 bytes) menos significativo. O quadword mais significativo do registrador não é alterado.

Já MOVLPD faz a mesma operação porém com um double contido no quadword menos significativo.

MOVHP(S|D) | Move High Packed (Single|Double)-precision floating-point

MOVHPS xmm(n), float(2)
MOVHPS float(2), xmm(n)


MOVHPD xmm(n), double(1)
MOVHPD double(1), xmm(n)

Semelhante a instrução acima porém armazena/ler o valor do registrador XMM no quadword mais significativo. O quadword menos significativo do registrador não é alterado.

MOVLHPS | Move Packed Single-precision floating-point Low to High

MOVLHPS xmm(n), xmm(n)

Move o quadword (8 bytes) menos significativo do registrador fonte (a direita) para o quadword mais significativo do registrador destino. O quadword menos significativo do registrador destino não é alterado.

MOVHLPS | Move Packed Single-precision floating-point High to Low

MOVHLPS xmm(n), xmm(n)

Move o quadword (8 bytes) mais significativo do registrador fonte (a direita) para o quadword menos significativo do registrador destino. O quadword mais significativo do registrador destino não é alterado.

MOVMSKP(S|D) | Move Packed (Single|Double)-precision floating-point mask

MOVMSKPS reg32/64, xmm(n)


MOVMSKPD reg32/64, xmm(n)

MOVMSKPS move os bits mais significativos (MSB) de cada um dos quatro valores float contido no registrador XMM para os 4 bits menos significativo do registrador de propósito geral. Os outros bits do registrador são zerados.

Já MOVMSKPD faz a mesma coisa porém com os 2 valores doubles contidos no registrador, assim definindo os 2 bits menos significativos do registrador de propósito geral.

Essa instrução pode ser usada com o intuito de verificar o sinal de cada um dos valores float/double, tendo em vista que o bit mais significativo é usado para indicar o sinal do número (0 caso positivo e 1 caso negativo).

Last updated