Что такое регистр% eiz?

В следующем ассемблере, который я сбросил с помощью objdump:

lea    0x0(%esi,%eiz,1),%esi

Что такое регистр %eiz? Что означает предыдущий код?

Ответ 1

См. Почему GCC LEA EIZ?:

По-видимому, %eiz - псевдореестр, который просто оценивает ноль в любое время (например, r0 на MIPS).

...

В конце концов я нашел почтовую рассылку binutils guuru Ian Lance Taylor, которая раскрывает ответ. Иногда GCC вставляет инструкции NOP в поток кода, чтобы обеспечить правильное выравнивание и тому подобное. Инструкция NOP занимает один байт, поэтому вы могли бы подумать, что можете просто добавить столько, сколько необходимо. Но, по словам Яна Лэнса Тейлора, быстрее для чипа выполнить одну длинную инструкцию, чем многие короткие инструкции. Поэтому вместо того, чтобы вставлять семь инструкций NOP, вместо этого они используют один bizarro LEA, который использует семь байтов и семантически эквивалентен NOP.

Ответ 2

(Очень поздно в игре, но это показалось интересным дополнением): Это не регистрация вообще, это причуда кодировки инструкций Intel. При использовании байта ModRM для загрузки из памяти в поле регистра используются 3 бита для хранения 8 возможных регистров. Но место, где ESP (указатель стека) "будет", вместо этого интерпретируется процессором, поскольку "байт SIB следует этой инструкции" (т.е. Это режим расширенной адресации, а не ссылка на ESP). По причинам, известным только авторам, ассемблер GNU всегда представлял этот "ноль, где регистр в противном случае был бы" как регистр "% eiz". Синтаксис Intel просто отбрасывает его.

Ответ 3

Энди Росс дает гораздо больше основополагающих аргументов, но, к сожалению, ошибается или, по крайней мере, путает технические детали. Верно, что эффективный адрес только (%esp) не может быть закодирован только с байтом ModR/M, а вместо того, чтобы декодироваться как (%esp), он используется, чтобы сигнализировать о включении байта SIB. Однако псевдореестр %eiz не всегда используется с байтом SIB для представления того, что использовался байт SIB.

Байт SIB (шкала/индекс/основание) имеет три элемента: индекс (регистр, например %eax или %ecx, к которому применяется шкала), масштаб (мощность двух от 1-8, что индексный регистр умножается на), а база (другой регистр, который добавляется к масштабированному индексу). Это то, что позволяет использовать такие команды, как add %al,(%ebx,%ecx,2) (машинный код: 00 04 4b - код операции, modr/m, sib (обратите внимание на регистр% eiz, даже если используется байт SIB)) (или в синтаксисе Intel, "add BYTE PTR [ecx * 2 + ebx], al" ).

Однако %esp не может использоваться как регистр индекса в байте SIB. Вместо того, чтобы разрешать эту опцию, Intel вместо этого добавляет возможность использовать базовый регистр, который не имеет масштабирования или индексации. Поэтому для устранения неоднозначности между случаем add %al,(%ecx) (машинный код: 00 01 - код операции, modr/m) и add %al,(%ecx) (машинный код: 00 04 21 - код операции, modr/m, sib), альтернативный синтаксис Вместо этого используется add %al,(%ecx,%eiz,1) (или для синтаксиса Intel: add BYTE PTR [ecx+eiz*1],al).

И как объясняется в статье, связанной с Sinan, эта конкретная инструкция (lea 0x0(%esi,%eiz,1),%esi) просто используется как многобайтовый nop (эквивалент esi = &*esi), так что должна выполняться только одна nop-подобная инструкция вместо нескольких инструкций nop.