_MM_TRANSPOSE4_PS 导致 GCC 中的编译器错误?

Posted

技术标签:

【中文标题】_MM_TRANSPOSE4_PS 导致 GCC 中的编译器错误?【英文标题】:_MM_TRANSPOSE4_PS causes compiler errors in GCC? 【发布时间】:2014-08-18 09:42:39 【问题描述】:

我第一次在 GCC 而不是 MSVC 中编译我的数学库,并经历了所有的小错误,我遇到了一个根本没有意义的错误:

Line 284: error: lvalue required as left operand of assignment

第 284 行是什么?这个:

_MM_TRANSPOSE4_PS(r, u, t, _mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f));

(r, u, t 都是__m128的实例)

熟悉使用xmmintrin.h 的人会知道_MM_TRANSPOSE4_PS 实际上并不是一个函数,而是一个宏,它扩展为:

/* Transpose the 4x4 matrix composed of row[0-3].  */
#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3)           \
do                                     \
  __v4sf __r0 = (row0), __r1 = (row1), __r2 = (row2), __r3 = (row3);    \
  __v4sf __t0 = __builtin_ia32_unpcklps (__r0, __r1);           \
  __v4sf __t1 = __builtin_ia32_unpcklps (__r2, __r3);           \
  __v4sf __t2 = __builtin_ia32_unpckhps (__r0, __r1);           \
  __v4sf __t3 = __builtin_ia32_unpckhps (__r2, __r3);           \
  (row0) = __builtin_ia32_movlhps (__t0, __t1);             \
  (row1) = __builtin_ia32_movhlps (__t1, __t0);             \
  (row2) = __builtin_ia32_movlhps (__t2, __t3);             \
  (row3) = __builtin_ia32_movhlps (__t3, __t2);             \
 while (0)

那么...是什么导致我的编译器错误?我不在这里重新定义任何我知道的东西。当我使用 MSVC 时,这个完全相同的代码编译并运行得非常好。

【问题讨论】:

显然_mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) 不会扩展为左值。 这是 C 代码还是 C++ 代码? 代码sn-p是C++。 我在您的问题中添加了 visual-c++ 和内在标记。这确实是 MSVC 的问题,而不是 GCC 的问题。 【参考方案1】:

你需要改变:

_MM_TRANSPOSE4_PS(r, u, t, _mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f));

到:

__m128 v = _mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f);
_MM_TRANSPOSE4_PS(r, u, t, v);

因为这是一个就地转置,并且4个输入向量也用于输出。

【讨论】:

谢谢,这解决了它。为什么它让我在 MSVC 中而不是在 GCC 中这样做? 两个编译器的_MM_TRANSPOSE4_PS 的定义是否相同? 它是直接使用内在函数。 GCC 将_mm_unpacklo_ps 定义为调用__builtin_ia32_unpackps 的内联函数。如果它在这个宏中使用了_mm_unpacklo_ps,你的代码也会因为同样的原因而失败。 @RossRidge,是的,我现在明白了。问题出在 MSVC 中。它编译并运行_mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) = _mm_shuffle_ps(tmp2,tmp3, 0XDD);就好了。 @PaulR,我的第一个答案真的很糟糕,我很惊讶我没有被否决。但无论如何,我用 MSVC 的程序集输出更新了我的答案。我不确定它在做什么,但它显然没有将结果存储在行中。【参考方案2】:

MSVC 使用自己的定义:

#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3)                  \
            __m128 tmp3, tmp2, tmp1, tmp0;                          \
                                                                    \
            tmp0   = _mm_shuffle_ps((row0), (row1), 0x44);          \
            tmp2   = _mm_shuffle_ps((row0), (row1), 0xEE);          \
            tmp1   = _mm_shuffle_ps((row2), (row3), 0x44);          \
            tmp3   = _mm_shuffle_ps((row2), (row3), 0xEE);          \
                                                                    \
            (row0) = _mm_shuffle_ps(tmp0, tmp1, 0x88);              \
            (row1) = _mm_shuffle_ps(tmp0, tmp1, 0xDD);              \
            (row2) = _mm_shuffle_ps(tmp2, tmp3, 0x88);              \
            (row3) = _mm_shuffle_ps(tmp2, tmp3, 0xDD);              \
        

最后一行被转换为_mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) = _mm_shuffle_ps(tmp2,tmp3, 0XDD);,它在 MSVC 中编译得很好,但在 GCC 中由于左值错误而失败。我不确定为什么 MSVC 允许这样做。

我在 MSVC2013 中查看了这段代码的汇编输出

#include <immintrin.h>
#include <stdio.h>
int main() 


    __m128 rows[4];
    //rows[0] = _mm_setr_ps( 1, 2, 3, 4);
    //rows[1] = _mm_setr_ps( 5, 6, 7, 8);
    rows[2] = _mm_setr_ps( 9,10,11,12);
    rows[3] = _mm_setr_ps(13,14,15,16);

    //_MM_TRANSPOSE4_PS(rows[0],rows[1],rows[2],rows[3]);
    //_MM_TRANSPOSE4_PS(rows[0],rows[1],rows[2],_mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f));
    rows[2] = _mm_shuffle_ps(rows[2], rows[3], 0x88);
    _mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) = _mm_shuffle_ps(rows[2],rows[3], 0XDD);

这里是相关的汇编代码

; Line 14
    mov eax, 16
    imul    rax, 3
    mov ecx, 16
    imul    rcx, 2
    movups  xmm0, XMMWORD PTR rows$[rsp+rcx]
    shufps  xmm0, XMMWORD PTR rows$[rsp+rax], 136   ; 00000088H
    movaps  XMMWORD PTR $T6[rsp], xmm0
    mov eax, 16
    imul    rax, 2
    movaps  xmm0, XMMWORD PTR $T6[rsp]
    movups  XMMWORD PTR rows$[rsp+rax], xmm0
; Line 15
    mov eax, 16
    imul    rax, 3
    mov ecx, 16
    imul    rcx, 2
    movups  xmm0, XMMWORD PTR rows$[rsp+rcx]
    shufps  xmm0, XMMWORD PTR rows$[rsp+rax], 221   ; 000000ddH
    movaps  XMMWORD PTR $T8[rsp], xmm0
    movaps  xmm0, XMMWORD PTR __xmm@3f800000000000000000000000000000
    movaps  XMMWORD PTR $T7[rsp], xmm0
    movaps  xmm0, XMMWORD PTR $T8[rsp]
    movaps  XMMWORD PTR $T7[rsp], xmm0

【讨论】:

我仍然不清楚如何将row3 参数设置为_mm_setr_ps(...),因为它是在宏的最后一行分配的? @PaulR,好点子。但即使它给出了错误的结果(_mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) = _mm_shuffle_ps(tmp2,tmp3, 0XDD)),它仍然应该编译。但我仍然想知道为什么 GCC 使用映射到内在函数而不是直接映射到内在函数的内置函数。我想一个原因是防止这种错误发生? 否 - 我认为 gcc 完全正确地使用 error: lvalue required as left operand of assignment 保释 - _mm_setr_ps(...) 不是左值,尝试分配给它确实是一个错误 - 我不知道这是如何过去的MSVC - 也许 MS 定义 _mm_setr_ps 的方式有些有趣? @PaulR,再次正确。我没想到他不够细心。无论如何,这就是 _mm_setr_ps(0.0f, 0.0f, 0.0f, 1.0f) = _mm_shuffle_ps(tmp2,tmp3, 0XDD); 在 MSVC 中编译良好但在 GCC 中给出 lvalue 错误的线索。我明天再调查一下。我不知道为什么我不再关心 MSVC...

以上是关于_MM_TRANSPOSE4_PS 导致 GCC 中的编译器错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?

C 内在函数、SSE2 点积和 gcc -O3 生成的程序集

安装Ta-lib会导致gcc错误

_mm256_load_ps 在调试模式下导致 google/benchmark 出现分段错误

SSE _mm_load_ps 导致分段错误

gcc编译时报错:对‘__gxx_personality_v0’未定义的引用