はじめに
x86が持ち合わせる汎用レジスタ(eax、ecxなど…)を覚えているでしょうか。これらを用いれば、32ビットまでの整数型の演算を行うことが可能です。ほとんどのプログラムはこれらの汎用レジスタを活用することでネイティブコード化を行うことが可能ですが、実は一つ二つ、不可能な問題もあるのです。
それは浮動小数演算。ネイティブコードで浮動小数演算を行う場合、汎用レジスタを使うわけにはいきません。そもそもx86の汎用レジスタは32ビットまでしか表現できないため、倍精度浮動小数点(64ビット)を扱うことができません。かろうじて単精度浮動小数点であれば汎用レジスタで値を保有することができますが、肝心な浮動小数点用の命令がx86の汎用レジスタ用には用意されていないのです。
そこで登場するのがSSE2になります。これらは64ビット・128ビットなど、汎用レジスタでは表現不可能な整数演算を高速に行ったり、浮動小数演算をサポートしてくれます。
実は浮動小数演算や並列処理が可能なマルチメディア用拡張命令セットはSSE2のほかに、x87 FPU、MMX、SSEなどが存在します(今後、AMD64アーキテクチャの定着と共にSSE2に一本化されていくと予想されています)。この他、SSE2をさらに拡張したSSE3が存在します。まずは、過去のこれらのCPU内部機構を振り返って解説を始めます。
CPU\拡張命令 | MMX | 3DNow! | SSE | SSE2 | SSE3 |
Intel MMX Pentium | ○ | - | - | - | - |
Intel Pentium II | ○ | - | - | - | - |
Intel Pentium III | ○ | - | ○ | - | - |
Intel Pentium 4 | ○ | - | ○ | ○ | - |
Intel Pentium 4(Prescott) | ○ | - | ○ | ○ | ○ |
Intel Pentium D | ○ | - | ○ | ○ | ○ |
AMD K6 | ○ | - | - | - | - |
AMD K6-2 | ○ | ○ | - | - | - |
AMD K6-III | ○ | ○ | - | - | - |
AMD Athlon | ○ | ○ | - | - | - |
AMD Athlon XP | ○ | ○ | ○ | - | - |
AMD Athlon 64 | ○ | ○ | ○ | ○ | - |
AMD Athlon 64 X2 | ○ | ○ | ○ | ○ | ○ |
x87 FPU
x87 FPUは、x86アーキテクチャが標準で搭載する命令セットの一つです。st(0)
~st(7)
までの8つのレジスタ(80ビット)が利用可能ですが、これらはスタック構造になっており、各レジスタに自由にアクセスすることはできません。命令セットに即値モードがないため、単純に値を代入するだけでもメモリアクセスを要します。
しかしながら、x86 CPU同士であれば環境依存の問題が生じないという大きなメリットも持ち合わせます。一般的なx86用Cコンパイラなどは、デフォルトでFPU用のネイティブコードを生成するようになっていることが多いようです。
MMX
MMX Pentium以降のIntel CPUに組み込まれる拡張命令セットです。FPUレジスタの64ビット分を活用し、8ビット整数8組、16ビット整数4組、32ビット整数2組に対する演算を同時に行うことができます。
ちなみに、MMX命令セットでは浮動小数演算はサポートされていません。あくまでも、複数の整数データを同時に扱うことで高速化を期待する技術であるということを押さえておきましょう。Intelが提供するマルチメディア用拡張命令セットで浮動小数演算が可能になるのはSSE以降ということになります。
MMXが発表されてから間もなく、AMDが32ビット浮動小数点データ2組を同時に扱うことができる3DNow!を発表することになります。
SSE
8本の128ビット レジスタ(xmm0
~xmm7
)、MMXテクノロジに70個の命令セットを追加したのがSSEです。32ビット浮動小数点データ4組に対する演算を同時に行うことができます。
SSE2
従来のSSEに114個の命令セットを追加し、64ビット浮動小数点データ2組に対する演算を同時に行えるようになったのがSSE2です。
AMD64/EM64-Tアーキテクチャでは、SSE/SSE2が必須となっています。
SSE3
SSE2に13個の命令セットを追加し、メモリアクセスおよび複素数計算の高速化が図られたのがSSE3です。
今回は、上記に挙げたCPU内部機構のうち、比較的新しく、x64で標準搭載されているSSE2について迫ります。
XMMレジスタ
SSE/SSE2では、128ビットのXMMレジスタを扱えます。XMMレジスタは8個用意されており、xmm0
~xmm7
という表記になります。ネイティブコードでは汎用レジスタ同様、下記のような値で識別されます。
レジスタ名 | 値(2進表記) |
xmm0 | 000 |
xmm1 | 001 |
xmm2 | 010 |
xmm3 | 011 |
xmm4 | 100 |
xmm5 | 101 |
xmm6 | 110 |
xmm7 | 111 |
AMD64の命令セットが扱える環境では、REXプリフィックスを用いることでxmm0
~xmm15
までの16個のxmmレジスタを扱うことが可能です。
SSE2で浮動小数点データを扱うための命令セット
単純に浮動小数演算を行うだけであれば、下記に列挙する命令セットで実現できます。
コピーと変換(キャスト)に関する命令セットは下記の通りです。
命令 | 説明 |
movss | 32ビット単精度実数のコピーを行います。 |
movsd | 64ビット倍精度実数のコピーを行います。 |
cvtss2sd | 32ビット単精度実数を64ビット倍精度実数に変換してコピーします。 |
cvtsd2ss | 64ビット倍精度実数を32ビット単精度実数に変換してコピーします。 |
cvttss2si | 32ビット単精度実数を整数値に変換して汎用レジスタにコピーします。 |
cvttsd2si | 64ビット倍精度実数を整数値に変換して汎用レジスタにコピーします。 |
cvtsi2ss | 汎用レジスタまたはメモリに格納されている整数値を32ビット単精度実数に変換してXMMレジスタにコピーします。 |
cvtsi2sd | 汎用レジスタまたはメモリに格納されている整数値を64ビット倍精度実数に変換してXMMレジスタにコピーします。 |
四則演算に関する命令セットは下記の通りです。
命令 | 説明 |
addsd | 64ビット倍精度実数の加算を行います。 |
addss | 32ビット単精度実数の加算を行います。 |
subsd | 64ビット倍精度実数の減算を行います。 |
subss | 32ビット単精度実数の減算を行います。 |
mulsd | 64ビット倍精度実数の乗算を行います。 |
mulss | 32ビット単精度実数の乗算を行います。 |
divss | 32ビット単精度実数の除算を行います。 |
divsd | 64ビット倍精度実数の除算を行います。 |
比較演算に関する命令セットは下記の通りです。
命令 | 説明 |
comiss | 32ビット倍精度実数の比較を行い、結果をEFLAGSに反映させます。 |
comisd | 64ビット倍精度実数の比較を行い、結果をEFLAGSに反映させます。 |
これらの命令を組み合わせて利用すれば、大抵の浮動小数演算が可能です。