Используя инструкции с двумя операндами в стиле x86, которые уничтожают свое место назначения, вы всегда можете смоделировать неразрушающую команду с тремя операндами с помощью mov
, чтобы скопировать один операнд в место назначения, а затем запустить деструктивную инструкцию в этом месте назначения.
# with ecx and edx holding your inputs (which I'm calling C and D).
mov ebx, ecx ; ebx = C
sub ebx, edx ; ebx = C - D
Это лучшее, что вы можете сделать в этом случае, когда вам нужно не уничтожать значения в ECX и EDX.
Если у вас мало доступных регистров, хорошим вариантом может быть сохранение ECX в стеке, а затем получение результата C - D
в ECX вместо нового регистра.
Часто вы можете использовать один и тот же регистр для одной и той же переменной во всей функции, но это не обязательно, а иногда и неоптимально. Используйте комментарии, чтобы следить за происходящим.
Компиляторы обычно неплохо справляются с распределением регистров, но их код может быть трудночитаемым, потому что они даже не пытаются согласовываться с использованием регистров. Для неразрушающих операций они часто без причины помещают результат в новый регистр. Тем не менее, вывод компилятора часто является хорошей отправной точкой для оптимизации. (Напишите крошечную функцию, которая что-то делает, и посмотрите, как она компилируется. Или напишите все на C с аргументами функции вместо констант в качестве входных данных и скомпилируйте ее.)
x86 имеет некоторую инструкцию копирования и эксплуатации для других операций (не sub
), в первую очередь LEA.
lea ebx, [ecx + ecx*4] ; ebx = C * 5
lea ebx, [ecx + ebx - 2] ; ebx = C + D - 2
Режимы адресации x86 могут добавлять или вычитать константы, но могут только сдвиг влево и добавление регистров.
форма непосредственного операнда imul
также является 3-операндом, для использования с множителями, которые вы можете не годится для 1 или 2 LEA:
imul ebx, ecx, 0x01010101 ; ebx = cl repeated 4 times, if upper bytes were zero
В отличие от большинства инструкций с непосредственным операндом, imul
не не перегружает /r
в байте ModRM как дополнительные биты кода операции. Таким образом, у него есть место для кодирования места назначения регистра и источника reg / mem, потому что 186 выделил ему целый байт кода операции.
Расширения ISA, такие как BMI1 и BMI2, добавили несколько новых целочисленных инструкций с 3 операндами, например ANDN и SHRX.
andn ebx, ecx, edx ; ebx = (~C) & D ; BMI1
shrx ebx, edx, ecx ; ebx = D >> C ; BMI2
Но они доступны не повсеместно, только Haswell, а затем и Ryzen. (И версии Haswell / Skylake для Pentium / Celeron по-прежнему продаются без них, что еще больше откладывает момент, когда они станут базовыми. Спасибо, Intel.)
И, конечно же, для векторных инструкций AVX предоставляет неразрушающие версии всех инструкций SSE.
movaps xmm2, xmm0 ; copy a whole register
subsd xmm2, xmm1 ; scalar double-precision FP subtract: xmm0-xmm1
vsubsd xmm3, xmm0, xmm1
или менее очевидный вариант использования
xorps xmm0, xmm0 ; zero the register and break any false dependencies
cvtsi2sd xmm0, eax ; convert to double-precision FP, with the upper element = 0
xorps xmm1, xmm1
cvtsi2sd xmm1, edx
по сравнению с AVX:
vxorps xmm1, xmm1,xmm1 ; xmm1 = all-zero
vcvtsi2sd xmm0, xmm1, eax
vcvtsi2sd xmm1, xmm1, edx
Это повторно использует тот же обнуленный регистр в качестве места назначения слияния, чтобы избежать ложных зависимостей (и имеет нулевые старшие 64 бита 128-битного регистра).
person
Peter Cordes
schedule
04.07.2018
ecx
вebx
, прежде чем выполнятьsub
непосредственно наebx
? (На самом деле это то, что делают и большинство компиляторов C ++) - person UnholySheep   schedule 05.07.2018lea
, но вычитайте вместо добавления, если вы это имеете в виду, вам нужны другие уловки - person harold   schedule 05.07.2018mov ebx,ecx
передsub ebx,edx
является наиболее оптимальным. Вы можете добиться того же многими другими способами, например,neg edx
lea ebx,[ecx+edx]
neg edx
или используяpush/pop
, как в ответе, но все эти варианты искусственно сложны, чтобы избежать самого простого и иметь дополнительное снижение производительности. - person Ped7g   schedule 05.07.2018