Старый вопрос, но я пытался решить ту же проблему, так что ...
Вы можете использовать palignr, если выровняете по правому краю его второй операнд, то есть нули в младших байтах. Вам нужна выровненная по левому краю версия второго, третьего и четвертого слова, а также выровненная по правому краю версия первого, второго и третьего слова.
Что касается второго и третьего слова, GCC будет немного счастливее, если я использую сдвиги для вычисления выравнивания по правому краю из выравнивания по левому краю. Если я использую два разных pshufb, он генерирует 3 ненужных хода.
Вот код. Он использует ровно 8 регистров; если вы находитесь в 64-битном режиме, вы можете попробовать развернуть его на два.
__m128i mask_right = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, 0x80, 0x80, 0x80, 0x80);
__m128i mask = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0);
for (; n; n -= 16, d += 48, s += 64) {
__m128i v0 = _mm_load_si128((__m128i *) &s[0]);
__m128i v1 = _mm_load_si128((__m128i *) &s[16]);
__m128i v2 = _mm_load_si128((__m128i *) &s[32]);
__m128i v3 = _mm_load_si128((__m128i *) &s[48]);
v0 = _mm_shuffle_epi8(v0, mask_right);
v1 = _mm_shuffle_epi8(v1, mask);
v2 = _mm_shuffle_epi8(v2, mask);
v3 = _mm_shuffle_epi8(v3, mask);
v0 = _mm_alignr_epi8(v1, v0, 4);
v1 = _mm_slli_si128(v1, 4); // mask -> mask_right
v1 = _mm_alignr_epi8(v2, v1, 8);
v2 = _mm_slli_si128(v2, 4); // mask -> mask_right
v2 = _mm_alignr_epi8(v3, v2, 12);
_mm_store_si128((__m128i *) &d[0], v0);
_mm_store_si128((__m128i *) &d[16], v1);
_mm_store_si128((__m128i *) &d[32], v2);
}
Центральная часть также может быть написана так. Компилятор производит на одну инструкцию меньше, и похоже, что он имеет немного больше параллелизма, но для правильного ответа необходим сравнительный анализ:
v0 = _mm_shuffle_epi8(v0, mask_right);
v1 = _mm_shuffle_epi8(v1, mask);
v2 = _mm_shuffle_epi8(v2, mask_right);
v3 = _mm_shuffle_epi8(v3, mask);
__m128i v2l = v2;
v0 = _mm_alignr_epi8(v1, v0, 4);
v1 = _mm_slli_si128(v1, 4); // mask -> mask_right
v2 = _mm_alignr_epi8(v3, v2, 12);
v2l = _mm_srli_si128(v2l, 4); // mask_right -> mask
v1 = _mm_alignr_epi8(v2l, v1, 8);