AMD64 - инструкция по сборке nopw? - PullRequest
18 голосов
/ 25 января 2011

В этом выводе компилятора я пытаюсь понять, как работает машинное кодирование инструкции nopw:

00000000004004d0 <main>:
  4004d0:       eb fe                   jmp    4004d0 <main>
  4004d2:       66 66 66 66 66 2e 0f    nopw   %cs:0x0(%rax,%rax,1)
  4004d9:       1f 84 00 00 00 00 00

Существует некоторое обсуждение "nopw" в http://john.freml.in/amd64-nopl. Кто-нибудь может объяснить значение 4004d2-4004e0?Из списка кодов операций видно, что коды 66 .. являются многобайтовыми расширениями.Я чувствую, что мог бы получить лучший ответ на этот вопрос здесь, чем я, если бы я не попытался получить список кодов операций в течение нескольких часов.


Этот вывод asm получен из следующего (безумного) кода в C, который оптимизируется до простого бесконечного цикла:

long i = 0;

main() {
    recurse();
}

recurse() {
    i++;
    recurse();
}

При компиляции с gcc -O2 компилятор распознает бесконечную рекурсию и превращает ее в бесконечный цикл;на самом деле он делает это настолько хорошо, что фактически выполняет цикл в main() без вызова функции recurse().


примечание редактора: функции заполнения с NOP не относятся к бесконечным циклам.Вот набор функций с диапазоном длин NOP, в проводнике компилятора Godbolt.

Ответы [ 4 ]

21 голосов
/ 26 января 2011

0x66 байты являются префиксом "Переопределение размера операнда". Наличие более одного из них эквивалентно наличию одного.

0x2e - это «нулевой префикс» в 64-битном режиме (в противном случае это переопределение сегмента CS: - именно поэтому он отображается в мнемонике сборки).

0x0f 0x1f - это 2-байтовый код операции для NOP, который принимает байт ModRM

0x84 - это байт ModRM , который в этом случае кодирует режим адресации, который использует еще 5 байтов.

Некоторые процессоры медленно декодируют инструкции со многими префиксами (например, более трех), поэтому байт ModRM, который задает SIB + disp32, является гораздо лучшим способом использовать дополнительные 5 байтов, чем пять байтов префикса.

Декодеры AMD K8 в микроархиве Agner Fog pdf :

Каждый из декодеров команд может обрабатывать три префикса за такт цикл. Это означает, что три инструкции с тремя префиксами могут быть декодирован в том же тактовом цикле. Инструкция с 4 - 6 префиксами требуется дополнительный тактовый цикл для декодирования.


По сути, эти байты являются одной длинной инструкцией NOP, которая никогда не будет выполнена в любом случае. Это сделано для того, чтобы следующая функция была выровнена по 16-байтовой границе, потому что компилятор выпустил директиву .p2align 4, поэтому ассемблер дополняется NOP. GCC по умолчанию для x86:
-falign-functions=16
. Для NOP, которые будут выполняться, оптимальный выбор long-NOP зависит от микроархитектуры. Для микроархитектуры, которая задыхается от многих префиксов, таких как Intel Silvermont или AMD K8, две NOP с 3 префиксами в каждой, возможно, были бы декодированы быстрее.

Статья в блоге, на которую связан вопрос (http://john.freml.in/amd64-nopl), объясняет, почему компилятор использует сложную одиночную инструкцию NOP вместо набора однобайтовых инструкций NOP размером 0x90.

Подробную информацию о кодировке инструкций можно найти в документах технической ссылки AMD:

Главным образом в «Руководстве по программированию для архитектуры AMD64, том 3: Общее назначение и системные инструкции». Я уверен, что технические ссылки Intel по архитектуре x64 будут содержать ту же информацию (и, возможно, даже более понятную).

2 голосов
/ 26 января 2011

Ассемблер (не компилятор) дополняет код до следующей границы выравнивания самой длинной инструкцией NOP, которую он может найти.Это то, что вы видите.

1 голос
/ 25 января 2011

Я думаю, это всего лишь инструкция задержки ветвления.

0 голосов
/ 26 января 2011

Я верю, что nopw - это мусор - я никогда не читаю в вашей программе, и поэтому нет необходимости увеличивать его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...