Кстати, отрицание 2-регистрового числа одинаково в 32-битном или 16-битном режиме с EDX: EAX или DX: AX. Используйте ту же последовательность инструкций.
Чтобы копировать и отрицать, ответ @phuclv показывает эффективный вывод компилятора. Лучше всего обнулить место назначения с помощью xor и затем использовать sub
/ sbb
.
Это 4 мупа для интерфейса на AMD, а также на Intel Broadwell и более поздних версиях. На Intel до Broadwell sbb reg,reg
составляет 2 мопса. Обнуление xor находится вне критического пути (может произойти до того, как данные, которые нужно инвертировать, будут готовы), поэтому общая задержка составляет 2 или 3 цикла для высокой половины. Младшая половина, конечно, готова с задержкой в 1 цикл.
Clang mov/neg
для младшей половины, возможно, лучше на Ryzen, у которого есть mov-elimination для целого числа GP, но по-прежнему требуется исполнительный блок ALU для обнуления xor. Но для старых процессоров это ставит mov
на критический путь задержки. Но обычно внутреннее давление ALU не так важно, как узкие места внешнего интерфейса, для инструкций, которые могут использовать любой порт ALU.
Чтобы отрицать на месте, используйте neg
для вычитания из 0
neg rdx ; high half first
neg rax ; subtract RDX:RAX from 0
sbb rdx, 0 ; with carry from low to high half
neg
в точности эквивалентен sub
от 0, что касается установки флагов и производительности.
АЦП / SBB с немедленный 0
- это всего лишь 1 мкоп на Intel SnB / IvB / Haswell, как особый случай. Тем не менее, на Nehalem и ранее это по-прежнему 2 мупа. Но без исключения перемещения mov
в другой регистр и затем sbb
обратно в RDX было бы медленнее.
Младшая половина (в RAX) готова в первом цикле после того, как она готова в качестве входа для neg
. (Таким образом, выполнение более позднего кода вне очереди может начаться с младшей половины.)
Верхняя половина neg rdx
может работать параллельно с нижней половиной. Затем sbb rdx,0
должен ждать rdx
от neg rdx
и CF от neg rax
. Таким образом, он готов позже, чем через 1 цикл после нижней половины или через 2 цикла после того, как будет готова входная высокая половина.
Вышеупомянутая последовательность лучше, чем любая в вопросе, поскольку на очень распространенных процессорах Intel меньше ошибок. На Бродвелле и более поздних версиях (однократный SBB
, а не только для немедленного 0)
;; equally good on Broadwell/Skylake, and AMD. But worse on Intel SnB through HSW
NOT RDX
NEG RAX
SBB RDX,-1 ; can't use the imm=0 special case
Очевидно, что любая из 4-командных последовательностей неоптимальна, а скорее всего. И некоторые из них имеют худшие ILP / цепочки зависимостей / задержку, например, 2 инструкции на критическом пути для нижней половины или цепочка из 3 циклов для верхней половины.
person
Peter Cordes
schedule
04.04.2019
ADC RDX,0
как последнюю инструкцию во втором примере. - person lurker   schedule 04.04.2015CMC
- низкая задержка, высокая пропускная способность на современных микроархитектурах, поэтому вторая версия, вероятно, конкурентоспособна. С другой стороны, рассмотрим цепочки зависимостей: 1:ADC
зависит от _3 _ / _ 4_ через флаги, зависит отNOT
. 2:ADC
зависит от _7 _ / _ 8_ зависит отNEG
. 3:SBB
зависит от _11 _ / _ 12_. Я бы сказал, что с последней версией вы нашли очень хитрый способ. - person EOF   schedule 04.04.2015