将单个浮点移动到xmm寄存器

时间:2011-11-27 14:34:14

标签: c++ gcc assembly simd

我想将存储在一个xmm寄存器中的数据与一个浮点值相乘,并将结果保存在xmm寄存器中。 我做了一个小图解来解释它好一点。

enter image description here

如你所见,我的数据中有一个xmm0寄存器。例如,它包含:

xmm0 = | 4.0 | 2.5 | 3.5 | 2.0 |

每个浮点存储在4个字节中。我的xmm0寄存器是128位,16字节长。

这非常好。现在我想将0.5存储在另一个xmm寄存器中,例如xmm1,并将该寄存器与xmm0寄存器相乘,以便存储在xmm0中的每个值乘以0.5。

我完全不知道如何在XMM寄存器中存储0.5。 有什么建议吗?

顺便说一句:它是C ++中的内联汇编程序。

void filter(image* src_image, image* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movaps (%%esi), %%xmm0\n"      
        // Multiply %xmm0 with a float, e.g. 0.5
        "movaps %%xmm0, (%%edi)\n" 

        :
        : "S"(src), "D"(dst) :  
    );
}

这是我想做的事情的安静简单版本。我得到了一些存储在浮点数组中的图像数据。指向这些数组的指针将传递给程序集。 movaps获取数组的前4个浮点值,将这16个字节存储在xmm0寄存器中。在此之后xmm0应乘以例如0.5。比“新”值应存储在edi。

的数组中

5 个答案:

答案 0 :(得分:8)

正如人们在评论中指出的那样,对于这种非常简单的操作,使用内在函数基本上总是更好:

void filter(image* src_image, image* dst_image)
{
    const __m128 data = _mm_load_ps(src_image->data);
    const __m128 scaled = _mm_mul_ps(data, _mm_set1_ps(0.5f));
    _mm_store_ps(dst_image->data, scaled);
}

如果编译器生成错误代码(并且仅在向编译器供应商提交错误之后),您应该只使用内联ASM。

如果你真的想留在汇编中,有许多方法来完成这项任务。您可以在ASM块之外定义比例向量:

    const __m128 half = _mm_set1_ps(0.5f);

然后在ASM中使用它,就像使用其他操作数一样。

如果你真的想要,你可以在没有任何负担的情况下完成:

    "mov    $0x3f000000, %%eax\n"  // encoding of 0.5
    "movd   %%eax,       %%xmm1\n" // move to xmm1
    "shufps $0, %%xmm1,  %%xmm1\n" // splat across all lanes of xmm1

这只是两种方法。还有很多其他方法。您可能会花一些时间在英特尔指令集参考上。

答案 1 :(得分:4)

假设您正在使用内在函数:__m128 halfx4 = _mm_set1_ps(0.5f);

修改

使用内在函数会好得多:

__m128 x = _mm_mul_ps(_mm_load_ps(src), halfx4);
_mm_store_ps(dst, x);

如果srcdst浮动数据不是16字节对齐的,则需要:_mm_loadu_ps_mm_storeu_ps - 这些数据较慢。

答案 2 :(得分:2)

您正在寻找MOVSS指令(将一个精度浮点数从存储器加载到SSE寄存器的最低4个字节中),然后进行随机抽取以使用此值填充其他3个浮点数:

movss  (whatever), %%xmm1
shufps %%xmm1, %%xmm1, $0

这也是_mm_set1_ps内在可能如何做到的。然后你可以将这些SSE值相乘或做任何你想做的事情:

mulps %%xmm1, %%xmm0

答案 3 :(得分:0)

如果您正在使用带有gcc的c ++并拥有EasySSE,则您的代码可以如下

void filter(float* src_image, float* dst_image){
    *(PackedFloat128*)dst_image =  Packefloat128(0.5) * (src_image+0);
}

这假设给定的指针是16byte对齐的。 您可以检查assy代码以验证变量是否正确映射到向量寄存器。

答案 4 :(得分:0)

这是一种方法:

#include <stdio.h>
#include <stdlib.h>

typedef struct img {
    float *data;
} image_t;

image_t *src_image;
image_t *dst_image;
void filter(image_t*, image_t*);

int main()
{
    image_t src, dst;
    src.data = malloc(64);
    dst.data = malloc(64);
    src_image=&src;
    dst_image=&dst;

    *src.data = 42.0;
    filter(src_image, dst_image);

    printf("%f\n", *dst.data);
    free(src.data);
    free(dst.data);
    return 0;
}

void filter(image_t* src_image, image_t* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movd   %%esi, %%xmm0;"
        "movd   %%xmm0, %%edi;"
        : "=D" (*dst)
        : "S" (*src)
    );
}