Почему инструкции x86-64 для 32-битных регистров обнуляют верхнюю часть полного 64-битного регистра?

В Руководстве по Intel x86-64 я прочитал

Возможно, самый удивительный факт заключается в том, что такая инструкция, как MOV EAX, EBX автоматически обнуляет старшие 32 бита RAX регистр.

Документация Intel (3.4.1.1 Регистры общего назначения в 64-битном режиме в ручной базовой архитектуре), приведенная в том же источнике, говорит нам:

  • 64-битные операнды генерируют 64-битный результат в целевом регистре назначения.
  • 32-битные операнды генерируют 32-битный результат, расширенный от нуля до 64-битного результата в целевом регистре назначения.
  • 8-битные и 16-битные операнды генерируют 8-битный или 16-битный результат. Верхние 56 бит или 48 бит (соответственно) регистра назначения общего назначения не изменяются операцией. Если результат 8-битной или 16-битной операции предназначен для вычисления 64-битного адреса, явно расширьте регистр до полных 64-битных.

В сборках x86-32 и x86-64, 16-битные инструкции, такие как

mov ax, bx

не показывайте такого "странного" поведения, когда верхнее слово eax обнуляется.

Таким образом: какова причина, почему это поведение было введено? На первый взгляд это кажется нелогичным (но причина может заключаться в том, что я привык к особенностям сборки x86-32).

4 ответа

Решение

Я не AMD или не говорю за них, но я бы сделал это так же. Поскольку обнуление старшей половины не создает зависимости от предыдущего значения, процессору придется ждать. Механизм переименования регистров, по сути, был бы побежден, если бы не было сделано таким образом Таким образом, вы можете написать быстрый 32-битный код в 64-битном режиме без необходимости постоянно нарушать зависимости. Без этого поведения каждая 32-битная инструкция в 64-битном режиме должна была бы ожидать чего-то, что произошло раньше, даже если эта старшая часть почти никогда не использовалась бы.

Поведение для 16-битных инструкций странное. Безумие зависимостей - одна из причин, по которой 16-битных инструкций теперь избегают.

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

Это также избавляет вас от необходимости кодировать 8-байтовые значения для MOV RAX, 42, когда MOV EAX, 42 можно использовать повторно.

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

Без расширения нуля до 64 бит это означало бы чтение инструкции из rax будет иметь 2 зависимости для своего rax операнд (инструкция, которая записывает eax и инструкция, которая пишет в raxперед этим), это означает, что 1) ROB должен иметь записи для нескольких зависимостей для одного операнда, что означает, что ROB потребует больше логики и транзисторов и займет больше места, а выполнение будет медленнее, ожидая ненужной секунды зависимость, выполнение которой может занять много времени; или, как вариант 2), что, как я предполагаю, происходит с 16-битными инструкциями, этап выделения, вероятно, остановится (т.е. если RAT имеет активное выделение дляax написать и eax read появляется, он глохнет, пока ax пишу уходит).

mov rdx, 1
mov rax, 6
imul rax, rdx
mov rbx, rax
mov eax, 7 //retires before add rax, 6
mov rdx, rax // has to wait for both imul rax, rdx and mov eax, 7 to finish before dispatch to the execution units, even though the higher order bits are identical anyway

Единственное преимущество ненулевого расширения - обеспечение битов более высокого порядка rax включены, например, если он изначально содержит 0xffffffffffffffff, результатом будет 0xffffffff00000007, но у ISA очень мало причин делать эту гарантию такими затратами, и более вероятно, что преимущества нулевого расширения действительно потребуются, поэтому он сохраняет лишнюю строку кода mov rax, 0. Гарантируя, что он всегда будет иметь нулевое расширение до 64 бит, компиляторы могут работать с этой аксиомой, покаmov rdx, rax, raxнужно только дождаться своей единственной зависимости, что означает, что он может начать выполнение быстрее и выйти из строя, высвободив исполнительные единицы. Кроме того, он также позволяет использовать более эффективные нулевые идиомы, такие какxor eax, eax к нулю rax не требуя байта REX.

С точки зрения аппаратного обеспечения возможность обновления половины регистра всегда была довольно затратной, но на исходном 8088 было полезно разрешить написанному вручную ассемблерному коду обрабатывать 8088 как имеющий либо два, не относящиеся к стеку, 16-разрядные регистры и восемь 8-битных регистров, шесть 16-битных регистров, не относящихся к стеку, и нулевые 8-битные регистры или другие промежуточные комбинации 16-битных и 8-битных регистров. Такая полезность стоила дополнительных затрат.

Когда 80386 добавил 32-битные регистры, не было предоставлено никаких средств для доступа только к верхней половине регистра, но была предусмотрена инструкция вроде будет достаточно быстрым, чтобы иметь возможность хранить два 16-битных значения в ESI и переключаться между ними.

С переходом на архитектуру x64 увеличенный набор регистров и другие архитектурные усовершенствования снизили необходимость для программистов втискивать максимальный объем информации в каждый регистр. Кроме того, переименование регистров увеличивало стоимость выполнения частичных обновлений регистров. Если бы код делал что-то вроде:

          mov rax,[whatever]
    mov [something],rax
    mov rax,[somethingElse]
    mov [yetAnother],rax

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

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