Декодирование команд, когда инструкции имеют переменную длину - PullRequest
11 голосов
/ 20 ноября 2011

Вот некоторые инструкции и их соответствующие кодировки:

55                      push   %ebp
89 e5                   mov    %esp,%ebp
83 ec 18                sub    $0x18,%esp
a1 0c 9f 04 08          mov    0x8049f0c,%eax
85 c0                   test   %eax,%eax
74 12                   je     80484b1 <frame_dummy+0x21>
b8 00 00 00 00          mov    $0x0,%eax
85 c0                   test   %eax,%eax
74 09                   je     80484b1 <frame_dummy+0x21>
c7 04 24 0c 9f 04 08    movl   $0x8049f0c,(%esp)

Современные микропроцессоры часто бывают 32- или 64-разрядными, и я предполагаю, что они обычно считывают данные из памяти в виде 4-байтовых или 8-байтовых блоков.Однако инструкции могут иметь переменную длину.Как микропроцессор декодирует эти инструкции и почему они не имеют постоянной длины, чтобы упростить реализацию?

Ответы [ 4 ]

8 голосов
/ 21 ноября 2011

Есть очень веские причины иметь фиксированную длину команд, простота реализации является наиболее важной. Вот почему многие процессоры действительно имеют фиксированную длину команд, как RISC-процессоры и многие ранние компьютеры.

Наборы команд CISC , как и x86, предназначены для последовательного (шаг за шагом) декодирования с помощью микрокода . (Вы можете думать о микрокоде как об интерпретаторе инструкций CISC). Это было состояние дел в начале 80-х, когда разрабатывался x86.

В настоящее время это проблема, потому что микрокод мертв. Инструкции x86 теперь разбиты на меньшие µ-ops , мало чем отличающиеся от инструкций RISC. Но для этого сначала необходимо декодировать инструкции x86. И текущие процессоры декодируют до 4 инструкций в каждом цикле. Поскольку нет времени для последовательного декодирования одной инструкции за другой, это работает просто грубой силой. Когда строка вводится из кэша инструкций , многие декодеры декодируют строку параллельно. Один декодер команд при каждом возможном смещении байта. После декодирования длина каждой инструкции известна, и процессор решает, какие декодеры фактически предоставляют действительные инструкции. Это расточительно, но очень быстро.

Переменные размеры команд вводят больше головных болей, например инструкция может занимать две строки кэша или даже две страницы в памяти. Таким образом, ваше наблюдение на месте. Сегодня никто не разработал бы набор инструкций CISC как x86. Однако некоторые RISC недавно ввели второй размер инструкции для получения более компактного кода: MIPS16, ARM-Thumb и т. Д.

7 голосов
/ 21 ноября 2011

РЕДАКТИРОВАТЬ: в надежде сделать его более читабельным.

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

Для x86, например, он должен смотреть на первый байт.В зависимости от декодирования этого байта может потребоваться прочитать больше байтов кода операции.Если инструкция требует адрес, смещение или другую форму непосредственного значения, эти байты также присутствуют.Очень быстро процессор точно знает, сколько байтов в этой инструкции.Если декодирование показывает, что инструкция содержит 5 байтов, и она начинается с адреса 0x10, то следующая инструкция имеет размер 0x10 + 5 или 0x15.Это продолжается вечно.Безусловные ветви, которые в зависимости от процессора могут иметь различные разновидности, вы не предполагаете, что байты, следующие за инструкцией, являются другой инструкцией.Ответвления, условные или безусловные, дают подсказку, где в памяти начинается другая инструкция или серия инструкций.

Обратите внимание, что сегодня X86 определенно не выбирает один байт за раз, когда он декодирует инструкцию, происходит чтение разумного размера,вероятно, 64 бита за раз, и процессор будет извлекать байты из этого по мере необходимости.При чтении одного байта из современного процессора шина памяти по-прежнему выполняет полноразмерное чтение и либо представляет все те биты на шине, где контроллер памяти извлекает только те биты, которые были после, либо может зайти так далеко, чтобы сохранить эти данные.,Вы увидите некоторые процессоры, в которых у вас могут быть две 32-битные инструкции чтения с двухадресных адресов, но только одно 64-битное чтение происходит на интерфейсе памяти.

Я настоятельно рекомендую вам написать дизассемблер и / или эмулятор.Для инструкций фиксированной длины это довольно просто, вы просто начинаете с начала и декодируете по мере прохождения памяти.Дизассемблер с фиксированной длиной слова может помочь узнать о командах декодирования, которые являются частью этого процесса, но это не поможет вам понять следование инструкциям с переменной длиной слова и разделить их без выравнивания.

MSP430хороший выбор в качестве первого дизассемблера.Есть инструменты GNU ASM и C, и т. Д. (И llvm в этом отношении).Начните с ассемблера, затем C или возьмите несколько готовых двоичных файлов.Ключ в том, что вы должны пройти код, как процессор, начать с вектора сброса и пройти свой путь.Когда вы декодируете одну инструкцию, вы знаете ее длину и знаете, где находится следующая инструкция, пока не дойдете до безусловной ветви.Если программист намеренно не оставил ловушку, чтобы обмануть дизассемблера, допустим, что все ветки условно или безусловно указывают на действительные инструкции.Полдень или вечер - это все, что требуется, чтобы разобраться в этом или хотя бы понять концепцию.Вам не обязательно нужно полностью декодировать инструкцию, не нужно превращать ее в полноценный дизассемблер, нужно только достаточно декодировать, чтобы определить длину инструкции и определить, является ли она ветвью, и если да, то где.Будучи 16-битной инструкцией, вы можете, если захотите, один раз создать таблицу всех возможных комбинаций битов инструкций и их длины, что может сэкономить некоторое время.Вы все еще должны расшифровать свой путь через ветви.

Некоторые люди могут использовать рекурсию, вместо этого я использую карту памяти, показывающую мне, какие байты являются началом инструкции, какие байты / слова являются частью инструкции, но непервый байт / слово и какие байты я еще не расшифровал.Я начинаю с взятия векторов прерывания и сброса и использую их для обозначения начальной точки для инструкций.а затем перейдите к циклу, который декодирует инструкции, ища больше отправных точек.Если проходитбез других отправных точек, то я закончил этот этап. Если в какой-то момент я нахожу начальную точку инструкции, находящуюся посередине инструкции, возникает проблема, для решения которой потребуется вмешательство человека. Например, разбирая старые диски с видеоиграми, вы, скорее всего, увидите это, написанный от руки ассемблер. Инструкции, сгенерированные компилятором имеют тенденцию быть очень чистыми и предсказуемыми. Если я пройду через это с чистой картой памяти инструкций и того, что осталось, (предположим, данные), я могу сделать один проход, зная, где находятся инструкции, и декодировать и распечатать их. То, что дизассемблер для наборов команд переменной длины слова никогда не сможет сделать, - это найти каждую инструкцию. Если в наборе команд есть, например, таблица переходов или какой-либо иной вычисляемый адрес во время выполнения для выполнения, вы не найдете все из них без фактического выполнения кода.

Существует целый ряд существующих эмуляторов и дизассемблеров, и если вы хотите попытаться следовать им, а не писать свои собственные, у меня есть несколько http://github.com/dwelch67.

Есть плюсы и минусы за и против переменной и фиксированной длины слова. У Fixed есть свои преимущества: легко читается, легко декодируется, все красиво и правильно, но подумайте о ram, в частности, о кеше, вы можете собрать гораздо больше инструкций x86 в том же кеше, что и ARM. С другой стороны, ARM может декодировать намного проще, гораздо меньше логики, мощности и т. Д. Больше отдачи от затраченных средств. Исторически память была дорогой, логика была дорогой, и байт на ходу был таким, как он работал. однобайтовый код операции ограничил вас 256 командами, поэтому он расширился до некоторых кодов операций, требующих больше байтов, чтобы не упоминать непосредственные значения и адреса, которые в любом случае делали его переменной длины слова. Сохраняйте обратную совместимость десятилетиями, и вы окажетесь там, где вы сейчас находитесь.

Для добавления ко всей этой путанице, например, в ARM теперь есть набор команд переменной длины слова. У Thumb была одна переменная инструкция слова, ветвь, но вы можете легко декодировать ее как фиксированную длину. Но они создали thumb2, который действительно напоминает набор команд переменной длины слова. Также многие / большинство процессоров, которые поддерживают 32-битные архитектуры ARM, также поддерживают 16-битные инструкции большого пальца, поэтому даже с процессором ARM вы не можете просто выровнять данные по словам и декодировать по ходу работы, вы должны использовать переменную длину слова. Что еще хуже, переходы ARM в / из большого пальца декодируются путем выполнения, обычно вы не можете просто разобрать и определить руку от большого пальца. Ветвь, сгенерированная компилятором смешанного режима, часто включает в себя загрузку регистра с адресом ветвления, чтобы затем использовать инструкцию bx для ветвления к нему, поэтому дизассемблеру нужно будет посмотреть на bx, посмотреть назад в исполнении для регистра, используемого в ветке, и надеюсь, что вы найдете загрузку и надеетесь, что это сегмент .text, из которого он загружается.

4 голосов
/ 20 ноября 2011

Я не смогу ответить, как именно они декодируются, но я могу ответить, почему они имеют переменную длину.

Причиной переменной длины является желание сохранить небольшой размер кода, а также непредвиденные расширения набора команд.


Уменьшение размера инструкции

Некоторым инструкциям (по природе) требуется намного больше места для кодирования. Если бы все инструкции были установлены на достаточно большую фиксированную длину, чтобы соответствовать им, в коде инструкции было бы много потерянного пространства. Инструкции переменной длины позволяют «сжать» инструкции до меньшего размера.


(непредвиденные) Расширения набора инструкций

Другая причина - расширения набора команд. Первоначально у x86 было только 256 кодов операций. (1 байт) Затем возникла необходимость добавить больше инструкций, поэтому они выбросили одну инструкцию и использовали ее код операции в качестве escape-символа для новых кодов операций. В результате новые инструкции стали длиннее. Но это был единственный способ расширить набор команд и сохранить обратную совместимость.

Что касается того, как процессор декодирует их, это сложный процесс. Для каждой инструкции процессор должен найти длину и декодировать оттуда. Это приводит к последовательному процессу декодирования, который является общим узким местом производительности.

Современные процессоры x86 имеют так называемый кэш uop (micro-op), который кэширует декодированные инструкции во что-то более управляемое (и RISC-подобное) процессором.

2 голосов
/ 20 ноября 2011

Вы заново изобрели RISC

Хмм, вы возражаете против классического x86 (см. CISC) именно то, что мотивировало разработчиков процессора RISCархитектуры для создания простых, выровненных архитектур набора команд фиксированного размера.

Оказывается, в наши дни x86 фактически превращает видимый пользователем ISA в более RISC-подобный микрооперационный поток, который живет ввнутренний кеш.

Хорошее наблюдение.


Примечания.
1.Микрооперации - это всего лишь один из доступных методов.В общем случае, пока декодирование и выравнивание команд происходит на одном или нескольких этапах конвейера, фактическое время не будет добавлено к среднему времени выполнения команды.Если прогнозирование ветвлений работает и конвейер поддерживается полным, дополнительное время, необходимое для декодирования и выравнивания команд, обрабатывается логикой, выполняемой параллельно с действительными операциями команд.Имея миллионы шлюзов, доступных сегодняшним дизайнерам, они могут посвятить много логики декодированию сложного x86 ISA.
2.Вы упомянули ширину шины памяти;оказывается, что путь к памяти обычно больше 32-х или 64-х бит.Размер архитектурного слова просто относится к ALU и размеру указателя.Фактическая ширина интерфейсов памяти и кэша часто в 2 или 4 раза превышает размер архитектурного слова.
...