SSE指令加载零扩展字节?

问题描述 投票:0回答:1

假设我有一个指向 RDI 中一堆

uint8_t
的指针,我想将 4 个
uint8_t
加载到 XMM0 中,并使用 SIMD 指令将其与 XMM1 相乘,其中我存储了 4 个
float
值。

如何将最初的 4 个

uint8_t
加载到 XMM0 中,使其始终“对齐”,这意味着每个“隔间”的低 8 位与
uint8_t
一起,高 24 位为 0?有相关说明吗?

我希望我的问题是可以理解的,对于我对我的问题的非常天真的解释感到抱歉。

movdqu xmm0, [rdi]

会导致加载 QWORD,而不是我需要的。

c assembly x86 x86-64 sse
1个回答
0
投票

为了简单起见,我忽略了浮点乘法。我认为使用

mulps
并不是那么难。真正的挑战是转换。

对于SSE2,没有简单的操作。 英特尔内在函数实际上附带了一个内在函数,它可以为此扩展为一系列重要的操作。

#include <immintrin.h>

void u8ps_sse2(float* out, unsigned char* in)
{
    __m64 in_low = _mm_cvtsi32_si64(*(const int*) in);
    __m128 as_float = _mm_cvtpu8_ps(in_low);
    _mm_storeu_ps(out, as_float);
}
u8ps_sse2(float*, unsigned char*):
        movd    xmm0, DWORD PTR [rsi]
        pxor    xmm1, xmm1
        punpcklbw       xmm0, xmm1
        pxor    xmm1, xmm1
        movdqa  xmm3, xmm0
        punpcklwd       xmm0, xmm1
        punpcklwd       xmm3, xmm1
        pxor    xmm1, xmm1
        pshufd  xmm0, xmm0, 0x4e
        movaps  xmm2, xmm1
        cvtdq2ps        xmm4, xmm3
        cvtdq2ps        xmm5, xmm0
        shufps  xmm1, xmm5, 0x4e
        shufps  xmm2, xmm4, 0x4e
        pshufd  xmm1, xmm1, 0x4e
        pshufd  xmm2, xmm2, 0x4e
        movlhps xmm2, xmm1
        movups  XMMWORD PTR [rdi], xmm2
        ret

SSSE3可以使用

pshufb
来实现零扩展。

void u8ps_ssse3(float* out, unsigned char* in)
{
    __m128i in_low = _mm_cvtsi32_si128(*(const int*) in);
    __m128i shuffle = _mm_set_epi8(
        -1, -1, -1, 3,
        -1, -1, -1, 2,
        -1, -1, -1, 1,
        -1, -1, -1, 0
    );
    __m128i zero_extended = _mm_shuffle_epi8(in_low, shuffle);
    __m128 as_float = _mm_cvtepi32_ps(zero_extended);
    _mm_storeu_ps(out, as_float);
}
u8ps_ssse3(float*, unsigned char*):
        movd    xmm0, DWORD PTR [rsi]
        pshufb  xmm0, XMMWORD PTR .LC0[rip]
        cvtdq2ps        xmm0, xmm0
        movups  XMMWORD PTR [rdi], xmm0
        ret
.LC0:
        .byte   0
        .byte   -1
        .byte   -1
        .byte   -1
        .byte   1
        .byte   -1
        .byte   -1
        .byte   -1
        .byte   2
        .byte   -1
        .byte   -1
        .byte   -1
        .byte   3
        .byte   -1
        .byte   -1
        .byte   -1

最后,SSE4.1 给了我们正确的指示

pmovzxbd

void u8ps_sse41(float* out, unsigned char* in)
{
    __m128i in_low = _mm_cvtsi32_si128(*(const int*) in);
    __m128i zero_extended = _mm_cvtepu8_epi32(in_low);
    __m128 as_float = _mm_cvtepi32_ps(zero_extended);
    _mm_storeu_ps(out, as_float);
}
u8ps_sse41(float*, unsigned char*):
        pmovzxbd        xmm0, DWORD PTR [rsi]
        cvtdq2ps        xmm0, xmm0
        movups  XMMWORD PTR [rdi], xmm0
        ret
© www.soinside.com 2019 - 2024. All rights reserved.