Ищем sse 128-битную операцию сдвига для не немедленного значения сдвига - PullRequest
13 голосов
/ 02 апреля 2012

Встроенный _mm_slli_si128 будет выполнять логический сдвиг влево от 128-битного регистра, но ограничен непосредственными значениями сдвига и сдвигается байтами, а не битами.

Я могу использовать встроенную функцию, такую ​​как _mm_sll_epi64 или _mm_sll_epi32, чтобы сдвинуть влево набор значений в регистре __m128i, но они не содержат биты "переполнения".

Для сдвига на N бит представьте, что я мог бы сделать что-то вроде:

  • _mm_sll_epi64
  • _mm_srr_epi64 (для бит, которые я хочу нести: переместите их в младший порядок)
  • перемешать результат srr
  • или все вместе.

(но, вероятно, также должны включать проверки N относительно 64).

Есть ли лучший способ?

Ответы [ 2 ]

4 голосов
/ 15 апреля 2013

Это стало побочной проблемой в моем блоге на необычном препроцессоре C, использующем . Для 127 различных смещений сдвига существует четыре различных оптимальных последовательности команд SSE2 для сдвига бита. Препроцессор делает разумным создание функции сдвига, которая составляет 129-позиционный оператор переключения. Простите за необработанный код здесь; Я не знаком с размещением кода прямо здесь. Проверьте сообщение в блоге для объяснения того, что происходит.

#include <emmintrin.h>

typedef __m128i XMM;
#define xmbshl(x,n)  _mm_slli_si128(x,n) // xm <<= 8*n  -- BYTE shift left
#define xmbshr(x,n)  _mm_srli_si128(x,n) // xm >>= 8*n  -- BYTE shift right
#define xmshl64(x,n) _mm_slli_epi64(x,n) // xm.hi <<= n, xm.lo <<= n
#define xmshr64(x,n) _mm_srli_epi64(x,n) // xm.hi >>= n, xm.lo >>= n
#define xmand(a,b)   _mm_and_si128(a,b)
#define xmor(a,b)    _mm_or_si128(a,b)
#define xmxor(a,b)   _mm_xor_si128(a,b)
#define xmzero       _mm_setzero_si128()

XMM xm_shl(XMM x, unsigned nbits)
{
    // These macros generate (1,2,5,6) SSE2 instructions, respectively:
    #define F1(n) case 8*(n): x = xmbshl(x, n); break;
    #define F2(n) case n: x = xmshl64(xmbshl(x, (n)>>3), (n)&15); break;
    #define F5(n) case n: x = xmor(xmshl64(x, n), xmshr64(xmbshl(x, 8), 64-(n))); break;
    #define F6(n) case n: x = xmor(xmshl64(xmbshl(x, (n)>>3), (n)&15),\
                                  xmshr64(xmbshl(x, 8+((n)>>3)), 64-((n)&155))); break;
    // These macros expand to 7 or 49 cases each:
    #define DO_7(f,x) f((x)+1) f((x)+2) f((x)+3) f((x)+4) f((x)+5) f((x)+6) f((x)+7)
    #define DO_7x7(f,y) DO_7(f,(y)+1*8) DO_7(f,(y)+2*8) DO_7(f,(y)+3*8) DO_7(f,(y)+4*8) \
                                        DO_7(f,(y)+5*8) DO_7(f,(y)+6*8) DO_7(f,(y)+7*8)
    switch (nbits) {
    case 0: break;
    DO_7(F5, 0) // 1..7
    DO_7(F1, 0) // 8,16,..56
    DO_7(F1, 7) // 64,72,..120
    DO_7x7(F6, 0) // 9..15 17..23 ... 57..63 i.e. [9..63]\[16,24,..,56]
    DO_7x7(F2,56) // 65..71 73..79 ... 121..127 i.e. [65..127]\[64,72,..,120]
    default: x = xmzero;
    }
    return x;
}

xm_shr соответствует вышеперечисленному, но меняет местами «shl» и «shr» везде в макросах F [1256]. НТН.

4 голосов
/ 02 апреля 2012

Не ваше идеальное решение, но если вы хотите повернуть или сдвинуть регистр SSE на число, кратное 8, то может помочь инструкция PSHUFB (и встроенная _mm_shuffle_epi8()). В качестве входа требуется второй регистр SSE; каждый байт в регистре содержит значение, которое используется для индексации байтов в первом входном регистре.

...