x86はCISC命令セットであり、RISC命令セットと異なりメモリ上のデータを命令のオペランドに取ることができます。 x86のレジスタは(特に32ビットと64ビットは)おおよそ汎用ですが、一部のレジスタはアドレッシングに使えない制限があります。 よくわからなくなるのでまとめてみました。
16ビット
レジスタは全く汎用ではありません。基本的にはAX、CX、DXを算術演算に使い、BX、SI、DIをアドレス計算に使う、ということが想定されているようです。
基本的には、以下のアドレッシングモードが使えます。
[addr + offset]addrは、以下の八種類BXBPSIDIBX+SIBX+DIBP+SIBP+DI
offsetは、以下の三種類- (何も足さない)
imm8imm16
[imm16]
ただし、このうち[BP](つまり、addrがBPで、offsetが「何も足さない」)は、そのエンコーディングが絶対16ビットアドレス指定に奪われているので、使えません。
もちろん、[BP+0]とすることで、1バイト増えてしまいますがエンコーディングすることは可能です。
なお、BPをフレームポインタとして使っている場合、[BP]に入っているのはリターンアドレスのはずなので、普通は使わないと思います。
32ビット
レジスタはほぼ汎用になりました。 基本的には、以下のアドレッシングモードが使えます。
[addr + offset]addrは、以下の七種類EAXEBXECXEDXESIEDIEBP
offsetは、以下の三種類- (何も足さない)
imm8imm32
[imm32][addr + index * scale + offset](SIBバイト使用)addrは、以下の八種類EAXEBXECXEDXESIEDIESPEBP
indexは、以下の八種類EAXEBXECXEDXESIEDI- (零)
EBP
scaleは、以下の四種類1248
offsetは、以下の三種類- (何も足さない)
imm8imm32
[imm32 + index * scale](SIBバイト使用)
16ビットの時からの伝統で、[EBP](つまり、addrがEBPで、offsetが「何も足さない」)は、そのエンコーディングが絶対32ビットアドレス指定に奪われているので、使えません。
16ビットの時同様、[EBP+0]とすることで、1バイト増えてしまいますがエンコーディングすることが可能です。
SIBバイト利用アドレッシングでは、[EBP + index * scale](つまり、addrがEBPで、offsetが「何も足さない」)は、そのエンコーディングが絶対32ビットベースアドレス指定に奪われているので、使えません。
同様に、[EBP + index * scale + 0]とすることで、1バイト増えてしまいますがエンコーディングすることが可能です。
indexがEBPではなくscaleが1の場合は、逆順にすればバイト数増加はありません。
八つの汎用レジスタの一員であるにもかかわらず、[ESP + offset]は三種類とも、そのエンコーディングがSIBバイト利用アドレッシングに奪われているため、使えません。
これをやりたい場合は、SIBバイト利用アドレッシングの中にある[ESP + (零)* scale + offset]を使うことで、1バイト増えてしまいますがエンコーディングできます。
scaleは、1を設定するのが一般的です。
ESPは、そのエンコーディングが零レジスタ指定に奪われているので、indexとして指定することができません。
とはいえ、ESPを2倍した値というのが意味を持つ局面はないはずなので、通常は問題となりません。
なお、零レジスタを2倍・4倍・8倍する、というのも意味がないものであり、エンコーディングに無駄があります。
64ビット
汎用レジスタは16本に増えました。 アドレッシングモードは32ビットとほぼ変わりませんが、以下の三点に注意する必要があります。
[imm32]がなくなり、代わりに[RIP + imm32]ができるようになった[imm32 + index * scale]は残存
RSPの“裏レジスタ”であるR12は、ModR/MでRSPが使えないことを引き継いでいるindexにRSPが使えない制限は引き継いでいない
RBPの“裏レジスタ”であるR13は、(ModR/Mとbaseで)RBPでできないことを引き継いでいるindexではRBPは自由に使える
つまり、直観的でない点は、
[RIP + index * scale + offset]はできない[R12]は[R12 + (零)*1]で表現されるので1バイト長い[R12 + offset]は[R12 + (零)*1 + offset]で表現されるので1バイト長い[R13]は[R13 + 0]で表現されるので1バイト長い[R13 + index * scale]は[R13 + index * scale + 0]で表現されるので1バイト長いscaleが1の場合は、逆順にすると短くできることがある
また、エンコーディングの無駄な点(rex.bが0か1かで意味が変化しない)として、
が挙げられます(まぁそもそもrex.xはSIBバイトを使わない場合、rex.bはメモリオペランドを取らない場合、rex.wはバイト命令の場合、は無意味なのですが)。
まとめ
- 16ビットの時は、アドレッシングに使えないレジスタが存在した
- 32ビット以降では、基本的にすべてのレジスタがアドレッシングに使える
- しかし、特定のレジスタを使うアドレッシングは、命令長が伸びることがある
[BP]、[EBP]、[RBP]は1バイトのびる(しかし、フレームポインタとして使っているならばそこに入っているのはリターンアドレスなので、あまり使われない)[EBP + index * scale]、[RBP + index * scale]は1バイトのびる(しかし、フレームポインタとして使っていて、indexが素直な配列インデクスであれば、このパターンは出てこないはず)[ESP]、[ESP + offset]、[RSP]、[RSP + offset]のようにESPかRSPがmodR/Mで指定されるとベースだと1バイトのびる(これはよく出てくる)ESPやRSPはindexには使えない(使われることはないはず)
- 64ビットでは、“裏レジスタ”の関係にあるレジスタが
EBPやESPのModR/Mとbaseにおける特性を引き継いでいるindexでは制約はない[R12]、[R12 + offset]、[R13]、[R13 + index * scale]は1バイトのびる