ベクトル命令における、LMUL>1の対応というのは結構難易度の高い問題だ。
今回はその解決方法として、LMULの値がVSETVL命令で変更されると、例外を出して物理レジスタの構成を整列し直す方式を考えたい。
この方式を実際にVerilogで記述して動作をテストしようと思ったのだが、問題にぶつかった。
以下の例外対応コードを使って物理レジスタをLMULの大きさに整列しようとしていた。以下のコードがやっていることは:
- まずはすべての現在のアクティブな物理レジスタの内容をメモリにストアする。
- LMULを所定の値に変更して、フリーリストから入手できる物理レジスタの値をすべてLMUL0, LMUL1, LMUL*2,... となるようにする(つまりLMULの倍数しか物理レジスタIDを使えないようにする)。
- 物理レジスタにデータを書き戻す。
.section .text csrrw sp, sscratch, sp la sp, temp_data sd x10, 0(sp) sd x11, 8(sp) sd x12, 16(sp) la x10, vector_stack_data csrr x11, vlenb vs1r.v v0 , (x10); add x10, x10, x11 vs1r.v v1 , (x10); add x10, x10, x11 vs1r.v v2 , (x10); add x10, x10, x11 vs1r.v v3 , (x10); add x10, x10, x11 /* ... 途中省略 ... */ vs1r.v v27, (x10); add x10, x10, x11 vs1r.v v28, (x10); add x10, x10, x11 vs1r.v v29, (x10); add x10, x10, x11 vs1r.v v30, (x10); add x10, x10, x11 vs1r.v v31, (x10); add x10, x10, x11 csrr x12, 0x00b # vscratch srli x12, x12, 20 fence.i vsetvl x0, x0, x12 la x10, vector_stack_data vl1r.v v0 , (x10); add x10, x10, x11 vl1r.v v1 , (x10); add x10, x10, x11 vl1r.v v2 , (x10); add x10, x10, x11 vl1r.v v3 , (x10); add x10, x10, x11 vl1r.v v4 , (x10); add x10, x10, x11 vl1r.v v5 , (x10); add x10, x10, x11 /* ... 途中省略 ... */ vl1r.v v28, (x10); add x10, x10, x11 vl1r.v v29, (x10); add x10, x10, x11 vl1r.v v30, (x10); add x10, x10, x11 vl1r.v v31, (x10); add x10, x10, x11 ld x10, 0(sp) ld x11, 8(sp) ld x12, 16(sp) csrrw sp, sscratch, sp mret
こうすることにより、レジスタの内容をキープしながら、物理レジスタIDを整列する。 そうすると、LMUL=8とした場合は`vadd v8, v0, v16を実行すると、v8の新たな物理レジスタの先頭をxとすると、x, x+1, x+2, x+3, ..., x+7がv8, v9, v10, ... v15 として扱うことができるようになる(LMUL=8なのでv8の物理レジスタIDから連続した8個の物理レジスタはマイクロアーキテクチャ的に自動的に確保されたものとする)。
その代わりにフリーリストのサイズはLMUL分の1となり、取れる値はLMULの倍数のみとなる。
こうすることでLMULへの簡単な対応を実現しようとしたのだが、問題にぶつかった。
vsetvl命令実行後にフリーリストの大きさが変わり、LMULの倍数しか物理レジスタが取れなくなるのだが、その後にvl1r.vを実行すると、これらの命令はLMULを無視して1レジスタ単位でデータを扱うので、取得できる物理レジスタIDと合わずに矛盾が生じる。
例えば、vl1r.v v7
が実行されると、新しい物理レジスタID=32が取得されると同時に、もともとの物理レジスタID=7が解放され、フリーリストにLMULの倍数ではない7というIDが入ってしまい矛盾が生じる。
この時、v7はすでにv0レジスタグループに入っているのだから、物理レジスタID=7を解放せずに、v0の持つID=0を解放すればいいのかもしれない。しかしそうするとv0の物理レジスタIDが0から32に自動的に切り替わってしまい、データの移動が発生しておらずやはり矛盾が生じる。やはりLMULの粒度と異なるベクトル操作命令を何も考えずにぶち込むのは非常に問題となる。
ベクトル・ロードストアはこのようにvsetvl命令と関係なくEMULを変えてくるので結構面倒くさい。演算命令だけならうまくいくはずなのだが...
と、ここまで考えて、これを一時的に解決するためには、LMUL事に通常のUnit Stride命令でデータの書き戻しを行えばいいんじゃないか。
vs1r x 32によるレジスタ・データのバックアップ vsetvl命令によるLMULの変更 if (LMUL==1) vle8.v v0, v1, v2, ... v31によるデータ復帰 else if (LMUL==2) vle8.v v0, v2, ... v30によるデータ復帰 else if (LMUL==4) vle8.v v0, v4, ... v28によるデータ復帰 else if (LMUL==8) vle8.v v0, v8, v16, v24によるデータ復帰
マイクロコードをこのように変更して再実験を行う。