FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-Vベクトル拡張仕様書 v1.0 を読み直す (22. ビットマスク命令)

RISC-Vベクトル拡張仕様書の読み直し。次はマスクを操作するための命令。論理命令など。

github.com

github.com


15.2. ベクトルマスクPopカウント vpopc

    vpopc.m rd, vs2, vm

ソースオペランドは、 マスクレジスタのレイアウト 節で説明されているように、マスクレジスタの値を保持する単一のベクトルレジスタです。

vpopc.m命令は,ベクトルソースマスクレジスタのアクティブ要素のうち、値が1であるマスク要素の数を数え、その結果をスカラのxレジスタに書き込む。

この操作はマスク下で行うことができ、その場合はマスクされた要素のみがカウントされます。

 vpopc.m rd, vs2, v0.t # x[rd] = sum__i ( vs2.mask[i] && v0.mask[i] )

vpopc.m 上の例外は、常に vstart が 0 で通知されます。 vpopc 命令は、vstart が0でない場合、不正命令例外を発生させます。

15.3. vfirst find-first-set マスクビット命令

    vfirst.m rd, vs2, vm

vfirst 命令は,ソースマスクベクトルの中から、値1を持つ最も低い番号のアクティブな要素を見つけ、その要素のインデックスをGPRに書き込みます。 値が1のアクティブな要素がない場合は,-1が書き込まれます。

ベクトルの長さはどのような実装でも2(XLEN-1) を超えることはありませんので、ソフトウェアは負の値(最上位ビットの設定)があれば、 要素が見つからないと仮定することができます。

vfirst の例外は常に 0 の vstart で報告されます。vstart が 0 でない場合、vfirst 命令は不正命令例外を発生させます。

15.4. vmsbf.m set-before-firstマスクビット

    vmsbf.m vd, vs2, vm
 # 例

     7 6 5 4 3 2 1 0   要素番号

     1 0 0 1 0 1 0 0   v3 の値
                       vmsbf.m v2, v3
     0 0 0 0 0 0 1 1   v2 の値

     1 0 0 1 0 1 0 1   v3 の値
                       vmsbf.m v2, v3
     0 0 0 0 0 0 0 0   v2

     0 0 0 0 0 0 0 0   v3vmsbf.m v2, v3
     1 1 1 1 1 1 1 1   v2

     1 1 0 0 0 0 1 1   v0 の値
     1 0 0 1 0 1 0 0   v3 の値
                       vmsbf.m v2, v3, v0.t
     0 1 x x x x 1 1   v2 の値

vmsbf.m 命令は、マスク・レジスタを入力とし、結果をマスク・レジスタに書き込みます。 この命令は,1である最初のソース要素の前にあるすべてのアクティブなマスク要素に1を書き込み、 その要素とそれに続くすべてのアクティブな要素に0を書き込みます。 ソースベクトルにセットビットがない場合、書き込みレジスタのすべてのアクティブ要素に1が書き込まれます。

書き込みマスクレジスタの末尾要素はtail-agnosticポリシに基づいて更新されます。

vmsbf.m の例外は、常に vstart =0の状態で報告されます。 vmsbf 命令は、vstart が 0 でない場合、不正命令例外を発生します。

書き込みレジスタはソースレジスタをオーバラップすることは出来ません。 マスク付き命令の場合、v0マスクレジスタとオーバラップすることは出来ません。

15.5. vmsif.m set-including-first マスクビット命令

ベクトルset-including-first命令はset-before-first命令と似ていますが、 セットビットを含めるところが異なります。

    vmsif.m vd, vs2, vm
 # 例

     7 6 5 4 3 2 1 0   要素番号

     1 0 0 1 0 1 0 0   v3 の値
                       vmsif.m v2, v3
     0 0 0 0 0 1 1 1   v2 の値

     1 0 0 1 0 1 0 1   v3 の値
                       vmsif.m v2, v3
     0 0 0 0 0 0 0 1   v2

     1 1 0 0 0 0 1 1   v0 の値
     1 0 0 1 0 1 0 0   v3 の値
                       vmsif.m v2, v3, v0.t
     1 1 x x x x 1 1   v2 の値

書き込みマスクレジスタの末尾要素はtail-agnosticポリシに基づいて更新されます。

vmsif.m の例外は、常に vstart =0の状態で報告されます。 vmsif 命令は、vstart が 0 でない場合、不正命令例外を発生します。

書き込みレジスタはソースレジスタをオーバラップすることは出来ません。 マスク付き命令の場合、v0マスクレジスタとオーバラップすることは出来ません。

15.6. vmsof.m set-only-first マスクビット命令

set-only-firstベクトル命令はset-before-first命令と似ていますが、 ビットがセットされている最初の要素のみを設定するところが異なります。

    vmsof.m vd, vs2, vm
 # 例

     7 6 5 4 3 2 1 0   要素番号

     1 0 0 1 0 1 0 0   v3 の値
                       vmsof.m v2, v3
     0 0 0 0 0 1 0 0   v2 の値

     1 0 0 1 0 1 0 1   v3 の値
                       vmsof.m v2, v3
     0 0 0 0 0 0 0 1   v2

     1 1 0 0 0 0 1 1   v0 の値
     1 1 0 1 0 1 0 0   v3 の値
                       vmsof.m v2, v3, v0.t
     0 1 x x x x 0 0   v2 の値

書き込みマスクレジスタの末尾要素はtail-agnosticポリシに基づいて更新されます。

vmsof.m の例外は、常に vstart =0の状態で報告されます。 vmsof 命令は、vstart が 0 でない場合、不正命令例外を発生します。

書き込みレジスタはソースレジスタをオーバラップすることは出来ません。 マスク付き命令の場合、v0マスクレジスタとオーバラップすることは出来ません。

15.7. ベクトルマスク命令の使用例

以下はベクトル化されたデータに依存するループ終了コードです。

15.8. ベクトルIota命令

viota.m 命令は,ソースベクトルマスクレジスタを読み込み、書き込みベクトルレジスタグループの各要素に マスク・レジスタの要素のうち、インデックスがその要素よりも小さい要素のすべてのビットの合計、 すなわち、マスク値のパラレルプレフィックス和を書き込みます。

この命令はマスクを使用することができます。この場合アクティブな要素のみが 加算に使用されます。

 viota.m vd, vs2, vm
 # 例

     7 6 5 4 3 2 1 0   要素数

     1 0 0 1 0 0 0 1   v2 の値
                       viota.m v4, v2 # Unmasked
     2 2 2 1 1 1 1 0   v4 の結果

     1 1 1 0 1 0 1 1   v0 の値
     1 0 0 1 0 0 0 1   v2 の値
     2 3 4 5 6 7 8 9   v4 の値
                       viota.m v4, v2, v0.t # Masked, vtype.vma=0
     1 1 1 5 1 7 1 0   v4 の値

SEWが結果の値よりも大きい場合は、結果値をゼロ拡張して出力要素を埋めます。 結果値が出力先のSEWをオーバーフローする場合は、最下位のSEWビットが保持されます。

viota.m の例外は、常に vstart =0として報告され、例外ハンドラの後に再開するときは、常に最初から実行が再開されます。 vstart が 0 でない場合は、不正命令例外が発生します。

書き込みレジスタグループはソース・レジスタと重なることはできず、マスクされている場合はマスク・レジスタ (v0) と重なることはできません。

Note: これらの制約は2つの理由で存在します。 1つ目の理由は、時間的に長いベクタ・レジスタを持ち、ベクタ・レジスタのリネームを行わない実装において、WARハザードの回避を容易にするためです。 第二に、トラップが単純化された後に実行を再開することを可能にするためです。 viota.m 命令は、メモリ・スキャッタ命令(インデックス・ストア)と組み合わせて、 ベクトル圧縮機能を実行することができます。

 # 入力メモリ配列から、非ゼロの要素を圧縮して出力メモリ配列に格納する
    #
    # size_t compact_non_zero(size_t n, const int* in, int* out)
    # {
    #   size__t i;
    #   size__t count = 0;
    #   int *p = out;
    #
    #   for (i=0; i<n; i++)
    #   {
    #       const int v = *in++;
    #       if (v != 0)
    #           *p++ = v;
    #   }
    #
    #   return (size__t) (p - out);
    # }
    #
    # a0 = n
    # a1 = &in
    # a2 = &out

compact_non_zero:
    li a6, 0                      # 非ゼロ要素のカウンタをクリアする
loop:
    vsetvli a5, a0, e32, m8, ta, ma   # 32-bit整数
    vle32.v v8, (a1)               # 入力ベクトルをロードする
      sub a0, a0, a5               # ロードした要素数を減算する
      slli a5, a5, 2               # 4倍する
    vmsne.vi v0, v8, 0             # 非ゼロの場所を特定する
      add a1, a1, a5               # 入力ポインタを進める
    vpopc.m a5, v0                 # v0中の非ゼロ値を数える
    viota.m v16, v0                # アクティブ要素の出力オフセットを取得する
      add a6, a6, a5               # 要素数を加算する
    vsll.vi v16, v16, 2, v0.t      # オフセットを4バイト分乗算する
      slli a5, a5, 2               # 非ゼロの要素の数を4バイト分乗算する
    vsuxei32.v v8, (a2), v16, v0.t # スケールしたviotaの結果をマスクに基づいてメモリに書き込む
      add a2, a2, a5               # 出力ポインタを進める
      bnez a0, loop                # これ以上あるか?

      mv a0, a6                    # カウント数を返す
      ret

vid.v 命令は、各要素のインデックスを、0からvl-1までの書き込みベクトルレジスタグループに書き込みます。

    vid.v vd, vm  # 要素のインデックスを書き込みレジスタに書き込む

この命令はマスクを使用することができます。

この命令のvs2フィールドはv0を設定しなければなりません、 そうでない場合のエンコーディングは 予約されています。

結果がSEWよりも小さいビット幅である場合、ゼロ拡張して書き込み要素に書き込まれます。 結果がSEWよりも大きい場合、下位のSEWビットが保持されます。

Note: マイクロアーキテクチャ上では、vid.v 命令は viota.m 命令と 同じデータパスを使用して、暗黙的なマスクソースを使用することで実装することができます。