呼び出し先は、一時的に値を変更する必要がある、呼び出し先が保存した (不揮発性、呼び出し保存) レジスタを保存/復元するだけで済みます (そのうちのいくつかは、スタック チェーン / スタックトレース内の呼び出し元によって使用されない可能性がありますが、呼び出し先は使用しません)。呼び出し元は、呼び出し後に必要な呼び出し元が保存した (揮発性で、呼び出しが上書きされた) レジスタを保存/復元するだけで済みます (将来のスタック チェーンの呼び出し先は実際には変更しない可能性がありますが、呼び出し元は変更しません)。これは知らない)。
通常、少なくとも Microsoft x64 呼び出し規約では、明示的に保存された不揮発性レジスタがスタックに多数表示されますが、明示的に保存された揮発性レジスタは表示されません。コンパイラは、呼び出し元が明示的に保存する必要がある段階に到達しないという考えがあると思います。呼び出しの直前のレジスタ、特にプログラム自体の変数ではない式。代わりに、事前に計画してこれらのレジスタを完全に使用しないようにすることができます。レジスタを使用しますが、スタックから離れた変数バッキング ストアを最適化しません。呼び出し先関数を呼び出した後に死んでいる、呼び出し先関数に渡されるパラメータにレジスタを使用します。プログラムで変数として定義するか、揮発性レジスタを使用します。
呼び出し先は、関数のプロローグでスタックに対して行った呼び出しの周りで変更を維持する必要がある不揮発性レジスタを明示的にプッシュし、エピローグでそれらを復元します。それらを揮発性レジスタに保存できますが、呼び出し先関数自体が呼び出しを行い、それを別のレジスタに保存できない場合は、それらを不揮発性レジスタに復元するか、スタックに保存する必要があります (この場合、保存/保存はスピルと呼ばれます)。不揮発性レジスタは、そのレジスタも保存する必要があるためです。
caller saved は、呼び出し元が使用するかどうかに関係なく、レジスタを保存する必要があることを意味することに同意します。これは正しくなく、レジスタを使用する場合でも、レジスタを保存する必要さえない場合があります。これは、呼び出し後にレジスタが必要ないか、まったく呼び出しを行わない可能性があることがわかっているためです。
均等なバランスが良いです。1 つのすべてを持ち、他のタイプを持たないことは不利な点にすぎませんが、1 つのタイプ (たとえば、そのレジスターが呼び出し元ではなく呼び出し先関数で主に使用される可能性がある不揮発性など) にバイアスをかけることが最適な場合があります。 Peterがレジスタで提案したように機能しxmm
ます。
呼び出し後に呼び出し元で死んでいる可能性のあるパラメーターを保存することになるため、すべての不揮発性レジスターを持つことは、すべて揮発性レジスターを持つことよりも害があると思います (これがパラメーターが揮発性である理由です。さらに、戻り値レジスターを保持することは不可能なので、そのために少なくとも 1 つの揮発性レジスタを用意するか、スタックに値を返す必要がありますが、これは遅くなります)。また、スタックに値を保存せずにレジスタを一時的に変更することもできません。不揮発性レジスタのみが利用可能ですが、それらがすべて揮発性レジスタである場合は、呼び出しが行われるまで、または呼び出しがまったくない場合に値をレジスタに格納できます。(ベース フレームでない限り) 呼び出し元関数は常に存在しますが、ベース フレームよりもはるかに多くのリーフ関数があります。
すべてのレジスターが揮発性だったとしても、非揮発性レジスターを使用すると独自のアプリケーションのコンパイルが容易になるため、これは依然として不利です。さらに、すべての揮発性レジスタはトラップ フレームを作成するときに保存され、不揮発性レジスタは保存されないため (これは、例外またはコンテキスト スイッチがない限り、少なくとも Microsoft x64 呼び出し規約に当てはまります)、より多くの時間/スペース ペナルティが発生します。すべてのレジスタが揮発性である場合、通常のシステム コールの場合。