155

インテルマニュアルのx86-64ツアーで、私は読んだ

おそらく最も驚くべき事実は、レジスタMOV EAX, EBXの上位32ビットを自動的にゼロにするなどの命令RAXです。

同じソースで引用されているIntelのドキュメント(手動の基本アーキテクチャの64ビットモードの3.4.1.1汎用レジスタ)には、次のように記載されています。

  • 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

4 に答える 4

120

私はAMDでも彼らのために話しているわけでもありませんが、同じようにやっていたでしょう。上位半分をゼロにすると、前の値への依存関係が作成されないため、CPUは待機する必要があります。レジスタリネーミングメカニズムは、そのように行われなかった場合、本質的に無効になります。

このようにして、依存関係を常に明示的に解除することなく、64ビットモードで32ビット値を使用して高速コードを記述できます。この動作がなければ、64ビットモードのすべての32ビット命令は、その上位部分がほとんど使用されない場合でも、以前に発生した何かを待機する必要があります。( 64ビットを作成intするとキャッシュフットプリントとメモリ帯域幅が無駄になります。x86-64は32ビットと64ビットのオペランドサイズを最も効率的にサポートします

8ビットおよび16ビットのオペランドサイズの動作は奇妙なものです。依存関係の狂気は、16ビット命令が現在回避されている理由の1つです。x86-64は、これを8ビットの8086および16ビットの386から継承し、8ビットおよび16ビットのレジスタを64ビットモードでも32ビットモードと同じように動作させることを決定しました。


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

于 2012-06-24T11:53:08.200 に答える
12

命令と命令セットのスペースを節約するだけです。既存の(32ビット)命令を使用して、小さなイミディエート値を64ビットレジスタに移動できます。

また、再利用できるMOV RAX, 42場合は、の8バイト値をエンコードする必要がなくなります。MOV EAX, 42

この最適化は、8ビットおよび16ビットの操作ではそれほど重要ではありません(サイズが小さいため)。そこでルールを変更すると、古いコードも破損します。

于 2012-06-24T11:50:14.067 に答える
5

ゼロが64ビットに拡張されない場合、から読み取る命令はそのオペランドraxに2つの依存関係(書き込みを行う命令とその前に書き込む命令)を持つことを意味します。これにより、部分的なレジスタストールが発生し、取得が開始されます。可能な幅が3つある場合は注意が必要です。そのため、レジスタ全体に書き込むのに役立ちます。つまり、64ビット命令セットでは部分的な名前変更の新しいレイヤーが導入されません。raxeaxraxraxeax

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が含まれるようにすることです。たとえば、元々0​​xffffffffffffffffが含まれている場合、結果は0xffffffff00000007になりますが、ISAがこのような費用でこの保証を行う理由はほとんどありません。ゼロ拡張の利点が実際にはもっと必要になる可能性が高いので、余分なコード行を節約できますmov rax, 0。常にゼロ拡張されて64ビットになることを保証することにより、コンパイラーはこの公理を念頭に置いて作業できますがmov rdx, raxraxその単一の依存関係を待つだけで済みます。つまり、実行をより迅速に開始して終了し、実行ユニットを解放できます。さらに、 REXバイトを必要とせずxor eax, eaxにゼロにするようなより効率的なゼロイディオムも可能にします。rax

于 2020-03-31T19:20:08.923 に答える
2

ハードウェアの観点からは、レジスタの半分を更新する機能は常にいくらか高価でしたが、元の8088では、手書きのアセンブリコードで8088をスタックに関連しない2つの16ビットとして扱うことができると便利でした。レジスタと8つの8ビットレジスタ、6つの非スタック関連の16ビットレジスタとゼロの8ビットレジスタ、または16ビットと8ビットレジスタの他の中間の組み合わせ。そのような有用性は追加費用の価値がありました。

80386が32ビットレジスタを追加したとき、レジスタの上半分だけにアクセスする機能は提供されませんでしたが、のような命令はROR ESI,16、ESIと2つの16ビット値を保持できることに価値があるほど高速です。それらを切り替えます。

x64アーキテクチャへの移行に伴い、レジスタセットの増加とその他のアーキテクチャの強化により、プログラマが各レジスタに最大量の情報を詰め込む必要性が減少しました。さらに、レジスタの名前を変更すると、レジスタの部分的な更新を行うコストが増加しました。コードが次のようなことをする場合:

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

レジスタの名前変更と関連するロジックにより、ロード元の値[whatever]をに書き込む必要があるという事実をCPUに記録somethingさせ、最後の2つのアドレスが異なる限り、ロードを許可しsomethingElseyetAnotherデータが実際に読み取られるのを待たずに処理されますwhatever。ただし、 3番目の命令がmov eax,[somethingElse上位ビットに影響を与えないように指定されている場合、4番目の命令は最初のロードが完了するまでRAXを格納できずEAX、プロセッサがプロセッサであるため、ロードさえも発生させることは困難です。下半分は使用可能でしたが、上半分は使用できなかったという事実を追跡する必要があります。

于 2021-04-26T18:59:33.537 に答える