Как процессоры 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-322.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: Различные комбинации нескольких
REPE
(F3
) а такжеREPNE
(F2
) префиксы наCMPSB
инструкция (A6
).Только последний префикс, который встречается, имеет эффект; другие префиксы из той же группы, которые предшествуют ему, игнорируются.
Фактически это выглядит стандартным поведением для всех процессоров x86 и согласуется с тем, как дизассемблер Microsoft показывает код. Ведущие (игнорируемые) префиксы не отображаются как часть инструкции.
Группа 2: Префиксы переопределения нескольких сегментов в нагрузке (
MOV
) инструкция.Опять же, последний префикс является единственным, который имеет значение. Все остальные игнорируются. И опять же, это кажется стандартным для всех процессоров x86.
(Я не удосужился протестировать префиксы подсказок ветвления, отдельно или в сочетании с префиксами переопределения сегментов, поскольку эти подсказки ветвления игнорируются на всех процессорах, кроме Pentium 4.)
Группа 3: несколько префиксов переопределения размера операнда (
66h
).Повторяющиеся префиксы игнорируются, поэтому множественные
66h
префиксы имеют точно такой же эффект, как один66h
префикс. Они не отменяют друг друга или что-то подобное.Различные источники в сети подтверждают, что это стандартное поведение для всех процессоров x86.
Группа 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 для тестирования.