RISC-Vのベクトル拡張について、いくつかのサンプルプログラムを用いて使い方を見ていく。
memcpy
とstrlen
について見たので、これ以外のサンプルについて確認する。
まずはsaxpy
これは単純に2つの配列のうち、一つの配列に対して定数を掛け合わしてもう一つの配列の値を足し込んでいくもの。
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]; }
riscv-v-spec/saxpy.s at master · riscv/riscv-v-spec · GitHub
これをベクトルで実装したものが以下。LMUL=8
となっているので、v0
からv7
までに最初の配列、v8
からv15
までに次の配列をロードし、vfmacc.vf
で一気に乗算加算しているところがポイント。
そして最後にvse32
でv8
からv15
までに格納されている値を一気にストアする。
saxpy: vsetvli a4, a0, e32, m8, ta, ma vle32.v v0, (a1) sub a0, a0, a4 slli a4, a4, 2 add a1, a1, a4 vle32.v v8, (a2) vfmacc.vf v8, fa0, v0 vse32.v v8, (a2) add a2, a2, a4 bnez a0, saxpy ret
次にstrcpy
の実装。memcpy
とは異なり、文字列がコピー対象となっているからか、定数メモリ領域が指定されていることを前提として安全のためにvle8ff.v
を使用して実装してある。それ以外はstrlen()
とよく似ている。
strcpy: mv a2, a0 # Copy dst li t0, -1 # Infinite AVL loop: vsetvli x0, t0, e8, m8, ta, ma # Max length vectors of bytes vle8ff.v v8, (a1) # Get src bytes csrr t1, vl # Get number of bytes fetched vmseq.vi v1, v8, 0 # Flag zero bytes vfirst.m a3, v1 # Zero found? add a1, a1, t1 # Bump pointer vmsif.m v0, v1 # Set mask up to and including zero byte. vse8.v v8, (a2), v0.t # Write out bytes add a2, a2, t1 # Bump pointer bltz a3, loop # Zero byte not found, so loop ret