1. ホーム
  2. assembly

[解決済み] x86_64 registers rax/eax/ax/al overwriting full register contents [duplicate] レジスタの内容をすべて上書きする。

2023-04-08 04:24:45

質問

広く宣伝されているように、最新の 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 レジスタの上位 32 ビットを消去することになります。

では、2点ほど質問させてください。

  1. この厄介な動作はどこかに文書化されているはずですが、(64 ビット レジスタの上位 32 ビットが正確にどのようにワイプされるのか)詳細な説明がどこにもないようです。私が正しいのは eax に書き込むと常に rax なのか、それとももっと複雑なものなのか?それはすべての 64 ビット レジスタに適用されるのでしょうか、それともいくつかの例外があるのでしょうか?

    A に強く関連する質問 は同じ動作に言及していますが、残念ながら、ドキュメントへの正確なリファレンスはまたありません。

    言い換えれば、私はこの動作を指定する文書へのリンクが欲しいのです。

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

    なぜなのかについての説明があれば、非常にありがたいです。

どのように解決するのですか?

Intel/AMD プロセッサーのマニュアルに記載されているプロセッサー モデルは、かなり不完全なモデルで 実質的な

の実行エンジンとしてはかなり不完全なモデルです。 特に、プロセッサ レジスタの概念は現実と一致しておらず、EAX または RAX レジスタのようなものは存在しません。

命令デコーダーの主な仕事の 1 つは、レガシー x86/x64 命令を マイクロオペレーションズ RISC ライクなプロセッサの命令です。 同時に実行するのが簡単な小さな命令で、複数の実行サブユニットを利用することができます。 同時に6つの命令を実行できる。

それを実現するために、プロセッサのレジスタの概念も仮想化されています。 命令デコーダは、レジスタの大きなバンクからレジスタを割り当てます。 命令が リタイア が終了すると、その動的に割り当てられたレジスタの値は、現在RAXの値を保持しているレジスタに書き戻されます。

この処理をスムーズかつ効率的に行い、多くの命令を同時に実行できるようにするためには、これらの処理に相互依存性がないことが非常に重要です。 そして、最悪のものは、レジスタの値が他の命令に依存することです。 EFLAGSレジスタは悪名高く、多くの命令がこれを変更します。

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