- 可移植于gcc/clang/ICC/MSVC,C和C++。
- 在所有优化级别下完全安全:没有严格别名违规UB
- 以u8、u16、u32或u64元素的十六进制形式打印(基于@AG1的答案)
- 按内存顺序打印(最低有效位元素先,例如
_mm_setr_epiX
)。如果您喜欢按照英特尔手册使用的相同顺序打印,则反转数组索引,其中最高有效元素在左侧(例如_mm_set_epiX
)。相关:显示向量寄存器的约定
使用__m128i*
从int
数组中加载是安全的,因为__m128
类型的定义允许别名,就像ISO C的unsigned char*
一样。 (例如,在gcc的头文件中,定义包括__attribute__((may_alias))
。)
反过来不安全(将指向__m128i
对象的一部分的int*
)。 MSVC保证是安全的,但GCC / clang不保证。(-fstrict-aliasing
默认开启)。 它有时在GCC / clang中起作用,但为什么要冒险呢? 它有时甚至会干扰优化;请参见this Q&A。 另请参见Is `reinterpret_cast`ing between hardware SIMD vector pointer and the corresponding type an undefined behavior?
请参见GCC AVX _m256i cast to int array leads to wrong values,其中提供了GCC破坏指向__m256i
的int*
代码的真实示例。
(uint32_t*) &my_vector
违反了 C 和 C++ 的别名规则,不能保证其按照预期工作。将其存储到本地数组中然后进行访问是安全的。大多数编译器甚至会进行优化,因此您可以直接从 xmm 寄存器移动 movq
/ pextrq
到整数寄存器,而不是进行实际的存储/重载。
在 Godbolt 编译器探索器上的源代码和汇编输出:证明它可以使用 MSVC 编译,等等。
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#ifndef __cplusplus
#include <stdalign.h>
#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];
_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) )
。
(如果您正在编写具有内部函数的代码,则应使用最新的编译器版本。新的编译器通常比旧的编译器生成更好的汇编代码,包括SSE / AVX内部函数。但是,也许你想使用gcc-6.3和-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)
。
__m128i
没有任何关于存储的类型信息。它可以是8位整数、16位整数、32位等等... 一些编译器支持.m128i_i32
字段扩展。但这绝对不是标准的,也不在GCC中。 - Mysticial%vld
打印一个__m128i
作为4个32位整数。 - Paul R0
жҳҜеҠ жі•зҡ„еҚ•дҪҚе…ғзҙ гҖӮеӣ жӯӨпјҢеұҸи”ҪжҺүе…¶дёӯдёҖдёӘиҫ“е…Ҙж“ҚдҪңж•°д»ҘеҸҠзӣёеә”зҡ„е…ғзҙ пјҢйӮЈд№Ҳc = a + (b & mask)
дёӯзҡ„з»“жһңе°Ҷдёәc = a + 0 = a
гҖӮ - Peter Cordes