IntelからOpenPOWERにポートする作業について記述してきたが、一つ必要なものがある。C言語において、2つの関数が同じ入力なら同じ結果を返すことを保障するテストフレームワークである。
例えば、以下のソースコードを見て欲しい。
extern __inline __m128d __attribute__((__gnu_inline__, __always_inline__,__artificial__))
_mm_add_pd (__m128d __A, __m128d __B)
{
return (__m128d) ((__v2df)__A + (__v2df)__B);
}
このソースコードは、_mm_add_pdというIntelのIntrinsic関数をOpenPOWERにポートしたものだが、このポートには一つ問題がある。
このソースコードは本当に正確な結果を返すのだろうか。
1つ2つ動作確認しても本当に正しいかどうかわからない。
ここで必要なものは、ランダムに生成した値をIntelとOpenPOWERの__Aと__Bに入力し、帰ってきた値がIntelとOpenPOWERで同じであるかどうかのチェックを自動的に行うテストフレームワークである。
今のところそういうテストフレームワークは存在しない。
今回は__Aにはいる型はベクトル型である。Intel固有の型であるため、OpenPOWERやRISC-Vでは構造体として定義してやる必要がある。
以下はちょっと違う型であるが、OpenPOWERでは以下のように定義する。
/* Internal data types for implementing the AVX in PowerISA intrinsics. */
typedef struct __v4df
{
__vector double vd0;
__vector double vd1;
} __vx4df;
/* The Intel API is flexible enough that we must allow aliasing with other
vector types, and their scalar components. */
typedef struct __m256d
{
__vector double vd0;
__vector double vd1;
}__attribute__ ((__may_alias__)) __m256d;
Intel側で、__m128dをテキストに変換してファイルとして格納し、OpenPOWERやRISC-V側でC言語の型に戻す処理が必要となる。ネットワークでリアルタイムにつなぐ必要はなく、ファイル転送で十分対応できる。
定義そのものが違うのと、アラインメントも考えないといけないので、一筋縄では行かない。
あと、Intel側で例外が出たときも、理想的にはOpenPOWER及びRISC-Vで同じ例外が出るのが良いので、Intelで作るファイルに例外が出たことを記録しておくのが良い。そこでテストが止まるのはよくない。
Intelで「ある程度」ランダムに値発生させて、値を場合によってはフィルターして、IntelのIntrinsic関数に入れて、戻り値と入力値のペアの集合をファイルに保存する。例外が発生した時は、例外が発生したことをファイルに保存して、次の値を、ファイルに格納する作業を再開する。
OpenPOWERおよびRISC-Vに同じIntelのIntrinsic関数をポートしたソースコードに値を入れて、ファイルに「例外が発生したよ」と書いてある場合は、同じ種類の例外が発生することを確認して、テストを続行。
ファイルはストレージに保存して移動。
C言語でIntelのIntrinsicをRISC-Vにポートしたモジュール作って、それにコマンドから値を受け渡す機構を作る。で、コマンドでそれをコントロールする機構をC言語かC言語以外で作る。
ということで、これからもやっていこうかと思う。