From 51f1f8f4b871e3b52eb0c5d18b03f362a2b37791 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 10 Aug 2026 11:11:18 -0700 Subject: Chapter 25: add separate compilation and simplify SSA construction Introduce Chapter 25 with separately compiled Simple modules, serialized IR, cross-compilation-unit linking, and native system-library integration. This also completes a broad parser and type-system cleanup needed to make graph construction independent of incomplete parse-time type information. Separate compilation and linking - Add compilation-unit Start/Stop nodes and track functions, classes, aliases, and global indices across compilation-unit boundaries. - Serialize canonical types and ideal graph nodes into ELF objects, including dependency metadata, relocations, global/local bitset remapping, and dense memory-alias information. - Load dependent objects on demand, recursively resolve source and object paths, relink calls and function pointers, and preserve external C symbols. - Distinguish the post-optimization ownership hook from unknown callers so private call graphs can remain precise while public functions remain externally callable. - Add command-line and build support for compiling modules and the reusable `sys` object library. Parser, constructors, and SSA - Replace inline constructor expansion with a hidden default `` followed by an explicit user constructor declared as `new Type = { ... }`. - Keep the default constructor private, pass private object memory through constructor calls, and require user constructors to initialize non-default fields before the object escapes. - Track possibly-uninitialized fields in parser `Var` state and diagnose early reads without encoding parser-only constraints in Load and Store nodes. - Make ordinary parser memory bulk and recover precise alias slices in the optimizer using `MemMerge`, `MemPhi`, and `BulkMemPhi` nodes. - Remove parser-supplied type constraints from Loads and ordinary Phis, defer integer/float arithmetic selection, and keep call shape driven by syntax instead of provisional inferred types. - Preserve chained field declarations as static parser information when flow types are not yet available, including postfix field updates through calls. Types, functions, and escape analysis - Add `TypeScalar` as the common lattice family for integer, float, memory pointer, and function-pointer values, with scalar TOP/BOT values for generic Loads, Stores, parameters, and invalid intermediate expressions. - Represent unresolved struct definitions directly with the `TypeStruct` forward-reference flag while retaining the separate open-structure concept; close and canonicalize recursive structures without side maps. - Add explicit `FunPtrNode` references so constant function pointers retain a graph edge to their Return, and keep referenced functions alive until Opto proves the pointer dead. - Refine public-function and class-initializer escape handling, including public-only fallback escape sets and monotone compilation-unit summaries. - Add guards, proof casts, read-only propagation, pointer-to-integer conversion for non-null FFI arguments, and consistent deep mutability checks at calls. Optimization and code generation - Make weak and unresolved graph inputs compute conservatively and monotonically throughout iterative peepholes and SCCP. - Preserve independent memory slices when sharpening Stores, retain control on conditional Stores, and freeze Store widths from their target field types. - Extend serialization for new node kinds, scalar types, Store modes, function pointers, and memory partitions, preserving serialize/deserialize bijection. - Complete native selection and encoding support across x86-64, RISC-V, and ARM, including FFI pointer conversion and missing integer operations. - Fix x86 immediate multiply REX encoding and assorted scheduling, register allocation, spilling, relocation, and constant-pool issues exposed by the larger linked graphs. System library, tests, and documentation - Compile and link the Simple `sys` library, including libc bindings, I/O, scanners, strings, vectors, array helpers, and executable entry points. - Add Chapter 25 module, serialization, constructor, forward-reference, postfix-update, system, Hello World, and Bubble Sort coverage; update golden graphs and spill expectations for the new IR shape. - Add module and type-system documentation, parser-simplification design notes, grammar updates, examples, diagrams, and graph visualization assets. The resulting compiler passes the raw Chapter 1-24 suite and the Chapter 25 system and separate-compilation tests, including a freshly rebuilt `sys.o` and the native Bubble Sort example. --- ASimpleReply/ASimpleReply2.md | 6 +++--- ASimpleReply/ASimpleReply3.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'ASimpleReply') diff --git a/ASimpleReply/ASimpleReply2.md b/ASimpleReply/ASimpleReply2.md index 45017b61..366462bc 100644 --- a/ASimpleReply/ASimpleReply2.md +++ b/ASimpleReply/ASimpleReply2.md @@ -93,7 +93,7 @@ maintains type info end-to-end, because its free to do so and because its useful. "Equivalence classes are much weaker for arrays" - sure. And basically make no -differece to 99% of all Java programs. 1-d arrays vectorize just fine; stencil +difference to 99% of all Java programs. 1-d arrays vectorize just fine; stencil calculations also fine. Register blocking and tiling? First Java needs to change how multi-D arrays are laid out then we can worry about loop carried aliasing. Basically, Java was never intended to replace Fortran, although in @@ -108,7 +108,7 @@ optimize some things around the call - all dwarfed by the call overhead itself. "Memory optimizations also cannot typically float past deoptimization state" - plenty of those in Java too. C2 also does not sink stores *below* a deopt state. At least for Java, we can float loads *up* past a deopt state (and do -so plenty). +so plenty). Also, call inline points *completely* disappear in C2. There is no optimization barrier after inlining, even for memory effects. This is @@ -123,7 +123,7 @@ alias, the other aliases are not even representable". This gets into a weak argument from me about how after specialization guards, we're back to equivalence classes... but I honestly dunno. Not tried it in JS -at least (C2 certainly does this, but its not so common as JS). +at least (C2 certainly does this, but its not so common as JS). Very curious though... Curious enough for Google to fund a reprise with me lending a hand? diff --git a/ASimpleReply/ASimpleReply3.md b/ASimpleReply/ASimpleReply3.md index 395f4b77..a432e239 100644 --- a/ASimpleReply/ASimpleReply3.md +++ b/ASimpleReply/ASimpleReply3.md @@ -94,7 +94,7 @@ Dadal: In a similar situation C2 will do the same. Of course, for sufficiently hot `foo` and modest `bar`, inlining is the answer. This makes me wonder if C2's -inlininhg budget is substantially higher than V8's. +inlining budget is substantially higher than V8's. Dadal: > "Oh, and `bar` could be changing the shape of `o` only some times" -- cgit v1.2.3