将8个16位SSE寄存器转换为8位数据。

3
考虑我有一个带有16位数据的SSE数组:
{1,2,3,4,5,6,7,8}
现在我需要通过仅将16位数据的较低字节存储在前8个字节中,将此SSE数组转换为8位数据:
{1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,0}。
是否有任何SSE指令可执行此操作?

3
“pshufb”可以实现它,实际的打包指令是饱和的。 - harold
请参见 https://dev59.com/SaXja4cB1Zd3GeqPSouk 以在数组上执行此操作,而不是打包一个单独的向量并在高64b中留下零。 - Peter Cordes
2个回答

5

正如上面评论中@harold所说,您可以使用pshufb 也称为 _mm_shuffle_epi8轻松实现此操作,例如:

#include <stdio.h>
#include <tmmintrin.h>

static __m128i pack_16_to_8(const __m128i v)
{
    const __m128i vperm = _mm_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);

    return _mm_shuffle_epi8(v, vperm);
}

int main(void)
{
    const __m128i v = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8);

    printf("%vhd -> %vd\n", v, pack_16_to_8(v));
    return 0;
}

编译并运行:

$ gcc -Wall -mssse3 pack_16_to_8.c && ./a.out

1 2 3 4 5 6 7 8 -> 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0

1

对于 Paul K 的回答,补充如下:

SSE2 扩展包含命令 PACKSSWB(_mm_packs_epi16)PACKUSWB (_mm_packus_epi16)。这些命令专门设计用于将 16 位向量转换为 8 位向量。如果这些值超出了 8 位无符号整数 (0..255) 的范围,则它们会对 16 位 (有符号和无符号) 值进行饱和处理。

#include <iostream>
#include <emmintrin.h>

template<class T> inline void Print(const __m128i & v)
{
    T b[sizeof(v) / sizeof(T)];
    _mm_storeu_si128((__m128i*)b, v);
    for (int i = 0; i < sizeof(v) / sizeof(T); i++)
        std::cout << int(b[i]) << " ";
    std::cout << std::endl;
}

int main()
{
    __m128i v16 = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8);

    Print<uint8_t>(_mm_packs_epi16(v16, _mm_setzero_si128()));
    Print<uint8_t>(_mm_packus_epi16(v16, _mm_setzero_si128()));

    return 0;
}

输出:

1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接