Как заставить NASM кодировать [1 + rax*2] как disp32 + index*2 вместо disp8 + base + index?
Чтобы эффективно сделать x = x*10 + 1
это, вероятно, оптимально использовать
lea eax, [rax + rax*4] ; x*=5
lea eax, [1 + rax*2] ; x = x*2 + 1
Трехкомпонентный 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]
для 2-го LEA, который нуждается только в disp8 вместо disp32. (Режимы адресации всегда имеют базовый регистр или дисп32).
то есть они всегда разделяются reg*2
в base+index
потому что это никогда не хуже для размера кода.
Я могу заставить использовать disp32 с lea eax, [dword 1 + rax*2]
, но это не останавливает NASM или YASM от разделения режима адресации. Руководство NASM, похоже, не документирует способ использования strict
ключевое слово на коэффициент масштабирования, и [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
, СИБ был 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.
У газа нет этой или большинства других оптимизаций (только размеры немедленных и смещений ветвей): lea 1(,%rax,2), %eax
собирает в 8d 04 45 01 00 00 00 lea eax,[rax*2+0x1]
и то же самое для .intel_syntax noprefix
версия.
Ответы для MASM или других ассемблеров также были бы интересны.
1 ответ
NOSPLIT
:
Точно так же NASM разделится
[eax*2]
в[eax+eax]
потому что это позволяет отсутствию поля смещения и экономии места; на самом деле, он также расколется[eax*2+offset]
в[eax+eax+offset]
,
Вы можете бороться с этим поведением с помощьюNOSPLIT
ключевое слово:[nosplit eax*2]
заставит[eax*2+0]
генерироваться буквально.[nosplit eax*1]
также имеет тот же эффект. По-другому, разделить форму советника[0, eax*2]
можно использовать тоже. Тем не мение,NOSPLIT
в[nosplit eax+eax]
будет игнорироваться, потому что намерение пользователя здесь рассматривается как[eax+eax]
,
lea eax, [NOSPLIT 1+rax*2]
lea eax, [1+rax*2]
00000000 8D044501000000 lea eax,[rax*2+0x1]
00000007 8D440001 lea eax,[rax+rax+0x1]