3

私は JIT コンパイラをプログラミングしていますが、非常に多くの x86-64 レジスタが Win64 呼び出し規約で不揮発性 (callee-preserved) であることを発見して驚いています。不揮発性レジスタは、これらのレジスタを使用できるすべての関数でより多くの作業を行うように思えます。これは、高度に最適化された行列乗算など、リーフ関数で多くのレジスタを使用する数値計算の場合に特に当てはまります。ただし、たとえば、16 個の SSE レジスタのうち 6 個だけが揮発性であるため、それ以上を使用する必要がある場合は、多くのスピルを行う必要があります。

ええ、わかりません。ここでのトレードオフは何ですか?

4

4 に答える 4

5

レジスターが呼び出し元保存である場合、呼び出し元は常に関数呼び出しの前後でそれらのレジスターを保存または再ロードする必要があります。ただし、レジスターが呼び出し先保存である場合、呼び出し先は、使用するレジスターを保存するだけでよく、それらが使用されることがわかっている場合にのみ(つまり、早期終了シナリオではまったく保存されない可能性があります)。この規則の欠点は、呼び出し先が呼び出し元の知識を持っていないため、とにかく死んでいるレジスターを保存する可能性があることですが、それは小さな懸念事項と見なされていると思います。

于 2012-05-01T02:46:14.657 に答える
0

nonvolatileレジスタを持つ利点は次のとおりです。パフォーマンス

移動するデータが少ないほど、CPU の効率が高くなります。

レジスタが多いほどvolatile、CPU が必要とするエネルギーが多くなります。

于 2016-01-16T08:04:45.900 に答える
0

呼び出し先は、一時的に値を変更する必要がある、呼び出し先が保存した (不揮発性、呼び出し保存) レジスタを保存/復元するだけで済みます (そのうちのいくつかは、スタック チェーン / スタックトレース内の呼び出し元によって使用されない可能性がありますが、呼び出し先は使用しません)。呼び出し元は、呼び出し後に必要な呼び出し元が保存した (揮発性で、呼び出しが上書きされた) レジスタを保存/復元するだけで済みます (将来のスタック チェーンの呼び出し先は実際には変更しない可能性がありますが、呼び出し元は変更しません)。これは知らない)。

通常、少なくとも Microsoft x64 呼び出し規約では、明示的に保存された不揮発性レジスタがスタックに多数表示されますが、明示的に保存された揮発性レジスタは表示されません。コンパイラは、呼び出し元が明示的に保存する必要がある段階に到達しないという考えがあると思います。呼び出しの直前のレジスタ、特にプログラム自体の変数ではない式。代わりに、事前に計画してこれらのレジスタを完全に使用しないようにすることができます。レジスタを使用しますが、スタックから離れた変数バッキング ストアを最適化しません。呼び出し先関数を呼び出した後に死んでいる、呼び出し先関数に渡されるパラメータにレジスタを使用します。プログラムで変数として定義するか、揮発性レジスタを使用します。

呼び出し先は、関数のプロローグでスタックに対して行った呼び出しの周りで変更を維持する必要がある不揮発性レジスタを明示的にプッシュし、エピローグでそれらを復元します。それらを揮発性レジスタに保存できますが、呼び出し先関数自体が呼び出しを行い、それを別のレジスタに保存できない場合は、それらを不揮発性レジスタに復元するか、スタックに保存する必要があります (この場合、保存/保存はスピルと呼ばれます)。不揮発性レジスタは、そのレジスタも保存する必要があるためです。

caller saved は、呼び出し元が使用するかどうかに関係なく、レジスタを保存する必要があることを意味することに同意します。これは正しくなく、レジスタを使用する場合でも、レジスタを保存する必要さえない場合があります。これは、呼び出し後にレジスタが必要ないか、まったく呼び出しを行わない可能性があることがわかっているためです。

均等なバランスが良いです。1 つのすべてを持ち、他のタイプを持たないことは不利な点にすぎませんが、1 つのタイプ (たとえば、そのレジスターが呼び出し元ではなく呼び出し先関数で主に使用される可能性がある不揮発性など) にバイアスをかけることが最適な場合があります。 Peterがレジスタで提案したように機能しxmmます。

呼び出し後に呼び出し元で死んでいる可能性のあるパラメーターを保存することになるため、すべての不揮発性レジスターを持つことは、すべて揮発性レジスターを持つことよりも害があると思います (これがパラメーターが揮発性である理由です。さらに、戻り値レジスターを保持することは不可能なので、そのために少なくとも 1 つの揮発性レジスタを用意するか、スタックに値を返す必要がありますが、これは遅くなります)。また、スタックに値を保存せずにレジスタを一時的に変更することもできません。不揮発性レジスタのみが利用可能ですが、それらがすべて揮発性レジスタである場合は、呼び出しが行われるまで、または呼び出しがまったくない場合に値をレジスタに格納できます。(ベース フレームでない限り) 呼び出し元関数は常に存在しますが、ベース フレームよりもはるかに多くのリーフ関数があります。

すべてのレジスターが揮発性だったとしても、非揮発性レジスターを使用すると独自のアプリケーションのコンパイルが容易になるため、これは依然として不利です。さらに、すべての揮発性レジスタはトラップ フレームを作成するときに保存され、不揮発性レジスタは保存されないため (これは、例外またはコンテキスト スイッチがない限り、少なくとも Microsoft x64 呼び出し規約に当てはまります)、より多くの時間/スペース ペナルティが発生します。すべてのレジスタが揮発性である場合、通常のシステム コールの場合。

于 2021-03-28T19:21:51.503 に答える