Конвертировать 8 16-битный регистр SSE в 8-битные данные

Предположим, у меня есть массив SSE с 16-битными данными:

{1,2,3,4,5,6,7,8}

Теперь мне нужно преобразовать этот массив SSE в 8-битные данные, сохранив только младший байт 16-битных данных в первых 8 байтах, например:

{1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,0}.

Есть ли какая-либо инструкция SSE для выполнения этой операции?

2 ответа

Решение

Как говорит 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

Дополнение к ответу Павла К:

Расширение SSE2 содержит команды PACKSSWB(_mm_packs_epi16) и PACKUSWB (_mm_packus_epi16). Эти команды специально предназначены для преобразования 16-битного вектора в 8-битный вектор. Они выполняют насыщение 16-битных (со знаком и без знака) значений, если эти значения превышают диапазон 8-битного целого числа без знака (0..255).

#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
Другие вопросы по тегам