x86_64 регистры rax/eax/ax/al перезаписывают полное содержимое регистра

Как широко рекламируется, современные процессоры x86_64 имеют 64-битные регистры, которые могут использоваться обратно совместимым образом в качестве 32-битных регистров, 16-битных регистров и даже 8-битных регистров, например:

0x1122334455667788
  ================ rax (64 bits)
          ======== eax (32 bits)
              ====  ax (16 bits)
              ==    ah (8 bits)
                ==  al (8 bits)

Такая схема может быть принята буквально, то есть всегда можно получить доступ только к той части регистра, которая использует назначенное имя для чтения или записи, и это было бы весьма логично. Фактически, это верно для всего, вплоть до 32-битного:

mov  eax, 0x11112222 ; eax = 0x11112222
mov  ax, 0x3333      ; eax = 0x11113333 (works, only low 16 bits changed)
mov  al, 0x44        ; eax = 0x11113344 (works, only low 8 bits changed)
mov  ah, 0x55        ; eax = 0x11115544 (works, only high 8 bits changed)
xor  ah, ah          ; eax = 0x11110044 (works, only high 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  al, al          ; eax = 0x11112200 (works, only low 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  ax, ax          ; eax = 0x11110000 (works, only low 16 bits cleared)

Однако, как только мы доберемся до 64-битных вещей, все будет довольно неловко:

mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  eax, 0x55556666         ; actual:   rax = 0x0000000055556666
                             ; expected: rax = 0x1111222255556666
                             ; upper 32 bits seem to be lost!
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  ax, 0x7777              ;           rax = 0x1111222233337777 (works!)
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
xor  eax, eax                ; actual:   rax = 0x0000000000000000
                             ; expected: rax = 0x1111222200000000
                             ; again, it wiped whole register

Такое поведение кажется мне очень нелепым и нелогичным. Похоже, что пытаться написать что-нибудь вообще eax любым способом приводит к стиранию старших 32 бит rax регистр.

Итак, у меня есть 2 вопроса:

  1. Я считаю, что это неуклюжее поведение должно быть где-то задокументировано, но я не могу найти подробное объяснение (как точно стираются 32-битные 64-битные регистры). Я прав, что пишу eax всегда вытирает raxили это что-то более сложное? Это относится ко всем 64-битным регистрам, или есть некоторые исключения?

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

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

  2. Это только я или вся эта вещь кажется действительно странной и нелогичной (например, eax-ax-ah-al, rax-ax-ah-al с одним поведением и rax-eax с другим)? Может быть, я упускаю какой-то важный момент здесь, почему это так реализовано?

    Объяснение "почему" будет высоко оценено.

1 ответ

Решение

Модель процессора, описанная в руководстве к процессору Intel/AMD, является довольно несовершенной моделью для реального механизма исполнения современного ядра. В частности, понятие регистров процессора не соответствует действительности, нет такого понятия, как регистр EAX или RAX.

Одна из основных задач декодера команд - преобразовать устаревшие инструкции x86/x64 в микрооперации, инструкции RISC-подобного процессора. Небольшие инструкции, которые легко выполнять одновременно и которые могут использовать преимущества нескольких исполнительных подразделений. Позволяет выполнять до 6 инструкций одновременно.

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

Чтобы это работало гладко и эффективно, позволяя многим инструкциям выполняться одновременно, очень важно, чтобы эти операции не имели взаимозависимости. И самое худшее, что вы можете иметь, это то, что значение регистра зависит от других инструкций. Регистр EFLAGS печально известен, многие инструкции его модифицируют.

Та же проблема с тем, как вам нравится работать. Большая проблема, она требует объединения двух значений регистра после удаления инструкции. Создание зависимости данных, которая собирается засорить ядро. Если принудительно установить верхний 32-разрядный бит в 0, эта зависимость мгновенно исчезнет, ​​и необходимость в объединении больше не будет. Деформация 9 скорость исполнения.

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