SSE2指令不适用于使用C++的内联汇编

use*_*526 1 c++ gcc inline-assembly sse2

我有这个函数使用SSE2将一些值加在一起它应该将lhs和rhs加在一起并将结果存储回lhs:

template<typename T>
void simdAdd(T *lhs,T *rhs)
{
    asm volatile("movups %0,%%xmm0"::"m"(lhs));
    asm volatile("movups %0,%%xmm1"::"m"(rhs));

    switch(sizeof(T))
    {
        case sizeof(uint8_t):
        asm volatile("paddb %%xmm0,%%xmm1":);
        break;

        case sizeof(uint16_t):
        asm volatile("paddw %%xmm0,%%xmm1":);
        break;

        case sizeof(float):
        asm volatile("addps %%xmm0,%%xmm1":);
        break;

        case sizeof(double):
        asm volatile("addpd %%xmm0,%%xmm1":);
        break;

        default:
        std::cout<<"error"<<std::endl;
        break;
    }

    asm volatile("movups %%xmm0,%0":"=m"(lhs));
}
Run Code Online (Sandbox Code Playgroud)

我的代码使用这样的函数:

float *values=new float[4];
float *values2=new float[4];

values[0]=1.0f;
values[1]=2.0f;
values[2]=3.0f;
values[3]=4.0f;

values2[0]=1.0f;
values2[1]=2.0f;
values2[2]=3.0f;
values2[3]=4.0f;

simdAdd(values,values2);
for(uint32_t count=0;count<4;count++) std::cout<<values[count]<<std::endl;
Run Code Online (Sandbox Code Playgroud)

但是这不起作用,因为当代码运行时,输出1,2,3,4而不是2,4,6,8

Cra*_*rks 5

我发现内联汇编支持在大多数现代编译器中都不可靠(例如,实现只是简单的错误).通常最好使用编译器内在函数,它是看起来像C函数的声明,但实际上编译为特定的操作码.

Intrinsics允许您指定操作码的确切序列,但将寄存器着色留给编译器.它比在C变量和asm寄存器之间移动数据更可靠,这是内联汇编程序总是让我失望的地方.它还允许编译器安排您的指令,如果它可以解决管道危险,则可以提供更好的性能.即,在这种情况下你可以做到

void simdAdd(float *lhs,float *rhs)
{
   _mm_storeu_ps( lhs, _mm_add_ps(_mm_loadu_ps( lhs ), _mm_loadu_ps( rhs )) );
}
Run Code Online (Sandbox Code Playgroud)

无论如何,在你的情况下,你有两个问题:

  1. 可怕的GCC内联汇编语法使得指针和值之间的差异很大.使用*lhs*rhs,而不是仅仅LHS和RHS; 显然,"= m"语法意味着"隐式使用指向我正在传递给你的东西的指针而不是东西本身."
  2. GCC有一个源,目标语法 - addps将其结果存储在第二个参数中,因此您需要输出xmm1,而不是xmm0.

在键盘上了一个固定的例子(以避免弄乱这个答案,并证明它有效).