arrow-left

All pages
gitbookPowered by GitBook
1 of 6

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Instruções de movimentação de dados

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

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

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:

circle-info

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:

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

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.

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

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.

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

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.

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

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.

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

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.

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

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).

Instruções com inteiros 128-bit

hashtag
PAVGB/PAVGW | Compute average of packed unsigned (byte|word) of integers

Calcula a média da soma de todos os valores dos dois operandos somados. PAVGB calcula a média somando 16 bytes em cada operando, enquanto PAVGW soma 8 words em cada um.

Entendendo SSE

Aprendendo sobre SIMD, SSE e registradores XMM.

Na computação existe um conceito de instrução chamado SIMD (Single Instruction, Multiple Data) que é basicamente uma instrução que processa múltiplos dados de uma única vez. Todas as instruções que vimos até agora processavam meramente um dado por vez, porém instruções SIMD podem processar diversos dados paralelamente. O principal objetivo das instruções SIMD é ganhar performance se aproveitando dos múltiplos núcleos do processador, a maioria das instruções SIMD foram implementadas com o intuito de otimizar cálculos comuns em áreas como processamento gráfico, inteligência artificial, criptografia, matemática etc.

A Intel criou a primeira versão do SSE (streaming SIMD extensions) ainda no IA-32 com o Pentium III, e de lá para cá já ganhou diversas novas versões que estendem a tecnologia adicionando novas instruções. Atualmente nos processadores mais modernos há as seguintes extensões: SSE, SSE2, SSE3, SSSE3 e SSE4.

circle-info

Instruções de conversão

Convertendo valores entre float, double e inteiro.

Essas instruções servem para conversão de tipos entre float, double e inteiro.

hashtag
Conversão entre double e float

Instruções lógicas e de comparação

hashtag
Instruções lógicas SSE

hashtag
ANDP(S|D) | bitwise logical AND of Packed (Single|Double)-precision floating-point values

Instruções aritméticas

Instruções de operação aritmética do SSE.

hashtag
ADDP(S|D) | Add Packed (Single|Double)-precision floating-point values

Soma 4 números float (ou 2 números double) de uma única vez no registrador destino com os quatro números float (ou 2 números double) do registrador/memória fonte. Exemplo:

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)
hashtag
PEXTRW | Extract word

Copia uma das 8 words contidas no registrador XMM e armazena no registrador de propósito geral de 32 ou 64 bits. O valor é movido para os 16 bits menos significativos do registrador e todos os outros bits são zerados. Também é possível armazenar a word diretamente na memória principal.

O operando imediato é um valor entre 0 e 7 que indica o índice da word no registrador XMM. Apenas os 3 bits menos significativos do valor são considerados, os demais são ignorados.

hashtag
PINSRW | Insert word

Copia uma word dos 16 bits menos significativos do registrador de propósito geral no segundo operando e armazena em uma das words no registrador XMM. Também é possível ler a word da memória principal.

Assim como no caso do PEXTRW o operando imediato serve para identificar o índice da word no registrador XMM.

hashtag
PMAXUB/PMAXUW | Maximum of packed unsigined (byte|word) of integers

Compara os bytes/words não-sinalizados dos dois operandos packed e armazena o maior deles em cada uma das comparações no operando destino (o primeiro).

hashtag
PMINUB/PMINUW | Minimum of packed unsigned (byte|word) of integers

Faz o mesmo que a instrução anterior porém armazenando o menor número de cada comparação.

hashtag
PMAXS(B|W|D) | Maximum of packed signed (byte|word|doubleword) integers

Faz o mesmo que PMAXUB/PMAXUW porém considerando o valor como sinalizado. Também há a instrução PMAXSD que compara quatro double words (4 bytes) empacotados.

hashtag
PMINS(B|W) | Minimum of packed signed (byte|word) integers

Faz o mesmo que PMAXSB/PMAXSW porém retornando o menor valor de cada comparação.

hashtag
PMOVMSKB | Move byte mask

Armazena nos 16 bits menos significativos do registrador de propósito geral cada um dos bits mais significativos (MSB) de todos os bytes contidos no registrador XMM.

hashtag
PMULHW/PMULHUW | Multiply packed (unsigned) word integers and store high result

Multiplica cada uma das words dos operandos onde o resultado temporário da multiplicação é de 32 bits de tamanho. Porém armazena no operando destino somente a word mais significativa do resultado da multiplicação.

PMULHW faz uma multiplicação sinalizada, enquanto PMULHUW faz uma multiplicação não-sinalizada.

hashtag
PSADBW | Compute sum of absolute differences

Calcula a diferença absoluta dos bytes dos dois operandos e armazena a soma de todas as diferenças.

A diferença dos 8 bytes menos significativos é somada e armazenada na word menos significativa do operando destino. Já a diferença dos 8 bytes mais significativos é somada e armazenada na quinta word (índice 4, bits [79:64]) do operando destino. Todas as outras words do registrador XMM são zeradas.

hashtag
MOVDQA | Move aligned double quadword

Move dois quadwords (8 bytes) entre registradores XMM ou de/para memória RAM. O endereço na memória precisa estar alinhado a 16 se não uma exceção #GP será disparada.

hashtag
MOVDQU | Move unaligned double quadword

Faz o mesmo que a instrução anterior porém o alinhamento da memória não é necessário, porém essa instrução é menos performática caso acesse um endereço desalinhado.

hashtag
PADD(B|W|D|Q) | Packed (byte|word|doubleword|quadword) add

Soma os bytes, words, double words ou quadwords dos operandos e armazena no operando destino.

hashtag
PSUBQ | Packed quadword subtract

Faz o mesmo que a instrução PADDQ porém com uma operação de subtração.

hashtag
PMULUDQ | Multiply packed unsigned doubleword integers

Multiplica o primeiro (índice 0) e o terceiro (índice 2) doublewords dos operandos e armazena o resultado como quadwords no operando destino. O resultado da multiplicação entre os primeiros doublewords é armazenado no quadword menos signfiicativo do operando destino, enquanto a multiplicação entre os terceiros doublewords é armazenada no quadword mais significativo.

Exemplo:

circle-info

RDI é o primeiro ponteiro recebido como argumento e RSI o segundo.

hashtag
PSLLDQ | Shift double quadword left logical

Faz uma operação de logical shiftarrow-up-right left com os dois quadwords do registrador XMM. O número de vezes que o shift deve ser feito é especificado pelo operando imediato de 8 bits. Os bits menos significativos são zerados.

hashtag
PSRLDQ | Shift double quadword right logical

Faz o mesmo que a instrução anterior porém com um shift right. Os bits mais significativos são zerados.

hashtag
CVTPS2PD | Convert packed single-precision floating-point values to packed double-precision floating-point values

Converte dois valores float do operando fonte (segundo) em dois valores double no operando destino (primeiro).

hashtag
CVTPD2PS | Convert packed double-precision floating-point values to packed single-precision floating-point values

Converte dois valores double do operando fonte (segundo) em dois valores float no operando destino (primeiro).

hashtag
CVTSS2SD | Convert scalar single-precision floating-point value to scalar double-precision floating-point value

Converte um valor float do operando fonte (segundo) em um valor double no operando destino (primeiro).

hashtag
CVTSD2SS | Convert scalar double-precision floating-point value to scalar single-precision floating-point value

Converte um valor double do operando fonte (segundo) em um valor float no operando destino (primeiro).

hashtag
Conversão entre double e inteiro

hashtag
CVTPD2DQ/CVTTPD2DQ | Convert (with truncation) packed double-precision floating-point values to packed doubleword integers

Converte os dois doubles no operando fonte para dois inteiros sinalizados de 32-bit no operando destino. A instrução CVTPD2DQ faz o arredondamento normal do valor enquanto CVTTPD2DQ trunca ele.

hashtag
CVTDQ2PD | Convert packed doubleword integers to packed double-precision floating-point values

Converte os dois inteiros sinalizados de 32-bit no operando fonte para dois doubles no operando destino.

hashtag
CVTSD2SI/CVTTSD2SI | Convert scalar double-precision floating-point value to doubleword integer

CVTSD2SI converte o valor double no operando fonte em inteiro de 32-bit sinalizado, e armazena o valor no registrador de propósito geral do operando destino. O registrador destino também pode ser um registrador de 64-bit onde nesse caso o valor sofrerá extensão de sinal (sign extensionarrow-up-right).

CVTTSD2SI faz a mesma coisa porém truncando o valor.

hashtag
CVTSI2SD | Convert doubleword integer to scalar double-precision floating-point value

Converte o valor inteiro sinalizado de 32 ou 64 bits do operando fonte e armazena como um double no operando destino.

hashtag
Conversão entre float e inteiro

hashtag
CVTPS2DQ/CVTTPS2DQ | Convert (with truncation) packed single-precision floating-point values to packed doubleword integers

Converte quatro floats do operando fonte em quatro inteiros sinalizados de 32-bit no operando destino. A instrução CVTPS2DQ faz o arredondamento normal dos valores enquanto CVTTPS2DQ trunca eles.

hashtag
CVTDQ2PS | Convert packed doubleword integers to packed single-precision floating-point values

Converte quatro inteiros sinalizados de 32-bit no operando fonte para quatro floats no operando destino.

hashtag
CVTSS2SI/CVTTSS2SI | Convert scalar single-precision floating-point value to doubleword integer

CVTSS2SI converte o valor float no operando fonte em inteiro de 32-bit sinalizado, e armazena o valor no registrador de propósito geral do operando destino. O registrador destino também pode ser um registrador de 64-bit onde nesse caso o valor sofrerá extensão de sinal (sign extensionarrow-up-right).

A instrução CVTTSS2SI faz a mesma coisa porém truncando o valor.

hashtag
CVTSI2SS | Convert doubleword integer to scalar single-precision floating-point value

Converte o valor inteiro sinalizado de 32 ou 64 bits do operando fonte e armazena como um float no operando destino.

#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;
}
bits 64
default rel

section .rodata align=16
    local_array: dd 1.23
                 dd 2.45
                 dd 3.67
                 dd 4.89

section .text

global assembly
assembly:
    movaps xmm5, [local_array]
    movaps [rdi], xmm5
    ret
#include <stdio.h>

void assembly(char *array);

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

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

section .rodata
    string: db "Hello World!", 0, 0, 0, 0

section .text

global assembly
assembly:
    movups xmm0, [string]
    movups [rdi], xmm0
    ret
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)
MOVLPS xmm(n), float(2)
MOVLPS float(2), xmm(n)


MOVLPD xmm(n), double(1)
MOVLPD double(1), xmm(n)
MOVHPS xmm(n), float(2)
MOVHPS float(2), xmm(n)


MOVHPD xmm(n), double(1)
MOVHPD double(1), xmm(n)
MOVLHPS xmm(n), xmm(n)
MOVHLPS xmm(n), xmm(n)
MOVMSKPS reg32/64, xmm(n)


MOVMSKPD reg32/64, xmm(n)
#include <stdio.h>
#include <inttypes.h>

void mularray(uint64_t *output, uint32_t *array);

int main(void)
{
  uint32_t array[] = {3, 1, 2, 1};
  uint64_t output[2];
  mularray(output, array);

  printf("Resultado: %" PRIu64 ", %" PRIu64 "\n", output[0], output[1]);
  return 0;
}
bits 64
default rel

section .rodata align=16
    mul_values: dd 2, 3, 4, 5

section .text

global mularray
mularray:
    movdqa xmm0, [mul_values]
    pmuludq xmm0, [rsi]
    movdqa [rdi], xmm0
    ret
PAVGB xmm(n), xmm(n)
PAVGB xmm(n), ubyte(16)


PAVGW xmm(n), xmm(n)
PAVGW xmm(n), uword(8)
PEXTRW reg32/64, xmm(n), imm8
PEXTRW uword(1), xmm(n), imm8  ; Adicionado no SSE4
PINSRW xmm(n), reg32, imm8
PINSRW xmm(n), uword(1), imm8
PMAXUB xmm(n), xmm(n)
PMAXUB xmm(n), ubyte(16)


PMAXUW xmm(n), xmm(n)      ; Adicionado no SSE4
PMAXUW xmm(n), uword(8)    ; Adicionado no SSE4
PMINUB xmm(n), xmm(n)
PMINUB xmm(n), ubyte(16)


PMINUW xmm(n), xmm(n)      ; Adicionado no SSE4
PMINUW xmm(n), uword(8)    ; Adicionado no SSE4
PMAXSB xmm(n), xmm(n)       ; Adicionado no SSE4
PMAXSB xmm(n), byte(16)     ; Adicionado no SSE4


PMAXSW xmm(n), xmm(n)
PMAXSW xmm(n), word(8)


PMAXSD xmm(n), xmm(n)       ; Adicionado no SSE4
PMAXSD xmm(n), dword(4)     ; Adicionado no SSE4
PMINSB xmm(n), xmm(n)       ; Adicionado no SSE4
PMINSB xmm(n), byte(16)     ; Adicionado no SSE4


PMINSW xmm(n), xmm(n)
PMINSW xmm(n), word(8)
PMOVMSKB reg32/64, xmm(n)
PMULHW xmm(n), xmm(n)
PMULHW xmm(n), uword(8)


PMULHUW xmm(n), xmm(n)
PMULHUW xmm(n), uword(8)
PSADBW xmm(n), xmm(n)
PSADBW xmm(n), ubyte(16)
MOVDQA xmm(n), xmm(n)
MOVDQA xmm(n), qword(2)
MOVDQA qword(2), xmm(n)
MOVDQU xmm(n), xmm(n)
MOVDQU xmm(n), qword(2)
MOVDQU qword(2), xmm(n)
PADDB xmm(n), xmm(n)
PADDB xmm(n), byte(16)


PADDW xmm(n), xmm(n)
PADDW xmm(n), word(8)


PADDD xmm(n), xmm(n)
PADDD xmm(n), dword(4)


PADDQ xmm(n), xmm(n)
PADDQ xmm(n), qword(2)
PSUBQ xmm(n), xmm(n)
PSUBQ xmm(n), qword(2)
PMULUDQ xmm(n), xmm(n)
PMULUDQ xmm(n), dword(4)
PSLLDQ xmm(n), imm8
PSRLDQ xmm(n), imm8
CVTPS2PD xmm(n), xmm(n)
CVTPS2PD xmm(n), float(2)
CVTPD2PS xmm(n), xmm(n)
CVTPD2PS xmm(n), double(2)
CVTSS2SD xmm(n), xmm(n)
CVTSS2SD xmm(n), float(1)
CVTSD2SS xmm(n), xmm(n)
CVTSD2SS xmm(n), double(1)
CVTPD2DQ xmm(n), xmm(n)
CVTPD2DQ xmm(n), double(2)


CVTTPD2DQ xmm(n), xmm(n)
CVTTPD2DQ xmm(n), double(2)
CVTDQ2PD xmm(n), xmm(n)
CVTDQ2PD xmm(n), dword(2)
CVTSD2SI reg32/64, xmm(n)
CVTSD2SI reg32/64, double(1)

CVTTSD2SI reg32/64, xmm(n)
CVTTSD2SI reg32/64, double(1)
CVTSI2SD xmm(n), reg32/64
CVTSI2SD xmm(n), dword(1)
CVTSI2SD xmm(n), qword(1)
CVTPS2DQ xmm(n), xmm(n)
CVTPS2DQ xmm(n), float(4)


CVTTPS2DQ xmm(n), xmm(n)
CVTTPS2DQ xmm(n), float(4)
CVTDQ2PS xmm(n), xmm(n)
CVTDQ2PS xmm(n), dword(4)
CVTSS2SI reg32/64, xmm(n)
CVTSS2SI reg32/64, float(1)


CVTTSS2SI reg32/64, xmm(n)
CVTTSS2SI reg32/64, float(1)
CVTSI2SS xmm(n), reg32/64
CVTSI2SS xmm(n), dword(1)
CVTSI2SS xmm(n), qword(1)
Processadores da arquitetura x86 têm diversas tecnologias SIMD, a primeira delas foi o MMX nos processadores Intel antes mesmo do SSE. Além de haver diversas outras como AVX, AVX-512, FMA, 3DNow! (da AMD) etc.

Na arquitetura x86 existem literalmente milhares de instruções SIMD. Esteja ciente que esse tópico está longe de cobrir todo o assunto e serve meramente como conteúdo introdutório.

hashtag
Registradores XMM

A tecnologia SSE adiciona novos registradores independentes de 128 bits de tamanho cada. Em todos os modos de operação são adicionados oito novos registradores XMM0 até XMM7, e em 64-bit também há mais oito registradores XMM8 até XMM15 que podem ser acessados usando o prefixo REX. Isso dá um total de 16 registradores em 64-bit e 8 registradores nos outros modos de operação.

Esses registradores podem armazenar vários dados diferentes de mesmo tipo/tamanho, conforme demonstra tabela abaixo:

Intel Developer's Manuals | 4.6.2 128-Bit Packed SIMD Data Types

Esses são os tipos empacotados (packed), onde em um único registrador há vários valores de um mesmo tipo. Existem instruções SIMD específicas que executam operações packed onde elas trabalham com os vários dados armazenados no registrador ao mesmo tempo. Em contraste existem também as operações escalares (scalar) que operam com um único dado (unpacked) no registrador, onde esse dado estaria armazenado na parte menos significativa do registrador.

circle-info

Na convenção de chamada para x86-64 da linguagem C os primeiros argumentos float/double passados para uma função vão nos registradores XMM0, XMM1 etc. como valores escalares. E o retorno do tipo float/double fica no registrador XMM0 também como um valor escalar.

Na lista de instruções haverá códigos de exemplo disso.

hashtag
Entendendo as instruções SSE

As instruções adicionadas pela tecnologia SSE podem ser divididas em quatro grupos:

  • Instruções packed e scalar que lidam com números float.

  • Instruções SIMD com inteiros de 64 bits.

  • Instruções de gerenciamento de estado.

  • Instruções de controle de cache e prefetch.

A tabela abaixo lista a nomenclatura que irei utilizar para descrever as instruções SSE.

Para facilitar o entendimento irei usar o termo float para se referir aos números de ponto flutuante de precisão única, ou seja, 32 bits de tamanho e 23 bits de precisão. Já o termo double será utilizado para se referir aos números de ponto flutuante de dupla precisão, ou seja, de 64 bits de tamanho e 52 bits de precisão. Esses são os mesmos nomes usados como tipos na linguagem C.

Nomenclatura

Descrição

xmm(n)

Indica qualquer um dos registradores XMM.

float(n)

Indica N números floats em sequência na memória RAM. Exemplo: float(4) seriam 4 números float totalizando 128 bits de tamanho.

double(n)

Indica N números double na memória RAM. Exemplo: double(2) que totaliza 128 bits.

ubyte(n)

Indica N bytes não-sinalizados na memória RAM. Exemplo: ubyte(16) que totaliza 128 bits.

byte(n)

Indica N bytes sinalizados na memória RAM.

As instruções SSE terminam com um sufixo de duas letras onde a penúltima indica se ela lida com dados packed ou scalar, e a última letra indica o tipo do dado sendo manipulado. Por exemplo a instrução MOVAPS onde o P indica que a instrução manipula dados packed, enquanto o S indica o tipo do dado como single-precision floating-point, ou seja, float de 32 bits de tamanho.

Já o D de MOVAPD indica que a instrução lida com valores do tipo double-precision floating-point (64 bits). Eis a lista de sufixos e seus respectivos tipos:

Sufixo

Tipo

S

Single-precision float. Equivalente ao tipo float em C.

D

Double-precision float. Equivalente ao tipo double em C.

Ou inteiro doubleword (4 bytes) que seria um inteiro de 32 bits.

B

Inteiro de um byte (8 bits).

W

Inteiro word (2 bytes | 16 bits).

Q

Inteiro quadword (8 bytes | 64 bits).

triangle-exclamation

Todas as instruções SSE que lidam com valores na memória exigem que o valor esteja em um endereço alinhado em 16 bytes, exceto as instruções que explicitamente dizem lidar com dados desalinhados (unaligned).

Caso uma instrução SSE seja executada com um dado desalinhado uma exceção #GP será disparada.

Faz uma operação E bit a bit (bitwise AND) em cada um dos valores float/double contidos no operando fonte e armazena o resultado no operando destino.

hashtag
ANDNP(S|D) | bitwise logical AND NOT of Packed (Single|Double)-precision floating-point values

Faz uma operação NAND bit a bit em cada um dos valores float/double contidos no operando fonte e armazena o resultado no operando destino.

hashtag
ORP(S|D) | bitwise logical OR of Packed (Single|Double)-precision floating-point values

Faz uma operação OU bit a bit (bitwise OR) em cada um dos valores float/double contidos no operando fonte e armazena o resultado no operando destino.

hashtag
XORP(S|D) | bitwise logical XOR of Packed (Single|Double)-precision floating-point values

Faz uma operação OU Exclusivo bit a bit (bitwise eXclusive OR) em cada um dos valores float/double contidos no operando fonte e armazena o resultado no operando destino.

hashtag
Instruções de comparação SSE

As instruções de comparação do SSE recebem um terceiro operando imediato de 8 bits que serve como identificador para indicar qual comparação deve ser efetuada com os valores, onde os valores válidos são de 0 até 7. Na tabela abaixo é indicado cada valor e qual operação de comparação ele representa:

Valor

Mnemônico

Descrição

0

EQ

Verifica se os valores são iguais.

1

LT

Verifica se o primeiro operando é menor que o segundo.

2

LE

Verifica se o primeiro operando é menor ou igual ao segundo.

3

Felizmente para facilitar nossa vida os assemblers, incluindo o NASM, adicionam pseudo-instruções que removem o operando imediato e, ao invés disso, usa os mnemônicos apresentados na tabela como conditional code para a instrução. Como é demonstrado no exemplo abaixo:

hashtag
CMPP(S|D)/CMPccP(S|D) | Compare Packed (Single|Double)-precision floating-point values

Essa instrução compara cada um dos valores float/double contido nos dois operandos e armazena o resultado da comparação no operando fonte (o primeiro). O valor imediato passado como terceiro operando é um código numérico para identificar qual operação de comparação deve ser executada em cada um dos valores.

O resultado é armazenado como todos os bits ligados (1) caso a comparação seja verdadeira, se não todos os bits estarão desligados (0) indicando que a comparação foi falsa. Cada número float/double tem um resultado distinto no registrador destino.

hashtag
CMPS(S|D)/CMPccS(S|D) | Compare Scalar (Single|Double)-precision floating-point value

Funciona da mesma forma que a instrução anterior porém comparando um único valor escalar. O resultado é armazenado no float/double menos significativo do operando fonte.

hashtag
COMIS(S|D)/UCOMIS(S|D) | (Unordered) Compare Scalar (Single|Double)-precision floating-point value and set EFLAGS

As quatro instruções comparam os dois operandos escalares e definem as status flags em EFLAGS de acordo com a comparação sem modificar os operandos. Comportamento semelhante ao da instrução CMP.

Quando uma operação aritmética com números floats resulta em NaN existem dois tipos diferentes:

  • quiet NaN (QNaN): O valor é apenas definido para NaN sem qualquer indicação de problema.

  • signaling NaN (SNaN): O valor é definido para NaN e uma exceção floating-point invalid-operation (#I) é disparada caso você execute alguma operação com o valor.

A diferença entre COMISS/COMISD e UCOMISS/UCOMISD é que COMISS/COMISD irá disparar a exceção #I se o primeiro operando for QNaN ou SNaN. Já UCOMISS/UCOMISD apenas dispara a exceção se o primeiro operando for SNaN.

hashtag
SUBP(S|D) | Subtract Packed (Single|Double)-precision floating-point values

Funciona da mesma forma que a instrução anterior porém faz uma operação de subtração nos valores.

hashtag
ADDS(S|D) | Add Scalar (Single|Double)-precision floating-point value

ADDSS faz a adição do float contido no double word (4 bytes) menos significativo do registrador XMM. Já ADDSD faz a adição do double contido na quadword (8 bytes) menos significativa do registrador.

Conforme exemplo abaixo:

hashtag
SUBS(S|D) | Subtract Scalar (Single|Double)-precision floating-point value

Funciona da mesma forma que a instrução anterior porém subtraindo os valores.

hashtag
MULP(S|D) | Multiply Packed (Single|Double)-precision floating-point values

Funciona como ADDPS/ADDPD porém multiplicando os números ao invés de somá-los.

hashtag
MULS(S|D) | Multiply Scalar (Single|Double)-precision floating-point value

Funciona como ADDSS/ADDSD porém multiplicando os números ao invés de somá-los.

hashtag
DIVP(S|D) | Divide Packed (Single|Double)-precision floating-point values

Funciona como ADDPS/ADDPD porém dividindo os números ao invés de somá-los.

hashtag
DIVS(S|D) | Divide Scalar (Single|Double)-precision floating-point value

Funciona como ADDSS/ADDSD porém dividindo os números ao invés de somá-los.

hashtag
RCPPS | Compute Reciprocals of Packed Single-precision floating-point values

Calcula o valor aproximado do inverso multiplicativoarrow-up-right dos floats no operando fonte (a direita) e armazena os valores no operando destino.

hashtag
RCPSS | Compute Reciprocal of Scalar Single-precision floating-point value

Calcula o valor aproximado do inverso multiplicativo do float no operando fonte (a direita) e armazena o resultado na double word (4 bytes) menos significativa do operando destino.

hashtag
SQRTP(S|D) | Compute square roots of Packed (Single|Double)-precision floating-point values

Calcula as raízes quadradas dos números floats/doubles no operando fonte e armazena os resultados no operando destino.

hashtag
SQRTS(S|D) | Compute square root of Scalar (Single|Double)-precision floating-point value

Calcula a raiz quadrada do número escalar no operando fonte e armazena o resultado no float/double menos significativo do operando destino. Exemplo:

hashtag
RSQRTPS | Compute Reciprocals of square roots of Packed Single-precision floating-point values

Calcula o inverso multiplicativoarrow-up-right das raízes quadradas dos floats no operando fonte, armazenando os resultados no operando destino. Essa instrução é equivalente ao uso de SQRTPS seguido de RCPPS.

hashtag
RSQRTSS | Compute Reciprocal of square root of Scalar Single-precision floating-point value

Calcula o inverso multiplicativo da raiz quadrada do número escalar no operando fonte e armazena o resultado no double word menos significativo do operando destino.

hashtag
MAXP(S|D) | return maximum of Packed (Single|Double)-precision floating-point values

Compara cada um dos valores contidos nos dois operandos e retorna o maior valor entre os dois.

hashtag
MAXS(S|D) | return maximum of Scalar (Single|Double)-precision floating-point value

Compara os dois valores escalares e armazena o maior deles no float/double menos significativo do operando destino.

hashtag
MINP(S|D) | return minimum of Packed (Single|Double)-precision floating-point values

Funciona da mesma forma que MAXPS/MAXPD porém retornando o menor valor entre cada comparação.

hashtag
MINS(S|D) | return minimum of Scalar (Single|Double)-precision floating-point value

Funciona da mesma forma que MAXSS/MAXSD porém retornando o menor valor entre os dois.

bits 64
default rel

section .rodata align=16
    sum_array: dd 1.5
               dd 2.5
               dd 3.5
               dd 4.5

section .text

global assembly
assembly:
    movaps xmm0, [rdi]
    addps xmm0, [sum_array]
    movaps [rdi], xmm0
    ret
#include <stdio.h>

void assembly(float *array);

int main(void)
{
  float array[4] = {5.0f, 5.0f, 5.0f, 5.0f};
  assembly(array);

  printf("Resultado: %f, %f, %f, %f\n", array[0],
  return 0;
}
ANDPS xmm(n), xmm(n)
ANDPS xmm(n), float(4)


ANDPD xmm(n), xmm(n)
ANDPD xmm(n), double(2)
ANDNPS xmm(n), xmm(n)
ANDNPS xmm(n), float(4)


ANDNPD xmm(n), xmm(n)
ANDNPD xmm(n), double(2)
ORPS xmm(n), xmm(n)
ORPS xmm(n), float(4)


ORPD xmm(n), xmm(n)
ORPD xmm(n), double(2)
XORPS xmm(n), xmm(n)
XORPS xmm(n), float(4)


XORPD xmm(n), xmm(n)
XORPD xmm(n), double(2)
; As duas instruções abaixo são equivalentes.

CMPPS xmm1, xmm2, 0
CMPEQPS xmm1, xmm2
CMPPS xmm(n), xmm(n), imm8
CMPPS xmm(n), float(4), imm8


CMPPD xmm(n), xmm(n), imm8
CMPPD xmm(n), double(2), imm8
CMPSS xmm(n), xmm(n), imm8
CMPSS xmm(n), float(4), imm8


CMPSD xmm(n), xmm(n), imm8
CMPSD xmm(n), double(2), imm8
COMISS xmm(n), xmm(n)
COMISS xmm(n), float(1)

UCOMISS xmm(n), xmm(n)
UCOMISS xmm(n), float(1)


COMISD xmm(n), xmm(n)
COMISD xmm(n), double(1)

UCOMISD xmm(n), xmm(n)
UCOMISD xmm(n), double(1)
#include <stdio.h>

float sum(float x, float y);

int main(void)
{
  printf("Resultado: %f\n", sum(5.0f, 1.5f));
  return 0;
}
bits 64

section .text

global sum
sum:
    addss xmm0, xmm1
    ret
#include <stdio.h>

double my_sqrt(double x);

int main(void)
{
  printf("Resultado: %f\n", my_sqrt(81.0));
  return 0;
}
bits 64

section .text

global my_sqrt
my_sqrt:
    sqrtsd xmm0, xmm0
    ret
ADDPS xmm(n), xmm(n)
ADDPS xmm(n), float(4)


ADDPD xmm(n), xmm(n)
ADDPD xmm(n), double(2)
SUBPS xmm(n), xmm(n)
SUBPS xmm(n), float(4)


SUBPD xmm(n), xmm(n)
SUBPD xmm(n), double(2)
ADDSS xmm(n), xmm(n)
ADDSS xmm(n), float(1)


ADDSD xmm(n), xmm(n)
ADDSD xmm(n), double(1)
SUBSS xmm(n), xmm(n)
SUBSS xmm(n), float(1)


SUBSD xmm(n), xmm(n)
SUBSD xmm(n), double(1)
MULPS xmm(n), xmm(n)
MULPS xmm(n), float(4)


MULPD xmm(n), xmm(n)
MULPD xmm(n), double(2)
MULSS xmm(n), xmm(n)
MULSS xmm(n), float(1)


MULSD xmm(n), xmm(n)
MULSD xmm(n), double(1)
DIVPS xmm(n), xmm(n)
DIVPS xmm(n), float(4)


DIVPD xmm(n), xmm(n)
DIVPD xmm(n), double(2)
DIVSS xmm(n), xmm(n)
DIVSS xmm(n), float(1)


DIVSD xmm(n), xmm(n)
DIVSD xmm(n), double(1)
RCPPS xmm(n), xmm(n)
RCPPS xmm(n), float(4)
RCPSS xmm(n), xmm(n)
RCPSS xmm(n), float(1)
SQRTPS xmm(n), xmm(n)
SQRTPS xmm(n), float(4)


SQRTPD xmm(n), xmm(n)
SQRTPD xmm(n), double(2)
SQRTSS xmm(n), xmm(n)
SQRTSS xmm(n), float(1)


SQRTSD xmm(n), xmm(n)
SQRTSD xmm(n), double(1)
RSQRTPS xmm(n), xmm(n)
RSQRTPS xmm(n), float(4)
RSQRTSS xmm(n), xmm(n)
RSQRTSS xmm(n), float(1)
MAXPS xmm(n), xmm(n)
MAXPS xmm(n), float(4)


MAXPD xmm(n), xmm(n)
MAXPD xmm(n), double(2)
MAXSS xmm(n), xmm(n)
MAXSS xmm(n), float(1)


MAXSD xmm(n), xmm(n)
MAXSD xmm(n), double(1)
MINPS xmm(n), xmm(n)
MINPS xmm(n), float(4)


MINPD xmm(n), xmm(n)
MINPD xmm(n), double(2)
MINSS xmm(n), xmm(n)
MINSS xmm(n), float(1)


MINSD xmm(n), xmm(n)
MINSD xmm(n), double(1)
array
[
1
],
array
[
2
],
array
[
3
]);

UNORD

Verifica se os valores não estão ordenados.

4

NEQ

Verifica se os valores não são iguais.

5

NLT

Verifica se o primeiro operando não é menor que o segundo (ou seja, se é igual ou maior).

6

NLE

Verifica se o primeiro operando não é menor ou igual que o segundo (ou seja, se é maior).

7

ORD

Verifica se os valores estão ordenados.

uword(n)

Indica N words (2 bytes) não-sinalizados na memória RAM. Exemplo: uword(8) que totaliza 128 bits.

word(n)

Indica N words sinalizadas na memória RAM.

dword(n)

Indica N double words (4 bytes) na memória RAM.

qword(n)

Indica N quadwords (8 bytes) na memória RAM.

reg32/64

de 32 ou 64 bits.

imm8

Operando imediato de 8 bits de tamanho.

Registrador de propósito geral