Используйте временный регистр (кроме EFLAGS), чтобы сделать эту меньшую задержку на процессорах без однократного цикла adc
:
mov ecx, eax
bswap eax
shl eax, 7 ; top bit in place
shr ax, 7+7 ; bottom bit in place (without disturbing top bit)
and ecx, 0x7ffffffe ; could optimize mov+and with BMI1 andn
and eax, 0x80000001
or eax, ecx ; merge the non-moving bits with the swapped bits
На процессорах Intel до Sandybridge, shr ax
и последующее чтение EAX будут отстойными (частичное зависание регистра).
Это похоже на 5-тактовую задержку от входа к выходу, то же самое, что и adc
/ adc
версия @ Fuz для ЦП, где это задержка на один цикл. (AMD и Intel со времен Broadwell). Но на Haswell и ранее это может быть лучше.
Мы могли бы сохранить mov
, используя либо BMI1 andn
с константой в регистре, либо, возможно, BMI2 rorx ecx, eax, 16
для копирования и замены вместо выполнения bswap
на месте. Но тогда биты находятся в менее удобных местах.
@ rkhb Идея проверить, отличаются ли биты и перевернуть их, хороша, особенно если использовать PF для проверки, установлены ли 0 или 2 бита против 1. PF устанавливается только на основе младшего байта результата, поэтому мы можем ' * просто and 0x80000001
без вращения первым.
Вы можете сделать это без ответвлений с cmov
; untested, but I think I have the parity correct
rorx ecx, eax, 31 ; ecx = rotate left by 1. low 2 bits are the ones we want
xor edx,edx
test cl, 3 ; sets PF=1 iff they're the same: even parity
mov ecx, 0x80000001
cmovpo edx, ecx ; edx=0 if bits match, 0x80000001 if they need swapping
xor eax, edx
При однократном включении cmov
(Broadwell и более поздние версии или AMD) эта задержка составляет 4 цикла . Обнуление XOR и MOV-немедленного находятся на критическом пути. MOV-немедленный может быть выведен из цикла, если вы используете регистр, отличный от ECX.
Или с setcc
, но он хуже (больше мопов), или привязан к процессорам с 2-мегапиксельной cmov
:
; untested, I might have the parity reversed.
rorx ecx, eax, 31 ; ecx = rotate left by 1. low 2 bits are the ones we want
xor edx,edx
and ecx, 3 ; sets PF=1 iff they're the same: even parity
setpe dl
dec edx ; 0 or -1
and edx, 0x80000001
xor eax, edx