SX-Aurora TSUBASAでvector intrinsicを使ってみる２：マスク編

## はじめに

今回はマスクです。256要素のベクトル演算に対し、演算するかどうかの有効無効を各要素ごとに指定できます。それがマスクです。

- 仕様書を色々読むと、256bitのマスクレジスタがある
- intrinsicでは`__vm256`というマスク型の変数を使う

とありますが、当然のように`__vm256`なんて型は標準ではありませんし、コンパイルしても使えません。

定義のヘッダーファイルもありません！

定義がwebのintrinsicチュートリアルにもありません！

マニュアル何てものもありません！

ベクトル変数`__vr`の真似をして色々書いてみても定義が間違っているといってコンパイル通りません。

マスク大尉「ベルリーーーー！！！！！！！」

ですよほんとに。(Gレコ面白いから見てみてください。ホント面白いから。)

## 見つけた

webのtutrial等では頻繁に登場する`velintrin.h`という謎のインクルードファイル。はい当然コンパイラ4.0.0というオフィシャルには入っていませんが、これを見つけられれば`__vm256`の定義が書いてありそうです。

次のサイトに見つかりました

[https://clang.llvm.org/doxygen/velintrin_8h_source.html](https://clang.llvm.org/doxygen/velintrin_8h_source.html)

ここによれば、マスク型`__vm256`の定義は次らしい
```c++
※オチまで読んでください。これは通りません
// Vector mask registers
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
 // For C99
 typedef _Bool __vm    __attribute__((ext_vector_type(256)));
 typedef _Bool __vm256 __attribute__((ext_vector_type(256)));
 typedef _Bool __vm512 __attribute__((ext_vector_type(512)));
 #else
 #ifdef __cplusplus
 // For C++
 typedef bool __vm    __attribute__((ext_vector_type(256)));
 typedef bool __vm256 __attribute__((ext_vector_type(256)));
 typedef bool __vm512 __attribute__((ext_vector_type(512)));
 #else
 #error need C++ or C99 to use vector intrinsics for VE
 #endif
 #endif
```

こんなもん勘でどれだけ試行錯誤してもたどり着けんわ。`ext_vector_type`って魔法の命令知らないと定義できないじゃん。

それで上記の定義をソースに書いてコンパイルして見ると、エラーメッセージが
```c++
$ nc++ -std=c++17 test2_mask.cpp
"test2_mask.cpp", line 16: error: the "ext_vector_type" attribute applies only
          to integer or floating-point types
  typedef bool __vm    __attribute__((ext_vector_type(256)));
                                      ^

"test2_mask.cpp", line 16: error: invalid redeclaration of type name "__vm"
  typedef bool __vm    __attribute__((ext_vector_type(256)));
               ^

"test2_mask.cpp", line 17: error: the "ext_vector_type" attribute applies only
          to integer or floating-point types
  typedef bool __vm256 __attribute__((ext_vector_type(256)));
                                      ^

"test2_mask.cpp", line 18: error: the "ext_vector_type" attribute applies only
          to integer or floating-point types
  typedef bool __vm512 __attribute__((ext_vector_type(512)));
                                      ^

4 errors detected in the compilation of "test2_mask.cpp".

```

コンパイル通らないんですよね。そう、件の`ext_vector_type`はintかdoubleに使えというんですね、boolには使うなと。

  

マスク大尉「ベルリーーーーーーー！！！」

  

ただ、定義は`__vm``__vm256``__vm512`の3件なのに、よく見るとエラーメッセージは4件です。その差は、
```c++
error: invalid redeclaration of type name "__vm"
```
え？もしかして`__vm`はデフォルトで定義されてるの？？

コンパイルしてみる
```c++
int main(){
    __vm a;
    return 0;
}
```
通った！！

さらに、`velintrin.h`を信じれば`__vm`と`__vm256`は同じサイズです。ということで、

結論：マスク型としては`__vm`を使いましょう。

## マスクを使った演算

前回のロードとストアのテストにマスク処理を加えます。元の配列に-1.0か1.0のどちらかをセットしベクトル型aに詰めます。その値が正の時だけmaskビットを立たせます。そして、aから0で埋めたバッファbに書き戻しますが、このときにマスクが立っているものだけ書き戻します。ソースは次になります。
```c++
#include <cstdio>

//the definition of vector type. 2048 means 8 byte x 256 //
typedef double __vr __attribute__((__vector_size__(2048)));  

//the definition of mask type. but the following definitions cannot be compiled//
//please use __vm without user definition, which is defined by default.
//typedef bool __vm    __attribute__((ext_vector_type(256)));
//typedef bool __vm256 __attribute__((ext_vector_type(256)));
//typedef bool __vm512 __attribute__((ext_vector_type(512)));


enum class _compare :int{
    _EQ,        /* src == 0 */
    _NE,        /* src != 0 */
    _GT,        /* src >  0 */
    _GE,        /* src >= 0 */
    _LT,        /* src <  0 */
    _LE,        /* src <= 0 */
    _EQNAN,     /* src == 0 or src == NaN */
    _NENAN,     /* src != 0 or src == NaN */
    _GTNAN,     /* src >  0 or src == NaN */
    _GENAN,     /* src >= 0 or src == NaN */
    _LTNAN,     /* src <  0 or src == NaN */
    _LENAN,     /* src <= 0 or src == NaN */
    _NAN,       /* src == NaN */
    _NUM        /* src != NaN */
};


int main(){
    constexpr int n = 256;

    double b[n]={0.0};

    for (int i = 0; i < n; ++i) {
        b[i] = (double)((i%2)*2) - 1.0;
    }

    //definition of vector type variable a//
    __vr a;

    //load to vector type a from double array b//
    __builtin_ve_vld(a, b, 8);

    //defenition of mask type mask//
    __vm mask;

    //stand mask bit if a > 0//
    __builtin_ve_vfmk(mask, a, (int)(_compare::_GT));

    // clear b
    for (int i = 0; i < n; ++i) {
        b[i] = 0.0;
    }

    //set from vector type a to double array b//
    __builtin_ve_vst(a, b, 8, mask);

    //if the vector opeartion is correctly used, the numbers of 0.0 and 1.0 are repeated.//
    for (int i = 0; i < n; ++i) {
        printf("b[%d] = %f\n", i, b[i]);
    }

    return 0;
}

```

上手く動作すると、`0.0`と`1.0`が繰り返し表示されます。

あらためて、`__vm`はユーザーで定義しなくても使えます。

`enum class _compare :int`のところは、比較操作の種類をしているする定数です。`_vector.h`とenumの書き方だけ変えていますが、順番は変えていません。ベクトル命令の方は結局整数で指定して欲しいようなので、順番を変えると整数値も変わってしまいます。

次の部分でマスク型の変数`mask`を定義しています。
```c++
//defenition of mask type mask//
    __vm mask;
```

次の部分で、ベクトル変数`a`の各要素に対して比較演算を行い、要素が0より大きいなら`mask`の該当bitを立てます。
```c++
    //stand mask bit if a > 0//
    __builtin_ve_vfmk(mask, a, (int)(_compare::_GT));
```
ここで`_GT`以外の演算を指定すれば、比較条件を変更できます。

最後に、次の部分でベクトル変数`a`から通常の配列`b`に値を書き戻します。
```c++
    //set from vector type a to double array b//
    __builtin_ve_vst(a, b, 8, mask);
```
このとき、命令`__builtin_ve_vst`自体は前回のストア命令と同じですが、今回は第四引数に`mask`変数をセットしています。これにより、**マスクのbitが立っている要素だけで書き込みが行われます**。

これでベクトル変数の比較からマスクを作れますね。

## 結論

マスク型としては`__vm`を使いましょう。

## おわりに

定義はしておいて欲しいよね。マニュアル欲しいよね。

マスク大尉「ベルリーーーーーー！！」

Gレコ面白いから見てね。あ、でもクリスマスだからポケットの中の戦争かな。

メリークリスマス！