呼び出し元が保存した/呼び出し先が保存した用語は、かなり頭がおかしい非効率的なプログラミングモデルに基づいており、呼び出し元は実際にすべての呼び出しが中断されたレジスタを保存/復元し(長期的に有用な値を他の場所に保持するのではなく)、呼び出し先は実際に保存します/すべての呼び出し保存レジスタを復元します(それらの一部または一部を使用しないのではなく)。
または、「発信者が保存した」とは「後で値が必要な場合に何らかの方法で保存した」ことを意味することを理解する必要があります。
実際には、効率的なコードを使用すると、値が不要になったときに値を破棄できます。コンパイラは通常、関数の開始時にいくつかの呼び出し保存レジスタを保存する(そして最後にそれらを復元する)関数を作成します。関数内では、関数呼び出し間で存続する必要がある値にこれらのregを使用します。
私は「call-preserved」と「call-clobbered」を好みます。これは、基本的な概念を聞いた後は明確で自己記述的であり、発信者の視点や呼び出し先の視点。(どちらの用語も同じ観点からのものです)。
さらに、これらの用語は複数の文字で異なります。
揮発性/不揮発性という用語は、電力損失の有無にかかわらずその価値を失うストレージ(DRAMとフラッシュのように)との類似性からすると、かなり良いものです。ただし、Cvolatile
キーワードの技術的な意味はまったく異なるため、Cの呼び出し規約を説明する場合の「(不揮発性)揮発性」の欠点です。
- Call-clobbered、別名caller-savedまたはvolatileレジスタは、次の関数呼び出しの後に必要とされないスクラッチ/一時値に適しています。
呼び出し先の観点からは、関数は保存/復元せずにこれらのレジスタを自由に上書き(別名clobber)できます。
呼び出し元の観点からは、call foo
すべてのcall-clobberedレジスターを破棄します(別名clobbers)。または、少なくとも、そうなると想定する必要があります。
カスタム呼び出し規約を持つプライベートヘルパー関数を作成できます。たとえば、特定のレジスタを変更しないことがわかっています。ただし、ターゲット関数が通常の呼び出し規約に従っていることだけを知っている(または想定または依存したい)場合は、関数呼び出しを、呼び出しが中断されたすべてのレジスタを破棄するかのように扱う必要があります。それは文字通りその名前の由来です。呼び出しはそれらのレジスターを覆い隠します。
手続き間最適化を行う一部のコンパイラーは、カスタム呼び出し規約を使用して、ABIに従わない関数の内部使用のみの定義を作成することもできます。
- 呼び出し保存、別名呼び出し先保存、または不揮発性レジスタは、関数呼び出し全体で値を保持します。これは、関数呼び出しを行うループ内のループ変数、または基本的に非リーフ関数内の一般的なものすべてに役立ちます。
呼び出し先の観点からは、元の値をどこかに保存して戻る前に復元できない限り、これらのレジスタを変更することはできません。または、スタックポインタ(ほとんどの場合、呼び出し保存)のようなレジスタの場合、古い値を実際にどこかに保存する代わりに、既知のオフセットを減算して、戻る前に再度加算することができます。つまり、ランタイム可変量のスタックスペースを割り当てない限り、推測航法で復元できます。次に、通常、別のレジスタからスタックポインタを復元します。
多くのレジスターを使用することで恩恵を受けることができる関数は、関数呼び出しを行わなくても、それらをより一時的なものとして使用できるように、一部の呼び出し保存レジスターを保存/復元できます。通常、これを行うのは、使用するcall-clobberedレジスタが不足した後でのみです。これは、保存/復元には通常、関数の開始/終了時にプッシュ/ポップがかかるためです。(または、関数に複数の出口パスがある場合はpop
、それぞれにaがあります。)
「発信者が保存した」という名前は誤解を招く可能性があります。特別に保存/復元する必要はありません。通常、コードは、呼び出し保存レジスター、スタックのどこか、またはリロードできる他の場所での関数呼び出しに耐える必要がある値を持つように配置します。call
一時的な値を破棄するのは通常のことです。
ABIまたは呼び出し規約は、どれがどれであるかを定義します
たとえば、 x86-64 SystemVABIのLinuxx86-64関数呼び出しによって保持されるレジスタを参照してください。
また、arg-passingレジスタは、私が知っているすべての関数呼び出し規約で常にcall-clobberedです。rdiおよびrsiの呼び出し元は保存されていますか、または呼び出し先はレジスタを保存していますか?を参照してください。
ただし、システムコールの呼び出し規約では、通常、戻り値を除くすべてのレジスタが呼び出し保存されます。(通常、条件コード/フラグも含まれます。)i386およびx86-64でのUNIXおよびLinuxシステムコールの呼び出し規約を参照してください。