Почему Intel не сделала доступной старшую часть регистров своих процессоров?

При программировании на ассемблере и выполнении каких-либо манипуляций со строками я использую al, ah, а иногда и другие для хранения символов, потому что это позволяет мне хранить больше данных в моих регистрах. Я думаю, что это очень удобная функция, но инженеры Intel, похоже, не согласны со мной, потому что они не сделали доступными два старших байта регистров (или я ошибаюсь?). Не понимаю почему. Я думал об этом некоторое время и мои догадки таковы:

  1. Они сделают процессор слишком сложным
  2. Они были бы бесполезны
  3. возможно оба из вышеперечисленных

Я придумал номер два, потому что я никогда не видел, чтобы скомпилированная программа (скажем, с gcc) использовала al или bh или любой из них.


person BlackBear    schedule 15.03.2011    source источник
comment
gcc действительно использует movzx из al / ah, если вы пишете C, который загружает uint32_t из массива, а затем использует сдвиги / AND для извлечения каждого байта для использования в качестве индекса массива. Вместо трех сдвигов на 8 бит вы получите один сдвиг на 16 бит. (Или с uint64_t в 64-битном режиме, несколько сдвигов на 16 бит.)   -  person Peter Cordes    schedule 29.06.2016
comment
Возможный дубликат Почему существует не регистр, содержащий старшие байты EAX?   -  person phuclv    schedule 09.07.2018


Ответы (5)


Хотя это немного неуклюже, вы можете просто поменять местами половины регистра с rol reg,16 (или ror reg,16, если хотите). На процессорах Netbust (Pentium IV) это довольно неэффективно, но на большинстве новых (или старых) процессоров у вас обычно есть баррель-шифтер, чтобы сделать это за один такт.

Что касается того, почему они этого не сделали, все довольно просто: им нужно было бы полностью изменить кодировку инструкций, если бы они действительно хотели это сделать. В первоначальном дизайне они использовали все коды, которые соответствовали размеру полей, которые они использовали для определения регистра. Фактически, они уже используют что-то вроде хака, когда значение кодировки зависит от режима, и есть префиксы размера адреса и размера операнда, если вам нужно использовать другой размер. Например, чтобы использовать AX, когда вы работаете в 32-битном режиме, инструкция будет иметь префикс переопределения операнда перед самой инструкцией. Если бы они действительно хотели этого достаточно сильно, они могли бы расширить эту концепцию, указав такие вещи, как «байт в битах 16-23 регистра X», но это сделало бы декодирование более сложным, а декодирование инструкций x86 уже было относительно болезненным. .

person Jerry Coffin    schedule 15.03.2011

Короткий ответ - из-за того, как он развился из 16 бит.

Почему нет реестра который содержит старшие байты EAX?

person dwidel    schedule 15.03.2011
comment
+1 за ваш ответ. Приму, если другого не будет :) - person BlackBear; 16.03.2011

Помимо проблемы с кодировкой инструкций, которую правильно упоминает Джерри, здесь работают и другие вещи.

Большинство нетривиальных процессоров являются конвейерными: это означает, что при обычной работе инструкции начинают выполняться до того, как завершатся предыдущие инструкции. Это означает, что процессор должен обнаруживать любые зависимости инструкции от более ранних инструкций и предотвращать выполнение инструкции до тех пор, пока не будут доступны данные (или флаги условий), от которых она зависит [1].

Наличие имен для разных частей реестра усложняет отслеживание зависимостей. Если я напишу:

mov  ax,  dx
add  eax, ecx

тогда ядро ​​должно знать, что ax является частью eax, и что добавление должно дождаться, пока не станет доступен результат перемещения. Это называется частичным обновлением реестра; хотя это кажется очень простым, разработчики оборудования обычно не любят их и стараются по возможности избегать необходимости отслеживать их (особенно в современных вышедших из строя процессорах).

Наличие имен для старших половин регистров добавляет дополнительный набор частичных имен регистров, которые необходимо отслеживать, что увеличивает площадь кристалла и потребление энергии, но дает небольшую выгоду. В конце концов, именно так принимаются решения по дизайну ЦП: компромисс между площадью кристалла (и мощностью) и преимуществами.

Частичные обновления регистров - не единственное, что было бы затруднительно из-за наличия имен для старших частей регистра, но это одно из самых простых для объяснения; есть много других мелких вещей, которые потребовалось бы усложнить в современном процессоре x86 для его поддержки; в совокупности дополнительная сложность была бы значительной.

[1] Есть и другие способы разрешения зависимостей, но мы игнорируем их здесь для простоты; они создают аналогичные проблемы.

person Stephen Canon    schedule 15.03.2011
comment
Это скорее удача, что 386 не сделал этого для разработчиков оборудования. 386 не отслеживал зависимости отдельно для частичных регистров, не так ли? Или вы думаете, что 386 архитекторов могут увидеть потенциальную проблему в будущем? Intel часто заметно сокращает срок подписания своих расширений ISA. например cvtsi2ss не обнуляет остальную часть регистра, поэтому имеет ложную зависимость. Вероятно, потому что PIII разделил регистры xmm пополам, поэтому потребовался бы дополнительный моп для обнуления верхней половины. Но теперь мы обременены тем, что gcc генерирует дополнительные pxor insns, чтобы сломать зависимости. - person Peter Cordes; 29.06.2016

Чтобы добавить к тому, что до сих пор сказали Джерри и Стивен.

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

Я думаю, что есть причина, по которой в большинстве мировых наборов инструкций нет этих четырех имен для одного и того же регистрового элемента. И я не думаю, что дело в патентах. В свое время это была, наверное, крутая функция или дизайн. Вероятно, у него есть корни в переходе людей от 8-битных процессоров к 8/16-битной штуке. В любом случае, я думаю, что al, ah, ax, eax - плохой дизайн, и все извлекли из этого урок. Как упоминал Стивен, у вас есть проблемы с оборудованием, если вы строго реализуете это в прямой логике, это беспорядок, крысиное гнездо мультиплексоров, чтобы все подключить (плохо для скорости и плохо для мощности), тогда вы попадаете в сроки кошмар, который творил Стивен. Но для этого набора инструкций существует история микрокодирования, поэтому вы, по сути, эмулируете эти инструкции с каким-то другим процессором, и таким же образом это усугубляет этот кошмар. Было бы разумно переопределить ax, сделав его 32-битным, и избавиться от ah и al. Мудро с точки зрения дизайна, но неразумно с точки зрения переносимости (хорошо для разработки, плохо для маркетинга, продаж и т. Д.). Я думаю, что причина, по которой этот утомленный старый набор инструкций не ограничивается учебниками по истории и музеями, (среди нескольких других причин) заключается в обратной совместимости.

Я настоятельно рекомендую изучить ряд других наборов инструкций, как новых, так и старых. msp430, ARM, thumb, mips, 6502, z80, PIC (старый, не являющийся mips) и т. д. Просто чтобы назвать несколько. ИМО, очень поучительно видеть различия и сходства между наборами инструкций. И в зависимости от того, насколько глубоко вы углубитесь в понимание (переменная длина слова или фиксированная длина и т. Д.), Понимание того, какие варианты мы доступны для Intel при переходе с 16 на 32 бит, а в последнее время с 32 бит на 64 бит, при попытке сохранить долю рынка .

Я думаю, что решение, которое они выбрали в то время, было правильным: вставить ранее неопределенный код операции перед тем, что обычно декодируется как 16-битный код операции, превращая его в 32-битный код операции. Или, иногда, нет, если нет следующих немедленных значений (требующих знания того, сколько читать). Казалось, что это соответствовало инструкциям, установленным в то время. Итак, это возвращение к ответу Джерри, причина заключается в сочетании конструкции 8/16-битной инструкции, задающей историю, и причин для ее расширения. Конечно, они могли бы с такой же легкостью использовать аналогичное кодирование для обеспечения доступа к старшим 16 битам топором, ах, al, и они могли бы так же легко умножить четыре базовых регистра A, B, C, D на 8 или 16. или 32 регистра общего назначения (A, B, C, D, E, F, G, H, ...), оставаясь при этом обратно совместимыми.

person old_timer    schedule 16.03.2011

Фактически, традиционные коды операций x86 позволяют выбрать как размер операнда (иногда в виде кодировки конкретной инструкции, иногда через байты префикса), так и биты выбора номера регистра. Для выбора регистра в кодировке инструкции всегда есть три бита. Это позволяет использовать всего восемь регистров.

Первоначально их было четыре: AX / BX / BP / SP для 16 бит и AL / AH / BL / BH для 8 бит.

Добавление еще двух дало CX / DX плюс CL / CH / DL / DH. Больше не осталось 8-битных регистров, но остались два неиспользованных значения в выборе регистров для 16-битных.

Которые были предоставлены в другой версии архитектуры Intel индексом regs DI / SI.

После этого они исчерпали все 3 бита выбора регистра (и сделали невозможным предоставление 8-битных регистров для SI / DI / BP / SP).

Таким образом, в 64-битном режиме AMD64 удалось удвоить набор регистров за счет использования байтов префикса (префикс «использовать новые регистры»), аналогично тому, как традиционный код x86 выбирает между 16- и 32-битными операциями. Тот же метод был использован для предоставления 8-битных регистров там, где их не было "традиционно", то есть для SP/BP/SI/DI.

Для иллюстрации см., Например, следующие кодировки инструкций:

0:     00 c0                add    %al,%al
2:     00 c1                add    %al,%cl
4:     00 c2                add    %al,%dl
6:     00 c3                add    %al,%bl
8:     00 c4                add    %al,%ah
a:     00 c5                add    %al,%ch
c:     00 c6                add    %al,%dh
e:     00 c7                add    %al,%bh
10: 40 00 c4                add    %al,%spl
13: 40 00 c5                add    %al,%bpl
16: 40 00 c6                add    %al,%sil
19: 40 00 c7                add    %al,%dil

И для [16 бит / 64 бит] / 32 бит, бок о бок, поскольку это так наглядно:

0   : [66/48] 01 c0     add   %?ax,%?ax
2/3 : [66/48] 01 c1     add   %?ax,%?cx
4/6 : [66/48] 01 c2     add   %?ax,%?dx
6/9 : [66/48] 01 c3     add   %?ax,%?bx
8/c : [66/48] 01 c4     add   %?ax,%?sp
a/f : [66/48] 01 c5     add   %?ax,%?bp
c/12: [66/48] 01 c6     add   %?ax,%?si
e/15: [66/48] 01 c7     add   %?ax,%?di

Префикс 0x66 отмечает 16-битную операцию, а 0x48 - один из байтов префикса для 64-битной операции (он был бы другим, если бы ваша цель и / или источник были одним из "новых" регистров с большими номерами).

Чтобы вернуться к исходному вопросу, как получить доступ к старшим битам; ну, в более новых процессорах для этой цели есть инструкции SSE; каждое 8/16/32/64-битное поле векторного регистра доступно отдельно, например, через инструкции перетасовки, и на самом деле много кода для манипуляции строками, предоставляемого Intel / AMD в их оптимизированных библиотеках, в наши дни больше не используют обычные регистры ЦП, а вместо этого используют векторные регистры. Если вам нужна симметрия между верхней / нижней половинами (или другими дробями) некоторого большего значения, используйте векторные регистры.

person FrankH.    schedule 16.03.2011
comment
thx ;-) Кстати, на всякий случай, если кто-то задается вопросом: 0x40 0x00 0xc0 и 0x00 0xc0 в 64-битном режиме являются кодировками для одной и той же инструкции, add %al, %al. AMD сделала новую схему довольно чистой. - person FrankH.; 16.03.2011