m_sincos[t]
C式です。ただし、アセンブリ命令 ( __asm
?) では、x86 アドレッシング モードとして解釈され、まったく異なる結果になります。たとえば、VS2008 SP1 は次のようにコンパイルします。
movaps xmm0, m_sincos[t]
に: (アプリがデバッグ モードでクラッシュしたときに、逆アセンブリ ウィンドウを参照してください)
movaps xmm0, xmmword ptr [t]
その解釈は、変数のアドレスに格納されている 128 ビット値t
を xmm0 にコピーしようとします。 t
ただし、アラインされていない可能性が高いアドレスの 32 ビット値です。命令を実行すると、アラインメント エラーが発生する可能性が高く、t
のアドレスがアラインされている奇妙なケースでは、誤った結果が得られます。
これは、適切な x86 アドレッシング モードを使用して修正できます。遅いが明確なバージョンは次のとおりです。
__asm mov eax, m_sincos ; eax <- m_sincos
__asm mov ebx, dword ptr t
__asm shl ebx, 4 ; ebx <- t * 16 ; each array element is 16-bytes (128 bit) long
__asm movaps xmm0, xmmword ptr [eax+ebx] ; xmm0 <- m_sincos[t]
サイドノート:
これを完全なプログラムに入れると、奇妙なことが起こります。
#include <math.h>
#include <tchar.h>
#include <xmmintrin.h>
int main()
{
static __m128 *m_sincos;
int Bins = 4;
m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++) {
m_sincos[t] = _mm_set_ps(cos((float) t), sin((float) t), sin((float) t), cos((float) t));
__asm movaps xmm0, m_sincos[t];
__asm mov eax, m_sincos
__asm mov ebx, t
__asm shl ebx, 4
__asm movaps xmm0, [eax+ebx];
}
return 0;
}
これを実行するとき、レジスタ ウィンドウを監視していると、何かおかしいことに気付くかもしれません。結果は正しいですが、命令が実行される前xmm0
に正しい値を取得しています。それはどのように起こりますか?movaps
生成されたアセンブリ コードを見ると_mm_set_ps()
、sin/cos の結果が にロードされxmm0
、それが のメモリ アドレスに保存されていることがわかりますm_sincos[t]
。しかし、価値もそこに残っていxmm0
ます。_mm_set_ps
関数呼び出しではなく「組み込み」です。完了後に使用するレジスタの値を復元しようとはしません。
このことから得られる教訓があるとすれば、SSE 組み込み関数を使用するときは、それらをずっと使用して、コンパイラーが最適化できるようにすることです。それ以外の場合は、インライン アセンブリを使用している場合は、それも使用してください。