Compiler-design-runtime-environment
コンパイラー設計-ランタイム環境
ソースコードとしてのプログラムは、単なるテキスト(コード、ステートメントなど)の集まりであり、それを生かすには、ターゲットマシンで実行するアクションが必要です。 プログラムは、命令を実行するためにメモリリソースを必要とします。 プログラムには、実行時の実際のメモリ位置とのマッピングを必要とするプロシージャ、識別子などの名前が含まれています。
ランタイムとは、実行中のプログラムを意味します。 ランタイム環境は、システムで実行されているプロセスにサービスを提供するためのソフトウェアライブラリ、環境変数などを含むターゲットマシンの状態です。
ランタイムサポートシステムはパッケージであり、主に実行可能プログラム自体で生成され、プロセスとランタイム環境の間のプロセス通信を容易にします。 プログラムの実行中にメモリの割り当てと割り当て解除を行います。
アクティベーションツリー
プログラムは、いくつかの手順に組み合わされた一連の命令です。 プロシージャ内の命令は順番に実行されます。 プロシージャには開始デリミタと終了デリミタがあり、その中のすべてはプロシージャの本体と呼ばれます。 プロシージャ識別子とその中の有限命令のシーケンスは、プロシージャの本体を構成します。
プロシージャの実行は、アクティベーションと呼ばれます。 アクティベーションレコードには、プロシージャの呼び出しに必要なすべての情報が含まれています。 アクティベーションレコードには、使用するソース言語に応じて、次のユニットが含まれる場合があります。
Temporaries | Stores temporary and intermediate values of an expression. |
Local Data | Stores local data of the called procedure. |
Machine Status | Stores machine status such as Registers, Program Counter etc., before the procedure is called. |
Control Link | Stores the address of activation record of the caller procedure. |
Access Link | Stores the information of data which is outside the local scope. |
Actual Parameters | Stores actual parameters, i.e., parameters which are used to send input to the called procedure. |
Return Value | Stores return values. |
プロシージャが実行されるたびに、そのアクティベーションレコードはコントロールスタックとも呼ばれるスタックに格納されます。 プロシージャが別のプロシージャを呼び出すと、呼び出されたプロシージャの実行が完了するまで、呼び出し元の実行は一時停止されます。 この時点で、呼び出されたプロシージャのアクティベーションレコードはスタックに格納されます。
プログラム制御はシーケンシャルに流れ、プロシージャが呼び出されると、その制御は呼び出されたプロシージャに転送されると想定しています。 呼び出されたプロシージャが実行されると、呼び出し元にコントロールを返します。 このタイプの制御フローにより、一連のアクティベーションを*アクティベーションツリー*と呼ばれるツリー形式で簡単に表すことができます。
この概念を理解するために、例としてコードを取り上げます。
. . .
printf(“Enter Your Name: “);
scanf(“%s”, username);
show_data(username);
printf(“Press any key to continue…”);
. . .
int show_data(char *user)
{
printf(“Your name is %s”, username);
return 0;
}
. . .
以下は、指定されたコードのアクティベーションツリーです。
プロシージャは深さ優先で実行されるため、スタックの割り当てがプロシージャのアクティブ化に最適なストレージ形式であることがわかりました。
ストレージ割り当て
ランタイム環境は、次のエンティティのランタイムメモリ要件を管理します。
- コード:実行時に変更されないプログラムのテキスト部分として知られています。 そのメモリ要件は、コンパイル時に知られています。
- 手順:テキスト部分は静的ですが、ランダムに呼び出されます。 そのため、スタックストレージはプロシージャコールとアクティベーションの管理に使用されます。
- 変数:変数は、グローバルまたは定数でない限り、実行時にのみ認識されます。 ヒープメモリ割り当てスキームは、実行時の変数のメモリの割り当てと割り当て解除を管理するために使用されます。
静的割り当て
この割り当て方式では、コンパイルデータはメモリ内の固定位置にバインドされ、プログラムの実行時に変更されません。 メモリ要件とストレージの場所は事前にわかっているため、メモリの割り当てと割り当て解除のランタイムサポートパッケージは必要ありません。
スタック割り当て
プロシージャコールとそのアクティブ化は、スタックメモリの割り当てによって管理されます。 これは後入れ先出し(LIFO)メソッドで機能し、この割り当て戦略は再帰的なプロシージャコールに非常に役立ちます。
ヒープ割り当て
プロシージャのローカル変数は、実行時にのみ割り当ておよび割り当て解除されます。 ヒープ割り当ては、変数にメモリを動的に割り当て、変数が不要になったときにメモリを要求するために使用されます。
静的に割り当てられたメモリ領域を除き、スタックメモリとヒープメモリの両方が動的に予期せずに拡大および縮小する可能性があります。 したがって、システムに一定量のメモリを提供することはできません。
上の画像に示すように、コードのテキスト部分には一定量のメモリが割り当てられます。 スタックおよびヒープメモリは、プログラムに割り当てられた合計メモリの極端に配置されます。 両方が互いに収縮および成長します。
パラメータの受け渡し
プロシージャ間の通信媒体は、パラメータの受け渡しとして知られています。 呼び出し元プロシージャからの変数の値は、何らかのメカニズムによって呼び出されたプロシージャに転送されます。 先に進む前に、まずプログラムの値に関するいくつかの基本的な用語を確認します。
r値
式の値は、そのr値と呼ばれます。 単一の変数に含まれる値は、代入演算子の右側に表示される場合もr値になります。 r値は、常に他の変数に割り当てることができます。
左辺値
式が保存されるメモリ(アドレス)の場所は、その式のl値として知られています。 常に代入演算子の左側に表示されます。
例えば:
day = 1;
week = day *7;
month = 1;
year = month* 12;
この例から、1、7、12などの定数値、および日、週、月、年などの変数はすべてr値を持つことがわかります。 変数に割り当てられたメモリ位置も表すため、変数のみにl値があります。
例えば:
7 = x + y;
定数7はメモリ位置を表さないため、l値エラーです。
正式なパラメーター
呼び出し元プロシージャから渡された情報を取る変数は、仮パラメータと呼ばれます。 これらの変数は、呼び出された関数の定義で宣言されます。
実パラメータ
値またはアドレスが呼び出されたプロシージャに渡される変数は、実パラメータと呼ばれます。 これらの変数は、関数呼び出しで引数として指定されます。
例:
fun_one()
{
int actual_parameter = 10;
call fun_two(int actual_parameter);
}
fun_two(int formal_parameter)
{
print formal_parameter;
}
仮パラメーターは、使用されるパラメーターの受け渡し方法に応じて、実際のパラメーターの情報を保持します。 値またはアドレスの場合があります。
値渡し
値渡しのメカニズムでは、呼び出し元のプロシージャが実際のパラメータのr値を渡し、コンパイラはそれを呼び出し先のプロシージャのアクティベーションレコードに入れます。 仮パラメータは、呼び出し側プロシージャから渡された値を保持します。 仮パラメータが保持する値が変更された場合、実際のパラメータに影響はありません。
参照渡し
参照メカニズムによるパスでは、実際のパラメーターのl値が、呼び出されたプロシージャのアクティベーションレコードにコピーされます。 このようにして、呼び出されたプロシージャは実際のパラメータのアドレス(メモリ位置)を持ち、仮パラメータは同じメモリ位置を参照します。 したがって、仮パラメータが指す値が変更された場合、実際のパラメータも同じ値を指すはずなので、実際のパラメータに影響が見られるはずです。
コピー復元で渡す
このパラメーター受け渡しメカニズムは、「参照による受け渡し」と同様に機能しますが、実際のパラメーターへの変更は、呼び出されたプロシージャの終了時に行われます。 関数呼び出し時に、実際のパラメーターの値は、呼び出されたプロシージャーの活動化レコードにコピーされます。 仮パラメーターを操作した場合、実際のパラメーターにリアルタイムの影響はありません(l値が渡されるため)が、呼び出されたプロシージャが終了すると、仮パラメーターのl値は実パラメーターのl値にコピーされます。
例:
int y;
calling_procedure()
{
y = 10;
copy_restore(y);//l-value of y is passed
printf y;//prints 99
}
copy_restore(int x)
{
x = 99;//y still has value 10 (unaffected)
y = 0;//y is now 0
}
この関数が終了すると、仮パラメータxのl値が実際のパラメータyにコピーされます。 プロシージャの終了前にyの値が変更された場合でも、xのl値はyのl値にコピーされ、参照による呼び出しのように動作します。
名前で渡す
Algolのような言語は、C言語のプリプロセッサのように機能する新しい種類のパラメーター受け渡しメカニズムを提供します。 名前渡しメカニズムでは、呼び出されるプロシージャの名前が実際の本体に置き換えられます。 名前渡しは、参照渡しのように、実際のパラメータで動作できるように、プロシージャの本文の対応するパラメータのプロシージャ呼び出しの引数式をテキストで置き換えます。