遅延スロット(ちえんスロット)またはディレイスロット(英: Delay slot)は、機械語において直前の命令が効力を発揮する前に実行される命令のスロット(位置)を指す。最も典型的な形態としては、RISCやDSPアーキテクチャでの分岐命令(遅延分岐[1])の直後の位置の命令がある。この命令は分岐が実際に行われる前に実行される。従って、その命令は(その場所が遅延スロットであることを理解していないと)無意味な位置にあるように見える。アセンブラは一般に自動的な命令の並べ替えを行い、コンパイラやプログラマが遅延スロットを気にせずにコードを書けるようにしている。
分岐遅延スロット
分岐命令があるとき、パイプライン上その直後に位置する遅延スロットを分岐遅延スロット(branch delay slot[2])と呼ぶ。分岐遅延スロットは主にDSPアーキテクチャや古いRISCアーキテクチャに見られる。MIPS、PA-RISC、SuperH、SPARCなどは1つの分岐遅延スロットを持つRISCアーキテクチャである。PowerPC、ARM、DEC Alpha などは分岐遅延スロットを持たない。 DSP アーキテクチャで1つの分岐遅延スロットを持つものとしては、例えばμPD77230[3]がある。SHARC という DSP は2個の分岐遅延スロットを持つ。つまり、分岐が実際に行われる前に直後の2つの命令を実行する。また、TMS320C3x の分岐遅延スロットは 3つである[4]。
以下のコード例は、SHARC DSP の遅延スロットを示したものである。レジスタ R0 から R9 を番号順にゼロクリアしている(R6 の次にクリアされているのは R9 ではなく R7 である)。どの命令も2回実行されることはない。
R0 = 0;
CALL fn (DB); /* 下の "fn" というラベルのついた関数をコール */
R1 = 0; /* 第一遅延スロット */
R2 = 0; /* 第二遅延スロット */
/***** ここで CALL が効果を発揮する *****/
R6 = 0; /* CALL/RTS はここに復帰する */
JUMP end (DB);
R7 = 0; /* 第一遅延スロット */
R8 = 0; /* 第二遅延スロット */
/***** ここで JUMP が効果を発揮する *****/
/* 以下の4命令は、関数 "fn" として上から呼び出される */
fn: R3 = 0;
RTS (DB); /* 呼び出し元(遅延スロットの後)へ戻る */
R4 = 0; /* 第一遅延スロット */
R5 = 0; /* 第二遅延スロット */
/***** ここで RTS が効果を発揮する *****/
end: R9 = 0;
パイプライン・アーキテクチャの目標は、常にパイプラインを命令で満たしておくことである。分岐遅延スロットはそのための副作用である。分岐命令はパイプラインをある程度進んだ時点でないと分岐先が決まらないため、分岐命令の次に実行される命令は既にパイプライン上に存在している。例えば、R3000のパイプラインは5段であり、1段目は命令フェッチ、2段目は命令デコード、3段目はアドレス計算である。従って、2段目で分岐命令であると判明した時点でパイプラインをフリーズさせ、3段目でアドレス計算を行いプログラムカウンタを書き換えるが、既に分岐命令の次の命令はパイプライン上にある。これを無効化しないことでパイプラインの効率を上げようとすると、分岐遅延スロットが生じる。分岐遅延スロットでは分岐命令の結果に依存しない任意の命令を実行できる。このような最適化は、コンパイラが命令を分岐遅延スロットに移動させることでなされる。また、デバッガでブレークポイントを設定する場合や、ステップ実行をする場合、分岐遅延スロットは特殊な取り扱いが必要になる。つまり、ブレークポイントについては分岐遅延スロットでは設定できないようにするか、もし設定できるようにするなら分岐遅延スロットからの実行再開後、該当する分岐命令のターゲットアドレスに分岐するように単なる実行再開とは区別した動作が必要となる。ステップ実行の場合、分岐遅延のステップ実行においては分岐遅延スロットの命令まで含めてまとめて実行し、ステップ実行後にプログラムカウンタが指し示すアドレスとしては分岐命令のターゲットアドレスになるようにするか、もしステップ実行で分岐遅延スロットでも命令境界でステップ実行するようにするなら、分岐遅延スロットからの実行再開後、該当する分岐命令のターゲットアドレスに分岐するように単なるステップ実行とは区別した動作が必要となる。
分岐遅延スロット数は、パイプラインの段数、レジスタ・フォワーディングの有無、分岐条件が計算されるのがパイプラインの何段目か、分岐先予測を行っているかなどの様々な要素が影響する。バイナリ互換を保つには、分岐遅延スロット数を変更することはできない。従って、技術の進歩によって遅延スロットが不要となっても、それを維持し続けることになる。
遅延スロットの存在は高パフォーマンスを保つ実装を複雑化させる[5]ことを意味し、例えば RISC-V においてはオプションで[6]分岐遅延スロットを持つ OpenRISC の設計と異なり、これを条件コードとともに廃することを選択している[5]。
遅延スロットは高パフォーマンスを保つ実装を複雑化させるだけでなく、割り込み受付機構も複雑化させる。デバッガで遅延スロットにブレークポイントを設定する場合や、ステップ実行をする場合、分岐遅延スロットに対して特殊な取り扱いが必要になるのと同様割り込み受付機構とCPUにて制御が複雑化する。
ロード遅延スロット
ロード遅延スロットは、メモリからレジスタへのロード命令の直後の命令位置を意味し、その命令でロード命令の結果が使えないことを意味する。一般にロードにかかる時間はキャッシュにヒットするか否かなどに依存するため予測できず、ロード遅延スロットを採用しているアーキテクチャはほとんどなく、非常に初期のRISC設計でのみ見られる。R3000 などの MIPS1 ISA には、ロード遅延スロットがある。
以下のコード例は MIPS1 のアセンブリコードであり、分岐遅延スロットとロード遅延スロットが含まれている。
lw v0,4(v1) # アドレス v1+4 から v0 にワードをロード
nop # ロード遅延スロット(何もしない)
jr v0 # v0 にあるアドレスに分岐
nop # 分岐遅延スロット(何もしない)
出典
関連項目
外部リンク