RISC-Vのベクトル拡張について、いくつかのサンプルプログラムを用いて使い方を見ていく。
次はstrlen()
について。これはmemcpy()
とは違って、メモリアクセスするサイズが良く分からない場合のもの。
この場合には、ベクトルロードはFault-only-Firstロード命令を用いる。 ちなみにこれに類似した命令はArmのA64FXにも実装されている。
この命令のモチベーションは、ベクトル長が分からない場合に「どこまでアクセスできるのか」が分からない場合において有効な命令である。
例えばVLEN=1024のベクトル拡張の場合はvle8.v
(8ビット単位の粒度でのベクトルロード命令)では、1024ビットのメモリに対してロードを実行する。
もしこの中にPMAやPMPによりメモリアクセス不可能な領域がある場合、ベクトルロード中にメモリアクセス例外が発生する。
ページ単位のメモリアクセス制御を行っている場合、一般的にページサイズは4Kであるが、ページの概念が存在しないシステムではアクセスできないページにアクセスしてしまう可能性がある。
この時に、Fault-only-firstロード命令は「どの領域までアクセスするのか分からない」場合に有効な命令となる。
Fault-only-firstロード命令は、「先頭のメモリアクセス要素」に対して例外が発生した場合には例外が発生し、それ以外のメモリアクセス要素に対して例外が発生する場合には例外を発生せずにその場で命令を終了しvl
をアップデートする。
この命令を用いることで、strlen
のようにアクセスしてはならない場所までアクセスする可能性のある命令でも、例外を発生せずにループを終了することができる。
この前提を元に、strlen
のサンプルコードを見ていく。
# size_t strlen(const char *str) # a0 holds *str strlen: mv a3, a0 # Save start loop: vsetvli a1, x0, e8, m8, ta, ma # Vector of bytes of maximum length vle8ff.v v8, (a3) # Load bytes csrr a1, vl # Get bytes read vmseq.vi v0, v8, 0 # Set v0[i] where v8[i] = 0 vfirst.m a2, v0 # Find first set bit add a3, a3, a1 # Bump pointer bltz a2, loop # Not found? add a0, a0, a1 # Sum start + bump add a3, a3, a2 # Add index sub a0, a3, a0 # Subtract start address+bump ret
vsetvli
の引数はx0
が設定され、取れる限り最大長のベクトル長が設定される(VLMAX)。
次にvle8ff.v
を用いてバイト単位でベクトルロードを行い、アクセス不許可の領域に対する配慮も行う。
そしてvmseq.vi
を用いて、メモリ要素中の0
の要素の場所置見つける。そしてvfirst.m
によりそのインデックスを見つけ、そうでなかったらループを繰り返す。