web-dev-qa-db-ja.com

rbpとrspが汎用レジスタと呼ばれるのはなぜですか?

X64のIntelによると、次のレジスタは汎用レジスタ(RAX、RBX、RCX、RDX、RBP、RSI、RDI、RSP、およびR8-R15)と呼ばれます https://software.intel.com/en- us/articles/introduction-to-x64-Assembly

次の記事では、RBPとRSPは特別な目的のレジスタであると書かれています(RBPは現在のスタックフレームのベースを指し、RSPは現在のスタックフレームの最上部を指します)。 https://www.recurse.com/blog/7-understanding-c-by-learning-Assembly

今、私は2つの矛盾した声明を持っています。 Intelの声明は信頼できるものでなければなりませんが、何が正しいのか、なぜRBPとRSPが汎用と呼ばれているのですか?

助けてくれてありがとう。

18
Dennis

汎用とは、これらのすべてのレジスタを汎用レジスタで計算する命令で使用できることを意味します。たとえば、命令ポインタ(RIP)やフラグレジスタ(RFLAGS)で何でもできません。

これらのレジスタのいくつかは、特定の用途に使用されることが想定されており、一般的に使用されています。最も重要なのはRSPとRBPです。

目的に合わせて使用​​する必要がある場合は、中身を保存する前に内容を保存し、完了したら元の値に復元する必要があります。

16
jlliagre

レジスタがaddのオペランドになるか、アドレス指定モードで使用される場合、FSセグメントレジスタのようなレジスタとは対照的に、「汎用」 、またはRIP。 GPレジスタは「整数レジスタ」とも呼ばれますが、他の種類のレジスタも整数を保持できます。

コンピュータアーキテクチャでは、CPUがFP/SIMDレジスタ/命令とは別に整数レジスタ/命令を内部で処理するのが一般的です。例えば Intel Sandybridge-family CPUs GP整数対FP /ベクトルレジスタの名前を変更するための個別の物理レジスタファイルがあります。これらは単に整数と呼ばれます。FPレジスタファイル。(FPは、カーネルが保存/復元する必要のないすべての略記です。ユーザー空間のFPU/SIMD状態をそのままにしてGPレジスタを使用する場合。)FPレジスタファイルの各エントリは256ビット幅(AVX ymmベクトルを保持するため)ですが、整数レジスタファイルエントリは64ビット幅である必要があります。

セグメントレジスタの名前を変更するCPU( Skylakeはしない )では、それが整数状態の一部であり、RFLAGS + RIPの一部になると思います。しかし、「整数レジスタ」と言うとき、通常は汎用レジスタを意味します。


すべてのレジスターは、x86-64で追加された完全に新しいレジスターのいくつか(R8-R15)を除き、一部の命令に対して特殊性を備えています。これらは、一般目的として失格になりません。元の(日付の低い16)8086まで遡り、元の8086でもそれらの暗黙の使用がありました。

RSPの場合、Push/pop/call/ret専用であるため、ほとんどのコードはそれを他の用途に使用しません。 (そして、割り込みに非同期的に使用されるカーネルモードでは、ユーザー空間コードでできるように余分なGPレジスタを取得するためにどこかに隠しておくことはできません: Is ESP = EAXと同様に汎用?

ただし、制御された条件(シグナルハンドラがないなど)では、スタックポインタにRSPを使用する必要はありません。例えば このcode-golfの答え のように、popを​​使用してループで配列を読み取るために使用できます。 (実際に32ビットコードでespを使用しましたが、Skylakeではpoplodsdよりも高速ですが、両方とも1バイトです。)


各レジスタの暗黙的な使用と特殊性:

x86アセンブリ-呼び出し規則で[e] bxが保持されるのはなぜですか? も参照してください。

私はこれを主にユーザー空間の命令、特に最新のコンパイラが実際にCまたはC++コードから出力する命令に限定しています。暗黙的な使用が多いregを網羅するつもりはありません。

  • rax:ワンオペランド[i] mul/[i] div/cdq/cdqe、文字列命令(stos)、cmpxchgなど。2バイトcmp al, 1または5バイトのような多くの即時命令用の特別な短いエンコーディングadd eax, 12345(ModRMバイトなし)。 codegolf.SE x86/x64マシンコードでのゴルフのヒント も参照してください。

    0x90 nopがeXをRAXにゼロ拡張し、したがってxchg eax,eaxエンコードを使用できないため、0x90が由来する場所であるxchg- with-eaxもあります(xchg rax,raxはx86-64で個別に文書化された命令になる前のnopになりました。しかし、rdx:raxcanはまだREX.W = 1 0x90にアセンブルします。)

  • rcx:シフトカウント、 rep- string カウント、 遅いloop命令
  • rdxcmpxchg8bは、除算と乗算、およびcwd/cdq/cqoによって使用され、それらをセットアップします。 rdtscBMI2 mulx
  • rbx:8086 xlatbcpuidは、EAX..EDXの4つすべてを使用します。 486 cmpxchg16bx86-64 cmpxchg8 。ほとんどの32ビットコンパイラは、std::atomic<long long>::compare_exchange_weakに対してlock cmpxchgを出力します。 (純粋なロード/純粋なストアは、SSE MOVQまたはx87 fild/fistpを使用できますが、Pentium以降を対象とする場合)。64ビットコンパイラは、cmpxchg8bではなく64ビットcmpxchg16bを使用します。

    一部の64ビットコンパイラは、atomic<struct_16_bytes>に対してlock cmpxchg16bを出力します。 RBXには、元の8の暗黙的な使用が最も少ないが、rep movsbは、実際に使用する数少ないコンパイラの1つです。

  • rsi/rdi:一部のコンパイラーがインライン化する場合があるrep cmpsbを含む文字列演算。 (gccは、場合によっては文字列リテラルのmov rsp, rbpもインライン化しますが、おそらく最適ではありません)。
  • rbpleavepop rbp/pop rbpよりも1 uopだけ遅い。gccは、r11だけではいけないときに、フレームポインターを持つ関数で実際に使用します)。また、恐ろしく遅いenterは、誰も使用しません。
  • rsp:スタック操作:Push/pop/call/ret、およびleave。 (およびenter)。カーネルモード(ユーザー空間ではない)では、割り込みコンテキストを保存するためのハードウェアによる非同期使用。これが、カーネルコードにレッドゾーンを設定できない理由です。

  • r13syscall/sysretは、ユーザースペースのRFLAGSを保存/復元するために使用します。 (RCXとともに、ユーザー空間のRIPを保存/復元します)。

アドレス指定モードのエンコードの特殊なケース:

rbpはSIBベースとして許可されていませんか? も参照してください。これはアドレス指定モードに関するもので、この回答のこの部分をコピーしました。)

rbp/rel32は、変位のないベースレジスタにはできません。代わりにそのエンコードは、(ModRMの場合)disp32(RIP相対)、または(SIBの場合)ベースレジスタなしのr13を意味します。 ([r13]はModRM/SIBで同じ3ビットを使用するため、この選択は命令長デコーダが REX.Bビット を見て4番目のベースレジスタビットを取得しないようにすることで、デコードを簡素化します)。 [r13 + disp8=0][r13+rdx]にアセンブルします。 [rdx+r13]r12にアセンブルされます(オプションの場合はbase/indexを交換することで問題を回避します)。

ベースレジスタとしてのrsp/r12は、常にSIBバイトを必要とします。 (base = RSPのModR/Mエンコードは、SIBバイトを通知するエスケープコードです。また、[rsp]の処理が異なる場合、デコーダーの多くはREXプレフィックスを気にする必要があります)。

rspはインデックスレジスタにできません。これにより、[rsp + rsp]よりも便利な[eax + esp*4]をエンコードできます。 (Intelは32ビットアドレッシングモード用のModRM/SIBエンコーディング(386の新機能)を設計できたため、SIB-with-no-indexはbase = ESPでのみ可能でした。それは[esp + esp*1/2/4/8]を可能にし、r12のみを除外します。便利なので、ベースに関係なくindex = ESPをインデックスなしのコードにすることでハードウェアを簡素化しました。これにより、任意のベースまたはベース+ dispアドレッシングモードをエンコードする2つの冗長な方法が可能になります。

[rsp + r12*4]はインデックスレジスタになります。他の場合とは異なり、これは命令長のデコードには影響しません。また、他の場合のように長いエンコードでは回避できません。 AMDは、AMD64のレジスタセットを可能な限り直交させることを望んでいたので、インデックス/インデックスなしデコードの一部としてREX.Xをチェックするためにいくつかの余分なトランジスタを費やすことは理にかなっています。たとえば、r12にはindex = r12が必要です。したがって、 0: 41 8b 03 mov eax,DWORD PTR [r11] 3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP 7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP b: 41 8b 06 mov eax,DWORD PTR [r14] e: 41 8b 07 mov eax,DWORD PTR [r15] 11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index が完全に一般的な目的ではない場合、AMD64はより悪いコンパイラターゲットになります。

__コード__

コンパイラは、すべてのレジスタをあらゆるものに使用できる場合、少数の特殊なケースの操作に対してのみレジスタの割り当てを制約する場合に、それを好みます。これがレジスタの直交性の意味です。

8
Peter Cordes