Как заставить 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]
Другие вопросы по тегам