summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/chapter28b/28b-grammar.ja.md175
-rw-r--r--docs/chapter28b/28b-grammar.md175
-rw-r--r--docs/chapter28b/README.ja.md108
-rw-r--r--docs/chapter28b/README.md177
4 files changed, 635 insertions, 0 deletions
diff --git a/docs/chapter28b/28b-grammar.ja.md b/docs/chapter28b/28b-grammar.ja.md
new file mode 100644
index 00000000..65944fe2
--- /dev/null
+++ b/docs/chapter28b/28b-grammar.ja.md
@@ -0,0 +1,175 @@
+# 第28b章の文法
+
+[English](28b-grammar.md) | 日本語
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+// A file is a compilation unit; its path supplies its namespace.
+// Qualified names resolve nested types and other units without an import keyword.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : constructorDeclaration | methodDeclaration | statement ;
+
+// The constructor name must match the enclosing struct's name.
+constructorDeclaration : 'new' IDENTIFIER '=' functionExpression ';' ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// Function types and expressions both receive their implicit receiver here.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol: a function, or explicit numeric native storage. Ordinary strings
+// elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : equalityExpression (('&' | '|' | '^') equalityExpression)*
+ ;
+
+equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ;
+
+// Chained comparisons test adjacent operands and short-circuit. A chain must
+// keep its direction; equality has lower precedence and does not join a chain.
+comparisonExpression
+ : shiftExpression
+ ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )?
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Allocation invokes the declared constructor; inline initializer blocks from
+// earlier chapters are replaced by constructor arguments.
+newExpression : 'new' type ('[' assignment ']' | callSuffix)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/docs/chapter28b/28b-grammar.md b/docs/chapter28b/28b-grammar.md
new file mode 100644
index 00000000..c7a279fa
--- /dev/null
+++ b/docs/chapter28b/28b-grammar.md
@@ -0,0 +1,175 @@
+# Grammar for Chapter 28b
+
+English | [日本語](28b-grammar.ja.md)
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+// A file is a compilation unit; its path supplies its namespace.
+// Qualified names resolve nested types and other units without an import keyword.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : constructorDeclaration | methodDeclaration | statement ;
+
+// The constructor name must match the enclosing struct's name.
+constructorDeclaration : 'new' IDENTIFIER '=' functionExpression ';' ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// Function types and expressions both receive their implicit receiver here.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol: a function, or explicit numeric native storage. Ordinary strings
+// elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : equalityExpression (('&' | '|' | '^') equalityExpression)*
+ ;
+
+equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ;
+
+// Chained comparisons test adjacent operands and short-circuit. A chain must
+// keep its direction; equality has lower precedence and does not join a chain.
+comparisonExpression
+ : shiftExpression
+ ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )?
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Allocation invokes the declared constructor; inline initializer blocks from
+// earlier chapters are replaced by constructor arguments.
+newExpression : 'new' type ('[' assignment ']' | callSuffix)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/docs/chapter28b/README.ja.md b/docs/chapter28b/README.ja.md
new file mode 100644
index 00000000..d6274788
--- /dev/null
+++ b/docs/chapter28b/README.ja.md
@@ -0,0 +1,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 を
+置きます。未完成のオブジェクトでも初期化済みフィールドは読めます。
+アンダースコアは公開インターフェースの選択に使いますが、ソースのアクセス制限は
+[レビュー待ち](../chapter-backports.md)です。
+
+## 検証と今後
+
+リポジトリのルートから次を実行します。
+
+```sh
+make tests CHAPTERS=chapter28b
+```
+
+保存したコンストラクタ、インポートをまたぐ初期化循環、共有データ、ネイティブ関数の
+サンク、ライブラリとクライアントの実行を検査します。Make はテスト用の `sys` も
+オブジェクトにします。`make release` は jar と標準ライブラリを作り、`make view` は
+共通のグラフビューアーを起動します。
+
+保存形式は `C0DE` 以外のバージョン番号をまだ持ちません。コンパイラや形式を変えたら
+オブジェクトを再構築してください。高コストの往復一致検査は無効のままで、古い増分
+ビルドの実験 `testModule0` も実行対象外です。
+
+[分割計画](../chapter-split.md)の主要な七つのスナップショットはこれで揃います。
+ネイティブライブラリの構築と最適化の調整は[付録](../appendices/README.md)にまとめています。
diff --git a/docs/chapter28b/README.md b/docs/chapter28b/README.md
new file mode 100644
index 00000000..da5a90a8
--- /dev/null
+++ b/docs/chapter28b/README.md
@@ -0,0 +1,177 @@
+# Chapter 28b: Separate Compilation
+
+English | [日本語](README.ja.md)
+
+[Previous: Chapter 28a](../chapter28a/README.md)
+
+The previous chapter compiled every discovered source file together. This
+chapter lets a library and other code compile separately, and have full type
+information. The client needs the library's *object* file, but not its source.
+In spirit this is similar to Java's *class* file, with its full execution and
+type semantics stored in the file; we happen to be using an ELF file for the
+same reason. The ahead-of-time compiled code is *also* available for the
+linking step.
+
+An ordinary native object contains machine code, data, symbols, and
+relocations. Simple adds a `.simple` section with types and the ideal IR.
+Importing `.simple` lets the client check types and optimize calls into the
+library. Calls that remain out of line use the library's machine code at
+native link time.
+
+## A library and a client
+
+The runnable example has two separate source roots:
+
+```text
+src/test/smp/separate/library/Counters.smp
+src/test/smp/separate/client/Main.smp
+```
+
+`Counters.smp` declares shared state and an instance constructor:
+
+```simple
+int calls=0;
+val next_noInline = { -> calls++; return calls; };
+
+struct Counter {
+ val value=1;
+ new Counter = { int n ->
+ value=0;
+ for( int i=0; i<n; i++ ) value=value*2+1;
+ };
+ val get = { -> value; };
+};
+```
+
+`Main.smp` uses both parts of the library:
+
+```simple
+val run = { int n ->
+ if( !n ) return 0;
+ val c = new Counters.Counter(n);
+ return c.get()+100*Counters.next_noInline();
+};
+return run(arg);
+```
+
+At runtime, `run(0)` returns without reaching or initializing `Counters`.
+
+For `run(3)`, `Counters` initializes, the constructor computes 7 and
+`calls`==1, so we get `(7+100*1)` or 107.
+
+Calling `run(3)`, then `run(5)`, in one process increments the shared `calls`
+twice. The first call gives `(7+100*1)`, or 107; the second gives
+`(31+100*2)`, or 231.
+
+(The `_noInline` suffix prevents inlining, so the example exercises native
+linking as well as imported IR.)
+
+From this chapter directory, build the compiler and compile the library:
+
+```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
+```
+
+Then compile and evaluate the client using that object:
+
+```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
+```
+
+
+## What crosses the separate compilation boundary
+
+The optimized IR owned by the object is compactly encoded in the `.simple`
+section and fully reconstructed when read back in. The saved state includes
+canonical declarations as well as optimized flow types. E.g. `int x=7` has
+declared type `int` and initial value `7`. Struct field types (both declared
+and optimized), function argument types (both declared and optimized), numeric
+modes, store widths, and source locations also survive serialization.
+
+Each loaded `.simple` section restores its units into the shared `CompUnit`
+tree, mirroring the original code layout. A dependency shared by several
+objects uses the same `CompUnit`. Loaded aliases and function indices are
+renumbered to avoid collisions; all the dense indices end up in the same
+namespace. `GlobalBits` has the mapping from the imported file indices to the
+local compilation indices; this remapping extends everywhere through the loaded
+code, including recursive types that span several files. Importing skips
+parsing the original source, but it still analysed and type checked with its
+new callers.
+
+`ParseAll` is the workhorse that discovers source and object dependencies. It
+tracks missing symbol references, looking them up in the search path one by one,
+and loading the matching files.
+
+The end result combines locally parsed and imported IR in one graph, while
+keeping their separate `CompUnit`s. This combined compilation can take
+advantage of e.g. constants in the library, or inline library code using the
+normal inlining heuristics. The combined graph goes through the normal
+compilation phases, including peepholes, Opto and TypeCheck.
+
+Dependencies between `CompUnit`s are recorded. An explicitly requested source
+file is always compiled. Other source files are recompiled if their object is
+missing, their source is newer, or a recorded dependency's object in the build
+directory is newer. These timestamp checks are not yet a complete incremental
+build system, there is some modest future work needed to finish this.
+
+Loading currently works at the object granularity. Unrelated objects can stay
+unloaded, but selecting an object loads its entire saved graph and follows its
+recorded dependencies. Future work will look at loading sub-parts more
+incrementally.
+
+
+
+## Analysis, ownership, and initialization
+
+An imported function can participate in analysis without belonging to the
+client's output. Loading the `io` library, with its `p` print function, means
+that `p` is part of the analysis - but when writing out the `helloWorld` app,
+`p` will not end up in the `helloWorld.o` file directly. It might be inlined
+fully, or called out of line, but there won't be a stand-alone copy of it.
+
+After optimization, `Serialize` saves ideal IR before `Lower` expands class
+initialization. `Unlink` then detaches imported bodies for machine compilation
+and removes analysis call edges. The client emits its own bodies and any code
+copied into them by inlining; the
+remaining calls refer to symbols supplied by the library object.
+
+Class initialization follows the previous chapter's rules. Importing a type
+does not initialize its class. Active uses retain `CLInit` checks until
+lowering (unless optimization clears them out). The library supplies its
+shared class object and flag. Initialization checks appear at all the active-touch
+locations, whether local or remote; including source locations for error
+reporting.
+
+
+## Building and checking
+
+From the repository root:
+
+```sh
+make tests CHAPTERS=chapter28b
+```
+
+The suite includes the preceding constructor and escape regressions, imported
+constructors, initialization cycles across objects, shared static data, native
+function thunks, and native library/client execution. Make also builds the
+modular `sys` object used by the library tests. Clients can import that object
+with `-L`; they do not need to parse the standard library's source again.
+`make release` builds the launcher jar and standard library; `make view`
+starts the shared graph viewer.
+
+The serialized format is internal and currently unversioned beyond its `C0DE`
+magic. Rebuild Simple objects when the compiler or layout changes. The expensive
+serialization bijection check remains disabled, and `testModule0`, the older
+incremental-rebuild experiment, remains ignored. They are not validation
+guarantees for this snapshot.
+
+This completes the seven main snapshots in the [approved split](../chapter-split.md).
+See the appendices on [native libraries](../appendices/native-libraries.md)
+and [optimizer/backend tuning](../appendices/optimizer-tuning.md).