CALLとRETの問題について、もう少しアサーションを組みながら徹底的に解析していたのだが、何となく問題が分かってきた。
RISC-Vの命令は32ビットが基本なのだが、RVC命令をサポートするために16ビット境界に配置することができる。この時、この16ビット境界に配置されたCALL命令とRET命令をどのように識別するかというのが問題になる。 キャッシュラインを跨いでいるので認識が大変なのだが、一つの方法としては、前のキャッシュラインの最上位の16ビットを覚えておき、次のキャッシュラインの下位の16ビットと合わせて比較する。これによりキャッシュラインを跨いでも正確にCALLとRETを見つけだすことが出来るようになる。
もう一つの問題は、CALLやRETが投機的に実行されてしまいRASのインデックスの場所がくるってしまうことだが、これは投機実行しても、コミット時のフラッシュや分岐予測ミスが発生した場合に、最新のCALL/RETの場所まで戻るように論理を組みなおすことによりインデックスが狂わなくなった。
Dhrystoneを実行してみると、とりあえずこれらの関数呼び出しと関数から戻る処理は殆どミスしないようになった。
1013526 : (09,1) pc_vaddr = 0000000080002202, target_addr = 00000000800020b8, pred_target_addr = 00000000800020b8, ras_index = 2, Succ, jal pc - 0x14a 1013554 : (13,1) pc_vaddr = 00000000800020c4, target_addr = 00000000800020b0, pred_target_addr = 00000000800020b0, ras_index = 3, Succ, jal pc - 0x14 1013562 : (15,2) pc_vaddr = 00000000800020b6, target_addr = 00000000800020c8, pred_target_addr = 00000000800020c8, ras_index = 3, Succ, ret 1013606 : (07,1) pc_vaddr = 0000000080002118, target_addr = 0000000080002206, pred_target_addr = 0000000080002206, ras_index = 2, Succ, ret 1013686 : (03,1) pc_vaddr = 0000000080002006, target_addr = 0000000080002bc6, pred_target_addr = 0000000080002bc6, ras_index = 1, Succ, ret 1013738 : (09,2) pc_vaddr = 0000000080002bee, target_addr = 0000000080002052, pred_target_addr = 0000000080002052, ras_index = 1, Succ, jal pc - 0xb9c 1013758 : (12,2) pc_vaddr = 0000000080002060, target_addr = 0000000080002bf2, pred_target_addr = 0000000080002bf2, ras_index = 1, Succ, ret 1013834 : (06,2) pc_vaddr = 0000000080002bee, target_addr = 0000000080002052, pred_target_addr = 0000000080002052, ras_index = 1, Succ, jal pc - 0xb9c 1013862 : (09,2) pc_vaddr = 0000000080002060, target_addr = 0000000080002bf2, pred_target_addr = 0000000080002bf2, ras_index = 1, Succ, ret 1013954 : (06,2) pc_vaddr = 0000000080002c68, target_addr = 000000008000213c, pred_target_addr = 000000008000213c, ras_index = 1, Succ, jal pc - 0xb2c 1014126 : (03,1) pc_vaddr = 000000008000215e, target_addr = 0000000080002c6c, pred_target_addr = 0000000080002b26, ras_index = 1, Miss, ret 1014170 : (08,1) pc_vaddr = 0000000080002b22, target_addr = 000000008000224e, pred_target_addr = 000000008000224e, ras_index = 1, Succ, jal pc - 0x8d4 1014238 : (12,2) pc_vaddr = 0000000080002262, target_addr = 0000000080002b26, pred_target_addr = 0000000080002b26, ras_index = 1, Succ, ret 1014246 : (13,1) pc_vaddr = 0000000080002b26, target_addr = 0000000080002222, pred_target_addr = 0000000080002222, ras_index = 1, Succ, jal pc - 0x904 1014326 : (06,1) pc_vaddr = 000000008000224c, target_addr = 0000000080002b2a, pred_target_addr = 0000000080002b2a, ras_index = 1, Succ, ret 1014510 : (04,2) pc_vaddr = 0000000080002b6a, target_addr = 000000008000206e, pred_target_addr = 000000008000206e, ras_index = 1, Succ, jal pc - 0xafc 1014546 : (09,2) pc_vaddr = 0000000080002082, target_addr = 0000000080002052, pred_target_addr = 0000000080002052, ras_index = 2, Succ, jal pc - 0x30 1014574 : (12,2) pc_vaddr = 0000000080002060, target_addr = 0000000080002086, pred_target_addr = 0000000080002086, ras_index = 2, Succ, ret 1014598 : (15,2) pc_vaddr = 000000008000208e, target_addr = 00000000800028e6, pred_target_addr = 00000000800028e6, ras_index = 2, Succ, jal pc + 0x858 1014630 : (05,2) pc_vaddr = 00000000800028fe, target_addr = 0000000080002092, pred_target_addr = 0000000080002092, ras_index = 2, Succ, ret
それでも時々Missが起きる。これはおそらく 1. CALLの投機実行:エントリ1を書き換え 2. RETの投機実行:エントリ1を使用 3. CALLの投機実行:エントリ1を書き換え 4. 投機実行ミス。2./3. が廃棄される 5. RETの投機実行。エントリ1を使用したが3.により書き換えられている
というシーケンスが発生しているものと思われる。念のため確認した方がいい気がするが、とりあえずほとんどの場合で予測に成功しているのでとりあえず良しと使用かな...