[英]print a __m128i variable
我正在嘗試學習使用內在函數進行編碼,下面是添加的代碼
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;
}
我收到以下錯誤:
test.c(9): error: expression must have pointer-to-object type
printf("%d\n",c[2]);
如何打印__m128i
類型的變量c
中的值
使用此功能打印它們:
#include <stdint.h>
#include <string.h>
void print128_num(__m128i var)
{
uint16_t val[8];
memcpy(val, &var, sizeof(val));
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]);
}
您在打印之前將 128 位拆分為 16 位(或 32 位)。
如果您有 64 位支持,這是 64 位拆分和打印的一種方式:
#include <inttypes.h>
void print128_num(__m128i var)
{
int64_t v64val[2];
memcpy(v64val, &var, sizeof(v64val));
printf("%.16llx %.16llx\n", v64val[1], v64val[0]);
}
注意:將&var
直接轉換為int*
或uint16_t*
也適用於 MSVC,但這違反了嚴格的別名並且是未定義的行為。 使用memcpy
是執行相同操作的標准兼容方式,並且通過最少的優化,編譯器將生成完全相同的二進制代碼。
_mm_setr_epiX
)。 如果您希望以英特爾手冊使用的相同順序打印,請反轉數組索引,其中最重要的元素位於左側(如_mm_set_epiX
)。 相關: 顯示向量寄存器的約定使用__m128i*
從int
數組加載是安全的,因為__m128
類型被定義為允許別名,就像 ISO C unsigned char*
一樣。 (例如在 gcc 的標題中,定義包括__attribute__((may_alias))
。)
反過來是不安全的(將int*
指向__m128i
對象的一部分)。 MSVC 保證這是安全的,但 GCC/clang 不保證。 ( -fstrict-aliasing
默認開啟)。 它有時適用於 GCC/clang,但為什么要冒險呢? 它有時甚至會干擾優化; 看到這個問答。 另請參閱硬件 SIMD 向量指針和相應類型之間的“重新解釋轉換”是否是未定義的行為?
請參閱GCC AVX _m256i cast to int array 導致 GCC 破壞代碼的真實示例的錯誤值,該示例將int*
指向__m256i
。
(uint32_t*) &my_vector
違反了 C 和 C++ 別名規則,並且不能保證按您期望的方式工作。 存儲到本地數組然后訪問它可以保證是安全的。 它甚至可以通過大多數編譯器進行優化,例如,您可以直接從 xmm 獲取movq
/ pextrq
到整數寄存器,而不是實際的存儲/重新加載。
Godbolt 編譯器資源管理器上的 Source + asm 輸出:證明它可以使用 MSVC 等進行編譯。
#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]);
}
如果您需要移植到 C99 或 C++03 或更早版本(即沒有 C11 / C++11),請刪除alignas()
並使用storeu
而不是store
。 或者使用__attribute__((aligned(16)))
或__declspec( align(16) )
代替。
(如果您使用內在函數編寫代碼,您應該使用最新的編譯器版本。較舊的編譯器通常比舊編譯器生成更好的 asm,包括 SSE/AVX 內在函數。但也許您想將 gcc-6.3 與-std=gnu++03
一起使用-std=gnu++03
C++03 模式,用於尚未為 C++11 或其他東西做好准備的代碼庫。)
調用所有 4 個函數的示例輸出
// 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
如果要使用前導零填充以獲得一致的輸出寬度,請調整格式字符串。 請參閱printf(3)
。
我知道這個問題被標記為 C,但在尋找相同問題的 C++ 解決方案時,它也是最好的搜索結果。
所以,這可能是一個 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
用法:
#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;
結果:
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
注意:有一種簡單的方法可以避免if (size(T)==1)
,請參閱https://stackoverflow.com/a/28414758/2436175
#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;
}
試試這個代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.