# 第28b章: 分離コンパイル [English](README.md) | 日本語 [Previous: Chapter 28a](../chapter28a/README.ja.md) 前の章では、見つかったソースを一度にコンパイルしました。この章ではライブラリと クライアントを別々にコンパイルします。クライアントにはライブラリのオブジェクト ファイルが必要ですが、そのソースは不要です。 ## オブジェクトに理想 IR を保存する 通常の ELF オブジェクトには機械語、データ、シンボル、再配置情報があります。 Simple はさらに `.simple` セクションへ型と理想 IR を保存します。クライアントは 宣言を読み込んで型を検査し、関数本体も読み込んで SCCP やインライン化に使います。 残った呼び出しは、ネイティブリンクでライブラリの機械語につなぎます。 宣言型と解析中の型は別々に保存します。ある時点でフィールドの値が 0 でも、 その宣言を定数 0 に変えてはいけません。引数の型の上限、数値演算のモード、 Store の幅、元のソース位置も保存します。入れ子の構造体には、関数本体だけでなく 割り当て関数とコンストラクタの宣言も必要です。 別々のコンパイルで同じ番号になったエイリアスや関数は、同じものとは限りません。 `GlobalBits` は定義元のファイルとファイル内の番号を保存し、インポート時にその 組み合わせを今回の番号へ写します。型とノードに同じ写像を使い、複数ファイルを またぐ再帰型も復元します。 読み込みの単位は現在オブジェクトです。無関係なオブジェクトは読み込まずに済みますが、選ばれた オブジェクトの保存済み IR は全体を復元し、記録された依存先も読み込みます。`sys.io.p` だけを 呼ぶ場合でも、`sys.o` にまとめられた十個の単位をすべて読み込み、`aryu8` と `aryi64` も含みます。 必要なメンバーだけの読み込みは今後の目標です。IR の読み込みだけでクラスが初期化されたり、 その本体がクライアントの出力にコピーされたりするわけではありません。元のソースの解析は省けますが、 インポートしたグラフも新しい呼び出し元とともに最適化し、型検査します。 ## 実行できる例 英語版の例は次の二つのソースルートにあります。 ```text src/test/smp/separate/library/Counters.smp src/test/smp/separate/client/Main.smp ``` ライブラリは、ループで final フィールドを計算する `Counter` と共有カウンタを 公開します。クライアントの `run(3)` は最初に 107 を返します。同じプロセスで 続けて `run(5)` を呼ぶと 231 になります。インスタンスは別々ですが、クラスの カウンタは共有されます。`run(0)` はライブラリを使わずに戻ります。 このディレクトリでライブラリをコンパイルします。 ```sh make lib make build/classes/test/.ttag java -ea -cp "build/classes/main:build/classes/test:lib/*" \ com.seaofnodes.simple.Simple --norun -o build/separate-example/lib/ \ src/test/smp/separate/library/Counters.smp ``` 次にオブジェクトを使ってクライアントを評価します。 ```sh java -ea -cp "build/classes/main:build/classes/test:lib/*" \ com.seaofnodes.simple.Simple --eval \ -L build/separate-example/lib/Counters.o \ src/test/smp/separate/client/Main.smp 3 ``` 結果は `107` です。Windows のクラスパス区切りには `;` を使います。クライアントの ソースルートには `Counters.smp` がありません。`SeparateCompilationTest` はさらに 二つのオブジェクトを C ドライバとリンクし、繰り返し呼び出す動作を検査します。 ## 解析とコードの所有者 インポートした本体は解析に参加しても、クライアントの出力には属しません。 `Serialize` は `Lower` がクラス初期化を展開する前に理想 IR を保存します。 `Unlink` は機械語生成に向けてインポートした本体と解析用の呼び出し辺を切り離します。 インライン化でクライアントへコピーされたコードは、クライアント自身の本体として 出力されます。 初期化規則は前の章と同じです。型のインポートだけでは初期化しません。能動的な使用は `CLInit` を通して初期化を要求し、ライブラリが持つクラスオブジェクトとフラグを使います。 依存関係の検査にはインポートした本体も含めます。ネイティブコードへ渡した関数の 未知の呼び出し元も保持します。 コンストラクタのフィールド更新はローカル値と Phi のままで、ヘルパーの出口に Store を 置きます。未完成のオブジェクトでも初期化済みフィールドは読めます。 アンダースコアは公開インターフェースの選択に使いますが、ソースのアクセス制限は [レビュー待ち](../chapter-backports.md)です。 ## 検証と今後 リポジトリのルートから次を実行します。 ```sh make tests CHAPTERS=chapter28b ``` 保存したコンストラクタ、インポートをまたぐ初期化循環、共有データ、ネイティブ関数の サンク、ライブラリとクライアントの実行を検査します。Make はテスト用の `sys` も オブジェクトにします。`make release` は jar と標準ライブラリを作り、`make view` は 共通のグラフビューアーを起動します。 保存形式は `C0DE` 以外のバージョン番号をまだ持ちません。コンパイラや形式を変えたら オブジェクトを再構築してください。高コストの往復一致検査は無効のままで、古い増分 ビルドの実験 `testModule0` も実行対象外です。 [分割計画](../chapter-split.md)の主要な七つのスナップショットはこれで揃います。 ネイティブライブラリの構築と最適化の調整は[付録](../appendices/README.md)にまとめています。