Да, обычно вы всегда должны использовать mov ecx, 9
из соображений производительности. Он работает более эффективно, чем push
/ pop`, как инструкция с одним входом и может выполняться на любом порту.(Это верно для всех существующих процессоров, которые тестировал Agner Fog: https://agner.org/optimize/)
Обычная причина для push imm8
/ pop r32
состоит в том, что машинный код не содержит нулевых байтов. Это важно для шелл-кода , который должен переполнять буфер через strcpy
или любым другим методом, который обрабатывает его как часть строки C неявной длины, оканчивающейся байтом 0
.
mov ecx, immediate
доступен только с 32-битным немедленным, поэтому машинный код будет выглядеть как B9 09 00 00 00
. Против 6a 09
push 9; 59
pop ecx.
(ECX - это номер регистра 1
, отсюда B9
и 59
: младшие 3 бита инструкции = 001
)
Другой вариант использованияимеет чисто кодовый размер : mov r32, imm32
составляет 5 байтов (используя кодировку без ModRM, которая помещает номер регистра в младшие 3 бита кода операции), потому что x86, к сожалению, не имеет расширенного знака кода операции imm8 для mov
(нет mov r/m32, imm8
). Это существует почти для всех инструкций ALU, относящихся к 8086 году.
В 16-битном 8086 это кодирование не сэкономило бы места: 3-байтовая краткая форма mov r16, imm16
была бы столь же хороша, как и гипотетическая mov r/m16, imm8
почти для всего, кроме перемещения непосредственно в память, гденеобходима форма mov r/m16, imm16
(с байтом ModRM).
Поскольку в 32-битном режиме 386 не добавлялись новые коды операций, просто изменили размер операнда по умолчанию и непосредственную ширину, эта «пропущенная оптимизация» вISA в 32-битном режиме началась с 386. С немедленной шириной сразу на 2 байта длина add r32,imm32
теперь длиннее add r/m32, imm8
.См. x86 сборка 16 бит против 8-битного кодирования непосредственного операнда .Но у нас нет этой опции для mov
, потому что нет никакого кода операции MOV, который расширяет (или расширяет ноль) его немедленное значение.
Интересный факт: clang -Oz
(оптимизация под размер даже за счетскорости) скомпилирует int foo(){return 9;}
до push 9
;pop rax
.
См. Также Советы по игре в гольф с машинным кодом x86 / x64 на Codegolf.SE (сайт, посвященный оптимизации размера, обычно для развлечения, а не для того, чтобы поместить код в маленькийПЗУ или загрузочный сектор. Но для машинного кода оптимизация по размеру иногда имеет практическое применение, даже в ущерб производительности.)
Если у вас уже был другой регистр с известным содержимым, создание 9 в другом регистре может бытьсделано с 3-байтовым lea ecx, [eax-0 + 9]
(если EAX содержит 0
).Просто Opcode + ModRM + disp8.Таким образом, вы можете избежать взлома push / pop, если вы уже собирались обнулять любой другой регистр.lea
чуть менее эффективен, чем mov
, и вы могли бы учитывать это при оптимизации для скорости, потому что меньший размер кода имеет незначительные преимущества в скорости в широком масштабе: попадание в кэш L1i, а иногда и декодирование, если кэш UOP еще негорячий.