SonicBOOMに限らず、多くのOoOプロセッサには分岐命令管理用のタグが付いているという認識だ。brtag
と一般的に呼ばれているこのタグは、分岐命令に対してそれぞれ付与されており、また分岐命令以外の命令にも一貫して付与されている。分岐タグは「この命令はどの分岐命令に依存しているか」というのを示すためのものであり、単純に言うならば、分岐タグ5が付けられた分岐命令が予測に失敗しパイプラインをフラッシュした場合、分岐タグ6以降の命令はすべてフラッシュする、という形で命令を管理する。
// 分岐タグ5 add x10, x11, x12 sub x30, x31, x1 beq x1, x2, label // ここまでが分岐タグ5 // 分岐タグ6 mul x10, x11, x12 div x2, x3, x4 bne x5, x6 // ここまでが分岐タグ6 // ここから先が分岐タグ7
上記の例では、もし分岐タグ5の分岐命令beq
が予測に失敗した場合、分岐タグ6移行のタグが付けられた命令をフラッシュすることで命令実行をやり直すという仕組みになっている。
「コミットIDを使ってフラッシュすれば良いじゃないか」というのもあるが、その場合コミットが発生するまでパイプラインをフラッシュできない。分岐タグは、分岐命令の実行が完了した段階ですぐにフラッシュを発生できるので、再実行までのタイミングがコミットIDを使うよりも早いという利点がある。
さて、SonicBOOMにも分岐タグが採用されている。アーキテクチャの仕様としては以下に説明がなされている。
If the branches (or jumps) have been correctly speculated by the Front-end, then the Branch Unit s only action is to broadcast the corresponding branch tag to all inflight UOPs<Micro-Op (UOP) that the branch has been resolved correctly. Each UOP<Micro-Op (UOP) can then clear the corresponding bit in its branch mask, and that branch tag can then be allocated to a new branch in the Decode stage.
If a branch (or jump) is misspeculated, the Branch Unit must redirect the PC to the correct target, kill the Front-end and Fetch Buffer, and broadcast the misspeculated branch tag so that all dependent, inflight UOPs<Micro-Op (UOP) may be killed. The PC redirect signal goes out immediately, to decrease the misprediction penalty. However, the kill signal is delayed a cycle for critical path reasons.
これだけではさっぱり分からないのでCoremarkを実行して波形を観測してみよう。
branch_mask
というのが、タグが分岐命令により現在使用中かどうかを示すビット。ここではbranch_mask[11:0]
なので最大で12個の分岐タグを使用することができる。デコード時に分岐命令に遭遇すると、新しい分岐タグを取得してbranch_mask
の当該ビットを1に設定し、分岐命令の実行完了すると落とす。つまり、このデザインではパイプライン中にInflightできる分岐命令は最大で12個に制限されるということが分かる(と言ってもスケジューラのスロットの方が少ないかもしれないが)。
分岐命令がデコードされると、新たに分岐タグが割り当てられる(下図のio_br_tag_0
とio_br_tag_1
がそれに相当する)。それと同時に「その分岐命令が実行されるまでにInflightになった分岐命令のビットパタンも同時に渡される(下図のio_br_mask_0
とio_br_mask_1
がそれに相当する)。これは、デコードされた分岐命令よりも以前に実行された分岐命令のタグを示している。つまり、もし当該分岐命令が分岐予測ミスを起こした場合、その命令に付属するbrmask
が0になっている(つまり当該分岐命令をデコード後に新たに割り当てられた可能性のある若い分岐命令)は確実にフラッシュされる。逆に言うと、brmask
が1になっているタグは当該分岐命令よりも古いので、フラッシュの対象になるとは限らない。
分岐命令の実行が完了すると、それぞれの分岐命令はi_brupdate_b1_resolve_mask
とio_brupdate_b2_xxx
の信号を用いて分岐命令の実行結果を通知する。resolve
は基本的にOneHotな信号(同時に複数の分岐命令が実行完了するとそうではなくなる)で、この通知に呼応してbranch_mask
のビットが下げられる。
その1サイクル後に、Mispredictionの信号が通知される、上記の例では、io_brupdate_b2_mispredict
が1に設定され、io_brupdate_b2_uop_br_mask
に値が設定されている。
このio_brupdate_b2_uop_br_mask
は、分岐タグフラッシュが発生した際に生き残って欲しい分岐タグを示している。つまり、当該分岐命令よりも「古い」命令が生き残るように設定される。
しかし一方で当該タグ10の分岐命令をデコードした時にはbrmask
は0xbbfが渡されていたではないか?なぜbbe
に値が変わってしまっているのか。
これはおそらく、分岐タグ0の命令が分岐タグ10よりも先に解決してしまったからだろう。ビット0はビット10がフラッシュする前に解決し、0に下げられている。したがってこれを別の分岐命令が監視しており、「分岐タグ0は解決したのでもう生き残らせる必要はない」ということで0に落としてしまったものと思われる。