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が汎用と呼ばれているのですか?
助けてくれてありがとう。
汎用とは、これらのすべてのレジスタを汎用レジスタで計算する命令で使用できることを意味します。たとえば、命令ポインタ(RIP)やフラグレジスタ(RFLAGS)で何でもできません。
これらのレジスタのいくつかは、特定の用途に使用されることが想定されており、一般的に使用されています。最も重要なのはRSPとRBPです。
目的に合わせて使用する必要がある場合は、中身を保存する前に内容を保存し、完了したら元の値に復元する必要があります。
レジスタが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ではpop
はlodsd
よりも高速ですが、両方とも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:rax
canはまだREX.W = 1 0x90にアセンブルします。)
rcx
:シフトカウント、 rep
- string カウント、 遅いloop
命令rdx
:cmpxchg8b
は、除算と乗算、およびcwd/cdq/cqoによって使用され、それらをセットアップします。 rdtsc
。 BMI2 mulx
。rbx
:8086 xlatb
。 cpuid
は、EAX..EDXの4つすべてを使用します。 486 cmpxchg16b
、 x86-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
もインライン化しますが、おそらく最適ではありません)。rbp
:leave
(pop rbp
/pop rbp
よりも1 uopだけ遅い。gccは、r11
だけではいけないときに、フレームポインターを持つ関数で実際に使用します)。また、恐ろしく遅いenter
は、誰も使用しません。rsp
:スタック操作:Push/pop/call/ret、およびleave
。 (およびenter
)。カーネルモード(ユーザー空間ではない)では、割り込みコンテキストを保存するためのハードウェアによる非同期使用。これが、カーネルコードにレッドゾーンを設定できない理由です。
r13
:syscall
/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はより悪いコンパイラターゲットになります。
__コード__
コンパイラは、すべてのレジスタをあらゆるものに使用できる場合、少数の特殊なケースの操作に対してのみレジスタの割り当てを制約する場合に、それを好みます。これがレジスタの直交性の意味です。