您的GCC太旧,需要GCC11才能在immintrin.h
之前定义它
And you need GCC11.3 or GCC12 for a non-broken version,将加载的字节放置在结果向量中的正确位置,并确保对齐/严格别名安全.GCC bug 99754
GCC和/或clang有时会错过定义一些"助手"内部函数,最终只能找到它们.这就是其中之一,更糟糕的是,第一次try 添加它是buggy.有一些GCC版本(GCC11.0到11.2)支持它,但编译错误(加载后将dword或文字洗牌到顶部元素,而不是底部,因为它们在头实现中使用_mm_set
而不是_mm_setr
.)
FP等效的4字节负载__m128 _mm_load_ss(float*)
已经被永远定义,但在GCC的实现中仍然不像在其他编译器中那样对对齐或严格别名安全.GCC的头对float*
进行解列,而不是使用memcpy
或__attribute__((aligned(1),may_alias))
指针类型.那是GCC bug PR84508.
不幸的是,在GCC中,使用_mm_castps_si128( _mm_load_ss( (float*)ptr ))
也是not安全的.
旧编译器的可移植实现
对于别名安全的未对齐4字节加载,最好的 Select 可能是这种可移植的实现:
__m128i movd_load(void *p)
{
int tmp; // int32_t on implementations that support intrinsics
memcpy(&tmp, p, sizeof(tmp)); // unaligned aliasing-safe load
return _mm_cvtsi32_si128(tmp);
}
这在GCC/clang/MSVC(100显示全部)上编译得很好.GCC和clang的旧版本和新版本:测试了GCC4.7和GCC12,只是预期的movd xmm0, [rdi]
/ret
.
但它在ICC上编译很愚蠢,加载到EAX,然后要么存储/重新加载,要么movd xmm0, eax
,而不是movd
的内存源操作数.
这对于pmovzx/pmovsx负载(将负载缩小到__m128i
,尤其是未对齐和混叠安全负载的重要用例之一)也很有用,例如
#if defined(__SSE4_1__) || defined (_MSC_VER)
__m128i pmovzxbd_load(void *p)
{
__m128i v = movd_load(p);
return _mm_cvtepu8_epi32(v); // folds the load with GCC9 or later
// but not ICC or MSVC, or earlier GCC: they all movd into an XMM reg and pmovzxbd xmm0,xmm0
// clang gets this right, with a mem src pmovzxbd
}
#endif
# GCC8.5 -O2 -march=skylake -mno-avx
# and MSVC19.14. ICC 2021 is even worse, going through EAX
pmovzxbd_load:
movd xmm0, DWORD PTR [rdi]
pmovzxbd xmm0, xmm0
ret
# GCC9.5 -O2 -march=skylake -mno-avx
# and clang
pmovzxbd_load:
pmovzxbd xmm0, DWORD PTR [rdi]
ret