6. コンフィグレーション設定命令

アプリケーションの要求に応じてvlvtypeを簡単に設定できるような命令群が定義されている。

6.1. vsetvli/vsetvl 命令

vsetvli命令は引数に基づいてvtypevl CSRの値を設定し、新しいvlの値をrdに書き込む。

vsetvli rd, rs1, vtypei # rd = 新しいvlの値, rs1 = AVL, vtypei = 新しいvtypeの値
vsetvl  rd, rs1, rs2    # rd = 新しいvlの値, rs1 = AVL, rs2 = 新しいvtypeの値

vtypeの新しい設定値はvsetvliのフィールドの即値として設定され、vsetvl命令の場合はrs2レジスタで指定される。新しいベクトル長の設定はアプリケーションベクトル長(AVL)によって設定される。この値はrs1rdフィールドの値によって次のようにしてエンコーディングされる。

Table 7. vsetvliおよびvsetvl命令によって使用されるAVL値。

rd rs1 AVL値 説明・使用方法
0 0 vlレジスタ内の値 vlの値を変更することなくvtypeが変更される。
!0 0 ~0 vlはVLMAXに設定される。
!0 x[rs1]内の値 通常のストリップマイニング

rs1x0でない場合、AVLはrs1で指定された整数レジスタ内の符号なし整数であり、新しいvlの値はrdによって指定された整数レジスタに書き込まれる。

rs1=x0かつrd!=0の場合は、AVLの値は整数の最大サイズ(~0)が使用される。結果としてvlにはVLMAXが書き込まれ、rdにより指定された整数レジスタにも書き込みが行われる。

rs1=x0およびrd=0である場合、現在のベクトル長vlがAVLとして使用される。結果の値はvlにのみ書き込まれる。

この形式の命令はvlを変更することなくvtypeレジスタを変更することを許している。VLMAXの値は減る事は無い。現在のvl値はそのままvl CSRから読み込まれる。
OP-Vメジャーオペコードを使用したベクトルコンフィグレーション命令のフォーマット。

 31 30         25 24      20 19      15 14   12 11      7 6     0
 0 |        zimm[10:0]      |    rs1   | 1 1 1 |    rd   |1010111| vsetvli
 1 |   000000    |   rs2    |    rs1   | 1 1 1 |    rd   |1010111| vsetvl
 1        6            5          5        3        5        7
ビット 名前 説明
XLEN-1 vill 不正な値が設定されたことを示す。
XLEN-2:7   予約領域(0が書き込まれる)
6:5 vediv[1:0] EDIV拡張に使用される。
4:2 vsew[2:0] Standard element width (SEW) の設定。
1:0 vlmul[1:0] Vector register group multiplier (LMUL) の設定
 vtypei設定に使用される値のアセンブラで使用される名前。

 e8    #   8b elements
 e16   #  16b elements
 e32   #  32b elements
 e64   #  64b elements
 e128  # 128b elements

 m1   # Vlmul x1, mの指定がない場合はこの値が使用される。
 m2   # Vlmul x2
 m4   # Vlmul x4
 m8   # Vlmul x8

 d1   # EDIV 1, dの指定がない場合はこの値が使用される。
 d2   # EDIV 2
 d4   # EDIV 4
 d8   # EDIV 8

例:
    vsetvli t0, a0, e8          # SEW= 8, LMUL=1, EDIV=1
    vsetvli t0, a0, e8,m2       # SEW= 8, LMUL=2, EDIV=1
    vsetvli t0, a0, e32,m2,d4   # SEW=32, LMUL=2, EDIV=4

vtypeの設定値が実装によってサポートされていない場合、vtypeレジスタ内のvillビットが設定され、それ以外のvtypeレジスタのビットフィールドはゼロが設定され、vlレジスタの値もゼロに設定される。

初期の仕様ではvtypeに不正な値を設定すると例外が発生する仕様になっていた。ISAへのCSR書き込みに最初にデータ依存の例外を追加することになる。現在のスキームでは、特定の設定に対してvillがクリアされているかどうかを確認することによってベクトルユニットの設定の問い合わせを軽量化する手法をサポートしている。

6.2. vlへの設定条件

The vsetvl{i} instructions first set VLMAX according to the vtype argument, then set vl obeying the following constraints:

vsetvl{i}命令は、最初に引数のvtypeに基づいてVLMAXを設定し、以下が成り立つ。

  1. AVL VLMAXである場合、vl = AVLである。
  2. AVL < (2 * VLMAX)である場合、ceil(AVL / 2) vl VLMAX を設定する。
  3. AVL (2 * VLMAX)である場合、vl = VLMAXを設定する。
  4. AVLとVLMAXの値が同じならば、どのような実装でも同じ値が書き込まれる。
  5. 以下の制約は、上記の設定に基づいて常に成立する。
    1. AVL = 0の場合vl = 0が設定される。
    2. AVL > 0の場合、vl > 0である。
    3. vl VLMAX
    4. vl AVL
    5. vsetvl {i}のAVL引数として使用されたときに vlから読み取られた値は、結果のVLMAXがvlが読み取られた時点のVLMAXの値に等しい場合、 vlで同じ値になる。
vlの設定制約は、AVL VLMAXのレジスタスピルとコンテキストスワップ全体でvlの動作を維持するのに十分に厳格でありながら、AVL >  VLMAXのベクターレーン使用率を実装できるように十分に柔軟に設計されている。例えば、これにより、実装がVLMAX < AVL < 2 * VLMAXの場合にvl = ceil(AVL / 2)を設定して、ストリップマインループの最後の2回の繰り返しで作業を均等に分散できる。 要件2は、リダクションループの最初のストリップマイン反復が、AVL < 2 * VLMAXの場合でも、すべての反復の最大ベクトル長を使用することを保証する。 これにより、ソフトウェアはストリップループ中に観測されたベクトル長の最大値を明示的に計算する必要がなくなる。

6.3. vsetvl命令

vsetvl命令はvsetvli命令と同様な動作をするが、vtypeの設定がrs2レジスタの内容に基づいて設定されることが異なる。コンテキストのリストアに使用さたり、vtypeiの即値フィールドが所望の設定に対して小さすぎる場合に使用される。

いくつかのアクティブな複雑な型では、xレジスタに異なる値を設定する必要があり、必要に応じてvsetvl命令を使用してスワップする必要がある。

6.4. 例

SEWとLMULの設定値は、単一ループ中で動的に切り替えることが可能で、これによりデータ型の幅を混合させても高いスループットを達成することが可能となる。

# Example: 16ビットの値をロードし、32ビットの乗算を行い、その結果を3ビット右シフトし、32ビット値をストアする。

# 最大幅のエレメントのみ使用するループ。

loop:
    vsetvli a3, a0, e32,m8  # 32-bitの要素のみ使用する。
    vlh.v v8, (a1)          # 16ビットの符号付ロード値を32ビットのレジスタ要素に格納する。
      sll t1, a3, 1         # 2バイト分要素の幅を計算する。
      add a1, a1, t1        # ポインタを進める。
    vmul.vx  v8, v8, x10    # 32bの乗算結果を得る。
    vsrl.vi  v8, v8, 3      # 要素をシフトする。
    vsw.v v8, (a2)          # 32ビットの値をストアする。
      sll t1, a3, 2         # 4バイト分要素の幅を計算する。
      add a2, a2, t1        # ポインタを進める。
      sub a0, a0, a3        # カウンタをデクリメントする。
      bnez a0, loop         # 要素が残っているか?

# 要素の幅をスイッチするタイプのループ

loop:
    vsetvli a3, a0, e16,m4  # `vtype`を16ビットの整数に設定する。
    vlh.v v4, (a1)          # 16ビットのデータをロードする。
      slli t1, a3, 1        # 2バイト分要素の幅を計算する。
      add a1, a1, t1        # ポインタを進める。
    vwmul.vx v8, v4, x10    # v8-v15を使って、32ビットの乗算を実行する。

    vsetvli x0, a0, e32,m8  # 32ビットのデータ幅に変更する。
    vsrl.vi v8, v8, 3
    vsw.v v8, (a2)          # 32ビットの値をストアする。
      slli t1, a3, 2        # 4バイト分要素の幅を計算する。
      add a2, a2, t1        # ポインタを進める。
      sub a0, a0, a3        # カウンタをデクリメントする。
      bnez a0, loop         # 要素が残っているか?

2番目のループの方がより複雑であるが、16ビット幅の乗算命令を使用することで32ビットの整数乗算よりも高速で、高い性能を得ることができる。また、ベクトルレジスタへの書き込み幅が小さいことで、16ビットのベクトルロードの方が高速である。