1. ホーム
  2. assembly

[解決済み] 32ビットレジスタに対するx86-64命令は、なぜフル64ビットレジスタの上部をゼロにするのですか?

2022-04-28 14:59:48

質問

インテルマニュアルx86-64ツアー を読みました。

<ブロッククオート

おそらく最も驚くべき事実は、次のような命令があることです。 MOV EAX, EBX の上位32ビットを自動的にゼロにします。 RAX レジスタを使用します。

同ソースから引用したインテルのドキュメント(3.4.1.1 Manual Basic Architectureにおける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 の上位ワードがゼロになるという、このような "strange" な動作は表示しないようにしてください。

このように、この挙動が導入された理由は何でしょうか。一見、非論理的なように思えますが(しかし、その理由は私がx86-32のアセンブリの癖に慣れているからかもしれません)。

解決方法は?

私はAMDでもなければ、彼らの代弁者でもありませんが、私なら同じようにしたでしょう。なぜなら、ハイ・ハーフをゼロにすることで、CPUが待機しなければならないような、前の値への依存性が生まれないからです。その レジスタ名変更 の仕組みは、そのようにしなければ本質的に破綻してしまいます。

こうすることで、常に依存関係を明示的に解除することなく、64ビットモードで32ビット値を使った高速なコードを書くことができるのです。この動作がなければ、64ビットモードでの32ビット命令のひとつひとつが、その高い部分はほとんど使われないにもかかわらず、前に起こった何かを待つ必要があります。 (作成 int 64ビットでは、キャッシュ・フットプリントとメモリ帯域幅を浪費することになります。 x86-64は32ビットと64ビットのオペランドサイズを最も効率的にサポートします。 )

8ビットと16ビットのオペランドサイズに対する挙動は、奇妙なものです。x86-64では、8ビットの8086と16ビットの386からこれを継承し、64ビットモードでも8ビットと16ビットのレジスタを32ビットモードと同じように動作させることにしたのです。


参照 なぜ、GCCは部分レジスタを使わないのですか? 8ビットと16ビットのパーシャルレジスタへの書き込み(およびその後のフルレジスタの読み出し)が実際のCPUでどのように処理されるかの実用的な詳細については、こちらをご覧ください。