c assembly sse simd intrinsics

c - imprimir una variable__m128i



assembly sse (4)

Sé que esta pregunta está etiquetada como C, pero también fue el mejor resultado de búsqueda al buscar una solución de C ++ para el mismo problema.

Entonces, esto podría ser una implementación de C ++:

#include <string> #include <cstring> #include <sstream> #if defined(__SSE2__) template <typename T> std::string __m128i_toString(const __m128i var) { std::stringstream sstr; T values[16/sizeof(T)]; std::memcpy(values,&var,sizeof(values)); //See discussion below if (sizeof(T) == 1) { for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible sstr << (int) values[i] << " "; } } else { for (unsigned int i = 0; i < sizeof(__m128i) / sizeof(T); i++) { //C++11: Range for also possible sstr << values[i] << " "; } } return sstr.str(); } #endif

Uso:

#include <iostream> [..] __m128i x [..] std::cout << __m128i_toString<uint8_t>(x) << std::endl; std::cout << __m128i_toString<uint16_t>(x) << std::endl; std::cout << __m128i_toString<uint32_t>(x) << std::endl; std::cout << __m128i_toString<uint64_t>(x) << std::endl;

Resultado:

141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0 29325 0 0 0 26775 0 0 0 29325 0 26775 0 29325 26775

Nota: existe una forma sencilla de evitar el if (size(T)==1) , consulte https://stackoverflow.com/a/28414758/2436175

Estoy tratando de aprender a codificar usando intrínsecos y a continuación hay un código que hace una suma

compiler used: icc

#include<stdio.h> #include<emmintrin.h> int main() { __m128i a = _mm_set_epi32(1,2,3,4); __m128i b = _mm_set_epi32(1,2,3,4); __m128i c; c = _mm_add_epi32(a,b); printf("%d/n",c[2]); return 0; }

Me sale el siguiente error:

test.c(9): error: expression must have pointer-to-object type printf("%d/n",c[2]);

¿Cómo __m128i los valores en la variable c que es de tipo __m128i


Utilice esta función para imprimirlos:

void print128_num(__m128i var) { uint16_t *val = (uint16_t*) &var; printf("Numerical: %i %i %i %i %i %i %i %i /n", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); }

Divide 128 bits en 16 bits (o 32 bits) antes de imprimirlos.

Esta es una forma de dividir e imprimir de 64 bits si tiene soporte de 64 bits disponible:

void print128_num(__m128i var) { int64_t *v64val = (int64_t*) &var; printf("%.16llx %.16llx/n", v64val[1], v64val[0]); }


#include<stdio.h> #include<emmintrin.h> int main() { __m128i a = _mm_set_epi32(1,2,3,4); __m128i b = _mm_set_epi32(1,2,3,4); __m128i c; const int32_t* q; //add a pointer c = _mm_add_epi32(a,b); q = (const int32_t*) &c; printf("%d/n",q[2]); //printf("%d/n",c[2]); return 0; }

Prueba este código.


  • Portátil a través de gcc / clang / ICC / MSVC, C y C ++.
  • totalmente seguro con todos los niveles de optimización: sin alias de punteros (a diferencia de la mayoría de las otras respuestas)
  • imprima en hexadecimal como u8, u16, u32 o u64 elementos ( basado en la respuesta de @ AG1 )
  • Imprime en orden de memoria (primero el elemento menos significativo, como _mm_setr_epiX ). Invierta los índices de matriz si prefiere imprimir en el mismo orden en que usan los manuales de Intel, donde el elemento más significativo está a la izquierda (como _mm_set_epiX ). Relacionado: Convención para la visualización de registros de vectores.

El uso de un __m128i* para cargar desde una matriz de int es seguro porque los tipos __m128 están definidos para permitir el aliasing. (Por ejemplo, en los encabezados de gcc, la definición incluye __attribute__((may_alias)) .)

El reverso no es seguro (un objeto __m128i y un puntero int ). Puede suceder que funcione en la mayoría de los casos, pero ¿por qué arriesgarse?

(uint32_t*) &my_vector viola las reglas de alias de C y C ++, y no se garantiza que funcione de la manera que esperabas . Se garantiza que el almacenamiento en una matriz local y luego el acceso a ella es seguro. Incluso se optimiza con la mayoría de los compiladores, por lo que obtiene movq / pextrq directamente de xmm a registros enteros en lugar de un almacenamiento / recarga real , por ejemplo.

Salida de Source + Asm en el explorador del compilador Godbolt : prueba que se compila con MSVC y así sucesivamente.

#include <immintrin.h> #include <stdint.h> #include <stdio.h> #ifndef __cplusplus #include <stdalign.h> // C11 defines _Alignas(). This header defines alignas() #endif void p128_hex_u8(__m128i in) { alignas(16) uint8_t v[16]; _mm_store_si128((__m128i*)v, in); printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x/n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); } void p128_hex_u16(__m128i in) { alignas(16) uint16_t v[8]; _mm_store_si128((__m128i*)v, in); printf("v8_u16: %x %x %x %x, %x %x %x %x/n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); } void p128_hex_u32(__m128i in) { alignas(16) uint32_t v[4]; _mm_store_si128((__m128i*)v, in); printf("v4_u32: %x %x %x %x/n", v[0], v[1], v[2], v[3]); } void p128_hex_u64(__m128i in) { alignas(16) unsigned long long v[2]; // uint64_t might give format-string warnings with %llx; it''s just long in some ABIs _mm_store_si128((__m128i*)v, in); printf("v2_u64: %llx %llx/n", v[0], v[1]); }

Si necesita portabilidad a C99 o C ++ 03 o anterior (es decir, sin C11 / C ++ 11), elimine el alignas() y use storeu lugar de store . O use __attribute__((aligned(16))) o __declspec( align(16) ) lugar.

(Si estás escribiendo código con intrínsecos, deberías usar una versión reciente del compilador. Los compiladores más nuevos generalmente son mejores asm que los compiladores más antiguos, incluso para SSE / AVX. Pero quizás quieras usar gcc-6.3 con -std=gnu++03 Modo C ++ 03 para un código base que no está listo para C ++ 11 o algo así.)

Salida de muestra de llamar a las 4 funciones en

// source used: __m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); // output: v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09 v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d v8_u16: 0x201 0x403 0x605 0x807 | 0xa09 0xc0b 0xe0d 0x100f v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10

Ajuste las cadenas de formato si desea rellenar con ceros iniciales para un ancho de salida constante. Ver printf(3) .