Подтвердить что ты не робот

Декодирование команд, когда инструкции являются переменными по длине

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

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 байтовых кусках. Однако инструкции могут иметь переменную длину. Как микропроцессор декодирует эти инструкции и почему они не имеют постоянной длины, чтобы облегчить реализацию?

4b9b3361

Ответ 1

EDIT: надеемся сделать его более читаемым.

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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


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

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


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

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

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

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

Ответ 4

Вы изобрели RISC

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

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

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


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