Toukaを使用するときの流れを以下に設計していく。
まずは、データを入力するときの元データを作成する。元データの作成はx86上で行う。
例えば、以下の _mm_add_ps をテスト対象とする場合を考える。
/* Perform the respective operation on the four SPFP values in A and B. */
extern __inline __m128 __attribute__((__gnu_inline__, __always_inline__,
__artificial__))
_mm_add_ps (__m128 __A, __m128 __B)
{
return (__m128) ((__v4sf)__A + (__v4sf)__B);
}
以上のソースコードにおいて、__Aと__Bに入力する値のセットをランダムに生成していく。生成したデータは以下の構造体を使ってバイナリファイルに保存する。
typedef struct{
__m128 __A;
__m128 __B;
}TOneIntrinsicOp;
#define MAXTOUKAVERSIONSTRING 200
#define MAXTOUKAINTRINSICOP 2000
#define MAXCOMMENTLENGTH 500
typedef enum {
ENDIAN_LITTLE = 0,
ENDIAN_BIG = 1
} EEndianType;
typedef struct{
EEndianType EndianMode;
char version[MAXTOUKAVERSIONSTRING];
TOneIntrinsicOp intrinsicop[MAXTOUKAINTRINSICOP];
char comment[MAXCOMMENTLENGTH];
}TOneToukaTestOp;
それらの同じファイルを、Intel x86とOpenPOWERのToukaに入力し、各環境での実行結果をログファイルなどとしてテキスト化して保存する。
x86とOpenPOWERにて生成されたテキストを同じPC上に置き、diffを取る。もしも出力値にズレがあった場合、diffによってズレは検出される。
OpenPOWERはローカル上で、qemuで動かすことが出来る。最終的には実機でテストしたい。
ToukaはすべてC++で作られる。
以下新たに出てきた課題
qNaNのビットパターンがアーキテクチャによって異なるので、そこも考えないといけない。
x86 (Intel/AMD): 0xFFC00000(符号ビットが 1 になることが多い)
ARM / OpenPOWER: 0x7FC00000(符号ビットが 0 になることが多い)
Nanの伝播パターンがアーキテクチャによって異なる。
「普通の数値 $A$」と「qNaN」を演算(例: $A + \text{qNaN}$)させた場合、結果はqNaNになる。しかし、「異なるペイロードを持つ NaN同士」を演算させた場合、どちらのビットパターンを引き継ぐかはCPU(またはコンパイラの最適化)に依存する。特に、MAX/MIN命令(例: _mm_max_ps)にNaNを渡したときの挙動は歴史的に非常に複雑で、x86とARM(NEON/SVE)で結果が真逆になるケースがある(x86は「片方がNaNならもう片方の数値を返す」挙動をする場合があるなど)。(ここは生成AIのをコピペしました)
以上事実関係の要確認と要解決。実機やqemuを動かして確認する必要があるかもしれない。