26

我有一些代码,最初是由与 MSVC 合作的人给我的,我正试图让它在 Clang 上工作。这是我遇到问题的功能:

float vectorGetByIndex( __m128 V, unsigned int i )
{
    assert( i <= 3 );
    return V.m128_f32[i];
}

我得到的错误如下:

Member reference has base type '__m128' is not a structure or union.

我环顾四周,发现 Clang(也许还有 GCC)在将 __m128 视为结构或联合时存在问题。但是,我还没有找到关于如何取回这些值的直接答案。我试过使用下标运算符但做不到,我浏览了大量的 SSE 内在函数列表,但还没有找到合适的。

4

5 回答 5

22

作为对 hirschhornsalz 解决方案的修改,如果i是编译时常量,您可以通过使用 shuffle 完全避免联合路径:

template<unsigned i>
float vectorGetByIndex( __m128 V)
{
    // shuffle V so that the element that you want is moved to the least-
    // significant element of the vector (V[0])
    V = _mm_shuffle_ps(V, V, _MM_SHUFFLE(i, i, i, i));
    // return the value in V[0]
    return _mm_cvtss_f32(V);
}

标量浮点数只是XMM 寄存器的底部元素,高元素允许非零;_mm_cvtss_f32是免费的,将编译为零指令。这将作为一个 shufps 内联(或者对于 i==0 什么都没有)。

编译器足够聪明,可以优化掉 shuffle i==0(长期过时的 ICC13 除外),因此不需要if (i). https://godbolt.org/z/K154Pe。clang 的 shuffle 优化器将编译vectorGetByIndex<2>movhlps xmm0, xmm0比它短 1 个字节shufps并产生相同的低元素。switch您可以使用/case为其他编译器手动执行此操作,因为i它是编译时常量,但是在手动矢量化时使用它的少数地方的 1 字节代码大小非常简单。


请注意,SSE4.1_mm_extract_epi32(V, i);在这里不是一个有用的 shuffle:extractps r/m32, xmm, imm只能将 FP 位模式提取到整数寄存器或内存(https://www.felixcloutier.com/x86/extractps)。(并且内在函数将其返回为int,因此它实际上会编译为extractps+cvtsi2ss以在 FP 位模式上进行 int->float 转换,除非您在 C++ 代码中对它进行类型双关。但是您希望它能够编译to extractps eax, xmm0, i/movd xmm0, eax这与 shufps 相比很糟糕。)

唯一extractps有用的情况是编译器希望将此结果直接存储到内存中,并将存储折叠到提取指令中。(对于 i!=0,否则它将使用movss)。将结果作为标量浮点数保留在 XMM 寄存器中shufps是很好的。

SSE4.1insertps可以使用但没必要:它可以在获取任意源元素的同时将其他元素归零。)

于 2012-09-27T16:46:43.717 回答
19

联合可能是最便携的方法:

union {
    __m128 v;    // SSE 4 x float vector
    float a[4];  // scalar array of 4 floats
} U;

float vectorGetByIndex(__m128 V, unsigned int i)
{
    U u;

    assert(i <= 3);
    u.v = V;
    return u.a[i];
}
于 2012-09-27T15:16:17.483 回答
18

利用

template<unsigned i>
float vectorGetByIndex( __m128 V) {
    union {
        __m128 v;    
        float a[4];  
    } converter;
    converter.v = V;
    return converter.a[i];
}

无论可用的指令集如何,这都将起作用。

注意:即使 SSE4.1 可用并且i是编译时间常数,也不能这样使用pextractetc.,因为这些指令提取的是 32 位整数,而不是float:

// broken code starts here
template<unsigned i>
float vectorGetByIndex( __m128 V) {
    return _mm_extract_epi32(V, i);
}
// broken code ends here

我不删除它,因为它是一个有用的提醒如何不做的事情。

于 2012-09-27T15:47:12.380 回答
3

我使用的方式是

union vec { __m128 sse, float f[4] };

float accessmember(__m128 v, int index)
{
    vec v.sse = v;
    return v.f[index];
}

似乎对我来说效果很好。

于 2013-07-10T04:16:33.093 回答
0

迟到了,但发现这在 MSVC 中对我有用,其中 z 是 __m128 类型的变量。

#define _mm_extract_f32(v, i)       _mm_cvtss_f32(_mm_shuffle_ps(v, v, i))

__m128 z = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);

float f = _mm_extract_f32(z, 2);

或者更简单

__m128 z;

float f = z.m128_f32[2];  // to get the 3rd float value in the vector
于 2021-01-07T21:19:51.880 回答