Как процессоры Pentium III обрабатывали несколько префиксов команд из одной группы?

В спецификации Intel x86 говорится, что использование более одного префикса инструкции из одной и той же группы приводит к неопределенному поведению. На практике, как реагировали процессоры Pentium III Coppermine в этой ситуации? К сожалению, у меня нет чипа для тестирования.

1 ответ

Решение

Хотя вы уже знаете это, я начну с пояснения. Команды x86 могут иметь до 4 префиксов (каждый из другой группы), которые изменяют интерпретацию инструкции процессором. Из руководства по архитектуре Intel IA-32, том 2A, раздел 2.1:

2.1 ФОРМАТ ИНСТРУКЦИИ ДЛЯ ЗАЩИЩЕННОГО РЕЖИМА, РЕЖИМА РЕАЛЬНОГО АДРЕСА И РЕЖИМА VIRTUAL-8086

Кодировки команд архитектур Intel 64 и IA-32 являются подмножествами формата, показанного на рисунке 2-1. Инструкции состоят из необязательных префиксов команд (в любом порядке), первичных байтов кода операции (до трех байтов), спецификатора формы адресации (если требуется), состоящего из байта ModR/M и иногда байта SIB (Scale-Index-Base), смещение (если требуется) и поле непосредственных данных (если требуется).


Рисунок 2-1. Формат команд для архитектуры Intel 64 и IA-32

2.1.1 Префиксы инструкций

Префиксы команд разделены на четыре группы, каждая с набором допустимых кодов префиксов. Для каждой инструкции полезно включать до одного префиксного кода из каждой из четырех групп (Группы 1, 2, 3, 4). Группы с 1 по 4 могут быть расположены в любом порядке относительно друг друга.

  • Группа 1
    • Блокировка и повтор префиксов:
      • Префикс LOCK кодируется с использованием F0H.
      • Префикс REPNE/REPNZ кодируется с использованием F2H. Префикс Repeat-Not-Zero применяется только к строкам и инструкциям ввода / вывода. (F2H также используется в качестве обязательного префикса для некоторых инструкций.)
      • REP или REPE/REPZ кодируются с использованием F3H. Префикс повторения применяется только к строкам и инструкциям ввода / вывода. F3H также используется в качестве обязательного префикса для инструкций POPCNT, LZCNT и ADOX.
    • Связанный префикс кодируется с использованием F2H, если выполняются следующие условия:
      • CPUID. (EAX = 07H, ECX = 0): установлен EBX.MPX[бит 14].
      • BNDCFGU.EN и / или IA32_BNDCFGS.EN установлен.
    • Когда префикс F2 предшествует команде ближнего CALL, ближнего RET, ближнего JMP или ближнего Jcc (см. Главу 17 "Intel® MPX" Руководства разработчика программного обеспечения для архитектур Intel® 64 и IA-32, том 1),
  • Группа 2
    • Префиксы переопределения сегментов:
      • 2EH - переопределение сегмента CS (использование с любой инструкцией ветвления зарезервировано).
      • 36H - префикс переопределения сегмента SS (использование с любой инструкцией ветвления зарезервировано).
      • 3EH - префикс переопределения сегмента DS (использование с любой инструкцией ветвления зарезервировано).
      • 26H - префикс переопределения сегмента ES (использование с любой инструкцией ветвления зарезервировано).
      • 64H - префикс переопределения сегмента FS (использование с любой инструкцией ветвления зарезервировано).
      • 65H - префикс переопределения сегмента GS (использование с любой инструкцией ветвления зарезервировано).
    • Подсказки ветвей (больше не используются; зарезервированы):
      • 2EH - ответвление не занято (используется только с инструкциями Jcc).
      • 3EH - взятая ветвь (используется только с инструкциями Jcc).
  • Группа 3
    • Префикс переопределения размера операнда кодируется с использованием 66H (66H также используется в качестве обязательного префикса для некоторых инструкций).
  • Группа 4
    • 67H - Префикс переопределения размера адреса.

Префикс LOCK (F0H) инициирует операцию, которая обеспечивает исключительное использование разделяемой памяти в многопроцессорной среде. Описание этого префикса см. В разделе "LOCK - подтверждение префикса LOCK# Signal" в главе 3 "Справочник по набору инструкций, AL".

Префикс повторения (F2H, F3H) приводит к тому, что инструкция повторяется для каждого элемента строки. Используйте эти префиксы только с строковыми инструкциями и инструкциями ввода / вывода (MOVS, CMPS, SCAS, LODS, STOS, INS и OUTS). Использование повторяющихся префиксов и / или неопределенных кодов операций с другими инструкциями Intel 64 или IA-32 зарезервировано; такое использование может вызвать непредсказуемое поведение.

Некоторые инструкции могут использовать F2H, F3H в качестве обязательного префикса для выражения различных функций.

Префиксы подсказок ветвления (2EH, 3EH) позволяют программе давать подсказку процессору о наиболее вероятном пути кода для ветки. Используйте эти префиксы только с инструкциями условного перехода (Jcc). Другое использование префиксов подсказок ветви и / или других неопределенных кодов операций с инструкциями Intel 64 или IA-32 зарезервировано; такое использование может вызвать непредсказуемое поведение.

Префикс переопределения размера операнда позволяет программе переключаться между 16- и 32-разрядными размерами операндов. Любой размер может быть по умолчанию; использование префикса выбирает размер не по умолчанию.

Некоторые инструкции и инструкции SSE2/SSE3/SSSE3/SSE4, использующие трехбайтовую последовательность первичных байтов кода операции, могут использовать 66H в качестве обязательного префикса для выражения различных функций.

Другое использование префикса 66H зарезервировано; такое использование может вызвать непредсказуемое поведение.

Префикс переопределения размера адреса (67H) позволяет программам переключаться между 16- и 32-битной адресацией. Любой размер может быть по умолчанию; префикс выбирает размер не по умолчанию. Использование этого префикса и / или других неопределенных кодов операций, когда операнды для инструкции не находятся в памяти, зарезервировано; такое использование может вызвать непредсказуемое поведение.

Обратите внимание, что на самом деле это не говорит о том, что множественные префиксы команд из одной группы приводят к "неопределенному поведению". Скорее, это просто говорит о том, что "полезно только" включить до одного из каждой группы. Это оставляет вещи довольно неопределенными.

Похоже, что единственные формальные гарантии, которые вы получаете из спецификации, заключаются в том, что определенные, конкретные комбинации инструкций и префиксов могут привести к "непредсказуемому поведению" или исключению, и что любая отдельная инструкция длиной более 15 байт приводит к "Неверный код операции" исключение

Это оставляет нам возможность проверять эмпирически несколько префиксов из каждой группы на предмет инструкций, где они в противном случае поддерживаются. С этой целью и в соответствии с просьбой я провел следующие тесты на Pentium III Coppermine 1:

  1. Группа 1: Различные комбинации нескольких REPE (F3) а также REPNE (F2) префиксы на CMPSB инструкция (A6).

    Только последний префикс, который встречается, имеет эффект; другие префиксы из той же группы, которые предшествуют ему, игнорируются.

    Фактически это выглядит стандартным поведением для всех процессоров x86 и согласуется с тем, как дизассемблер Microsoft показывает код. Ведущие (игнорируемые) префиксы не отображаются как часть инструкции.

  2. Группа 2: Префиксы переопределения нескольких сегментов в нагрузке (MOV) инструкция.

    Опять же, последний префикс является единственным, который имеет значение. Все остальные игнорируются. И опять же, это кажется стандартным для всех процессоров x86.

    (Я не удосужился протестировать префиксы подсказок ветвления, отдельно или в сочетании с префиксами переопределения сегментов, поскольку эти подсказки ветвления игнорируются на всех процессорах, кроме Pentium 4.)

  3. Группа 3: несколько префиксов переопределения размера операнда (66h).

    Повторяющиеся префиксы игнорируются, поэтому множественные 66h префиксы имеют точно такой же эффект, как один 66h префикс. Они не отменяют друг друга или что-то подобное.

    Различные источники в сети подтверждают, что это стандартное поведение для всех процессоров x86.

  4. Группа 4: Префиксы переопределения нескольких адресов (67h).

    То же, что и в группе 3: повторяющиеся префиксы игнорируются.

Итак , все префиксы из определенной группы, кроме последнего, игнорируются. Самый последний префикс, встречающийся в инструкции, - это тот, который вступает в силу. Все предыдущие избыточные или бессмысленные префиксы игнорируются. Похоже, что это верно для всех процессоров x86, а это означает, что код эмуляции не требует особого случая такого поведения для какого-либо конкретного поколения / микроархитектуры. Однако префиксы, которые не влияют ни на один контекст, могут быть переопределены, чтобы иметь какое-то значение для будущих процессоров, так что это то, что нужно остерегаться.

Если возможно, чтобы избавить себя от головной боли, вы можете рассмотреть возможность передачи этой интерпретационной работы вашему декодеру. В частности, одна написана Intel, библиотека Intel XED (репозиторий здесь, на GitHub). Вы просто даете ему от 1 до 15 байтов, и он возвращает декодированный код операции (включая префиксы) и операнды. Декодирование - это сложная часть x86, поэтому это избавит вас от головной боли. Он реализует тот же алгоритм, который описан здесь - см., Например, эти заметки и этот код.

__
1 В частности, Intel Pentium III EB с частотой 866 МГц (семейство 6, модель 8, степпинг 6, версия cC0). Это чип Socket 370 FC-PGA, работающий на системе Compaq Deskpro EN с материнской платой на базе Intel 815 (FSB 133 МГц). В случае, если это имеет значение (и, очевидно, не должно), операционная среда была Windows 2000 SP4. Я использовал MASM и отладчик Visual Studio для тестирования.

Другие вопросы по тегам