Действительно, варианты оптимизации NASM непоследовательны, если предположить, что ss
и ds
взаимозаменяемы (т. Е. Плоская модель памяти) при разделении [ebp*2]
на [ebp+ebp]
для сохранения 3 байтов (disp32 против disp8), но не оптимизации [ebp + esi]
в [esi + ebp]
, чтобы избежать ошибки disp8.
(И в руководстве NASM даже упоминается другой сегмент по умолчанию, что противоречит вывод, который вы сделали из неверной информации о [0 + ebp*2]
и [0+ebp+ebp*1]
.)
EBP или ESP в качестве базового регистра подразумевают SS, в противном случае значение по умолчанию - DS. Когда в режиме адресации NASM используются два регистра, первый является базовым, если вы не написали [ebp*1 + esi]
, явно применяя масштабный коэффициент к первому. Индексный регистр никогда не подразумевает сегмент, что имеет смысл, если вы думаете о замысле проекта: индекс относительно сегмента: смещение, заданное базовым регистром или абсолютным disp32
.
Как написано, [ebp*2]
- это режим индексированной адресации, неявно требующий 4 байта нулей в качестве 32-битных смещений. Вы можете заставить NASM закодировать его таким образом с помощью [nosplit ebp*2]
.
em> может использовать 32-битные режимы адресации в 16-битном коде, чтобы воспользоваться преимуществами масштабных коэффициентов и более широкого выбора регистров, даже в чисто реальном режиме, а не в " нереальный "режим, который позволяет установите пределы сегмента достаточно высокими, чтобы можно было использовать смещения> 2 ^ 16.)
Все основные 32- и 64-разрядные ОС x86 используют плоскую модель памяти, где SS и DS являются взаимозаменяемыми, что делает эту оптимизацию безопасной в этих ОС, когда вы не делаете ничего странного. Сегментация иногда использовалась для создания неисполняемых стеков, прежде чем это поддерживалось таблицами страниц, но это все еще плоская модель памяти. (64-битный код фиксирует базовый / предел для CS / DS / ES / SS, поэтому такая оптимизация всегда безопасна, если только SS
не является полностью непригодным для использования сегментом, например, возможно, защищенным от записи, если это возможно.)
Тем не менее, любое предположение о плоской модели памяти должно быть необязательным. Это ошибка в NASM и YASM. Им следует либо уважать разницу между SS и DS, либо в полной мере использовать преимущества плоской модели памяти, чтобы помочь программистам, которые не помнят, в каких режимах адресации требуются «скрытые» дополнительные байты, например, оптимизация [ebp+esi]
без смещения в [esi+ebp]
. Предпочтительно должна быть опция или директива, сообщающая ассемблеру, что он может предполагать, что SS и DS - это одно и то же.
Операнды LEA всегда могут воспользоваться преимуществом, потому что LEA работает только со смещенной частью адреса, поэтому сегменты не имеют значения. (И это будет наиболее распространенный вариант использования такого режима адресации, как [ebp*2]
без смещения: использование этого адреса в качестве адреса памяти могло бы имитировать память с адресной памятью? Это просто странно, обычно в качестве одного из компонентов адреса есть реальный указатель. )
Основные сведения о 32/64-разрядных режимах адресации x86:
Помимо 64-битной относительной адресации RIP, 32/64-битные режимы адресации представляют собой любое подмножество disp0/8/32 + base_reg + idx_reg*1/2/4/8
, где каждый из 3 терминов / компонентов является необязательным. Но требуется по крайней мере один из регистра disp32 или базового. (См. Также Ссылка на содержимое ячейки памяти. ( режимы адресации x86)).
[disp32=0 + ebp*2]
(с disp32 = ноль) имеет сегмент по умолчанию = DS. Вы можете получить эту кодировку в NASM из [nosplit ebp*2]
, а адреса типа [ebp*4]
не могут быть разделены.
[ebp + ebp + disp8=0]
имеет сегмент по умолчанию = SS, потому что EBP используется как базовый регистр.
Кодировка, которая будет означать ebp
без смещения, на самом деле означает disp32 без базового регистра, поэтому disp32 фактически является базой (подразумевая сегментный регистр DS, потому что база не EBP или ESP). Это случай с байтом SIB или без него, поэтому [ebp + ebp*1]
все равно нужно закодировать с помощью disp8 = 0. В других регистрах такой проблемы нет, поэтому обычно при разделении сохраняется 4 байта вместо 3 для EBP. (За исключением r13
, который использует ту же кодировку ModR / M, что и RBP, я полагаю, что части оборудования декодирования не требуется дополнительный бит из префикса REX.)
ESP не может быть индексным регистром, поэтому [esp*2]
невозможно кодировать с разделением или без него. Таким образом, особый случай оптимизации NASM только влияет на EBP*2
. (base = ESP - это escape-код для байта SIB, а index = ESP в байте SIB означает отсутствие индекса, что позволяет кодировать [esp + 12]
.)
Но, к сожалению, NASM / YASM разделяет EBP*2
, даже если есть константа, которой все равно требуется disp32, например [symbol + ebp*2]
, где она не сохраняет никаких байтов и фактически снижает производительность LEA (но не загружает / сохраняет) на ЦП семейства Sandybridge. Трехкомпонентный lea eax, [symbol + ebp + ebp*1]
медленнее, чем двухкомпонентный lea eax, [symbol + ebp*2]
: более высокая задержка и пропускная способность 1 на такт вместо 2. Согласно http://agner.org/optimize/, они будут одинаково медленными на AMD Bulldozer / Ryzen, потому что масштабированный индекс делает «медленный LEA» даже с двумя компонентами.
IDK, если какие-либо старые процессоры лучше работают с немасштабируемым индексом и режимами трехкомпонентной адресации, для LEA или для фактических операндов памяти.
Поведение NASM и YASM:
$ nasm -felf32 -g -Fdwarf foo.asm
$ objdump -drwC -Mintel -S foo.o | sed 's/DWORD PTR//'
# (edited to put the NASM source line's addressing mode onto the same line as the disassembler output, instead of separate lines)
00000000 <sym-0x2c>:
0: 8b 04 2e mov eax, [esi+ebp*1] ; [esi+ebp]
3: 8b 44 35 00 mov eax, [ebp+esi*1+0x0] ; [ebp + esi]
7: 8b 04 2e mov eax, [esi+ebp*1] ; [ebp*1 + esi]
a: 8b 44 2d 00 mov eax, [ebp+ebp*1+0x0] ; [ebp*2]
e: 8b 04 6d 00 00 00 00 mov eax, [ebp*2+0x0] ; [nosplit ebp*2]
15: 8b 45 00 mov eax, [ebp+0x0] ; [ebp*1] ; "split" into base=ebp with no SIB byte
18: 8b 04 2d 00 00 00 00 mov eax, [ebp*1+0x0] ; [nosplit ebp*1]
1f: 8b 84 2d d2 04 00 00 mov eax, [ebp+ebp*1+0x4d2] ; [ebp*2 + 1234] ; bad split for LEA, neutral on modern CPUs for load/store
26: 8b 85 15 cd 5b 07 mov eax, [ebp+0x75bcd15] ; [ebp*1 + 123456789]
sym: ; using a symbol reference instead of a numeric constant doesn't change anything
2c: 8b 84 2d 2c 00 00 00 mov eax, [ebp+ebp*1+0x2c] 2f: R_386_32 .text ; [ebp*2 + sym]
33: 8b 84 2d 2c 00 00 00 mov eax, [ebp+ebp*1+0x2c] 36: R_386_32 .text ; [sym + ebp*2]
YASM кодирует все эти случаи идентично NASM.
person
Peter Cordes
schedule
10.04.2018
[ebp*2]
- ›[ebp+ebp]
) предполагает плоскую модель памяти, в которойss
иds
эквивалентны, что имеет место во всех основных основных операционных системах x86. Это интересный угловой случай, потому что режим чистой[idx*2]
адресации без регистра или 32-битной абсолютной базы также очень необычен (за исключением LEA для копирования и сдвига). Обычно люди используют настоящие указатели вместо того, чтобы имитировать адресную память по словам, масштабируя их на 2 или что-то еще, что вы делаете. - person Peter Cordes   schedule 09.04.2018ndisasm
, чтобы увидеть, что думала сама программа? Поскольку информация, которую вы получили, была неправильной:[esi + ebp]
используетds
. И если вы предполагаете, чтоss
иds
взаимозаменяемы, вы должны оптимизировать[ebp + esi]
до[esi + ebp]
, чтобы избежать необходимости в disp8 = 0. (EBP как базовый регистр кодируется только с помощью disp8 или disp32; кодировка, которая будет означать EBP + на самом деле отсутствие смещения означает, что существует disp32 без базового регистра (но потенциально это индекс). - person Peter Cordes   schedule 09.04.2018[symbol + ebp*2]
.bin
- это простой двоичный файл, не имеющий никаких сведений о том, что вы можете делать с результирующим машинным кодом. например использовать его как исполняемый файл .COM, загрузочный сектор или встраивать в другое место. (Режим по умолчанию дляbin
-bits 16
, то есть 16-битный режим.) - person Peter Cordes   schedule 09.04.2018[1 + rax*2]
какdisp32 + index*2
вместоdisp8 + base + index
? - person phuclv   schedule 09.04.2018