_mm_set_epi64
和类似的* _epi64指令似乎使用并依赖于__m64
类型。我想初始化类型__m128
的变量,使其高64位为0,其低64位设置为x
,其中x
的类型为{ {1}}(或类似的无符号64位类型)。什么是"对"这样做的方式?
最好,这应该以独立于编译器的方式完成。
答案 0 :(得分:9)
回答有关如何将64位值加载到XMM寄存器的低64位,同时将高64位_mm_loadl_epi64(&x)
will do exactly what you want归零的问题。
关于to _mm_set_epi64
我曾经说过,查看Agner Fog Vector Class Library的源代码可以回答关于SO的SSE / AVX问题的95%。 Agner为多个编译器以及64位和32位实现了这个(来自文件vectori128.h)。请注意,MSVC 32位Agner的解决方案说"这是低效的,但其他解决方案更糟糕"。我想这就是Mysticial所代表的东西"没有一个好方法可以做到。"。
Vec2q(int64_t i0, int64_t i1) {
#if defined (_MSC_VER) && ! defined(__INTEL_COMPILER)
// MS compiler has no _mm_set_epi64x in 32 bit mode
#if defined(__x86_64__) // 64 bit mode
#if _MSC_VER < 1700
__m128i x0 = _mm_cvtsi64_si128(i0); // 64 bit load
__m128i x1 = _mm_cvtsi64_si128(i1); // 64 bit load
xmm = _mm_unpacklo_epi64(x0,x1); // combine
#else
xmm = _mm_set_epi64x(i1, i0);
#endif
#else // MS compiler in 32-bit mode
union {
int64_t q[2];
int32_t r[4];
} u;
u.q[0] = i0; u.q[1] = i1;
// this is inefficient, but other solutions are worse
xmm = _mm_setr_epi32(u.r[0], u.r[1], u.r[2], u.r[3]);
#endif // __x86_64__
#else // Other compilers
xmm = _mm_set_epi64x(i1, i0);
#endif
};
答案 1 :(得分:8)
最常见的“标准”内在因素是_mm_set_epi64x。
对于缺少_mm_set_epi64x
的平台,您可以像这样定义替换宏:
#define _mm_set_epi64x(m0, m1) _mm_set_epi64(_m_from_int64(m0), _m_from_int64(m1))
答案 2 :(得分:4)
我想初始化
__m128
类型的变量...其中x的类型为uint64_t
取uint64_t
的内在因素是_mm_set_epi64x
(与_mm_set_epi64
相对,后者需要__m64
)。
我最近在Solaris上遇到了这个问题。 Sun Studio 12.3及更低版本缺少_mm_set_epi64x
。它也缺乏解决方法,例如_mm_cvtsi64_si128
和_m_from_int64
。
这是我使用的黑客,如果有兴趣的话。另一个选择是禁用SSE2,这不太吸引人(它在基准测试中慢了3倍):
// Sun Studio 12.3 and earlier lack SSE2's _mm_set_epi64 and _mm_set_epi64x.
#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130)
inline __m128i _mm_set_epi64x(const uint64_t a, const uint64_t b)
{
union INT_128_64 {
__m128i v128;
uint64_t v64[2];
};
INT_128_64 v;
v.v64[0] = b; v.v64[1] = a;
return v.v128;
}
#endif
我相信C ++ 11可以做更多的事情来帮助编译器和性能,比如初始化一个常量数组:
const INT_128_64 v = {a,b};
return v.v128;
有一个很大的警告......我相信有undefined behavior因为使用联合的v64
成员进行写入,然后使用v128
成员进行读取工会在SunCC下进行测试表明编译器正在进行预期的(但在技术上不正确)。
我相信你可以使用memcpy
来回避未定义的行为,但这可能会破坏性能。另见Peter Cordes&#39;在How to swap two __m128i variables in C++03 given its an opaque type and an array?回答和讨论。
以下也可能是避免使用非活动联盟成员的未定义行为的不错选择。但我不确定是否会受到惩罚。
INT_128_64 v;
v.v64[0] = b; v.v64[1] = a;
return *(reinterpret_cast<__m128i*>(v.v64));
编辑 (三个月后):Solaris和SunCC不喜欢这种惩罚。它为我们制作了错误的代码,我们不得不将memcpy
的值转换为__m128i
。 Unix,Linux,Windows,GCC,Clang,ICC,MSC都可以。只有SunCC给我们带来了麻烦。