Как вы знаете, мы имеем ниже инструкции Shift в SIMD SSE: PSLL
(W-D-Q) и PSRL
(W-D-Q)
Нет инструкции PSLLB
, поэтому как мы можем сдвинуть векторы 8-битных значений (одиночные байты)?
Как вы знаете, мы имеем ниже инструкции Shift в SIMD SSE: PSLL
(W-D-Q) и PSRL
(W-D-Q)
Нет инструкции PSLLB
, поэтому как мы можем сдвинуть векторы 8-битных значений (одиночные байты)?
В специальном случае с левым сдвигом по одному вы можете использовать paddb xmm0, xmm0
.
Как отмечает Jester в комментариях, наилучшим вариантом эмулировать несуществующий psllb
(и эквивалент с правом сдвига) является использование более широкого сдвига, а затем маскировка любых битов, пересекающих границы элементов.
например.
psllw xmm0, 1 ; doesn't matter what size (w/d/q): performance is the same for all sizes on all CPUs
pand xmm0, [mask]
section .rodata
;; required mask depends on the shift count
mask1 dd 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f
Или передайте 0x7f в векторный регистр перед циклом каким-то другим способом, например vpbroadcastb
из одного байта, или сгенерируйте "на лету" с такой последовательностью, как pcmpeqw xmm0,xmm0
/psrlw xmm0, 9
/packuswb xmm0,xmm0
. При правильном выборе количества сдвигов вы можете сгенерировать любой шаблон из 2 n -1 байтов (повторяющиеся нули, а затем повторяющиеся).
mov r32, imm32
/movd xmm, r32
, и shuffle также является опцией, но, вероятно, не сохранит байты команд по сравнению с последовательностью pcmpeqw
/.... (Обратите внимание, что версия с регистром-источником VBROADCASTSS
является только AVX2, что здесь не имеет значения, так как целые сдвиги 256b также являются только AVX2.)
Я не вижу такого же эффективного способа эмулировать арифметический сдвиг вправо (несуществующий PSRAB
). Высокий байт каждого слова обрабатывается корректно PSRAW
. Сдвиг младшего байта каждого слова в верхнее положение позволял другому PSRAW
копировать свой бит знака столько раз, сколько требуется.
; input in xmm0. Using AVX to save on mov instructions
VPSLLDQ xmm1, xmm0, 1 ; or VPSLLW xmm1, xmm0, 8, but this distributes one of the uops to the shuffle port
VPSRAW xmm1, xmm1, 8+2 ; shift low bytes back to final destination
VPSRAW xmm0, xmm0, 2 ; shift high bytes, leaving garbage in low bytes
VPBLENDVB xmm0, xmm1, xmm0, xmm2 ; (where xmm2 holds a mask of alternating 0 and -1, which could be generated with pcmpeqw / psrlw 8). This insn is fairly slow
Не существует немедленного сочетания с детализацией по байтам, потому что один непосредственный байт может кодировать только 8 элементов.
Без VPBLENDVB (возможно, даже если он доступен, если генерация или загрузка константы для него медленная):
VPSLLDQ xmm1, xmm0, 1 ; or VPSLLW 8
VPSRAW xmm1, xmm1, n ; low bytes in the wrong place
VPSRAW xmm0, xmm0, 8+n ; shift high bytes all the way to the bottom of the element
VPSLLW xmm0, xmm0, 8 ; high bytes back in place, with zero in the low byte. (VPSLLDQ can't work: PSRAW 8+n leaves garbage we need to clear)
VPSRLW xmm1, xmm1, 8 ; shift low bytes into place, leaving zero in the high byte. (VPSRLDQ 1 could do this, if we started with VPSLLW instead of VPSLLDQ)
VPOR xmm0, xmm0, xmm1
Использование PAND/PANDN/POR с постоянной (чередующимися 0/-1 байтами) в регистре также будет работать (с гораздо меньшим давлением на порт сдвига) для выполнения байтовой комбинации, и это лучший выбор, если вы должны сделать это в цикле.