付録A: ベクトルアセンブリコード例

以下は、ベクターISAの説明に役立つ非規範的なテキストとして提供されている。

A.1. ベクトル - ベクトル 加算プログラム

    # 32ビット整数のベクトル - ベクトル加算
    # void vvaddint32(size_t n, const int*x, const int*y, int*z)
    # { for (size_t i=0; i<n; i++) { z[i]=x[i]+y[i]; } }
    #
    # a0 = n, a1 = x, a2 = y, a3 = z
    # Non-vector instructions are indented
vvaddint32:
    vsetvli t0, a0, e32      # ベクトル長を32ビットベクトルをベースに設定する。
    vlw.v v0, (a1)           # 最初のベクトルをフェッチ
      sub a0, a0, t0         # フェッチした要素の数をデクリメント
      slli t0, t0, 2         # 処理が完了した要素の数をバイト数に変換
      add a1, a1, t0         # ポインタを進める
    vlw.v v1, (a2)           # 2番目のベクトルをフェッチ
      add a2, a2, t0         # ポインタを進める
    vadd.vv v2, v0, v1        # ベクトルを加算する
    vsw.v v2, (a3)           # 結果をストアする
      add a3, a3, t0         # ポインタを進める
      bnez a0, vvaddint32    # ループに戻る
      ret                    # 終了

A.2. 複数ビット幅の混在するマスクと計算

# 分岐判定のためのベクトルと、選択のためのベクトルのビット幅が異なる場合の計算。
#   int8_t a[]; int32_t b[], c[];
#   for (i=0;  i<n; i++) { b[i] =  (a[i] < 5) ? c[i] : 1; }
#
# Mixed-width code that keeps SEW/LMUL=8
  loop:
    vsetvli a4, a0, e8,m1  # ベクトルの幅をマスク判定のためのビット幅に設定する
    vlb.v v1, (a1)                # a[i]をロード
      add a1, a1, a4              # ポインタを進める.
    vmslt.vi v0, v1, 5            # a[i] < 5?

    vsetvli x0, a0, e32,m4 # ベクトルを32ビット整数用に設定する。
      sub a0, a0, a4              # カウンタをデクリメントする
    vmv.v.i v4, 1                 # 書き込みベクトルレジスタ用に即値を拡散する。
    vlw.v v4, (a3), v0.t          # Cのベクトル要素をロードする。
      sll t1, a4, 2
      add a3, a3, t1              # ポインタを進める.
    vsw.v v4, (a2)                # bをメモリにストアする。
      add a2, a2, t1              # ポインタを進める.
      bnez a0, loop               # これ以上あるか?

A.3. Memcpy プログラム

  # void *memcpy(void* dest, const void* src, size_t n)
  # a0=dest, a1=src, a2=n
  #
memcpy:
    mv a3, a0 # Copy destination
loop:
  vsetvli t0, a2, e8,m8  # Vectors of 8b
  vlb.v v0, (a1)                # バイト列をロードする
    add a1, a1, t0              # ポインタを進める
    sub a2, a2, t0              # カウンタをデクリメントする
  vsb.v v0, (a3)                # バイト列をストアする
    add a3, a3, t0              # ポインタを進める
    bnez a2, loop               # これ以上あるか?
    ret                         # 戻る

A.4. データ選択のプログラム

# (int16) z[i] = ((int8) x[i] < 5) ? (int16) a[i] : (int16) b[i];
#
# Fixed 16b SEW:

loop:
    vsetvli t0, a0, e16  # 16ビットのベクトル要素を使用する
    vlb.v v0, (a1)          # 符号付き16ビット整数x[i]をロードする。
      sub a0, a0, t0        # 要素の数をデクリメントする
      add a1, a1, t0        # x[i] ポインタを進める
    vmslt.vi v0, v0, 5      # v0内のマスクを設定する。
      slli t0, t0, 1        # バイト単位に変換する。
    vlh.v v1, (a2), v0.t    # z[i] = a[i] の場合
    vmnot.m v0, v0          # Invert v0
      add a2, a2, t0        # a[i] ポインタを進める
    vlh.v v1, (a3), v0.t    # z[i] = b[i] の場合
      add a3, a3, t0        # b[i] ポインタを進める
    vsh.v v1, (a4)          # z をストアする。
      add a4, a4, t0        # b[i] ポインタを進める
      bnez a0, loop

A.5. SAXPY example

# void
# saxpy(size_t n, const float a, const float *x, float *y)
# {
#   size_t i;
#   for (i=0; i<n; i++)
#     y[i] = a * x[i] + y[i];
# }
#
# register arguments:
#     a0      n
#     fa0     a
#     a1      x
#     a2      y

saxpy:
    vsetvli a4, a0, e32, m8
    vlw.v v0, (a1)
    sub a0, a0, a4
    slli a4, a4, 2
    add a1, a1, a4
    vlw.v v8, (a2)
    vfmacc.vf v8, fa0, v0
    vsw.v v8, (a2)
    add a2, a2, a4
    bnez a0, saxpy
    ret

A.6. SGEMM example

# RV64IDV system
#
# void
# sgemm_nn(size_t n,
#          size_t m,
#          size_t k,
#          const float*a,   // m * k matrix
#          size_t lda,
#          const float*b,   // k * n matrix
#          size_t ldb,
#          float*c,         // m * n matrix
#          size_t ldc)
#
#  c += a*b (alpha=1, no transpose on input matrices)
#  matrices stored in C row-major order

#define n a0
#define m a1
#define k a2
#define ap a3
#define astride a4
#define bp a5
#define bstride a6
#define cp a7
#define cstride t0
#define kt t1
#define nt t2
#define bnp t3
#define cnp t4
#define akp t5
#define bkp s0
#define nvl s1
#define ccp s2
#define amp s3

# Use args as additional temporaries
#define ft12 fa0
#define ft13 fa1
#define ft14 fa2
#define ft15 fa3

# This version holds a 16*VLMAX block of C matrix in vector registers
# in inner loop, but otherwise does not cache or TLB tiling.

sgemm_nn:
    addi sp, sp, -FRAMESIZE
    sd s0, OFFSET(sp)
    sd s1, OFFSET(sp)
    sd s2, OFFSET(sp)

    # Check for zero size matrices
    beqz n, exit
    beqz m, exit
    beqz k, exit

    # Convert elements strides to byte strides.
    ld cstride, OFFSET(sp)   # Get arg from stack frame
    slli astride, astride, 2
    slli bstride, bstride, 2
    slli cstride, cstride, 2

    slti t6, m, 16
    bnez t6, end_rows

c_row_loop: # Loop across rows of C blocks

    mv nt, n  # Initialize n counter for next row of C blocks

    mv bnp, bp # Initialize B n-loop pointer to start
    mv cnp, cp # Initialize C n-loop pointer

c_col_loop: # Loop across one row of C blocks
    vsetvli nvl, nt, e32  # 32-bit vectors, LMUL=1

    mv akp, ap   # reset pointer into A to beginning
    mv bkp, bnp # step to next column in B matrix

    # Initalize current C submatrix block from memory.
    vlw.v  v0, (cnp); add ccp, cnp, cstride;
    vlw.v  v1, (ccp); add ccp, ccp, cstride;
    vlw.v  v2, (ccp); add ccp, ccp, cstride;
    vlw.v  v3, (ccp); add ccp, ccp, cstride;
    vlw.v  v4, (ccp); add ccp, ccp, cstride;
    vlw.v  v5, (ccp); add ccp, ccp, cstride;
    vlw.v  v6, (ccp); add ccp, ccp, cstride;
    vlw.v  v7, (ccp); add ccp, ccp, cstride;
    vlw.v  v8, (ccp); add ccp, ccp, cstride;
    vlw.v  v9, (ccp); add ccp, ccp, cstride;
    vlw.v v10, (ccp); add ccp, ccp, cstride;
    vlw.v v11, (ccp); add ccp, ccp, cstride;
    vlw.v v12, (ccp); add ccp, ccp, cstride;
    vlw.v v13, (ccp); add ccp, ccp, cstride;
    vlw.v v14, (ccp); add ccp, ccp, cstride;
    vlw.v v15, (ccp)


    mv kt, k # Initialize inner loop counter

    # Inner loop scheduled assuming 4-clock occupancy of vfmacc instruction and single-issue pipeline
    # Software pipeline loads
    flw ft0, (akp); add amp, akp, astride;
    flw ft1, (amp); add amp, amp, astride;
    flw ft2, (amp); add amp, amp, astride;
    flw ft3, (amp); add amp, amp, astride;
    # Get vector from B matrix
    vlw.v v16, (bkp)

    # Loop on inner dimension for current C block
 k_loop:
    vfmacc.vf v0, ft0, v16
    add bkp, bkp, bstride
    flw ft4, (amp)
    add amp, amp, astride
    vfmacc.vf v1, ft1, v16
    addi kt, kt, -1    # Decrement k counter
    flw ft5, (amp)
    add amp, amp, astride
    vfmacc.vf v2, ft2, v16
    flw ft6, (amp)
    add amp, amp, astride
    flw ft7, (amp)
    vfmacc.vf v3, ft3, v16
    add amp, amp, astride
    flw ft8, (amp)
    add amp, amp, astride
    vfmacc.vf v4, ft4, v16
    flw ft9, (amp)
    add amp, amp, astride
    vfmacc.vf v5, ft5, v16
    flw ft10, (amp)
    add amp, amp, astride
    vfmacc.vf v6, ft6, v16
    flw ft11, (amp)
    add amp, amp, astride
    vfmacc.vf v7, ft7, v16
    flw ft12, (amp)
    add amp, amp, astride
    vfmacc.vf v8, ft8, v16
    flw ft13, (amp)
    add amp, amp, astride
    vfmacc.vf v9, ft9, v16
    flw ft14, (amp)
    add amp, amp, astride
    vfmacc.vf v10, ft10, v16
    flw ft15, (amp)
    add amp, amp, astride
    addi akp, akp, 4            # Move to next column of a
    vfmacc.vf v11, ft11, v16
    beqz kt, 1f                 # Don't load past end of matrix
    flw ft0, (akp)
    add amp, akp, astride
1:  vfmacc.vf v12, ft12, v16
    beqz kt, 1f
    flw ft1, (amp)
    add amp, amp, astride
1:  vfmacc.vf v13, ft13, v16
    beqz kt, 1f
    flw ft2, (amp)
    add amp, amp, astride
1:  vfmacc.vf v14, ft14, v16
    beqz kt, 1f                 # Exit out of loop
    flw ft3, (amp)
    add amp, amp, astride
    vfmacc.vf v15, ft15, v16
    vlw.v v16, (bkp)            # Get next vector from B matrix, overlap loads with jump stalls
    j k_loop

1:  vfmacc.vf v15, ft15, v16

    # Save C matrix block back to memory
    vsw.v  v0, (cnp); add ccp, cnp, cstride;
    vsw.v  v1, (ccp); add ccp, ccp, cstride;
    vsw.v  v2, (ccp); add ccp, ccp, cstride;
    vsw.v  v3, (ccp); add ccp, ccp, cstride;
    vsw.v  v4, (ccp); add ccp, ccp, cstride;
    vsw.v  v5, (ccp); add ccp, ccp, cstride;
    vsw.v  v6, (ccp); add ccp, ccp, cstride;
    vsw.v  v7, (ccp); add ccp, ccp, cstride;
    vsw.v  v8, (ccp); add ccp, ccp, cstride;
    vsw.v  v9, (ccp); add ccp, ccp, cstride;
    vsw.v v10, (ccp); add ccp, ccp, cstride;
    vsw.v v11, (ccp); add ccp, ccp, cstride;
    vsw.v v12, (ccp); add ccp, ccp, cstride;
    vsw.v v13, (ccp); add ccp, ccp, cstride;
    vsw.v v14, (ccp); add ccp, ccp, cstride;
    vsw.v v15, (ccp)

    # Following tail instructions should be scheduled earlier in free slots during C block save.
    # Leaving here for clarity.

    # ポインタを進めるs for loop across blocks in one row
    slli t6, nvl, 2
    add cnp, cnp, t6                         # Move C block pointer over
    add bnp, bnp, t6                         # Move B block pointer over
    sub nt, nt, nvl                          # 要素の数をデクリメントする in n dimension
    bnez nt, c_col_loop                      # Any more to do?

    # Move to next set of rows
    addi m, m, -16  # Did 16 rows above
    slli t6, astride, 4  # Multiply astride by 16
    add ap, ap, t6         # Move A matrix pointer down 16 rows
    slli t6, cstride, 4  # Multiply cstride by 16
    add cp, cp, t6         # Move C matrix pointer down 16 rows

    slti t6, m, 16
    beqz t6, c_row_loop

    # Handle end of matrix with fewer than 16 rows.
    # Can use smaller versions of above decreasing in powers-of-2 depending on code-size concerns.
end_rows:
    # Not done.

exit:
    ld s0, OFFSET(sp)
    ld s1, OFFSET(sp)
    ld s2, OFFSET(sp)
    addi sp, sp, FRAMESIZE
    ret