从64位unsigned int初始化__m128类型

时间:2014-05-05 19:25:02

标签: c++ sse intrinsics

_mm_set_epi64和类似的* _epi64指令似乎使用并依赖于__m64类型。我想初始化类型__m128的变量,使其高64位为0,其低64位设置为x,其中x的类型为{ {1}}(或类似的无符号64位类型)。什么是"对"这样做的方式?

最好,这应该以独立于编译器的方式完成。

3 个答案:

答案 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给我们带来了麻烦。