web-dev-qa-db-ja.com

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に何かを書き込もうとすると、raxレジスタの上位32ビットが消去されます。

だから、私は2つの質問があります:

  1. この厄介な動作はどこかに文書化する必要があると思いますが、詳細な説明(64ビットレジスタの32ビットが正確に消去される方法)はどこにも見当たりません。 eaxへの書き込みは常にraxをワイプしますか、それとももっと複雑なことですか?すべての64ビットレジスタに適用されますか、それとも例外がありますか?

    強く関連した質問 は同じ振る舞いについて言及していますが、残念ながら、ドキュメントへの正確な参照はありません。

    つまり、この動作を指定するドキュメントへのリンクが必要です。

  2. それは私だけですか、この全体は本当に奇妙で非論理的です(つまり、eax-ax-ah-al、rax-ax-ah-alには1つの動作があり、rax-eaxには別の動作があります)?なぜそれがそのように実装されたのかについて、ここで何らかの重要なポイントを見逃しているのでしょうか?

    「理由」についての説明は大歓迎です。

67
GreyCat

Intel/AMDプロセッサマニュアルに記載されているプロセッサモデルは、最新のコアのreal実行エンジンのかなり不完全なモデルです。特に、プロセッサレジスタの概念は現実と一致せず、EAXまたはRAXレジスタのようなものはありません。

命令デコーダーの主な仕事の1つは、レガシーx86/x64命令をmicro-ops、RISCのようなプロセッサーの命令に変換することです。同時に実行しやすく、複数の実行サブユニットを活用できる小さな命令。最大6つの命令を同時に実行できます。

これを機能させるために、プロセッサレジスタの概念も仮想化されています。命令デコーダは、レジスタの大きなバンクからレジスタを割り当てます。命令が非推奨の場合、その動的に割り当てられたレジスタの値は、たとえばRAXの値を現在保持しているレジスタに書き戻されます。

それを円滑かつ効率的に動作させ、多くの命令を同時に実行できるようにするには、これらの操作に相互依存性がないことが非常に重要です。最悪の場合、レジスタ値は他の命令に依存します。 EFLAGSレジスタは悪名高く、多くの命令で修正されています。

like動作する方法と同じ問題。大きな問題は、命令が廃止されたときに2つのレジスタ値をマージする必要があることです。コアを詰まらせるデータ依存関係を作成します。上位32ビットを0に強制すると、その依存関係は即座に消え、マージする必要がなくなります。ワープ9の実行速度。

71
Hans Passant