これまでの例では、レジスタの読み出しからADDなどの演算を行い、結果をレジスタに書き込むという処理を1サイクルで実行できるとしてきた。このような構成では、レジスタの読み出し時間、演算時間、レジスタの書き込み時間の合計でサイクルタイムが決まり、クロック周波数が低くなってしまう。

そのため、クロックを上げて高性能を狙うプロセサでは、これらの処理を独立のパイプステージとしてサイクルタイムを短縮する方法が用いられる。この場合、レジスタファイルの読み出しを1サイクル、ALUでの演算を1サイクル、レジスタファイルへの書き込みを1サイクルというような構成が一般的である。

このようなパイプラインで、ADD R0+R1→R2、ADD R2+R3→R4のようにデータ依存性のあるADD命令が実行されると、実行状態は次の図4.8のようになる。

図4.8:Reg読み込み、演算、Reg書き込みを行うパイプラインの実行状況

しかし、R2に書き込まれる値は第4サイクルの終わりには演算器から出力されており、それを第5サイクルでレジスタに書き込み、それを第6サイクルで読み出して、第7サイクルで加算するという処理になっており、この図4.8の処理は無駄が多い。

4サイクル目で得られた演算結果を次のADD命令に直接渡すことができるようにすれば、より早く2番目のADD命令を実行できる。これを実現するのがバイパス(Forwardingとも言う)という手法である。

図4.9:バイパスを行う演算器の構成

図4.9のように、レジスタファイルと演算器の間にマルチプレクサを設け、演算器の出力とそれをFFで1サイクル遅延した結果を入力する。このように接続すると、演算結果をレジスタファイルを経由せず、直接、次の演算の入力とすることができる。この構造で、データ依存の命令が次のサイクルにある場合にはBP0入力を選択することにより、前のADD命令の結果をレジスタファイルをバイパスして、直接、フォワードできるのである。

データ依存のある命令が次の命令ではなく、その次の命令の場合は、演算器の出力を直接フィードバックしたのでは上手く行かないが、この場合は、FFを使って1サイクル遅延したBP1入力を選択することにより、無駄なサイクルを無くすことができる。

このパイプラインでは、依存関係のある命令がもう1サイクル後の命令の場合は、レジスタ経由で間に合うので、特別な回路は必要ないが、パイプラインの構成によっては、FFを2段、3段と経由したバイパスが必要となる場合もある。

なお、この演算結果を利用する命令が、次とその次の命令だけと決まっていれば、演算結果をレジスタファイルに書き込む必要は無いが、結果を使う命令がいつ出てくるかはハードウェアには分からないので、レジスタへの書込みは省略できない。

図4.10:バイパスの有無によるパイプライン処理状況の違い

図4.10に示すように、バイパス無しのケースでは、後続のADD命令の完了は第8サイクルであるが、バイパスを設けることにより、灰色の空きサイクルがなくなり、ADD命令の完了が6サイクル目と高速化することができる。

このようにバイパスを使うとデータ依存関係にある命令を早く実行できるが、そのためには、命令のデコード時点で、バイパスから必要なオペランドが得られるかどうかが判定できなければならない。

図4.10のR2と書かれた部分は、それぞれのサイクルでR2をオペランドとして使用する場合に、どこから持ってくれば良いかを示している。バイパス無しの場合は、緑のボックスで示したように、最初のADD命令の実行を開始すると、R2の状態をInvalidとする。そして、R2への書込みは第5サイクルの終わりであるが、この書込みが終わるのは確実であるので、多少フライングであるが、第5サイクルにはValidにする。これにより、オペランドのR2に依存する後続のADD命令が第6サイクルから実行が開始できるようになる。

一方、バイパスを行う場合は、最初のADD命令を実行開始する第3サイクルには、R2の状態をInvalidではなく、Bypass 0とする。そして、次の第4サイクルにはBypass 1、そして第5サイクルにはValidとする。最初のADD命令の演算結果が得られるのは第4サイクルの終わりであるが、後続の命令のデコードから演算までは2サイクルを必要とするので、R2の状態は、2サイクル後にどこからオペランドが得られるかを示すようにしている。

このようにバイパスを利用するには、レジスタの状態として、Valid/Invalidに追加して、Bypass 0、Bypass 1という状態を定義し、後続のADD命令のデコードサイクルにおいて、Validだけでなく、Bypass 0あるいはBypass 1の状態であるかどうかをチェックできるようにしてやればよい。

そして、レジスタ読み出しステージでは、レジスタの状態がValidの場合は、レジスタファイル、Bypass 1の状態の場合はBP1、Bypass 0の場合は、BP0からのデータを選択するようにバイパスマルチプレクサを切り替える。

また、別の方法としては、Bypass 0/1に対して予定表のようなシフトレジスタ構造を作り、各サイクルにそれぞれのバイパスに乗っているデータの格納先のレジスタ番号を書いておく。そして、後続の命令のデコード時に、レジスタのValidチェックに加えて、その命令が演算を行うサイクルのBypass 0とBypass 1の予定表をチェックし、そのサイクルに、どちらかのバイパスに必要なオペランドレジスタへの書き込みデータが載っている場合には、そのバイパスから入力を選択することにして命令の実行を開始するというやり方をとることも可能である。