[Contents] [Back] [Prev] [Up] [Next] [Forward]
コンパイラの全体的な制御構造は、
`toplev.c'にあります。
このファイルは、
初期化、引数のデコード、ファイルのオープンとクローズ、パスの実行順序に責任を持ちます。
解析パスは、
入力全体を解析するために、
一度だけ実行されます。
関数が解析されると、
その関数に対応するRTL中間コードが、
一度に一文ずつ生成されます。
個々の文は、
構文ツリーとして読み込まれ、
RTLに変換されます。
その後に、
その文に対応するツリーの記憶域が解放されます。
型(および、そのサイズを表す式)、宣言、
およびバインディングの輪郭(binding contour)とそのネストの仕方を表す情報のための記憶域は、
関数のコンパイルが終了するまで保持されます。
これらはいずれも、
デバッグ情報を出力するのに必要となります。
解析パスにおいて、
完全な関数定義、
またはトップ・レベルの宣言が読み込まれる度に、
`toplev.c'の中の関数rest_of_compilation
、
または関数rest_of_decl_compilation
が呼び出されます。
この関数は、
必要な残余の処理すべての実行に責任を持ち、
最終的には、
アセンブラ言語を出力します。
コンパイラの、
解析パス以外のすべてのパスは、
rest_of_compilation
の中で順々に実行されます。
コンパイルの対象がインライン関数でなければ、
この関数が関数定義のコンパイルから復帰したときに、
その関数定義のコンパイルに使われた記憶域が完全に解放されます。
(see section マクロと同程度に高速なインライン関数)。
以下に、
コンパイラのすべてのパスと、
そのソース・ファイルを一覧にして示します。
`-d'オプションによって、
デバッグ用のダンプ出力を要求することのできる個所に関する説明もあります。
-
解析パス。
このパスにおいて、
関数定義の全テキストが読み込まれ、
部分的な構文ツリーが構築されます。
現在では、
これとRTL生成は全く別のパスではなくなっています
(以前は、
全く別のパスでした)。
しかし、
これらは別のパスであると考えた方が分かりやすいでしょう。
ツリー表現は、
Cの構文に完全には一致しません。
これは、
ツリー表現が、
他の言語のサポートも想定しているからです。
言語固有のデータ型の解析も、
このパスにおいて実行されます。
式を表す個々のツリー・ノードにはデータ型が付与されます。
変数は、
宣言ノードとして表されます。
定数の計算(constant folding)と算術の単純化もまた、
このパスにおいて実行されます。
解析パスに関連するソース・ファイルのうち言語に依存しないものは、
`stor-layout.c'、`fold-const.c'、`tree.c'です。
ツリー表現の形式を定義するヘッダ・ファイル`tree.h'、`tree.def'もあります。
Cを解析するソース・ファイルは、
`c-parse.in'、
`c-decl.c'、
`c-typeck.c'、
`c-aux-info.c'、
`c-convert.c'、
`c-lang.c'です。
また、
ヘッダ・ファイルは、
`c-lex.h'と`c-tree.h'です。
C++を解析するソース・ファイルは、
`cp-parse.y'、`cp-class.c'、
`cp-cvt.c'、`cp-decl.c'、`cp-decl2.c'、
`cp-dem.c'、`cp-except.c'、
`cp-expr.c'、`cp-init.c'、`cp-lex.c'、
`cp-method.c'、`cp-ptree.c'、
`cp-search.c'、`cp-tree.c'、`cp-type2.c'、
`cp-typeck.c'です。
また、
ヘッダ・ファイルは、
`cp-tree.def'、`cp-tree.h'、`cp-decl.h'です。
Objective Cを解析するための特別なソース・ファイルとして、
`objc-parse.y'、`objc-actions.c'、`objc-tree.def'、
`objc-actions.h'があります。
Objective Cの解析には、
C用のファイルもいくつか使われます。
ファイル`c-common.c'は、
上に挙げたすべての言語で使われます。
-
RTL生成。
これは、
構文ツリーからRTLコードへの変換です。
実際には、
この変換は、
解析パスにおいて一文ずつ実行されるのですが、
ほとんどの場合、
独立したパスと見なすことができます。
ターゲット・パラメータに依存するコードの大部分は、
ここで見つけ出されます。
というのは、
特定の標準的な種類の命令が利用可能な場合に限って、
ストラテジ(strategy)を適用することが必要になる場合が多いからです。
名前付けされた命令パターンの目的は、
RTL生成パスに対して、
この情報を提供することにあります。
比較、ブール演算、条件式などの
if
条件の最適化は、
このパスにおいて実行されます。
末尾再帰(tail recursion)もここで検出されます。
ループの最適な配置方法とswitch
文の出力方法に関する決定も、
ここで下されます。
RTL生成のソース・ファイルには、
`stmt.c'、
`calls.c'、
`expr.c'、
`explow.c'、
`expmed.c'、
`function.c'、
`optabs.c'、
`emit-rtl.c'があります。
プログラムgenemit
によってマシン記述から生成されるファイル`insn-emit.c'も、
このパスにおいて使われます。
ヘッダ・ファイル`expr.h'は、
このパスにおける情報の交換に使われます。
プログラムgenflags
とgencodes
によってマシン記述から生成される、
ヘッダ・ファイル`insn-flags.h'と`insn-codes.h'は、
このパスに対して、
どの標準的な名前が利用可能であり、
どのパターンがその名前に対応するかを通知します。
デバッグ情報出力パスを除けば、
これより後のパスはいずれも、
関数のツリー構造表現を参照することはありません
(関数のツリー構造表現は、
その一部分だけがセーブされます)。
この後の呼び出し個所において、
その関数をインライン展開することが可能かどうか、
また、
インライン展開するべきかどうかの決定は、
RTL生成パスの最後で下されます。
関数は特定の基準を満足しなければなりませんが、
現在のところ、
その基準は、
関数のサイズと、
関数の取る引数の型と個数に関連付けられています。
インライン展開される関数は、
ループ、
自己自身への再帰的呼び出し
(末尾再帰を行う関数もインライン展開できます!)、
goto文を持つことができることに注意してください。
要するに、
GNU CCによってサポートされているすべての構文を持つことができるのです。
ファイル`integrate.c'には、
後のインライン化に備えて関数のRTLをセーブしておき、
その関数の呼び出し個所においてRTLをインライン展開するコードが含まれています。
ヘッダ・ファイル`integrate.h'も、
同じ目的で使われます。
オプション`-dr'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.rtl'を付加することによって作られます。
-
ジャンプ最適化。
このパスにおいて、
直後の命令へのジャンプ、
互いに交差するジャンプ、
ジャンプへのジャンプが単純化されます。
また、
参照されないラベルと到達不可能なコードを削除します。
ただし、
このパスにおいては、
到達不可能なコードであっても、
ループを含むものは、
到達不可能なコードとして認識されません。
(このようなループは、
後の基本ブロック解析において削除されます。)
さらに、
このパスは、
比較の結果から値を直接セットする命令をマシンが持っている場合には、
もともとジャンプを使って記述されていたコードを、
そのような命令が連続したものに変換することもあります。
ジャンプの最適化は、
2回、
または3回実行されます。
最初に実行されるのは、
RTL生成の直後です。
2回目はCSE(16)の後ですが、
これは、
CSEの結果、
ジャンプの最適化を再実行することが必要であると判断された場合に限ります。
最後は、
最終パスの直前です。
この時には、
交差するジャンプの処理(cross-jumping)や、
何も実行しないmove命令の削除が、
上記の最適化とともに実行されます。
このパスのソース・ファイルは、
`jump.c'です。
オプション`-dj'が指定されると、
このパスが最初に実行された後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.jump'を付加することによって作られます。
-
レジスタ・スキャン。
このパスにおいて、
個々のレジスタの最初の使用と最後の使用が見つけ出されます。
これは、
共通部分式の除去に対する指針となります。
ソース・ファイルは、
`regclass.c'にあります。
-
ジャンプ縫合(threading)。
このパスにおいて、
同一の条件テスト、
または正反対の条件テストに分岐する条件ジャンプが検出されます。
このようなジャンプは、
第2の条件テストに「縫合」することができます。
このパスのソース・コードは、
`jump.c'にあります。
この最適化は、
`-fthread-jumps'が有効になっている場合のみ実行されます。
-
共通部分式の除去。
このパスにおいては、
定数伝播(constant propagation)も実行されます。
ソース・ファイルは`cse.c'です。
定数伝播の結果、
条件ジャンプが無条件ジャンプになったり、
何も実行されないことになった場合、
CSEの終了後に、
ジャンプの最適化が再度実行されることになります。
オプション`-ds'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.cse'を付加することによって作られます。
-
ループ最適化。
このパスにおいて、
ループの内側にある定数式が、
ループの外に移動されます。
また、
場合によっては、
強度の削減(strength-reduction)とループ展開(loop unrolling)も実行されます。
ソース・ファイルは、
`loop.c'と`unroll.c'です。
また、
ヘッダ・ファイル`loop.h'が、
ソース・ファイル間の情報の交換のために使われます。
ループ展開においては、
`integrate.c'の中の関数と、
ヘッダ・ファイル`integrate.h'が使われます。
オプション`-dL'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.loop'を付加することによって作られます。
-
`-frerun-cse-after-loop'が有効であれば、
ループ最適化パスの後に、
2回目の共通部分式除去のパスが実行されます。
指定されていれば、
このタイミングでジャンプ縫合も実行されます。
オプション`-dt'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.cse2'を付加することによって作られます。
-
最適化を行わないコンパイルでは、
この時点で、
最適化されないレジスタ割り当てが実行されます。
また、
ちょっとしたデータ・フロー解析も行われます。
この最適化されないレジスタ割り当てが適用された場合、
次に実行されるパスは、
再ロード・パスとなります。
このパスと再ロード・パスの間のパスは、
実行されません。
ソース・ファイルは`stupid.c'です。
-
データ・フロー解析(`flow.c')。
このパスにおいて、
プログラムは、
基本ブロックに分割されます
(その過程において、
到達不可能なループが削除されます)。
その後に、
プログラム中の個々のポイントにおいて、
どの仮想レジスタが活用されているかを調べて、
ある値を最初に使う命令から、
その値を計算した命令へのリンクを作ります。
また、
このパスにおいては、
ある計算結果がまったく使われない場合、
その計算自体を削除します。
さらに、
メモリ参照と加算、減算命令を組み合わせることによって、
自動インクリメントや自動デクリメントによるアドレス調整(addressing)を実現します。
オプション`-df'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.flow'を付加することによって作られます。
最適化されないレジスタ割り当てが適用された場合、
このダンプ・ファイルには、
そのような割り当ての結果が完全に反映されます。
-
命令結合(`combine.c')。
このパスにおいて、
データ・フローによって相互に関連を持つ、
2個ないし3個の命令グループを、
単一命令へ結合することが試みられます。
まず、
命令グループのRTL式を代替処理によって結合します。
結合結果を代数処理(algebra)を使って単純化し、
次に、
単純化の結果に合うものを、
マシン記述から見つけようと試みます。
オプション`-dc'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.combine'を付加することによって作られます。
-
命令スケジューリング(`sched.c')。
ある命令の出力した結果が、
後続の命令によって使われるべき時には、
入手できなくなってしまうということがありますが、
このパスにおいては、
そのような結果を出力する命令が探されます。
(RISCマシンにおいて、
メモリのロード命令や浮動小数点命令が、
このような振る舞いをすることがよくあります。)
命令の順序を変更しない限りパイプライン処理を失速させてしまうような項目については、
その定義個所と使用個所の分離を試みるために、
基本ブロックの内部で命令の順序変更を行います。
命令スケジューリングは、
2回実行されます。
1回目は命令結合の直後、
2回目は再ロードの直後です。
オプション`-dS'が指定されると、
このパスの1回目の実行が終了した後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.sched'を付加することによって作られます。
-
レジスタ・クラス優先付け。
個々の仮想レジスタに対してどのレジスタ・クラスを割り当てるのが最適であるかを知るために、
RTLコードがスキャンされます。
ソース・ファイルは`regclass.c'です。
-
局所レジスタ割り当て(`local-alloc.c')。
このパスにおいて、
1つの基本ブロックの中でのみ使われる仮想レジスタに対して、
ハード・レジスタが割り当てられます。
基本ブロックは線型(linear)であるため、
質の高い仕事をするために高速かつ強力なテクニックを使うことができます。
オプション`-dl'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.lreg'を付加することによって作られます。
-
広域レジスタ割り当て(`global.c')。
このパスにおいては、
残りの仮想レジスタに対してハード・レジスタが割り当てられます
(ここで、
残りの仮想レジスタとは、
その存続期間が、
1つの基本ブロックに収まらないものを指します)。
-
再ロード。
このパスにおいて、
割り当てられたハードウェア・レジスタ番号を使って、
仮想レジスタの番号を振り直します。
ハード・レジスタを割り当てられなかった仮想レジスタは、
スタック上のスロットによって置き換えられます。
次に、
値がレジスタの中に入れられなくなったために、
または、
値が正しくない種類のレジスタに入れられることになったために、
正当ではなくなってしまった命令を見つけ出します。
問題のある値を一時的にレジスタに再ロードすることによって、
これらの命令を修正します。
コピーを行うための命令が生成され、
追加されます。
再ロード・パスにおいては、
フレーム・ポインタが除去されたり、
関数呼び出しによって内容の破壊されるレジスタを、
その関数呼び出しの前後で待避、復元する命令が挿入されたりすることもあります。
ソース・ファイルは、
`reload.c'と`reload1.c'です。
また、
ヘッダ・ファイル`reload.h'が、
ソース・ファイル間における情報交換の目的で使われます。
オプション`-dg'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.greg'を付加することによって作られます。
-
ここで、
命令スケジューリングが再実行されます。
これは、
ハード・レジスタを割り当てられなかった仮想レジスタのために生成されたメモリ・ロード命令によって引き起こされる、
パイプライン処理の失速を回避することを試みるためです。
オプション`-dR'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.sched2'を付加することによって作られます。
-
ジャンプの最適化が再実行されます。
今回は、
互いに交差するジャンプ(cross-jumping)や、
何も実行しないmove命令の削除も対象となります。
オプション`-dJ'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.jump2'を付加することによって作られます。
-
遅延分岐(delayed branch)スケジューリング。
この必須ではないパスにおいて、
他の命令の遅延スロット(delay slot)に入れることのできる命令を見つけ出すことが試みられます。
このような命令は、
通常は、
ジャンプと関数呼び出しです。
ソース・ファイル名は`reorg.c'です。
オプション`-dd'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.dbr'を付加することによって作られます。
-
この時点で、
特定のハード・レジスタの使用が、
レジスタ・スタックの使用に置き換えられることがあります。
現在のところ、
これは、
Intel 80387コプロセッサの浮動小数点レジスタにおいてのみサポートされています。
ソース・ファイル名は`reg-stack.c'です。
オプション`-dk'が指定されると、
このパスの終了後に、
RTLコードのデバッグ用のダンプが出力されます。
このダンプ・ファイルの名前は、
入力ファイル名の末尾に`.stack'を付加することによって作られます。
-
最終パス。
このパスにおいて、
関数に対応するアセンブラ・コードが出力されます。
見せかけだけで無意味なテスト命令や比較命令を鑑定するのも、
このパスの責任です。
同時に、
マシン固有のピープホール(peephole)最適化も実行されます。
関数の入口と出口の命令シーケンスは、
このパスにおいて、
直接アセンブラ・コードとして生成されます。
これらがRTLとして存在することは、
決してありません。
ソース・ファイルは`final.c'と`insn-output.c'です。
後者は、
ツール`genoutput'によって、
マシン記述から自動的に生成されます。
ヘッダ・ファイル`conditions.h'が、
これらのファイル間の情報交換の目的で使われます。
-
デバッグ情報出力。
これが最終パスの後に実行されるのは、
ハード・レジスタを割り当てられなかった仮想レジスタに対応するスタック・スロットのオフセットを出力しなければならないからです。
DBXシンボル・テーブル形式に対応するソース・ファイルは`dbxout.c'、
SDBシンボル・テーブル形式に対応するソース・ファイルは`sdbout.c'、
DWARFシンボル・テーブル形式に対応するソース・ファイルは`dwarfout.c'です。
さらにいくつかのファイルが、
すべてのパス、
または多くのパスにおいて使われます。
-
すべてのパスにおいて、
マシン・モードを定義する`machmode.def'と`machmode.h'が使われます。
-
いくつかのパスにおいて`real.h'が使われます。
このファイルは、
浮動小数点定数のデフォルト表現と、
その操作方法を定義しています。
-
RTLを処理するすべてのパスにおいて、
ヘッダ・ファイル`rtl.h'と`rtl.def'が使われます。
また、
ファイル`rtl.c'の中のサブルーチンも使われます。
ツール群
gen*
も、
マシン記述RTLを読んだり、
処理したりするために、
これらのファイルを使います。
-
いくつかのパスにおいて、
ヘッダ・ファイル`insn-config.h'が参照されます。
このヘッダ・ファイルには、
ツール
genconfig
によって、
マシン記述RTLから自動的に生成された2、3のパラメータ
(Cのマクロ定義)
が含まれています。
-
いくつかのパスにおいて、
命令識別プログラムが使われます。
これは、
`recog.c'、`recog.h'、`insn-recog.c'、`insn-extract.c'
から構成されます。
`insn-recog.c'と`insn-extract.c'は、
ツール`genrecog'と`genextract'によって、
マシン記述から自動的に生成されます。
-
いくつかのパスにおいて、
ヘッダ・ファイル`regs.h'と`basic-block.h'が使われます。
`regs.h'には、
仮想レジスタの使用に関して記録された情報が定義されています。
また、
`basic-block.h'には、
基本ブロックに関して記録された情報が定義されています。
-
`hard-reg-set.h'には、
型
HARD_REG_SET
、
各ビットが個々のハード・レジスタに対応するビット・ベクタ、
および、
それを操作するマクロが定義されています。
マシンの持つハード・レジスタの数が十分に少ない場合には、
ビット・ベクタの型はただのint
です。
これ以外の場合は、
int
の配列となり、
マクロのいくつかはループに展開されます。
-
いくつかのパスにおいて、
命令属性(instruction attribute)が使われます。
特定のマシンに対して定義された属性の定義は、
ファイル`insn-attr.h'にあります。
このファイルは、
プログラム`genattr'によって、
マシン記述から生成されます。
ファイル`insn-attrtab.c'には、
insnに対する属性値を獲得するためのサブルーチンが含まれています。
これは、
プログラム`genattrtab'によって、
マシン記述から生成されます。
[Contents] [Back] [Prev] [Up] [Next] [Forward]