• 検索結果がありません。

C/C++ コンパイラでは、関数呼び出しに対して一連の厳格な規則を適用します。特

別なランタイムサポート関数を除き、C/C++ 関数を呼び出すか C/C++ 関数によって 呼び出されるすべての関数は、以下の規則に従わなければなりません。これらの規 則に従わない場合は C/C++ 環境が損なわれ、プログラムが異常終了する恐れがあり ます。

図 7-1 に、典型的な関数の呼び出しを示します。この例では、レジスタ内に配置で

きないパラメータはスタック上の関数に渡されます。その後、関数はローカル変数 を割り当て、別の関数を呼び出します。この例では、割り当てられたローカル・フ レームおよび呼び出された関数の引数ブロックを示しています。ローカル変数をも たず、かつ引数ブロックを必要としない関数は、ローカル・フレームを割り当てま せん。

引数ブロックとは、他の関数に引数を渡すために使われるローカル・フレームの部 分を指します。パラメータは、スタックにプッシュされるのではなく引数ブロック の中へ転送され、関数に渡されます。ローカル・フレームおよび引数ブロックは同 時に割り当てられます。

図 7-1. 関数呼び出し時のスタックの使用

ࡠ࡯ࠞ࡞

ᒁᢙ ࡉࡠ࠶ࠢ

๭߮಴ߒ೨

ࡄ࡜ࡔ࡯࠲ࠍᒁᢙࡉࡠ࠶ࠢ

߅ࠃ߮࡟ࠫࠬ࠲߳ォㅍߔࠆޕ 㑐ᢙࠍ๭߮಴ߔޕ

ࡠ࡯ࠞ࡞࡮

ᣂߒ޿ࡈ࡟࡯ࡓ߅ࠃ߮

ᒁᢙࡉࡠ࠶ࠢࠍ

ഀࠅᒰߡࠆޕ

SP RPC = ᚯࠅ ࠕ࠼࡟ࠬ

関数呼び出し規則

7.3.1 関数の呼び出し方法

呼び出し側の関数は、別の関数を呼び出すときに次の作業を実行します。

1) 呼び出し先の関数が必ずしも値を保持しているとは限らないが関数の復帰後に 必要なレジスタ(エントリ時に保存(SOE)されないレジスタ)は、スタック 上に保存されます。

2) 呼び出し先の関数が構造体の場合、呼び出し元の関数が構造体のためのスペー スを割り当て、そのスペースのアドレスを呼び出し先の関数に最初の引数とし て渡します。

3) 呼び出し先の関数に渡される引数は、レジスタに、また必要に応じてスタック に置かれます。

以下に示す方針に従って、引数はレジスタに置かれます。

a) 64 ビット整数引数(long long 型)がある場合、最初の引数は ACC および P

に置かれます(ACC は上位 32 ビットを保持し、P は下位 32 ビットを保持し ます)。これ以外の 64 ビットの引数は、逆の順序でスタック上に置かれま す。

P レジスタが引数渡しのために使われる場合には、プロローグ / エピローグ 抽象化は該当関数に対しては無効です。抽象化の詳細は、3.8 節「コード・

サイズ最適化の強化(-ms オプション)」(3-18 ページ)を参照してくださ い。

b) 32 ビットの引数(long 型または float 型)がある場合、最初の引数は 32 ビッ

トの ACC(AH/AL)に置かれます。これ以外の 32 ビットの引数は、逆の順

序でスタック上に置かれます。

func1(long a, long long b, int c, int* d);

stack ACC/P XAR5, XAR4

c) ポインタ引数は、XAR4 および XAR5 に置かれます。これ以外のポインタは すべてスタック上に置かれます。

d) 残りの 16 ビットの引数は、使用できる場合には、AL、AH、XAR4、XAR5 の順に格納されます。

4) レジスタに格納されない残りの引数は、逆の順序でスタック上にプッシュされ ます。つまり、スタック上に置かれる左端の引数がスタックの最後にプッシュ されます。すべての 32 ビットの引数は、スタック上の偶数アドレスに位置合わ せされます。

構造体の引数は、構造体のアドレスとして渡されます。呼び出し先の関数は、

関数呼び出し規則

5) スタック・ポインタ(SP)は、子関数を呼び出す前に親関数によって偶数に位 置合わせされている必要があります。これを行うには、必要に応じてスタッ ク・ポインタを 1 だけ増分します。コードを記述する場合、必要に応じて呼び 出し前に SP を増分してください。

引数が置かれる場所を示す関数呼び出しの例を以下に示します。

func1 (int a, int b, long c) ; XAR4 XAR5 AH/AL func1 (long a, int b, long c) ; AH/AL XAR4 stack

vararg (int a, int b, int c, ...) AL AH stack

6) 呼び出し側が LCR 命令を使用して関数を呼び出します。 RPC レジスタ値は、ス タック上にプッシュされます。その後、戻りアドレスは、RPC レジスタに格納 されます。

7) スタックは関数境界に位置合わせされます。

7.3.2 呼び出し先の関数の対応方法

呼び出し先の関数は、以下の作業を実行します。

1) 呼び出し先の関数が XAR1、XAR2、XAR3 のいずれかを変更する場合、呼び出 し先ではそのレジスタを保存する必要があります。これは呼び出し元の関数は これらのレジスタの値が復帰時に保存されるということを想定しているからで す。これら以外のレジスタでは、その値を保存せず変更される場合があります。

2) 呼び出し先の関数は、ローカル変数、一時記憶域、およびこの関数が呼び出す 関数の引数用に十分なスペースをスタック上に割り当てます。この割り当ては、

SP レジスタに定数を加算することにより、関数の先頭で一度行われます。

3) スタックは関数境界に位置合わせされます。

呼び出し先の関数が構造体引数を要求する場合、関数は構造体を指すポインタ

関数呼び出し規則

5) 呼び出し先の関数は、関数のコードを実行します。

6) 呼び出し先の関数は値を戻します。次の規則を使用して、その値はレジスタに 格納されます。

16ビット整数値: AL 32 ビット整数値: ACC

64 ビット整数値: ACC/P

16 または 22 ビット・ポインタ: XAR4

関数が構造体を戻す場合、呼び出し元は構造体用のスペースを割り当ててから、

XAR4 内の呼び出し先の関数に戻りスペースのアドレスを渡します。構造体を戻

すには、呼び出し先関数は、その構造体を追加の引数が指すメモリ・ブロックに コピーします。

このように、呼び出し元が呼び出し先関数に構造体を戻す場所を指示するという ことは優れているといえます。たとえば、s= f(x) という文で、S は構造体、F は 構造体を戻す関数とします。呼び出し元は、f(&s, x) として実際に呼び出しを行 うことができます。関数 f は戻りの構造体を直接 s にコピーし、自動的に代入を 行います。

呼び出し元が戻りの構造体値を使用しない場合は、最初の引数としてアドレス値 0 を渡すことができます。この 0 は呼び出し先関数に対して、戻りの構造体をコ ピーしないように指示します。

構造体を戻す関数を宣言する場合は、(さらに引数が渡されるように)その関数 が呼び出される時点、および(その関数が結果をコピーすることを認識するよう に)宣言される時点の両方で、正しく宣言するように注意が必要です。64 ビット の浮動小数点値(long double 型)を戻すことは、構造体に戻すことと似ています。

7) 呼び出し先の関数は、SP に以前加算した値を減算することでフレームの割り当 てを解除します。

8) 呼び出し先の関数は、ステップ 1 で保存したレジスタの値をすべて復元します。

9) 呼び出し先の関数は、LRETR 命令を使って値を戻します。PC は RPC レジスタ 内の値に設定されます。以前の RPC 値は、スタックからポップされ、RPC レジ スタに格納されます。

関数呼び出し規則

7.3.3 呼び出し先関数の特殊な場合(ラージ・フレーム)

スタックに割り当てられる必要があるスペース(前項のステップ 2)が 63 ワードよ り大きい場合、すべてのローカルの非レジスタ変数に確実にアクセスできるように するには、ステップとリソースがさらに必要になります。ローカルの非レジスタ変 数を参照するために、ラージ・フレームはフレーム・ポインタ・レジスタ(XAR2) を使用することを要求します。フレームにスペースを割り当てる前に、呼び出し先 の関数に渡される、スタック上の最初の引数を指すようにフレーム・ポインタを設 定します。渡されるはずの引数がスタック上に渡されない場合、フレーム・ポイン タは呼び出し元の関数へのエントリ時にスタックの一番上にある呼び出し元の関数 の戻りアドレスを指します。

大量のローカル・データを割り当てることは可能であれば避けてください。たとえ ば、大きな配列を関数内で宣言しないでください。

7.3.4 引数とローカル変数のアクセス方法

関数は、SP または FP(XAR2 に指定されているフレーム・ポインタ)のいずれかか

らローカルの非レジスタ変数およびスタック引数を間接的にアクセスします。SP を 使ってアクセスできるすべてのローカル・データおよび引数のデータは、*-SP [offset]

アドレッシング・モードを使用します。これは SP がスタックの一番上をすぎたデー タを指すので、スタックがより大きなアドレスに向かって増大するからです。

: PAGE0 モード・ビットはリセットする必要がある

コンパイラは *-SP [offset] アドレッシング・モードを使うので、PAGE0 モード・ビッ トはリセット(0 に設定)する必要があります。

*-SP [offset] を使ったときに得られる最大のオフセットは 63 です。オブジェクトが SP から離れすぎているのでこのアクセス・モードを使用できない場合、コンパイラ

は FP (XAR2) を使用します。FP はフレームの最後を指すので、FP を使って行われ

るアクセスは *+FP [offset] または *+FP [AR0/AR1] のいずれかのアドレッシング・