呼び出し先と呼び出し元の保存済みレジスターとは何ですか?



What Are Callee Caller Saved Registers



解決:

発信者が保存したレジスタ (別名 揮発性 レジスタ、または コールクローバー )は、呼び出し間で保持する必要のない一時的な数量を保持するために使用されます。

そのため、これらのレジスタをスタックにプッシュするか、別の場所にコピーするのは呼び出し元の責任です。 もしも プロシージャコールの後にこの値を復元したいと考えています。



を許可するのは正常ですただし、これらのレジスタのdestroy一時値を呼び出します。

呼び出し先に保存されたレジスタ (別名 不揮発性 レジスタ、または 通話保存 )は、呼び出し間で保持する必要がある長期的な値を保持するために使用されます。



呼び出し元がプロシージャコールを行う場合、呼び出し先が戻った後、これらのレジスタは同じ値を保持することが期待できるため、呼び出し元に戻る前に、それらを保存して復元するのは呼び出し先の責任です。またはそれらに触れないために。


保存された呼び出し先と呼び出し元は、呼び出し全体でレジスターの値を保存および復元する責任がある人の規則です。すべてのレジスタは「グローバル」であり、どこのコードでもレジスタを表示(または変更)でき、それらの変更は後のコードでどこでも表示されます。レジスタ保存規則のポイントは、他のコードは値が変更されないと想定しているため、コードが特定のレジスタを変更することは想定されていないということです。

サンプルコードでは、レジスタ値を保存または復元しようとしないため、どのレジスタも呼び出し先保存ではありません。ただし、未定義のラベルへの分岐が含まれているため、プロシージャ全体ではないように見えます(l $ loop)。したがって、一部のレジスタを呼び出し先の保存として扱うのは、プロシージャの途中からのコードの断片である可能性があります。保存/復元の手順が欠落しているだけです。




呼び出し元が保存した/呼び出し先が保存した用語は、かなり頭がおかしい非効率的なプログラミングモデルに基づいており、呼び出し元は実際にすべての呼び出しが中断されたレジスタを保存/復元し(長期的に有用な値を他の場所に保持するのではなく)、呼び出し先は実際に保存します/すべての呼び出し保存レジスタを復元します(それらの一部または一部を使用しないのではなく)。

または、「発信者が保存した」とは「何らかの方法で保存した」という意味であることを理解する必要があります もしも 後で値が必要です」。

実際には、効率的なコードを使用すると、値が不要になったときに値を破棄できます。コンパイラーは通常、関数の開始時にいくつかの呼び出し保存レジスターを保存する(そしてそれらを最後に復元する)関数を作成します。関数内では、関数呼び出し間で存続する必要がある値にこれらのregを使用します。

私は「call-preserved」よりも「call-clobbered」の方が好きです 、基本的な概念を聞いたら明確で自己記述的であり、発信者の視点または着信者の視点から考えるために深刻な精神体操を必要としません。 (どちらの用語も 同じ 視点)。

さらに、これらの用語は複数の文字で異なります。

用語 揮発性/不揮発性 電力損失でその価値を失うかどうかにかかわらず、ストレージとの類推によって、かなり良いです(DRAM対フラッシュのように)。しかし、Cvolatileキーワードの技術的意味はまったく異なるため、Cの呼び出し規約を説明する場合、「(non)-volatile」の欠点になります。


  • コールクローバー 、別名 発信者が保存 また 揮発性 レジスターは、次の関数呼び出しの後で必要とされないスクラッチ/一時値に適しています。

呼び出し先の観点からは、関数はこれらのレジスタを保存/復元せずに自由に上書き(別名クローバー)できます。

発信者の観点から、call fooは、すべてのcall-clobberedレジスタを破棄します(別名clobbers)。または、少なくとも、そうなると想定する必要があります。

カスタム呼び出し規約を持つプライベートヘルパー関数を作成できます。あなたは彼らが特定のレジスタを変更しないことを知っています。ただし、ターゲット関数が通常の呼び出し規約に従っていることだけを知っている(または想定または依存したい)場合は、関数呼び出しを、すべての呼び出しが中断されたレジスタを破棄するかのように扱う必要があります。それは文字通りその名前の由来です。呼び出しはそれらのレジスターを覆い隠します。

手続き間最適化を行う一部のコンパイラーは、カスタム呼び出し規約を使用して、ABIに準拠しない関数の内部使用のみの定義を作成することもできます。

  • 通話保存 、別名 callee-saved また 不揮発性 レジスタ 関数呼び出し間で値を保持する 。これは、関数呼び出しを行うループ内のループ変数、または基本的に非リーフ関数内の一般的なものすべてに役立ちます。

呼び出し先の観点からは、元の値をどこかに保存して戻る前に復元できない限り、これらのレジスタを変更することはできません。または、スタックポインタ(ほとんどの場合、呼び出し保存)のようなレジスタの場合、実際の代わりに、既知のオフセットを減算して、戻る前に再度加算することができます。 保存 どこでも古い値。つまり、ランタイム可変量のスタックスペースを割り当てない限り、推測航法で復元できます。次に、通常、別のレジスタからスタックポインタを復元します。

多くのレジスターを使用することで恩恵を受けることができる関数は、関数呼び出しを行わなくても、それらをより一時的なものとして使用できるように、いくつかの呼び出し保存レジスターを保存/復元できます。通常、これを行うのは、使用するコールクローバーレジスタが不足した後でのみです。これは、保存/復元には通常、関数の開始/終了時にプッシュ/ポップがかかるためです。 (または、関数に複数の出口パスがある場合、それらのそれぞれにポップします。)


「発信者が保存した」という名前は誤解を招く可能性があります。 持ってる それらを特別に保存/復元します。通常、コードは、呼び出し保存レジスター、スタックのどこか、またはリロードできる他の場所での関数呼び出しに耐える必要がある値を持つように配置します。を許可するのは正常ですdestroy一時値を呼び出します。


ABIまたは呼び出し規約は、どれがどれであるかを定義します

たとえば、x86-64 System VABIのlinuxx86-64関数呼び出しによって保持されるレジスタを参照してください。

また、arg-passingレジスタは、私が知っているすべての関数呼び出し規約で常にcall-clobberedです。 rdiおよびrsiの呼び出し元は保存されていますか、それとも呼び出し先はレジスタを保存していますか?を参照してください。

ただし、システムコールの呼び出し規約では、通常、戻り値を除くすべてのレジスタが呼び出し保存されます。 (通常、条件コード/フラグも含まれます。)i386およびx86-64でのUNIXおよびLinuxシステムコールの呼び出し規約を参照してください。