64

呼び出し元と呼び出し先の保存済みレジスタの違いと、いつ何を使用するかを理解するのに問題があります。

私はMSP430を使用しています:

手順:

mov.w #0,R7 
mov.w #0,R6 
add.w R6,R7 
inc.w R6 
cmp.w R12,R6 
jl l$loop 
mov.w R7,R12
ret

上記のコードは呼び出し先であり、教科書の例で使用されているため、規則に従います。R6とR7は呼び出し先が保存され、R12は呼び出し元が保存されます。私の理解では、呼び出し先が保存したregは、プロシージャ内の値を変更してもプロシージャ外の値に影響を与えないという意味で「グローバル」ではありません。これが、最初に呼び出し先regに新しい値を保存する必要がある理由です。

R12、保存された発信者は「グローバル」です。より良い言葉がないためです。プロシージャが行うことは、呼び出し後のR12に永続的な影響を及ぼします。

私の理解は正しいですか?私は他のものが欠けていますか?

4

5 に答える 5

132

呼び出し元が保存したレジスタ(別名、揮発性レジスタ、またはcall-clobbered)は、呼び出し間で保存する必要のない一時的な量を保持するために使用されます。

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

callただし、これらのレジスタの一時的な値を破棄するのは通常のことです。

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

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

于 2013-04-28T17:25:29.313 に答える
19

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

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

于 2012-02-13T22:11:20.083 に答える
12

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

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

実際には、効率的なコードを使用すると、値が不要になったときに値を破棄できます。コンパイラは通常、関数の開始時にいくつかの呼び出し保存レジスタを保存する(そして最後にそれらを復元する)関数を作成します。関数内では、関数呼び出し間で存続する必要がある値にこれらの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システムコールの呼び出し規約を参照してください。

于 2019-05-17T00:22:00.747 に答える
3

発信者が保存した(別名、揮発性またはコールクローバー)レジスタ

  • 呼び出し元が保存したレジスタの値は短期間のものであり、呼び出しごとに保存されません。  
  • 一時的な(つまり短期的な)データを保持します

Callee-Saved(AKA不揮発性または通話保存)レジスタ

  • 呼び出し先に保存されたレジスタは、呼び出し全体で値を保持し、長期的です
  • 複数の関数/呼び出しを通じて使用される非一時的(つまり長期)データを保持します
于 2020-08-22T18:05:18.017 に答える
1

これが何かを追加するかどうかはよくわかりませんが、

呼び出し元が保存されたということは、呼び出しでレジスターがクローバーされ、呼び出しが戻った後、クローバーされた状態のままになるため、呼び出し元がレジスターを保存する必要があることを意味します(たとえば、eaxcdeclの戻り値はありません。戻り値は戻り値であるため、呼び出し先が呼び出す前の値に復元されることを意味します)。

呼び出し先が保存されたということは、関数が戻った後に同じ値を含むことが呼び出し元に保証されているため、呼び出し先がレジスターを保存してから呼び出しの最後にそれらを復元する必要があることを意味します。通話中のある時点で破壊されます。

eaxただし、上記の定義の問題は、たとえばWikipedia cdeclで、呼び出し元が保存されecxedx残りが呼び出し先が保存されることです。これは、これらのレジスタのいずれも保存されていない場合でも、呼び出し元がこれらの3つのレジスタすべてを保存する必要があることを示しています。そもそも発信者によって使用されました。この場合、呼び出し元の「保存済み」は誤った名称になりますが、「呼び出しの変更」は引き続き正しく適用されます。これは、「残り」が呼び出し先保存と呼ばれるのと同じです。これは、他のすべてのx86レジスタが、呼び出しで使用されないレジスタがある場合に、呼び出し先によって保存および復元されることを意味します。cdeclではeax:edx、64ビット値を返すために使用できます。ecx必要に応じて発信者も保存される理由はわかりませんが、保存されています。

于 2020-03-28T16:56:08.117 に答える