LLVM16になって、RISC-VのVectorizationの状態について確認していきたい。
Scalable Vectorizationの機能について、テストプログラムを動かしながら動作確認をしていきたい。 以下のような、簡単なプログラムを作ってどのようにコンパイルされるのかを見てみた。
そもそもScalable Vectorizationで何ができるのかよく分かっていなかったのだが、以下のように分類していきたい。
以下のコードをどのようにコンパイルするのか、というのを考える:
typedef float data_t; void vec_test (data_t *a, const data_t *b, const data_t c, const int N) { for (int i = 0; i < N; i++) { a[i] = b[i] * c; } }
- 自動ベクトル化を使用しない完全なコード生成の場合:
要素数Nをうまく利用して、VLレジスタを活用して端数を処理することで、無駄なく処理が実行できる。また、LMULを活用することで、最大限までベクトルレジスタを活用できる。
Loop: vsetvli t0, a3, e32, m8 vle32.v v8, (a1) vfmul.vf v16, v8, fa0 vse32.v v16, (a0) slli t1, t0, 2 add a1, a1, t1 add a0, a0, t1 sub a3, a3, t0 bnez a3, Loop
- Scalable Vectorizationを使用しない、固定長ベクトル・レジスタを使用した場合のコード生成:
以下のオプションでコンパイルする。
-march=rv64gcv -O3 -Ofast -mllvm -scalable-vectorization=off -mllvm -riscv-v-vector-bits-min=256
見てわかるとおり、scalable-vectorization
を有効にしないと、固定長のベクトル・レジスタ長 (256-bit)を使用すると仮定している。
端数は、スカラ命令列で処理している。
.LBB0_4: # =>This Inner Loop Header: Depth=1 addi a7, a3, 32 vle32.v v8, (a3) vle32.v v9, (a7) // ここの部分。ベクトル・レジスタ長が256-bit(32B)なので、ポインタをそのサイズで進めている。 // つまり、VLEN=256以外で動かすと、おそらくハードウェアの動きがおかしくなる。 // 固定値なので、幅が合うようにアラインしたうえで、2回のループアンローリングを行っている addi a7, a5, 32 vfmul.vf v8, v8, fa0 vfmul.vf v9, v9, fa0 vse32.v v8, (a5) vse32.v v9, (a7) addi a3, a3, 64 addi a4, a4, -16 addi a5, a5, 64 bnez a4, .LBB0_4 beq a6, a2, .LBB0_8
- Scalable Vectorizationを使用した場合:
以下のオプションでコンパイルする。
-march=rv64gcv -O3 -Ofast -mllvm -scalable-vectorization=on
上記の固定ベクトル・レジスタ長の場合と異なり、ベクトルレジスタ長をVLENB
システムレジスタから取り出し、ポインタのステップ数をここから算出する。
blez a2, .LBB0_8 csrr t1, vlenb srli a3, t1, 1 li a4, 8 bltu a4, a3, .LBB0_3 li a3, 8 ... /* ... t1にVLENB(ベクトル・レジスタ長)が格納されている ... */ .LBB0_10: # =>This Inner Loop Header: Depth=1 vl1re32.v v8, (a4) // t1=VLENBを使用してベクトル・レジスタ長に合わせながらステップしていく // それでも、端数が処理されていないので、スカラ命令列と組み合わせて動かす必要がある add a3, a4, t1 vl1re32.v v9, (a3) vfmul.vf v8, v8, fa0 vfmul.vf v9, v9, fa0 vs1r.v v8, (a5) add a3, a5, t1 vs1r.v v9, (a3) add a4, a4, t2 sub t3, t3, t0 add a5, a5, t2 bnez t3, .LBB0_10
まとめとしては、それぞれのコンパイルモードで以下のような違いがあるだろう。
固定ベクトル・レジスタモード | Scalable Vectorization有効化 | アセンブリ言語による記述 | |
---|---|---|---|
ベクトル・レジスタ長 | 固定長を仮定 | VLENBレジスタを使用して取得 | VSETVLI命令を使用して取得 |
端数の処理 | スカラ命令により処理 | スカラ命令により処理 | 必要なし |
複数ベクトルレジスタLMUL>1の扱い | 未サポート | 未サポート | サポート |