Для эффективного выполнения x = x*10 + 1
, вероятно, оптимально использовать
lea eax, [rax + rax*4] ; x*=5
lea eax, [1 + rax*2] ; x = x*2 + 1
3-компонентный LEA имеет более высокую задержку на современных процессорах Intel, например 3 цикла против 1 в семействе Sandybridge, поэтому disp32 + index*2
быстрее, чем disp8 + base + index*1
в семействе SnB, то есть в большинстве основных процессоров x86, для которых мы заботимся об оптимизации. (В основном это относится только к LEA, не загружает / сохраняет, потому что LEA работает на исполнительных модулях ALU, а не на AGU в большинстве современных процессоров x86.) Процессоры AMD имеют более медленный LEA с 3 компонентами или scale > 1
(http://agner.org/optimize/)
Но NASM и YASM оптимизируют размер кода, используя [1 + rax + rax*1]
для второго LEA, которому требуется только disp8 вместо disp32. (Режимы адресации всегда имеют базовый регистр или disp32).
т.е. они всегда разбивают reg*2
на base+index
, потому что это никогда не хуже для размера кода.
Я могу принудительно использовать disp32 с lea eax, [dword 1 + rax*2]
, но это не мешает NASM или YASM разделить режим адресации. Руководство NASM, похоже, не описывает способ использования ключевого слова strict
a > От масштабного коэффициента, а [1 + strict rax*2]
не собирается. Есть ли способ использовать strict
или какой-либо другой синтаксис для принудительного выбора желаемой кодировки режима адресации?
nasm -O0
отключить оптимизацию не получается. Очевидно, это контролирует только многопроходную оптимизацию смещения ветвей, а не все оптимизации, которые делает NASM. Конечно, вы не хотите делать это в первую очередь для всего исходного файла, даже если он действительно работает. Я все еще получаю
8d 84 00 01 00 00 00 lea eax,[rax+rax*1+0x1]
Единственное решение, которое я могу придумать, - это закодировать его вручную с помощью db
. Это довольно неудобно. Для записи, ручное кодирование:
db 0x8d, 0x04, 0x45 ; opcode, modrm, SIB for lea eax, [disp32 + rax*2]
dd 1 ; disp32
Масштабный коэффициент кодируется в старших 2 битах байта SIB. Я собрал lea eax, [dword 1 + rax*4]
, чтобы получить машинный код для правильных регистров, потому что оптимизация NASM работает только для *2
. SIB был 0x85
, и уменьшение этого 2-битного поля в верхней части байта уменьшило масштабный коэффициент с 4 до 2.
Но возникает вопрос: как записать его в удобочитаемом виде, чтобы упростить изменение регистров и заставить NASM кодировать режим адресации за вас? (Полагаю, гигантский макрос мог бы сделать это с помощью обработка текста и ручное db
кодирование, но это не совсем тот ответ, который я ищу. На самом деле мне это ни для чего не нужно прямо сейчас, я в основном хочу знать, есть ли у NASM или YASM синтаксис для принудительного выполнения этого.)
Другие известные мне оптимизации, такие как mov rax, 1
сборка в 5-байтовый mov eax,1
, являются чистым выигрышем для всех процессоров, если вам не нужны более длинные инструкции для получения заполнения без NOP, и может быть отключен с помощью mov rax, strict dword 1
, чтобы получить 7-байтовую кодировку с расширенным знаком, или strict qword
для 10-байтового imm64.
gas не выполняет эту или большинство других оптимизаций (только размеры непосредственных смещений и смещений ветвей): lea 1(,%rax,2), %eax
собирается в 8d 04 45 01 00 00 00 lea eax,[rax*2+0x1]
, и то же самое для версии .intel_syntax noprefix
.
Тем не менее, ответы для MASM или других ассемблеров также были бы интересны.