回答:
レジスタの名前を変更する現代のマイクロアーキテクチャでは、フラグの実装コストまたはフラグの実装コストはかなり似ています。私が考えることができる主な違いは、いくつかのフラグは値の特性を示します(値は負ですか?値はゼロですか?値は偶数または奇数パリティを持っていますか?)が、いくつかは前の操作中に発生したイベントを表します(add命令にキャリーアウトまたはオーバーフローがありましたか?)これにより、32ビットアーキテクチャで64ビット加算をシミュレートする場合(または、 64ビットアーキテクチャ。)キャリーフラグのあるほとんどのアーキテクチャには、特別なものがあります。add-with-carry
前の追加命令からのキャリーフラグを含む命令。これにより、フラグレジスタを備えた多くのアーキテクチャで、多精度演算のシミュレーションが比較的安価になります。
反対に、Nビットレジスタのゼロまたはゼロ以外のテストは、実際には驚くほど高価です。Nビットレジスタのゼロをテストするには、NビットNOR演算を実行する必要があります。計算にはレベルのロジックが必要です。フラグレジスタを使用するアーキテクチャでは、ALUステージの最後にゼロ/非ゼロ計算用の追加のロジックを使用すると、クロックが遅くなる(またはALUに2サイクルの動作を強制する)可能性があります。 SPARCのようなアーキテクチャには、各算術演算の2つのバージョンがあり、1つはフラグを設定し、もう1つは設定しませんでした。
しかし、MIPSはここには何も保存しません。彼らは問題をどこか別の場所に移動しただけです。MIPSにはbranch-on-equal
指示があります。これは、分岐がどの方向に進むかを決定する前に、分岐命令に実際にALUステージ(ビット単位のxor
演算とそれに続くnor
単一の等しい/等しくないビットまで減らすことなど)が必要であることを意味します。
DEC Alphaアーキテクチャは、トリックを使用して違いを分割しようとしました。DEC Alphaにはフラグレジスタがありませんでしたが、branch-on-equal
命令もありませんでした。代わりに、分岐命令はすべて単一の汎用レジスターの状態を調べます。ありbranch-on-zero
、branch-on-not-zero
、branch-on-less-than-zero
トリックはあなたが他の64ビットがすべてゼロであるか否かを示しています余分な65ビットのレジスタのすべての一般的な目的を与えることができるということであるなど、。これにより、フラグレジスタを持つようになります。すべての分岐命令は、単一のビット(既に計算済み)を見て決定を下しますが、通常のALUでその余分なゼロインジケータービットを計算する方法に戻りますサイクル。(また、前の操作のキャリーフラグを見ただけでは、多精度の算術演算を行うことはできません。)
フラグのみを設定するテスト命令を使用することは、レジスタが不足しているアーキテクチャでレジスタのプレッシャーを軽減する方法にすぎません。十分なレジスタがある場合は、そのうちの1つを変更し、結果を無視します。入力値が0のレジスタ0を持つトリックは、レジスタの1つを0に固定する方が命令数を増やすよりも優れている場合に便利なエンコードトリックです。ターゲットとしても使用すると便利です(誤った依存関係の数を減らします)。
再度エンコード。ジャンプで条件をエンコードする場合、3つのオペランド(比較する2つとジャンプターゲット)を持つジャンプがあり、そのうち2つは即値にしたい、1つはできるだけ大きくしたい可能(ターゲットはできるだけ多くのビットを使用できるように、多くの場合、ジャンプには独自のエンコード形式があります)。または、可能性を落とします。
フラグを使用すると、フラグを設定する機会が増えます。フラグを設定できるのは比較操作だけではなく、必要なものです。(フラグを設定する操作が増えるほど、フラグを設定する最後の操作が目的のものになるように注意する必要があります)。フラグがある場合は、条件の数(多くの場合16)にフラグを設定できる命令の数を掛けてテストできます(フラグを使用していない場合、ほぼ同じ数の条件付きジャンプが発生します)テストするものがあるか、テストが簡単にできないものがあります(キャリーまたはオーバーフローなど)。
フラグのテストは簡単で、すぐに実行できます。テストが複雑になるほど、サイクル時間(またはパイプライン化されている場合はパイプライン構造)に与える影響が大きくなります。本のすべてのトリックを使用してハイエンドプロセッサに到達する場合、それは特に単純な実装に当てはまります。その影響はごくわずかです。
フラグがあるということは、多くの命令が複数の結果(自然な結果と変更された各フラグ)を持つことを意味します。また、マイクロアーキテクチャPOVからは、複数の結果が悪い(それらの関連付けを追跡する必要がある)。依存関係を導入するフラグのセットが1つしかない場合(フラグが使用されない場合は不要)、何らかの方法で処理する必要があります。繰り返しますが、特に単純な実装では、本のすべてのトリックを使用してハイエンドプロセッサに到達すると、プロセッサの残りの部分によって追加の困難がd小化されます。
32ビットマシンでは、多精度加算シーケンスの一部として使用される「キャリー付き加算」命令は、65ビットのオペランドを受け入れ、33ビットの合計を計算する必要があります。ソースレジスタの仕様は、64オペランドビットの出所を特定し、宛先レジスタの仕様は、結果の下位32ビットがどこに行くべきかを示しますが、「1つ余分に追加」オペランドまたは上位ビットをどうするか結果の?追加のオペランドがどこから来て、追加の結果ビットがどこに行くべきかを命令の一部として指定することは中程度に有用ですが、通常はオペコード内の追加フィールドを正当化するほど有用ではありません。キャリーフラグを処理するための固定された「場所」を持つことは、命令スケジューリングの観点からは少し厄介な場合がありますが、
多精度演算を可能にする命令セットを設計しようとしていて、各命令が2つの32ビットオペランドと1つの32ビット宛先オペランドに制限されている場合、4つの命令で64ビット「追加」を実装できます。 r0 + r2の場合はr5から1、それ以外の場合はゼロ、r4 = r1 + r3の計算、r5 = r4 + r5の計算、r4 = r0 + r2の計算」を超えます。キャリーフラグを補助的なソースおよびデスティネーションとして使用できるようにすると、コストがワードごとに1命令になります。
なお、フラグビットを使用または変更する命令は相互にシーケンスを維持する必要がありますが、どちらも実行しない命令は、順序を維持する必要があるため自由に再配置されます。与えられたシーケンス:
ldr r0,[r1]
add r0,r0,r2
eors r4,r5,r6
実行ユニットは、データの読み取りを待たずに3番目の命令を実行できることを非常に簡単に認識できます[r1]
が、2番目の命令がadds r0,r0,r2
あれば、実行ユニットが何かを使用しようとしたときにフラグ、ゼロフラグは3番目の命令で設定された値を保持しますが、キャリーフラグは2番目の命令で値を保持します。
簡単な答え...命令自体を除いて内部バスの使用をまったく必要としない、迅速で安価なメモリ操作。メモリなしで、スタックまたはプロセスビットなしのスタックboolとして使用できます。