summaryrefslogtreecommitdiff
path: root/chapter28b/README.ja.md
blob: d04e9ccb9a46740d2aeea4ead291d4a9307913b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# 第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 を
置きます。未完成のオブジェクトでも初期化済みフィールドは読めます。
アンダースコアは公開インターフェースの選択に使いますが、ソースのアクセス制限は
[レビュー待ち](../docs/chapter-backports.md)です。

## 検証と今後

リポジトリのルートから次を実行します。

```sh
make tests CHAPTERS=chapter28b
```

保存したコンストラクタ、インポートをまたぐ初期化循環、共有データ、ネイティブ関数の
サンク、ライブラリとクライアントの実行を検査します。Make はテスト用の `sys` も
オブジェクトにします。`make release` は jar と標準ライブラリを作り、`make view` は
共通のグラフビューアーを起動します。

保存形式は `C0DE` 以外のバージョン番号をまだ持ちません。コンパイラや形式を変えたら
オブジェクトを再構築してください。高コストの往復一致検査は無効のままで、古い増分
ビルドの実験 `testModule0` も実行対象外です。

[分割計画](../docs/chapter-split.md)の主要な七つのスナップショットはこれで揃います。
ネイティブライブラリの構築と最適化の調整は[付録](../docs/appendices/README.md)にまとめています。