# Chapter backport review This is a queue of pending work. Completed corrections leave the queue; the [validation summary](#validation-record) records their scope. Detailed old logs are disposable build artifacts. Reusable rules live in [the AI notes](../skills/chapter25-codex-notes.md). Introduce independent fixes in the earliest applicable chapter and propagate them through every affected snapshot. Testing Chapter 25's inherited tests does not validate the compilers in the earlier chapter directories. Chapters 10 and 11 introduce bulk memory and lazy memory partitioning; Chapter 12 adds references, Chapter 13 schedules the graph, and Chapter 14 introduces floats and narrow numeric types. Keep the rest of SSA construction with incomplete types in Chapter 25 for now. Cliff wants to revisit splitting Chapter 25 into smaller chapters; escape analysis remains in 25 until that larger review. Reset checkpoint (2026-10-03): Cliff committed the chapter reorganization and mutability split as `22df8918` (`Chapter renumber, and split ch 17`). The compiler work and validation below are complete; review is ongoing. Chapter 18 README review and backup-file deletion followed in `3c13b271` (`Update README.md`). The tree was clean before this notes refresh. No build/test process is hung; check Git for subsequent changes. Splitting Chapter 25 remains deferred. ## Chapter numbering after the memory reorganization | Previous chapter | Current chapter | |---|---| | 10a: whole memory | 10 | | 10b: lazy memory splitting | 11 | | 13: references, plus typed fields from 12 | 12 | | 11: GCM, retaining reference support | 13 | | 12: floats, and 14: narrow types | 14 | Chapters 15-16 and 18-25 retain their numbering; the subsequent mutability split replaces 17 with 17a and 17b. New Chapter 12 has no GCM or floating point nodes; Chapter 13 introduces GCM without floats. Chapter 14 adds both floating point and narrow numeric types. The inherited float test suite is now `Chapter14FloatTest`; reference and GCM suites are `Chapter12Test` and `Chapter13Test`. The README in Chapter 12 records the intentional `L0`/`L1` shallow-reference boundary and resolving field definitions by name. Historical validation entries below use the chapter numbers and test names in effect when the work was done. In particular, old 10a/10b records refer to current 10/11, and old `Chapter12Test` float regressions now live in `Chapter14FloatTest`. Float-to-int assignment conversion was subsequently added with the numeric material in Chapter 14 and propagated through 25. Validation of the reorganization (2026-10-03): Make suites pass in every chapter 10-25, including the shared printer/ISA tests and Chapter 25's native runtime tests. Chapters 10-14 produce release jars. Fresh standalone checkouts of 12, 13, and 14 pass their suites and package without parent chapter sources. Browser playback checks pass in 12 and 13. POM module order, portable IDEA XML, chapter navigation, local links, SVG XML, shell syntax, and UTF-8/LF checks pass. Logs are in `build/chapter-reorg/`. The new main-Java increments (added + removed lines) are 529 for 10 to 11, 280 for 11 to 12, 867 for 12 to 13, and 1,185 for 13 to 14. These exclude tests, prose, assets, and generated files. The references chapter is now a small, separate language extension; the larger increments introduce scheduling and numeric types respectively. ## Mutability syntax and chapter split (2026-10-03) Chapter 16 introduces constructors. Chapter 17a introduces independent binding and reference permissions; Chapter 17b adds inference, updates, conditional expressions, and `for`. POMs, portable IDEA descriptors, Make discovery, README navigation, and inherited test names follow the split. `Chapter17aTest` owns the former final-field constructor tests; `Chapter17bTest` owns syntax sugar. From 17a through 25, `!Point`/`~Point` control pointee access and `!p`/`~p` control reassignment. Primitive bindings default mutable; references default fixed and struct access defaults read-only. Arrays default writable, with `[~]` specifying a read-only layer. `var` and `val` preserve initializer access. Read-only views are deep but do not freeze other aliases or remove memory dependencies. Writable array slots require matching element permissions. `TypeMemPtr` carries the access bit independently of fields and preserves it through forward-reference resolution, cyclic interning, inference, loads, calls, and Chapter 25 serialization. Cyclic factories build the complete type before interning to avoid orphaning temporary types. Array aliases are shared across access views, while their element permissions remain distinct. The 23/24 static-field distinction now requires an explicit initializer; a fixed nullable field's implicit null is still an instance field. Constructor permissions cover only allocation fields, including assignments in nested blocks. They do not permit rebinding surrounding or constructor-local fixed variables. Chapter 25's constructor-exit check also handles required primitive fields. The object layout changes under the unversioned `C0DE` header; rebuilding `sys.o` is required and exercised by its native tests. The changed graphs exposed two small existing assumptions: Chapter 24 must recognize a Phi whose own backedge is still under construction, and a memory query through New must record a dependency even when the queried loop MemPhi is a user of the Store. The memory-query dependency is propagated across the applicable bulk-memory implementations. Validation: Make suites pass in 16 (244), 17a (255), 17b (304), 18 (339), 19 (383), 20 (405), 21 (444), 22 (465), 23 (487), and 24 (517); applicable chapter-local fuzz regression wrappers and the shared printer/ISA suites pass. Chapter 25's complete Make suite passes, including native/library execution and rebuilt serialized objects. Fresh standalone 17a/17b copies pass their tests and produce release jars; 16 also packages. POMs, IDEA XML, and navigation links are checked. Test-export tooling recognizes 17a/17b and its suite passes; unsupported Java test patterns are skipped per method rather than per class. Existing spill expectations changed in the String fixture: 22's RISC-V count is 1 instead of 3; 24's x86 count is 0 instead of 9. ## Completed integration notes These records describe completed work, not outstanding implementation tasks. Historical chapter numbers and measurements below refer to their original runs. ### Loop store sinking: Chapters 17b-25 (2026-10-05) The Chapter 17b presentation peephole is forwarded through Chapter 25. A loop memory Phi whose backedge is a sole-use Store becomes a Store of a scalar Phi. An entry Load preserves zero-trip behavior. The address must be a constant field offset of an allocation dominating the loop; array elements are excluded. Wait for parallel bulk-memory splitting before replacing the precise Phi. Chapter 25 preserves the Store's frozen width and excludes unresolved aliases, undecided widths, and constructor initialization Stores. Existing suites exposed three small supporting fixes: Loads through a TOP pointer stay TOP in 17b-23; Chapter 25 keeps a MemMerge alive while flattening a dead memory cycle; the shared printer computes placement for constants even when they have no cached block. Existing graph assertions now count reachable definitions and allow scalar field Phis. The synthetic-exit fixture uses a varying pointer so it still exercises loop-carried memory after store sinking. No new test methods were added for the forward port. Spill expectations were updated for the changed graphs, including the infinite-loop, String, and Scan fixtures; runtime and register-correctness assertions remain enabled. Validation: full Make suites pass in 17b (306 including its fuzzer wrapper), 18 (339), 19 (383), 20 (405), 21 (444), 22 (465), 23 (487), and 24 (517), plus the separate fuzzer wrappers in 18-24. Chapter 25 passes all six suite groups (403, 39, 9, 23, 1, and 19), including rebuilt sys.o, native execution, and the existing fuzzer seeds. Shared printer and ISA checks pass. Logs are in `build/rebase-talk/loop-store-*.log`. The presentation example still has 333 capture frames and 52 final nodes, with both Totals stores outside the loop. ### Shared ISA encoders and evaluators: Chapters 21-25 Issue #257's extraction is implemented in [`isa/`](../isa/README.md). `X86` accepts concrete registers, widths, immediates and addresses; `Arm64` and `RiscV` pack instruction words. Chapter `Encoding` classes implement `CodeSink` using their existing byte writers. ARM logical immediates and constant construction are shared too. None of these helpers imports compiler classes. Instruction selection and masks, compiler-type interpretation, frame and block layout, branch relaxation, relocations, constants, and ELF remain chapter-local. Branch byte sizes and patching are shared; the chapter still runs relaxation and supplies targets. One shared `EvalArm64` and `EvalRisc5` replace the five copies of each. Image construction and test assertions stay in chapter harnesses. Evaluators compile as test support. Make continues to bundle tests in its release jars; Maven and IDEA retain separate main/test dependencies. Direct byte tests also cover corrections exposed by consolidation: x86 legacy prefix order for 16-bit memory operations, indirect register calls and their REX bits, imm8 shift counts, high-register PUSH/POP, low-byte SETcc, and five-byte unconditional near jumps. RISC-V JAL now takes its sign bit from displacement bit 20. ARM masks negative literal-load offsets, checks byte displacement ranges correctly, and its evaluator distinguishes MOVN from AND-immediate. A disposable GNU assembler comparison agrees on all 5,760 tested x86 load/store/LEA instructions (`build/IsaOracle.java`). Shared tests add fixed ARM and RISC-V words and evaluator checks for multi-instruction ARM constants. Make, Maven source paths, IDEA wiring, and linearized checkouts include `isa/` starting at Chapter 21; Chapters 1-20 have no ISA dependency. Validation: full Chapter 21-25 Make suites pass, including native execution and the existing allocator checks (`build/isa-final-tests.log`). The independent shared ISA tests pass; a fresh linear Chapter 21 release contains all three encoders and shared evaluators (`build/isa-linear21-complete-release.log`). Maven/IDE XML parses; the standalone POM gains main and test ISA source roots at Chapter 21 only. Shell syntax and LF checks pass. ### Chapter 15 allocation design (implemented) Chapters 12-14 retain explicit initialization Stores. Chapter 15 replaces them with New's zeroing operation while keeping one parser-visible `$mem`. | Interface | Chapter 15 | |---|---| | New inputs | `{ctrl, $mem, size}` | | New outputs | `{ptr, $mem}`; no control result or per-alias projections | | Coverage | field aliases from New's internal TypeStruct/TypeMemPtr | | Input memory | partial MemMerge, no default, entries for affected aliases | | Result integration | whole-memory MemMerge, prior default, affected entries all using the same New memory projection | | Memory types | existing alias plus stored-value type; no lattice extension | A null MemMerge default means absent aliases are uncovered. Empty structs therefore have an empty partial input; their allocation does not change the whole-memory aggregate. New's output also covers only its own aliases. An alias-specific contents query combines incoming values with the field's initializer from New's struct (integer zero, floating zero, or null). It records optimizer dependencies and stops at cached Phi types to break loops. MemPhi merges the scalar contents of its precise alias. Start's incoming heap is empty in this chapter; functions with heap arguments will require a different entry query in Chapter 18. Loads fold against their own New and bypass a proven distinct allocation via New's memory input. Different pointer nodes alone do not establish disjointness. Array length remains a subsequent Store; array access control inputs remain. Global code motion and the evaluator scheduler both follow an alias through MemMerge to discover the allocation's ordering constraint. MemMerge is packaging, not itself a clobber. The viewer and evaluator use the new slot conventions. ### Chapter 16 constructor integration (reviewed) ScopeNode now owns the variable records and one `$mem` binding; the obsolete ScopeMin alias table is removed. New inputs are `{ctrl, $mem, size, fields...}`, with initializer inputs in struct-field order. Outputs remain `{ptr, $mem}`; partial memory coverage and whole-memory integration are unchanged from 15. Alias contents read initializer types with dependencies, rather than assuming zero. The evaluator reads these same inputs, including array length. Constructor body effects participate in ordinary lazy branch/loop memory SSA. The sole-use Store-to-New check is deliberately unchanged. A shared allocation memory result has more users than a per-alias result, so it permits fewer folds. `Chapter15Test.testBasic5` retains a `.y` Load instead of folding it to 3.14; both runtime results remain checked. Other changed printed expectations only adjust Region/Loop IDs after removing parser memory nodes. ### Chapter 18 function memory (reviewed) Chapter 17 retains readonly casts, forward reference type updates, lexical guards, and increment/assignment semantics while replacing ScopeMin with one memory binding. Chapter 18 keeps its top-level Var class and makes MemMerge an ordinary optimizer node rather than a parser alias table. Memory Parm 1 stays opaque; local branches, loops, and merged returns use BulkMemPhi/MemPhi. Function entry and CallEnd memory have unknown contents, including heap arguments. Calls consume and return the complete memory state. There are no alias summaries or private constructor effects. Inlining can expose the existing alias rewrites. New retains `{ctrl, $mem, size, fields...}` inputs and `{ptr, $mem}` outputs. The evaluator, Eval2, graph viewer, and scheduling use those slots. Call is a clobber for every alias. GCM raises a Load before the Call's block terminator; it must not append the Load to the Call's inputs, which encode arguments and a last-slot function pointer. The one-step Phi Load guard also stops at Calls. ### Chapter 19 instruction selection (reviewed) Both selectors preserve BulkMemPhi exclusions and MemPhi aliases. Selected memory instructions carry their alias through MemOpNode; GCM follows aggregates for ordinary and folded reads, waits for matching writers, and inserts ordering edges that require no register. Direct machine calls embed their target and must keep their argument lists intact, just like ideal Calls. Preserve this chapter's existing lowering of nonzero initializers into Stores. New now takes `{ctrl, $mem, size}` and produces `{ptr, $mem}`; there are no field value inputs at this stage. Partial coverage and whole-memory integration are unchanged. New's alias contents meet incoming memory with the field's zero type. Both CPU allocation register masks and both evaluators use the new slots. The nested-loop regression reaches x86's existing right-hand Load/Add TODO; that commutative case now uses AddMemX86 just like a left-hand Load. No other unfinished instruction patterns or encoding work is included. ### Chapter 20 register allocation (reviewed) ARM joins x86-64 and RISC-V in preserving bulk/precise memory Phis, allocation slots, and selected memory effects. The allocator already excludes TypeMem Phis and gives memory/ordering edges no register constraints; no allocator heuristic changes are needed. Allocation regressions check that memory stays unallocated and the pointer projection receives a legal register on all three targets. Folded x86 Add must retain a value input for its two-address result even when that value is constant. The old immediate form omitted input 4 and caused a null live range in BuildLRG. Both left- and right-hand Load patterns now retain the other addend; the existing constant selector materializes it. Keep Chapter 20's inlining-safe Return typing and the dependency registrations on a rejected Phi factoring attempt. Those fixes predate this memory port. ### Chapter 21 encoding (reviewed) All three targets retain the memory Phi subclasses and New's pointer slot 0, memory slot 1, and size input 2. Chapter 21 caches New's register masks in the shared node during register allocation; update that shared interface. Writers are placed before adding read-before-write constraints; the dominator walk must still find conditional writers above a Load's proposed merge placement. Calls receive no scheduling-only operands. Dependency registration reverses direction here: `consumer.addDep(producer)`. The forwarded memory queries and explicit forward dependencies follow that API. Synthetic no-exit loops collect MemPhi inputs by alias and use BulkMemPhi for the merged return memory. The one-step Load factoring guard is unchanged. Actual execution exposed a full-width integer Load allocated to XMM but encoded as a GPR load. LoadX86 now selects the encoding from the allocated register bank, preserving register masks. ARM's emulator now supports MOVK and MOVZ's complete 16-bit immediate and halfword shift. Carry these narrow fixes with later ports. BrainFuck follow-up: both the original and ported compilers retain 12 ideal Loads and 114 Stores (106 Stores initialize the program). In the RISC-V Hello World run, both execute 3,261 heap loads and 646 heap stores; stack loads/stores change from 15/15 to 1,006/572, mostly spilling `d+4`. There are also pre-existing missed length optimizations: `program#` reloads the known 106 on all 907 loop tests, and `old#` is reloaded after allocating the replacement output array. Load forwarding stops at memory Phis; its loop-profit test does not see through the backedge's multiway Phi. The old-output pointer is itself a Phi, beyond the two-direct-allocations disjointness test. Field aliases distinguish array length from array data, but do not distinguish separate arrays of the same element type. A scratch source replacing only `program#` with 106 passes Hello World, removes 907 heap loads, and returns stack traffic to 15/15. Its weighted split score nevertheless rises from 130 to 165 as allocation inserts many pointer copies before initialization stores; this score is not a dynamic memory-traffic measurement. No compiler optimization was changed in this investigation. Probe sources and before/after logs: `build/BrainMemory{Probe,Variants}.java`, `build/brain-memory-*.log`, and `build/brain-variants-*.log`. The subsequent Chapter 21 Load search now folds `program#` through the loop. The search returns a folding witness without creating nodes or rewiring edges; it retains optimizer dependencies. At a non-loop MemPhi it must prove every arm, with a BitSet remembering successful merges. Returning to the original loop memory is success only for an invariant pointer. Unknown clobbers, other loops, and incomplete merges fail. A separate builder creates value Phis after the proof; unchanged paths refer to the new loop value Phi. Enqueue that Phi: a self-edge can otherwise keep an obsolete Phi alive after local folding. Controlled array accesses are not hoisted across a merge. The one-step `Load.clobbered` factoring guard is unchanged. Carry this search with later ports. The unmodified BrainFuck source now has 11 Loads. RISC-V Hello World executes 2,354 heap loads, 646 heap stores, and 15/15 stack loads/stores, compared with 3,261 / 646 / 1,006 / 572 before this search. Emulator instruction count drops 22,965 to 21,920 (not a hardware timing). Retained/weighted moves change 46/130 to 144/165 as allocation inserts repeated initialization-store pointer copies. Four reduced cases cover unchanged N-way arms, a possibly aliasing arm, loop-carried pointers, and distinct values folding on all arms; they pass 2,800 evaluations across 100 seeds. The existing 9,800 memory evaluations pass. At that checkpoint, the 432-test suite had only the user-edited BrainFuck golden failure (42 versus 165). A temporary test copy restoring native BrainFuck execution and using 165 passes all 91 spill-cohort entries and their runtime checks: 943 / 1,594 moves / weighted moves. The user's test edits were preserved. Logs: `build/load-search21-tests5.log`, `build/load-search21-seeds.log`, `build/load-search21-loop-seeds.log`, `build/brain-variants-search.log`, and `build/load-search21-spills-complete.log`. ### Chapters 22-24 lazy memory (implemented, review checkpoint) The forward port now reaches Chapter 24. Parameters, calls, and returns carry whole memory; BulkMemPhi/MemPhi discover aliases within functions. New consumes partial memory for its instance fields and produces `{ptr, $mem}`. The Chapter 21 loop Load search, one-step Load factoring guard, dependency fixes, and alias-aware scheduling/evaluation carry through all three snapshots. Chapter 22 preserves C calls and its default-main handling. Chapter 23 keeps cyclic field types, deep-final Loads, and symbolic field offsets; class-wide fields do not participate in an allocation's memory coverage. Phi factoring rejects operand Phis mixing pointers and integers, even for boolean operations. A precise Phi waits for its bulk input to split instead of hiding that request behind an upcast. Eval2 keeps unknown-caller pointer placeholders distinct from actual heap constants. Chapter 24 retains its optimistic solver: MemPhi ignores TOP/XCONTROL arms, New returns high results while its control is high, and alias contents register a dependency on that control. The existing integer widening and call-graph solver remain intact. Register checks run before encoding adds untyped branches. Private constructor memory, escape tracking, and incomplete-type SSA stay in 25. Pretty-printer consolidation is implemented below. ### Chapter 25 memory follow-through (implemented, same review batch) The memory representation was already present. The remaining port carries the pure loop Load search, generalized Phi factoring with the one-step Load guard, aggregate-aware scheduling, Call operand protection, and dependency wakeups. It also carries the x86 register-bank Load encoding and ARM MOVZ/MOVK emulator corrections. Bulk splitting queues new precise Phis instead of assuming an eager fold still returns a MemPhi. The search preserves private constructor memory and Escape publication. A Store whose alias is still 1 blocks the proof; external storage cannot be bypassed using allocation identity. Length reads can drop control, while indexed array reads retain it. Integer loop value Phis use Convert to preserve the declared load width, and every new nested value Phi is queued for optimization. The constructor, escape, serialization, and incomplete-type architecture stays intact. ### Adjacent-copy forwarding: Chapters 21-25 Chapter 21 now removes a post-color Split when its sole use is the immediately following non-Phi, non-CFG instruction, the operand accepts the source register, and the operand is not two-address tied. Adjacency proves no intervening clobber. The same rule and focused guard regression are present in 22-25; Chapter 20 retains its baseline allocator. No spill-selection or CFG splitting rule changed. RISC-V BrainFuck keeps its original source and now has 35 / 42 retained / weighted moves instead of 144 / 165. Hello World executes 21,787 emulator instructions instead of 21,920; heap traffic remains 2,354 loads / 646 stores and stack traffic 15 / 15. Full Make suites and fuzzer targets pass in 21-25. Chapters 21-24 also pass complete spill reports after refreshing reduced-move expectations: | Compiler | Allocations | Retained moves | Weighted moves | |---|---:|---:|---:| | 21 | 91 | 821 | 1,444 | | 22 | 117 | 826 | 1,470 | | 23 | 147 | 913 | 1,711 | | 24 | 214 | 1,310 | 2,969 | Cohort 22 includes the two zero-move C return ABI checks added since the older 24-entry audit. Chapter 25's frozen 212-entry manifest remains unchanged. At that audit, its reporter failed on ten pre-existing String-constructor parse errors; the unmodified allocator reproduces all ten. The same 216 successful allocations (including 14 current cohort-25 allocations) improve from 2,563 / 5,496 to 2,494 / 5,399, but these are partial totals. The subsequent historical String fixture repair below restores complete reporting. Fresh system-library encoding and native cohort-25 checks pass. Logs: `build/copy-forward21-spills-final.log` through `build/copy-forward24-spills-final.log`, `build/copy-forward25-spills.log`, `build/copy-forward25-baseline.log`, and `build/copy-forward*-tests*.log`. ### Shared debug printers: Chapters 2-25 (implemented) The new [`print/`](../print/README.md) module owns expression recursion, IR row formatting and ordering, scheduled dumps, and assembly listing layout. Chapters retain their node spelling and small adapters for graph/machine facts. All chapter Nodes extend the shared `BaseNode`, providing one read-only node contract for printers and the viewer. Chapter 2 arithmetic needs only a one-line `format()` hook such as `"(%1+%2)"`; variable forms use protected hooks with `p.p(...)` and `p.n(...)`. Repeat tracking is universal, with repeated expansion for short constants. Common graph edge and snapshot construction has also moved out of chapter adapters. This also addresses issue #259's outside-package subclass use case. PR #260's public `_print1` change is superseded by the context API, rather than merged. The shared CFG-first RPO carries the Chapter 21 BrainFuck ordering correction through 25: loop headers precede their bodies, closing Regions follow them, and Phis stay with their headers. Function boundaries exclude caller arguments; shared floating expressions of constants print once with globals. All traversal and placement state is private; raw edges and captured encoding/layout metadata keep diagnostics read-only. `Node.toString()`/`print()` remain the expression entry points; `Node.p(depth)` starts structured dumps in Chapter 8, and `CodeGen.toString()` starts whole-program dumps in Chapter 18. The unused Chapter 7 IR adapter, `Node._printLine`, legacy LLVM-format wrappers, and `prettyPrintScheduled` aliases are removed. Evaluator/compiler `printLine` callers and the scheduled whole-program path in Chapters 20-24 remain. The wrapper cleanup passes all 26 chapter suites and the shared printer contracts (`build/prune-print-tests.log`, Chapters 1-9, and `build/prune-print-later-tests.log`, Chapters 10a-25). Make, Maven source paths, IDEA module dependencies, release packaging, and linearized checkout support include the shared sources. Shared contracts cover cycles, function boundaries, shared globals, Phi/projection grouping, multiline assembly, and captured pool metadata. Disposable real-graph checks cover BrainFuck and call/loop programs through Chapter 25, including repeatability, complete unique output, CFG order, and graph/cache identity. An outside-package Chapter 2 subclass compiles and prints nested operands using the protected hook. Validation: all 26 chapter Make suites and the shared contracts pass (`build/print-final-tests.log`). Chapters 21-24 each pass 18 real-graph snapshots; 25 passes 12 (`build/print-probe-*.log`). Assembly checks cover all available CPUs in 19-25 before/after allocation and encoding (`build/asm-probe-*.log`). Fresh linearized Chapter 2 and 21 releases contain the shared printer classes; Chapter 2's fresh tests also pass (`build/print-linear{02,21}.log`). Maven/IDE XML parses, the linearization script passes its syntax check, and changes are LF-only. The subsequent BaseNode cleanup also passes all 26 suites (`build/base-early-final-tests.log`, `build/base-late-tests.log`) and the updated shared contracts, including multi-digit slots, escaped percent signs, missing inputs, cycles and matching-suffix trimming. Three Chapter 5-6 print expectations now use a short reference on a repeated Region. Nine Chapter 21 viewer snapshots match their pre-cleanup baseline except for canonical constant labels; both 21 and 25 pass nine read-only, repeatable captures. Outside-package expression and format hooks compile. Fresh linearized Chapter 2/21 releases contain BaseNode and viewer classes (`build/base-linear{02,21}.log`). The graph IDEA module now depends on print. This cleanup removes about 2,800 node-source lines and 550 viewer-adapter lines beyond the original extraction. ## Pending corrections Allocator tuning is good enough: the residual Win64 NewtonExport ranking issue is closed without a fix. Additional emulator-driver/measurement coverage is also closed without expansion (Cliff, 2026-10-08). The remaining active work is AOT class initialization below; the other items stay deferred for the chapter split. - **Chapter 25 deterministic inlining (deferred until after the Chapter 25 split).** As of 2026-10-07, ordinary functions reject `_approxUIDs >= 100` before checking the optimized body. `_approxUIDs` counts node IDs consumed during parsing and is not refreshed after optimization. In the now-private Chapter 20 String replay, `_hashCodeString` is rejected at 100 although its live body has 25 nodes. MergeSort's `_merge` is rejected at 182 although its body has 54; both are below the ordinary live-body threshold of 100. A shadow build removing only that early rejection inlines both helpers and drops their out-of-line bodies; MergeSort retains its recursive `_split_merge` as expected. The ordinary-function policy is unchanged. Cliff deferred the larger repair until after splitting Chapter 25: converge shrinking rewrites, then choose one growing inline deterministically using current body sizes cached per convergence round. Stable call-site provenance must break ties rather than randomized worklist order or newly assigned node IDs. Avoid dependencies on every body node and repeated scans during each peephole. The bounded boilerplate check below is the small interim heuristic; the larger constructor parse allowance was replaced. Evidence: `build/ra-repair/inlining{,-ablation}.log`. - **Chapter 24 Load BOTTOM-on-error backport, unwound/deferred (2026-09-28).** Cliff requested starting with 24 and analyzing failures before proceeding. Literal global BOTTOM memory/pointer inputs already propagate BOTTOM. The missing behavior is `LoadNode.compute()` returning BOTTOM instead of the declared field type when `err()!=null`; Cliff unwound that one-line experiment. Chapter 25's Convert nodes supply the surrounding expression/loop's typed result while retaining the erroneous BOTTOM-producing Load for TypeCheck. Chapter 24 relies on the Load itself supplying that typed result. Defer this backport with the conversion split rather than importing it alone. The experimental full Make suite had four failures out of 477 tests (`build/load-bottom-24.log`); the fuzzer recipe was not reached. - `BrainFuckTest.testBrainFuck`: during parsing, the unfinished loop Phi for mutable `program` has its declared nullable type. The element load becomes BOTTOM, and reading the initialized `command` local triggers Parser's BOTTOM-means-uninitialized check before the loop can finish. Reduced valid source: `var !program="a"; for(int pc=0; pc`, plus two serialized importers sharing one mutable class-object address. The third check was added after that full run. All 199 historical allocations and 108 dynamic cases pass; cohort membership, inputs and results remain fixed. Tables are refreshed. Logs live under `build/aot-init/` and `build/{spill,dynamic}-stats/`. **Initialization semantics decided:** dynamic `` runs once on first active touch, after its parent completes. Passive class references do not trigger execution. Reject initialization cycles across modules; there is no early parent-ready point. Untouched classes execute no initialization code. The [lazy initialization design](../chapter25/initialization.md) covers the proposed initialization dependencies directly on CompUnit, CLInitNode checks with parser-flow and ideal simplification, a hidden `$init` field set inside ``, static proofs, and serialization/composition validation. The constant-field lowering above does not implement the runtime initialization protocol. The [implementation checkpoints](../chapter25/initialization.md#implementation-sequence) choose CompUnit dependency edges and function may-touch summaries for cycle checking, separately from ordinary alias-memory facts for runtime `$init`. No separate ClassInit object or analysis-only memory slice is planned initially. **Checkpoint 1 reviewed (2026-10-10):** initialization cycles now count only requirements surviving Opto. Parser inserts ideal CLInit obligations on control; the collector walks optimized function bodies and propagates their final call-target effects into CompUnit dependencies. Removed branches/targets leave no historical edges. All generic Node watcher hooks are removed. Parent edges, cycle witnesses, and stable topological ordering remain. Nonreturning calls left out-of-line retain conservative continuations; touches after them can contribute extra initialization ordering. The ideal obligations are erased after validation for this checkpoint; runtime flags/calls and serialized summaries are still pending. Imported/native/ unresolved effects remain explicitly incomplete and `_initStatic` remains false. All 492 Chapter 25 tests pass, plus focused x86/ARM/RISC-V encoding checks for the nonreturning-call fix added after the full run. Two existing RISC-V spill expectations improve from 9 to 7. Log: `build/init-deps-opto-final.log`. See the [checkpoint notes](../chapter25/initialization.md#1-compunit-dependencies-and-cycle-checking). **Non-exiting function inlining (2026-10-10):** general body cloning starts in Chapter 25. Chapters 20-24 clone only restricted straight-line boilerplate; their older `FunNode.body()` helpers have no callers. Chapter 25 now discovers body control forwards from Fun, including infinite-loop arms, and explicitly copies its Return even when that node has no executable incoming control. The nonreturning-inline guard is removed. Serialization also permits a unit whose unused memory projection disappeared. The regression covers a wholly non-exiting function and a two-call function with both returning and infinite arms, checking inlining and Eval2 behavior across three seeds and all three machine encoders with allocation checks. All 493 Chapter 25 tests pass; log: `build/inline-never-exit.log`. Changing data-pointer width and moving fully initialized class objects to read-only storage remain separate follow-ons. **Checkpoint 2 implemented for review (2026-10-10):** each class initializer's object has a normal mutable boolean `$init` field with a zero initial image. The C0DE header now carries owned units, initialization edges, completeness/static flags, and function effect summaries. Ideal CLInit markers survive serialization; backend erasure happens afterward. Loading remaps identities and validates the combined graph, including imported initializers without live bodies. Object discovery distinguishes definitions from referenced class names, and serialized IR excludes imported bodies. Focused fixtures cover cross-object cycles, shared diamond identities, interleaving module orders, imported helper inlining, and flag layout/remapping. Runtime check/call/store lowering remains checkpoint 3; rebuild old objects after the format change. All 494 Chapter 25 Make tests pass. Diagnostic witnesses use compact `ClassCyclicError` records with numeric locations and shared call-chain suffixes, including across serialization. Strings are formatted only on rejection. Validation includes source call-chain and imported field locations. Logs: `build/init-checkpoint2-*.log` and `build/class-cycle-witness-all.log`. The original broader design below remains a proposal, not a completion claim. The goal is to pre-allocate AOT class objects in ELF data and pre-fill their fields, letting ordinary graph optimization remove redundant initialization. Lazy-loaded classes retain runtime initialization for now. - Represent static object creation with a dedicated node, or a ConstantNode carrying a TypeStruct with the appropriate final field values. Preserve the class's identity and canonical layout independently of its current field values; equal contents must not merge distinct class objects. - Use normal StoreNode peepholes to remove same-value-over-same-value stores. If needed, add a peephole that folds initializing stores into the static creator's type. Create a replacement ConstantNode/type; never mutate a shared constant's type. Prove that folding preserves initialization ordering and does not expose a final value prematurely through another reference. - When `` reduces to returning the static object, with no remaining I/O or public-memory effects, its runtime work can disappear. Initially it is sufficient to emit a trivial `` and keep its calls. Omitting the function or calls is a later optimization; mixed initializers retain their remaining effects. Adapt this conceptual return to the existing class-memory representation rather than assuming the current `` returns a pointer. - Extend encoding's relocation information to describe addresses stored inside data: functions, other class objects, and constant objects such as strings. Emit the required data-section relocations, including their symbol, field offset, width, and addend. Keep native linking and emulator linking consistent. - Give the function and data distinct stable symbols, e.g. `A.B.` and `A.B.$class`. The defining CompUnit owns the class object and emits its one definition in its ELF. Other compilation units, including source currently being compiled, reference those symbols without emitting another copy of the class data. Unifying loaded Simple types/IR alone does not unify native data; preserve ownership and identity across partial compiles and serialization. - Target four-byte function **and data** pointers, with code and data addresses restricted to the low 4 GB. Function pointers stay four bytes; data pointers must be brought into agreement. Account for layout, loads/stores, relocations, native runtime/FFI boundaries, and actual placement; do not silently truncate an out-of-range address. This is a proposed representation constraint, not a claim that the current runtime already satisfies it. - Emit immutable, fully preinitialized objects in read-only storage. Objects with mutable fields or remaining runtime initialization writes stay writable. Retain the current writable-class-storage correction until those writes have actually been eliminated. Acceptance should cover scalar and pointer fields, shared/cyclic object references, preserved effects in mixed initializers, and serialization round-trips. A diamond of separately compiled users must share one class-object address and observe each other's mutations. Check ELF definitions/relocations, address-range enforcement, native execution, emulator execution, and the full Chapter 25 suite, including `make -j 4 tests`. ## Larger or entangled changes: defer | Group | Eventual home | Why not in the small queue yet | |---|---|---| | Conditional Store and array Load control | Memory/arrays chapters | Reproduce under the earlier alias model before extracting fixes from the new memory implementation. | | SCCP dependencies, function revival, reachability | 24; some foundations may fit 18 | Separate old-IR corrections from new Guard/Escape/BulkMemPhi and external-caller machinery. | | TypeScalar, numeric modes, guards, symbolic fields, open/forward types | Revisit earlier homes later | A connected incomplete-types architecture, including phase ordering and errors. | | BulkMemPhi/MemPhi, private constructor memory, allocation helpers | Revisit memory / constructors / methods | Move invariants and regressions together. | | Serialization, global identity remapping, module escape summaries | Separate compilation | Remain with modules. | ## Register allocation: completed chapter progression The allocator review is complete through Chapter 25; Cliff reviewed Chapter 23 on 2026-10-07. Each README ends with the introduced technique, a triangular matrix of fixed cohorts across compiler chapters, and commentary. The current [spill-cost matrix](regalloc-spills.md) is regenerated by root `make spill-stats`, which checks matching test/CPU/ABI membership with assertions enabled. | Chapter | Additional technique | Final cohort sizes | |---|---|---| | 20 | Basic coloring, rematerialization and loop splitting, with shared legality/progress fixes | 39 | | 21 | Conservative copy coalescing and adjacent-copy forwarding | 39, 52 | | 22 | Stronger copy-chain/backedge bias and cheap-spill ordering | 39, 52, 26 | | 23 | Group popular single-def uses by compatible register classes | 39, 52, 26, 24 | | 24 | One cold-only attempt for loop-Phi self-conflicts, then mandatory fallback | 39, 52, 26, 24, 58 | | 25 | Existing area/cost ranking and later conflict strategies | 39, 52, 26, 24, 58, 14 | Shared corrections include RegMask/LRG bookkeeping, null use masks, kills without an output LRG, self-conflict splitting, compatible rematerialization, clobber-aware copy reuse/bypass, deterministic ordering, and side-effect-free diagnostics. The final audit adds the missing narrow x86 masks in 20-21 and RISC-V mask in 21; fixed-neighbor color bias in 20-25; and ARM memory register-bank selection, float opcodes, and matching emulator fixes in 21-25. Chapter 25's additional fixed-use/deep-side splitting thresholds remain there. The def-use-count snapshot and null-use guard support its extra split branch; 20-24 do not have that branch. Earlier kill handling subtracts killed registers; 25 can choose to split cloneables earlier. No independent earlier-chapter correctness failure was established for these strategy differences. They are not uncompleted promises to copy the complete allocator backwards. Compilation- unit ownership and void/metadata handling also stay with the later representation. The final ARM failure was substantive: the original Newton float programs reached the allocation round limit because a float store demanded a GPR; the deeper loop split kept cutting the wrong portion of the range. Permitting the supported FP registers removes that conflict. Encoding must then use the allocated register bank, with five-bit register numbers, for both immediate and indexed loads/stores. The emulator must preserve floating bits and scale double offsets by eight. Chapter 25 freezes 199 earlier compilation entries, after excluding Jig scratch tests, in [documented source fixtures](../chapter25/src/test/java/com/seaofnodes/simple/spill/README.md). Constructor/library adaptations change IR, so this is a program-cohort comparison, not identical machine graphs. Those rows replay allocation and legality checks; current Chapter25Test native checks and a fresh system-library encoding contribute 14 more recorded allocations. The complete 2026-10-02 replay totals **2,568 moves / 5,459 loop-weighted moves** over 226 compilations after the fixture repair below. Earlier audit total, before adjacent-copy forwarding: **2,597 moves / 5,579 loop-weighted moves** over 222 compilations. Chapter 25's area/cost implementation is retained. Substituting the earlier ranking saves five moves on the clients but fails a fresh `sys` allocation at the eight-round limit; a failed library cannot be omitted from the comparison. The earlier tables are refreshed where the final mask fixes changed them: Chapter 20 was 238 / 357 before the subsequent lazy-memory port (now 234 / 360); Chapter 21 was 840 / 1,456 before lazy memory and adjacent-copy forwarding. The audit's coalescing on/off comparison saves 230 / 433 moves in Chapter 21 with identical legality fixes. Current totals for 21-24 appear in the adjacent-copy record above. Full details and comparison limits belong in the individual READMEs rather than a second evolving set of tables here. ## Review and test protocol 1. Establish the destination baseline. Reproduce a real failure with a small source or constrained machine graph before classifying a change as a bug fix. 2. Apply the smallest correction, propagate it to all affected snapshots, and run each snapshot's focused checks and full `make tests`. Respect the current user-authorized review boundary; do not push without explicit instructions. 3. Force Java rebuilds when older Makefiles miss dependencies. Rebuild `sys.o` after compiler changes; include a fresh library compilation in allocator measurements rather than relying on an up-to-date object. 4. Keep legality/progress/runtime checks separate from spill expectations. Eight-round exhaustion is a failure; neither increasing the cutoff nor changing a golden proves it fixed. Preserve test membership, targets/ABIs, and fixed seeds. Do not sweep optimizer seeds for routine backend validation. 5. Report aggregate and local changes. Use same-compiler heuristic comparisons; changed frontend/lowering graphs are not allocator-only measurements. 6. Keep edited text LF-only, preserve unrelated changes, and run `git diff --check`. The top-level runner accepts explicit chapter lists, e.g. `make -k tests CHAPTERS="chapter20 chapter21"`. Tests in 25 alone are insufficient. ## Validation record - **Equality and ARM execution corrections, 2026-10-07.** Chapters 21-25 retain RISC-V's subtraction when expanding equality and use unsigned SEQZ for logical-not (including negative inputs), exclude SP/XZR from narrow ARM load destinations, and encode ARM logical-not with CMP-to-XZR followed by CSET-to-result. ARM condition inversion is corrected in 21-23; FCMP operand order in 21-24. Chapter 20 has different selection/stub encoders and does not contain these faulty expansions. Shared evaluators now use unsigned SLT/SLTIU comparisons and discard ARM XZR destination writes. The existing Chapter 24 nested-equality test in 24/25 expected the wrong result for argument 1; it now checks -1, 0 and 1. Independent ISA checks and full Make suites pass in 21-25 (including native/system tests). All previously excluded measurement cases execute successfully; the reporter also checks nested equality at 0-3. Logs: `build/dynamic-stats/fix-tests{21..25}.log` and `fix-isa.log`. The expanded matrices and a measured breakdown of the Chapter 24-to-25 increase are in [the allocation report](regalloc-spills.md). Cohort 23's +476 Ops is chiefly class-field initialization; its +282 RA instructions comprise 280 rematerializations and only two extra splits. - **Dynamic allocation metrics, 2026-10-07.** Chapter 20-25 README matrices now report executed ARM/RISC-V instructions, the allocator-created subset (copies, spills/reloads and rematerializations), and x86-only weighted split estimates. Dynamic membership and input vectors are fixed per cohort, with results checked across compilers/targets. Native-library bodies are excluded. Chapter 20 has no encoder. The dynamic rows cover 22/20/14/14/36/2 program-target cases; compilation-only/API exclusions are explicit. The original full allocation cohorts are unchanged. Expanded shared evaluator support fixes ARM SDIV, ORR-immediate and false CSET values, and RISC-V floating stores and FEQ/FLT decoding. Independent instruction-word checks pass in `make -C isa tests`; full Make suites pass in 21-25, including Chapter 25's native/system groups. Logs: `build/dynamic-stats/tests-{21..25}.log` and `isa-tests.log`. The reporter validates cohort identities, inputs and results; generated per-case records are in `build/spill-stats/dynamic-counts.csv`. - **Remaining replay fixture cleanup, 2026-10-07.** Fourteen Chapter 25 replay sources make implementation helpers/classes private while retaining export APIs, explicitly return zero from three String API fixtures, and correct two guarded short-circuit adaptations to return their RHS value (not `!!value`). Disposable Eval2 checks at arguments 0-3 return 44, 1, 44, 1 for both loops. `make -C chapter25 spill-stats CTAGS=` passes all 199 historical allocations, fresh library encoding, 23 native tests and spill expectations. Fixed cohort membership remains unchanged. Cohort 21 changes 1,077 to 1,036; 23 changes 152 to 104; 24 changes 1,503 to 1,315. Other rows are unchanged. The matrix reporter validates membership against saved Chapter 20-24 reports and updates the documentation. No compiler changes or permanent tests were added. Logs and disposable allocation/assembly probes: `build/fixture-cleanup/`. - **Ignore debugging Jigs (2026-10-07).** All `testJig*` methods are scratch placeholders, not regression tests. Added missing `@Ignore` annotations in Chapters 14-25, preserving their bodies, and removed all 15 Jig entries and four unused frozen sources from Chapter 25's replay corpus. Cohorts 23/24 now have 24/58 allocations; the corpus has 199 entries. The debug Fibonacci in Chapters 22-24's `progs/jig.smp` uses private `_fib`; `fib.smp` and the genuine `testFibExport` fixtures retain their exports, as confirmed by Cliff. Full Make suites pass in all 13 affected snapshots, including 17a/17b and Chapter 25's native groups, plus shared printer/ISA checks. Fresh spill reports pass in 20-25, and every retained raw/scaled allocation record matches its prior measurement. Cohort 23 is now 103/103/152 across compilers 23/24/25; cohort 24 is 1,387/1,503 across 24/25. The historical heuristic comparison was recomputed from saved records with the same exclusions. All tables refreshed. A Chapter 25 allocation probe confirms the private debug Fibonacci leaves only ``, with no separate Fibonacci body. Logs: `build/jig-cleanup/`. - **Tiny-body inlining backport, Chapters 20-24 (2026-10-07).** Preserved Cliff's reusable static 15-node worklist and Return-input/prefix dependencies. These chapters previously supported only single-caller inlining, so the port adds restricted copying of straight-line Store/MemMerge bodies, substituting call arguments and control directly. Growing inlines run from a separate worklist after ordinary peepholes settle; existing single-caller inlining is unchanged. Constructor defaults already expand directly in these chapters; they have no Chapter 25-style constructor calls or parse-size gate to relax. All full Make suites pass: 406/445/466/488/518 tests respectively, plus each fuzzer wrapper. The existing `Chapter18Test.testInline` graph now contains literal `1` on one branch; no new permanent tests were added. Disposable two-call store probes pass with seeds 123/456/789 in all five chapters, including an intervening store to another alias. All 39/91/117/147/214 allocation records, including raw and scaled costs, match the prior reports exactly. Logs: `build/tiny-inline/final*.log`. Chapter 25 code and the deferred general inliner redesign are unchanged. - **Bounded tiny-body inlining (2026-10-07).** Replaced the constructor-specific 1,000-node parse allowance with a conservative at-most-15-node check when the parse estimate exceeds the normal limit. It recognizes entry/parameter/store/ memory-merge/Return boilerplate, follows uses to avoid sparse alias arrays, bounds fanout, and stops at Return. Dependencies watch Return inputs and only the inspected prefix/frontier; failed checks can retry after folding. Actual body limits remain 100, or 200 for `` and instance ``. All six replay constructor calls still fold, and all 228 raw/scaled allocation records match the preceding allowance trial. Full Chapter 25 Make tests pass, as do fresh Win64/SystemV library compilations. Disposable probes force parse estimates to 10,000 on Bubble Sort initializers and verify successful inlining/allocation on all three targets. A dependency probe verifies requeueing without a Return type change, rejection above the small-body bound, and acceptance after a memory-chain fold. Logs: `build/tiny-inline/`. The general inliner redesign remains deferred until after the chapter split. - **Chapter 25 constructor inlining allowance (2026-10-07).** `` and instance `` share a 200-node optimized-body limit, including the cloning assertion. A 1,000-node parse-time cutoff accommodates nested-method and memory construction overhead; ordinary functions stay at 100 for both gates. The initial 200/200 trial changed none of 228 allocation records. The larger parse allowance removes all six remaining constructor calls in the frozen replay sources (Bubble Sort), while public constructor definitions remain. Cohorts 20-23 are unchanged; cohort 24 improves 1,637 to 1,634, while cohort 25 rises 1,829 to 1,862. Win64 sys improves 842 to 809, Dijkstra 584 to 582; FileIO rises 293 to 361. Fresh SystemV sys improves 803 to 777. These static move costs do not establish a runtime improvement. Full Chapter 25 Make tests pass (405 + 39 + 9 + 23 + 1 + 19), and the complete spill report passes its 214 replay allocations and native/system tests. No goldens, replay sources, seeds or memberships changed during this experiment. Matrices are refreshed. Earlier compiler snapshots are unchanged: 20-24 have single-use non-cloning inlining rather than this size-based cloning gate. Logs: `build/ctor-inline/`. - **Chapter 25 function constants and private helpers (2026-10-07).** Single-use, single-definition function addresses separated from their use in the same block get priority for rematerialization over spilling callee-save registers. Adjacent, multi-use and cross-block cases keep the existing ranking. On unchanged inputs this removes all 28 C-return initializer moves, plus 13 weighted moves from cohort 24 and 22 from cohort 25, with no other cohort changes. Eleven replay fixtures separately mark local helpers/classes private; exported interfaces stay public. Final Ch25 weighted costs by cohort are 491 / 1,077 / 71 / 380 / 1,637 / 1,829, with unchanged membership and counts. The fixture README and matrix document the changed visibility baseline. Full `make -C chapter25 tests` passes (405 + 39 + 9 + 23 + 1 + 19 tests), as do both complete spill reports and fresh SystemV sys encoding on Windows (327 raw / 803 weighted moves). The reports each include 214 replay allocations, fresh Win64 library encoding and 23 native/system tests. No inlining-policy change or earlier-chapter allocator change was made. Logs: `build/ra-repair/`. - **Spill matrices and bounded CI processes (2026-10-07).** Reran Chapter 20-25 statistics with fixed cohort sizes 39, 52, 26, 30, 67, and 14. Chapter 25 now includes the two Chapter 22 C-return ABI cases missing from its old manifest. The matrix generator checks the multiset of test/CPU/ABI identities and emits no totals row; each README shows columns through its chapter. Chapter 23's copied Java implementation snippets are replaced with direct source links. CI run 37675851863 timed out in `nm` after 10 seconds; run 37678408500 passed. The bounded GCC/nm/test-executable calls now allow 60 seconds and retain forced termination and captured stdout/stderr, with PID/CPU-time diagnostics. CLI subprocesses retain their existing unbounded behavior. Root `make spill-stats` passes and reproduces the matrix; the reporter rejects changed membership even when counts match, and rejects failed runs. All 23 Chapter 25 system tests pass. An 11-second subprocess passes the new bound; an intentional hang is killed at a short test deadline with diagnostics and no live child. Logs: `build/spill-stats/`, `build/ci-timeout-60-tests.log`, and `build/ci-timeout-60-probe.log`. - **Numeric assignment conversions and evaluators (2026-10-07).** Chapters 14-25 accept float-to-integer assignment using ToIntegerNode: truncate toward zero, saturate to i64 on overflow, map NaN to zero, then apply the declared integer width. Locals, fields, and arrays use the conversion; Chapters 22-24 also convert before their store-specific width handling. Chapter 25 lowers authoritative Convert nodes and serializes ToInteger in exported graphs. Both evaluators now execute ToInteger and RoundF32, including nonconstant f32 array stores. The Chapter 14 README documents the assignment syntax. Instruction selection starts in 19, ARM support in 20, and shared encoding in 21. x86 and RISC-V sequences normalize hardware overflow/NaN differences; ARM uses FCVTZS. Full Make suites pass in 14-25 and the shared ISA module. Tests cover constant and dynamic conversions, signed values, narrow integer results, limits, infinities, and NaN; Chapter 25 runs native x86 plus compiled ARM/RISC-V emulation. Disposable probes also check 19-20 selection/allocation, 21-24 encoding/emulation, and 25 importing an exported conversion function. Logs: `build/float-convert-.log`, `build/float-convert-isa.log`. - **Floating arrays in Chapter 18 (2026-10-07).** Added TypeFloat to the `TypeStruct.makeAry` assertion, matching Chapter 19 onward; earlier array chapters already accept floating elements. The original `return new flt[1];` reproduction passes with assertions enabled. One regression covers flt/f64/f32 zero initialization, flt/f64 looped loads and stores, and constant f32 rounding. It fails at the original assertion before the correction. The complete Chapter 18 suite passes (340 tests plus its fuzzer wrapper); no other compiler snapshot needed this assertion change. Nonconstant scalar and array narrowing passes type checking with the RoundF32 correction below. Subsequent evaluator support also exercises nonconstant f32 stores at runtime. Original logs: `build/float-arrays-18-before.log`, `build/float-arrays-18-after.log`. - **Nonconstant f32 narrowing (2026-10-07).** RoundF32Node now returns F32 for an F64 input in Chapters 18-25, and the equivalent B32 for BOT in Chapters 14-17b. Constant rounding and other input types are unchanged. Chapter 18's floating-array regression also checks nonconstant scalar and array assignments. Complete Make suites pass in every affected chapter, including Chapter 25's native tests; no expected graph or allocation results changed. Logs: `build/round-f32-.log`. - **Shared Phi inputs and deterministic inlining (2026-10-07).** Chapters 20-25 split a self-conflicting Phi's inputs when another Phi shares them; splitting only the entry and result could leave two loop Phis joined through their backedge forever. Chapters 24-25 keep their first cold-only attempt. Chapter 25 relinks cloned calls in node-ID order, removing identity-map iteration order from graph edges and optimizer worklists. The original SystemV sys-library allocation failure reproduced directly on Windows with `-XX:+UnlockExperimentalVMOptions -XX:hashCode=2`. With both fixes, complete phase dumps through allocation agree between Windows Java 21.0.4, Windows with constant identity hashes, and Linux Temurin 21.0.12.1. Windows suites pass in all six affected chapters (406, 445, 466, 488, 518, and 494 tests), including native tests and unchanged spill expectations. No tests were added. A fresh Linux checkout with Temurin 21 passes the exact CI command `make lib tests CTAGS=`, including all chapters, a fresh SystemV sys library, native tests, and shared printer/ISA checks. Logs: `build/ci-linux-all-tests.log`, `build/ci-windows-tests.log`, `build/ci-windows-ch25-final.log`, and `build/ci-fixed-{default,constant,linux}.log`. - **Unused parser memory aggregates, Chapters 11-24 (2026-10-03).** Release temporary prior-memory keeps with dead-node cleanup after publishing the new memory value. Flattening a MemMerge could remove its last real use while the parser kept it alive; plain `unkeep()` then left it unused and off the worklist. The Chapter 17b presentation alias example's MemMerge#18 now disappears. Its partial allocation input (#14) remains: null default means uncovered aliases, so it cannot be replaced with whole memory just because its covered slices coincide. Chapter 25's different construction sites were not changed. Make builds passed in all affected chapters; complete suites passed in 11 (163 tests) and 12 (177). Existing Chapter10/11 tests passed in 13-24, plus Chapter15/16 tests where present and Chapter17a/17b tests in 17b. Updated earlier graph-shape expectations for the newly eliminated initializing Store and a different equivalent Phi name. Direct graph checks and evaluator runs passed for both presentation memory examples; their JSON/SVG captures were regenerated. Logs: `build/rebase-talk/memory-cleanup/`. - **Chapter 25 historical String fixtures (2026-10-02).** Added an explicit constructor initializing the required non-null `cs` field in `Chapter21Test-testStringExport.smp`, `Chapter21AllocTest-testString.smp`, and `Chapter23AllocTest-testString.smp`. Equality/hash bodies and the frozen 212-entry manifest are unchanged; all ten formerly failing entries now pass. These source adaptations establish a new comparison baseline, rather than demonstrating an allocator-only improvement. `make -C chapter25 spill-stats` passes all 212 historical entries, fresh system-library encoding, and all 23 Chapter25Test tests, with zero spill-golden failures. The complete 226 recorded allocations total **2,568 retained moves / 5,459 weighted moves**; the README contains the cohort breakdown. Log: `build/string-fixtures-spill.log`. - **Function loop-tree boundaries, Chapters 18-25.** Stop the post-order loop walk at Return, seed each function's outer tree before visiting its body, and use that tree for synthetic exit projections, return Regions and the parent of otherwise exitless loops. In Chapter 25, walking through Return/StopCU/Stop into Start mistook the external-world cycle for another loop: the function entry had depth 1 while its return Region retained depth 0. Function bodies now consistently have depth 0, with real loops adding depth. Chapters 18-20 also assign the tree to the newly created Never and both projections. `Chapter18Test.testNeverExitLoopDepth`, forwarded through 25, covers single, multiple, unconditional and nested synthetic exits. The RISC-V pointer-store reproducer `Chapter25Test.testNeverReturnPointerRiscV` now runs through Encoding without changing the spill heuristic or eight-round limit; its `@Ignore` is removed. Baseline failures are recorded in `build/loop-tree18-baseline.log` and `build/loop-tree25-baseline.log`. Full Make suites pass in `build/loop-tree-all.log`: 18 **333+1**, 19 **377+1**, 20 **399+1**, 21 **438+1**, 22 **459+1**, 23 **481+1**, 24 **511+1**, and 25 **403+39+9+23+1+14**, plus shared printer/ISA tests. Spill goldens remain unchanged. - **Synthetic TOP returns, Chapters 20-25.** TOP remains neutral in the type merge but has no runtime register value. BuildLRG excludes TOP definitions and Phi inputs; IFG does not propagate TOP arms into predecessor liveness; spilling ignores TOP for loop depth and inserts no copies for it. Chapter 20's register-bias walk also stops when a Phi arm has no LRG. Coalesce keeps its normal requirement that both ends of a real copy have live ranges. `Chapter20Test.testAllocatorTopPhi` checks either Phi slot in scheduled graphs from Chapter 20 onward. Source regressions in 21-25 (where loop repair precedes instruction selection) cover integer, non-null pointer and floating returns, multiple synthetic exits, no real exit, and the original two-field memory reproducer. Existing `testNeverMemory` tests now continue through Encoding. Full Make suites pass: 20 **398+1**, 21 **437+1**, 22 **458+1**, 23 **480+1**, 24 **510+1**, and 25 **402+39+9+22+1+14**, plus shared printer/ISA tests. Spill goldens were unchanged. The RISC-V pointer-store convergence test was ignored in that run; the subsequent loop-tree correction above enables it. Chapter 21 ARM/RISC-V execution of the original reproducer returns 0 for `arg=0` and stays in the loop for 10,000 instructions for `arg=1`. Logs: `build/top-ra-all.log` (20-24 and shared modules), `build/top-ra25-final.log`, `build/top-ra-execution.log`. - **GCM conditional-writer ordering, Chapters 21-25.** Restored the `idom()` walk from a writer's late block through its early bound, including matching memory-Phi predecessor paths. Chapters 11-20 already retained this walk. Exact-block checking missed a conditional Store that executes before a Load sunk below the merge. `Chapter21Test.testReadBeforeConditionalStore`, forwarded through 25, checks 120 for the aliasing case and 60 for the distinct-pointer case. Generated ARM/RISC-V execution agrees in Chapter 21. The walk was removed in `798bd959` (April 12, 2025, PR #201), before the Chapter 21 squash. Chapter 21's two RISC-V BrainFuck rows change from 35 / 42 to 48 / 146 retained / weighted moves; the full 91-entry audit is now 847 / 1,652. Runtime checks pass and the two quality goldens are updated; no allocator heuristic changed. Validation logs: `build/gcm-walk-tests.log` (22-24), `build/gcm-walk21-final.log` (complete 21 rerun and spill audit), and `build/gcm-walk25-final.log` (complete 25 rerun). - **Chapter 25 memory follow-through.** Full Make validation passes **482 tests plus the 17-seed fuzzer wrapper**, and the release jar and system library build. The loop-search and read-before-write regressions fail against the original LoadNode and pass with the port. A scratch run across **100 optimizer seeds / 3,100 evaluations** checks the invariant length, a possibly aliasing arm, a changing loop pointer, differing stored values at a multiway merge, and reads before writes. Seeds 0 and 9 are retained in the regression for unresolved Store aliases and nested Phi queueing. At that audit, the historical spill replay retained exactly its **ten known String-constructor parse failures**, with no spill-golden or native-check failures. The same 216 successful allocations total **2,488 moves / 5,379 weighted moves**, compared with 2,494 / 5,399 before this port; this is not a complete-suite total. The subsequent historical String fixture repair above restores a complete replay. Logs: `build/mem25-negative.log`, `build/mem25-seeds.log`, `build/mem25-final2.log` (spill replay), and `build/mem25-final4.log` (full tests and release). - **Chapters 22-24 lazy memory.** Full Make suites pass **454 / 476 / 506 tests**, respectively, plus each chapter's fuzzer wrapper. Release jars build for all three chapters. Forwarded checks cover nested memory, constructors, calls and recursion, allocation registers, native x86 and emulated RISC-V/ARM execution, no-exit loop memory, and the loop Load search. Existing Chapter 23 null-guard tests cover the mixed pointer/integer factoring rejection; Chapter 24's BubbleSort workload covers the precise-Phi/bulk-input collapse guard. Spill reports pass all **117 / 147 / 214** compilation entries, including runtime and allocation checks, with no golden failures. Current raw/weighted totals are **823 / 1,474**, **901 / 1,699**, and **1,307 / 3,008**; prior totals were 826 / 1,470, 913 / 1,711, and 1,310 / 2,969. These include graph changes outside allocation, so they are not allocator-only comparisons. The chapter README tables contain the current cohort breakdowns. Logs: `build/mem22-final.log`, `build/mem22-release.log`, `build/mem23-final2.log`, `build/mem23-release.log`, and `build/mem24-final3.log`. - **Chapter 21 lazy memory and encoding.** Baseline: **412 tests plus the fuzzer wrapper**. Final: **431 tests plus the fuzzer wrapper**. Make release and spill-stats pass. The 16 forwarded memory checks retain scheduling and allocation coverage; three execution/synthetic-exit tests add 42 native x86 and 84 ARM/RISC-V evaluations with independent expected results, plus checks of no-exit loop memory through scheduling. **9,800 scheduled evaluations** pass across 100 optimizer seeds. Emulated multi-function programs enter at main's encoded offset, which need not be zero. The unchanged **91-entry** spill cohort changes **840 / 1,456** retained / weighted moves to **845 / 1,559**. RISC-V BrainFuck changes 35 / 42 to 37 / 58 in the Chapter 20 cohort and 35 / 42 to 46 / 130 in Chapter 21. An ablation omitting only read-before-New ordering returns those rows to 35 / 42 and 37 / 58; the ordering is retained, with no allocator tuning. All eleven changed rows are recorded in the chapter README. Logs: `build/memory21-baseline.log`, `build/memory21-final.log`, `build/memory21-seeds.log`, `build/memory21-spills-baseline.log`, `build/memory21-spills-final.log`, `build/memory21-spills-ablation.log`, and `build/memory21-release.log`. The later 22-24 port is recorded above. - **Chapter 20 lazy memory and register allocation.** Baseline: 380 tests plus the fuzzer wrapper. Final: **396 tests plus the fuzzer wrapper**, with the 15 forwarded regressions and one allocation regression spanning all three targets. **9,800 scheduled runtime evaluations** pass across 100 optimizer seeds. **42 selected/scheduled/allocated graphs** pass legality checks at fixed seed 123 on x86-64, RISC-V, and ARM. Make release and spill-stats pass. The unchanged 39-entry spill cohort changes from **238 / 357** retained / weighted moves to **234 / 360**. The x86 array prefix sum changes 5 / 5 to 6 / 13 because selected-read ordering adds a loop move; a scratch ablation omitting that ordering returns to 5 / 5. No allocator heuristics or benchmark membership changed. The chapter README records all six changed program rows. Logs: `build/memory20-baseline.log`, `build/memory20-final.log`, `build/memory20-seeds.log`, `build/memory20-machine.log`, `build/memory20-spills-baseline.log`, `build/memory20-spills-final.log`, `build/memory20-spills-ablation.log`, and `build/memory20-release.log`. This checkpoint was reviewed and committed as `f01ee052`. - **Chapter 19 lazy memory and instruction selection.** Baseline: 360 tests plus the fuzzer wrapper. Final: **375 tests plus the fuzzer wrapper**; release jar builds with Make alone. Forwarded 13 memory regressions and added two instruction-selection checks: alias/Phi preservation and allocation/call slots on both targets, and folded x86 reads ordered before clobbering Stores. **9,800 scheduled evaluations** and **2,800 selected/scheduled graphs** pass across 100 optimizer seeds, with x86_64_v2 and riscv selection exercised. The latter checks graph scheduling and shape; this chapter does not execute encoded machine code. Logs: `build/memory19-baseline.log`, `build/memory19-final.log`, `build/memory19-seeds.log`, `build/memory19-machine-seeds.log`, and `build/memory19-release.log`. Chapter 14's inherited And graph factors here without widening; its runtime assertion remains. This checkpoint was reviewed and committed as `612555ee`. - **Lazy memory through 17 and 18.** Baselines: 17 has 288 tests; 18 has 318 plus its fuzzer wrapper. The port passes **298 tests in 17** and **331 tests plus the fuzzer wrapper in 18**. Both carry the nine memory regressions and constructor regression; 18 adds checks for non-inlined calls, recursive heap updates, and allocation inside an inlined function. The non-inlined check also verifies Call's input count and last-slot function pointer after GCM. **7,700 / 9,800 evaluations across 100 optimizer seeds** pass in 17 / 18, checking final memory values, complete scheduling, partial New inputs, and two-result New. Full suites and both release jars build with Make alone. Logs: `build/memory17-18-baseline.log`, `build/memory17-18-final.log`, `build/memory17-seeds.log`, and `build/memory18-seeds.log`. Chapter 17's And graph remains unfactored when the result would widen; the existing runtime assertion is unchanged. Shared allocation memory retains the `.y` Load in its inherited array example, as in 16. Nullable diagnostics select the earlier array dereference in 17/18. The Chapter 17 null test now makes `p2` mutable so its setup does not fail first on an unrelated final-field store. This checkpoint was reviewed and committed as `2d51b647`. - **Chapter 16 lazy memory and constructors.** Baseline: 237 tests. The port passes 247 tests, including nine carried memory regressions and one constructor case combining reads, writes, a loop, and an unrelated alias. Full Make tests and release builds for 10a-16 pass: **1,568 tests across eight snapshots**. An additional **7,700 evaluations across 100 optimizer seeds** check memory behavior, complete scheduling, two-result New, and partial allocation inputs. Logs: `build/memory16-baseline.log`, `build/memory16-all-tests.log`, and `build/memory16-seeds.log`. The changed graph order exposed a dependency notification gap in `Chapter14Test.testCloneAnd`: replacing Minus#25 rewired And#28 without waking its dependent Phi#26. `Node.subsume` now wakes each rewired user's recorded dependents in 10a-16. The existing test passes without weakening its assertions. Chapters 17+ are unchanged; constructor integration is the review boundary. - **Generalized Phi factoring, 10a through 15.** Replaced binary-only factoring and the blanket memory exclusion with operand-wise Phis and exact-class `copyEmpty`. Matching Loads and sole-use Stores are eligible when unbound to control; operation attributes, field/alias identity, operand types, and result-type monotonicity are preserved. Load safety examines memory users before explicit GCM anti-dependencies exist. The final guard checks only immediate users, stopping at Stores, Phis, MemMerges, and New in 15. Memory factoring waits for bulk partitioning at its Region; MemMerge and BulkMemPhi are never factored. Stores bound to control in 11-14 remain excluded. User removal now wakes recorded dependents, and empty-diamond folding observes projection rewiring. Three regressions live in each snapshot's `Chapter10Test`; the original 10a Phi fails both positive folding checks, and disabling the Load safety guard exposes an invalid read/write schedule in the negative probe. Full Make tests pass **159 / 162 / 177 / 181 / 196 / 216 / 230** and all seven release jars build. A 10a Phi label and one 14 Region ID were refreshed; expression/value expectations remain unchanged. Each snapshot also passes **3,500 evaluations across 100 optimizer seeds**, and 15 passes another **7,000 allocation/memory evaluations**. Logs: `build/phi-drop-baseline.log`, `build/phi-drop-tests.log`, `build/phi-drop-seeds*.log`, `build/phi-drop-negative.log`, `build/phi-drop-antidep-negative.log`, and `build/phi-drop-allocation-seeds15.log`. Chapters 16+ are unchanged. The subsequent guard refactor moves memory eligibility into virtual `MemOpNode.canDrop` overrides and clobber inspection into Load. Removed 10a's unused visited set. All 1,321 tests and seven release builds passed (`build/phi-guards-refactor.log`). At Cliff's request, the subsequent simplification also removes recursive aggregate traversal from 10b-15: a one-step check conservatively stops at possible indirect clobbers, keeping the teaching cost proportionate to saving one Load. The aggregate example now keeps two Loads in 10b-15; its runtime checks remain unchanged. All 1,321 tests and seven release builds pass with the simpler guard (`build/phi-guards-one-step.log`). - **Chapter 15 partial allocation memory (review checkpoint).** Implemented the approved two-result New and carried the lazy memory model through parser, scheduling, evaluator, viewer roles, and chapter documentation. Full Make suite passes **227 tests** and the release jar builds, up from baseline **218**: six carried memory regressions plus three allocation regressions. They cover unrelated aliases, mixed integer/float/pointer contents, reads across allocation, and allocation inside loops. A disposable probe also passed **7,000 evaluations across 100 optimizer seeds**, including empty structs, arrays, both slot conventions, partial coverage, and complete scheduling of live definitions. Region/Loop golden changes only alter IDs; the nullable-access diagnostic matches 13-14. Logs: `build/memory15-baseline.log`, `build/memory-forward-15.log`, and `build/memory15-seeds.log`; final tests and release: `build/memory15-final.log`. - **Memory forward port through Chapters 12-14.** Preserved floating-point operations, typed reference initialization, and narrow stores while forwarding the reviewed parser, memory nodes, GCM, Return slots, and evaluator. Each chapter inherits all six memory regressions. Make tests pass **178 / 193 / 213**, up from baselines **172 / 187 / 207**; all three release jars build. Updated printed Region/Loop IDs without changing expression expectations. The invalid `head.next.i` test in 13 and 14 now reports the possibly-null `i` access first; both accesses remain invalid, and the test still requires rejection. Logs: `build/memory-forward-baselines.log` and `build/memory-forward-12-14.log`. The subsequent allocation port is recorded above. - **Control/memory slot convention (10a, 10b, 11).** Return now takes `{ctrl, $mem, result}` and describes its result tuple in that order, with whole memory typed as `TypeMem.BOT`. Evaluators use `expr()` rather than a literal result index. Updated viewer roles, existing projection checks, documentation, and numbered diagram edges. Make tests pass **156 / 159 / 174** respectively, and all three release jars build. Log: `build/return-memory-slots.log`. Preserve control slot 0 and memory slot 1 when forwarding Call, CallEnd, and other nodes in later chapters. - **Chapter 11 memory forward port (2026-09-29, review checkpoint).** Parser, Start, Scope, and Return now use 10b's one-memory model and its three memory nodes. GCM schedules MemMerge as a dependency node, filters both Load readiness and anti-dependencies by alias, and retains the full Store placement range needed by the evaluator. Synthetic never-taken loop returns remain scope-free. Updated the evaluator, viewer Return edge roles, walkthrough diagrams, and scheduling code excerpts. The baseline passed **168 tests**; the port passes **174**, including all six forwarded memory regressions. Fifty existing printed expectations changed only Region/Loop node IDs. Five memory programs pass seven inputs across 100 optimizer seeds, with scheduling assertions and final heap/value checks (**3,500 evaluations**). Make also builds the release jar and regenerates all six updated walkthrough diagrams; `git diff --check` passes. Logs: `build/memory-forward-11-baseline.log`, `build/memory-forward-11.log`. Chapters 12-24 are untouched pending review. - **Chapter 10a/10b split (2026-09-29).** Replaced `chapter10` with two independently buildable snapshots: whole-memory SSA in 10a, then lazy `MemMerge`/`MemPhi`/`BulkMemPhi` partitioning in 10b. Updated Make discovery, Maven modules, IDEA descriptors/local module list, chapter navigation, linear-history link handling, and regenerated the memory diagrams. Fresh Make builds pass **156 tests in 10a and 159 in 10b**, including the chapter-local fuzzer regression wrapper. Both release jars and tags build. Offline Maven tests pass in both modules. Five memory programs each pass seven inputs across 100 optimizer seeds in each compiler (**3,500 executions per chapter**). Seed 97's missed bulk-Phi revisit is retained as a regression. Logs: `build/chapter10-clean-build.log`, `build/chapter10-maven.log`, `build/10a-seeds.log`, and `build/10b-seeds.log`. At this checkpoint Chapters 11-24 had not yet adopted the new memory representation; forward-port progress is recorded above. This is a condensed completion record, not a list of current suite counts for old revisions. Earlier detailed traces are in Git history; durable invariants have been consolidated into the [AI notes](../skills/chapter25-codex-notes.md). Unresolved reproductions have been promoted to the pending queue above. On 2026-09-28, the constructor/memory-check backport passed the full Make suites in every compiler snapshot from 10 through 24, including their default fuzzer wrappers (`build/error-backport-tests.log`). The destination baseline was also green (`build/error-backport-baseline.log`). The arithmetic contract experiment and its separate validation are recorded independently. | Completed work | Scope and evidence retained | |---|---| | Narrow C integer returns | On 2026-09-28, instruction selection in 22-25 sign/zero-extends direct C call results according to the declared 8/16/32-bit return type. x86 uses MOVSX/MOVZX/MOVSXD or a 32-bit MOV; ARM uses SBFM/UBFM; RISC-V uses shifts, including zero-extension of its sign-extended u32 ABI result. Chapter 25 consults external function identities because optimization can replace ExternNode with an ordinary constant. Native regressions run both x86 calling conventions with garbage upper return bits; ARM/RISC-V stubs exercise signed/unsigned boundaries and unchanged i64 results. Chapter 25's errno test now also requires `close(-1)==-1`. Original selectors reproduce failures in 22 and 25, including the libc case (`build/c-return-negative-{22,25}.log`). Full Make suites passed: 22 (433), 23 (455), 24 (485), 25 (479), plus each default fuzzer wrapper; 25 rebuilt sys.o (`build/c-return-final2.log`). | | ARM bitfield emulation | SBFM/UBFM decoding now handles both immediate fields, covering signed/unsigned extracts as well as ASR/LSR/LSL. The previous emulator treated all SBFM as ASR and all UBFM as LSL, hiding a logical-right-shift error and rejecting the new extension results. Introduced the correction and independent instruction-word regression in 21 and forwarded through 25. Full 21 suite passed (412 tests plus fuzzer; baseline 411); 22-25 validation is included above. | | FunPtr lifetime and callable entries | On 2026-09-28, introduced Return-linked FunPtrNode in 22-24, including semantic constant folding. Direct call targets retain existing call-graph treatment; all other address uses conservatively retain unknown callers, without escape analysis. Stop retains each callable Return through instruction selection; machine operands omit the lifetime edge. Anonymous/library returns failed relocation on all three targets before the change; 24 also failed named returns. New tests call returned anonymous/named/library pointers on ARM/RISC-V and from native C, and check discarded anonymous bodies disappear. Full Make suites passed: 21 (411), 22 (430), 23 (452), 24 (482), each plus its fuzzer wrapper. Baselines passed at 410/427/449/479. Chapter 24's Phi-selected functions retain general parameter types; graph expectations reflect that conservative rule. | | Function-address backend and worklist corrections | RISC-V TFPRISC now patches ADDI at opStart+4, rounds AUIPC for signed low bits, and reports its actual eight-byte size (21-24). ELF export emits anonymous bodies without exported names (21-24). Actual invocation exposed both bugs after retention was repaired; Chapter 21's reduced conditional-return probe trapped before the encoding fix. Chapter 22 gains the later inlining dominator null guard. In 24, killing Call#618 reduced FunPtr#540 to one use without waking dependent CallEnd#560; Node.kill now wakes the remaining definition's dependencies after removing a use (24-25). The no-return String workload loses unused hash bodies; its spill expectations become zero on all targets. Chapter 24 spill-stats passes all 212 original compilation entries at 1,351 moves / 3,115 weighted moves; its README separates this measurement from the original allocator ablation. Full Chapter 25 Make validation also passed: 476 ordinary tests plus its 17-seed fuzzer wrapper, with sys.o rebuilt. | | Chapter 25 direct C data bindings | On 2026-09-28, full `make tests` passed after the Field/ExternOffset refactor: 476 ordinary tests plus the fuzzer wrapper (17 seeds). Coverage checks zero layout space for external fields, native shared integer/float storage across C calls, compound updates, source/precompiled imports sharing aliases and preserving storage metadata, ARM/RISC-V emulated address relocation and signed loads, and the errno accessor after a failed native call. Direct bindings remain in 25: 22-24 have different singleton-pointer constant and instruction-selection contracts. Serialization retains magic C0DE; the changed layout requires rebuilding all objects. Only the independent RISC-V fixes below were selected for backport. | | RISC-V right shifts and pointer relocation | Backported from 25 to 22-24 on 2026-09-28. EvalRisc5 selects arithmetic/logical right shifts correctly; TMPRISC rounds the AUIPC high part to account for signed ADDI low bits. New regressions cover immediate/register shifts and forward/backward relocation boundaries. Both fail before the Chapter 22 fixes (arithmetic shift becomes logical; delta 0x800 lands 4 KB low). Full Make suites passed: 22 (427), 23 (449), 24 (479), each plus its fuzzer wrapper. Baseline suites also passed (425/447/477). | | Chapter 25 Load fuzzer recheck | At `8212a66f`, seed `-4628356252269023530` passes its direct fuzzPeepsRegression replay (`build/load-seed-recheck.log`). The complete `make fuzzer` wrapper also passes all 17 seeds on 2026-09-28 (one JUnit wrapper test). The seed remains a regression; OPEN_FAILING_SEEDS is empty. No new compiler fix was needed. The older Load TODO report is superseded. | | Arithmetic high results at the SCCP boundary | Chapter 24 integer and float arithmetic now return global TOP for high operands, matching 25. Widened ArithNode.compute's Java return signature to Type; no caller requires TypeInteger. Retained the typed integer fallback for unresolved/error operands, for the parser dependency in the pending queue. The TOP change starts with SCCP in 24; earlier snapshots retain their typed contracts. Full Chapter 24 Make suite passed: 477 tests plus its fuzzer wrapper (`build/arithmetic-top-full-24.log`). | | Required-field and memory-check backport | Chapters 16-24 retain allocation-time checks for inline `new S { ... }` initialization. Chapter 17 had its TOP/unset-field check commented out; restored it. Expanded the existing missing-initializer regression through 24 (introduced its array-field cases in 16), and forwarded the known-null load regression to 10-24 using their existing parse-time diagnostic. Added the fresh-allocation zero-fold monotonicity assertion in 19-24 and the TOP-pointer store-diagnostic guard in 18-23 (already present in 24). Before 18, TOP pointers already produce a diagnostic before the store's pointer cast. Chapter 25's constructor-exit snapshots and implicit-constructor declaration check stay with its separate allocation/constructor model. Its CallEnd return and MemMerge load-dependency corrections already have equivalent behavior in the applicable earlier implementations; storage-type nullability machinery stays with 25. Global error-result type changes are paused in the queue above. | | Chapter 25 required constructor fields | Parser records each explicit constructor's merged exit field types by Var identity and checks them against the complete field list when the struct closes. With no user constructor, declaration defaults are checked as an implicit empty constructor. Never-returning constructors have no completion obligation; nested blocks retain constructor context. Declaration-body assignments to required fields now emit their initializing stores. Load's fresh-allocation zero fold is restored with a monotonicity guard. Existing constructor tests cover partial/early returns, shadowing, later fields, valid defaults and non-returning constructors; older unused-struct/String fixtures now provide constructors, with measured spill expectations updated. Seed `6506797708065910879` is rejected during parsing and passes its regression. On 2026-09-28, all 472 non-fuzzer tests passed across `build/ctor-check-tests.log` and `build/ctor-remaining-tests.log`; the full Make run remains red solely on the independent Load TODO seed above. Allocation-time initialization checking was lost in the August 10 rewrite (`51f1f8f4`); this restores the obligation in the parser without depending on optimized private memory. | | Chapter 25 null-dereference diagnostic | Existing `Chapter10Test.testNullGuardErrors` includes `if (p == null) return p.x; return -1;`. The earlier recursive constant-fold error guard was removed by Cliff in favor of erroneous nodes computing BOTTOM, with related eager-folding and monotonicity corrections. The earlier full-suite pass in `build/null-deref-arith-top.log` applies to the superseded guard implementation, not the current edits. The exposed uninitialized-field failure is fixed by the constructor checks recorded above. | | Chapter 22 String without an explicit return | Backported 23's default-main teardown: when top-level control falls through, replace main's Start input with XCTRL and queue its users. Previously 22 omitted the return while retaining live control, leaving loop fragments with missing exits. Current seed 0 reproduced a missing-successor failure in Encoding; the earlier GCM changes had moved it past the originally recorded failure. The unchanged `Chapter20Test.testString` now also checks seed 0 through Encoding on x86/RISC-V/ARM, preserving its existing seed-123 allocator checks. Two Chapter09Test graph expectations now reflect removal of a default main with no live return; their sources are unchanged. Baseline and final full Chapter 22 suites passed (425 tests plus fuzzer), logged in `build/string-baseline.log` and `build/string-final.log`. Chapters 23-24 already contain this teardown; no later compiler changes were needed. | | Infinite-loop evaluation | Eval2 now runs the missing loop-tree phase in 21 and explicitly follows NeverNode projection 0 in 21-25. Existing `testFcn9` expects timeout in 21-24. Full 18-25 Make suites passed on 2026-09-26 (`build/never-exit-tests.log` and `build/never-exit-final.log`). On 2026-09-27, Cliff requested removal of the frozen-source fixture, replay tests, related reductions, and seed-list entry as unnecessary test overhead; the evaluator fixes remain. | | Always-on peepholes | Removed the global disable flag and branches in 2-18, the Chapter 18 parse overload, test toggles, and transpiler metadata/Rust output. Updated early graph expectations and READMEs; removed duplicate no-peephole scope tests. Chapter 8 fuzzing checks compilation/evaluation for failures; 9-18 compare worklist seeds 123/456 like later chapters. The Chapter 18 Load type workaround was unnecessary with normal forwarding and was removed. All 1-25 default Make suites passed across `build/always-peeps-tests.log` and `build/always-peeps-final.log`; later evaluator corrections are recorded above. The baseline was green in `build/always-peeps-baseline.log`. Direct checks exercised both worklist seeds in 9-18. The transpiler compiles and its focused grammar/Rust-output check passes; its existing repository-wide test stops at source-free chapter00 with `no source files`. | | B10 and integer/constant-array widening | Widening starts in 24. Both 24-25 now use levels 0-3, dual widening `3-widen`, retained singleton widening, and widening on TypeConAry. `nonZero` preserves the coordinate. The same TypeTest regression and integer gather ranges run in both snapshots; before the backport, Chapter 24 fails the widening regression and lattice associativity. Loop Phis widen only below-center nonconstant ranges: widening an above-center range to level 1 then falling to zero at level 0 violated SCCP monotonicity in 24's existing `testCloneAnd` and `testInfinite`; both now pass, with the guard also in 25. | | TypeConAry API backport and validation | 24-25 share the reduced raw `_make(any,widen)` API, integer meet dispatch through the distinct `TCONARY` tag, and gathered samples. Preserve 24's GLB hooks and ordinary byte stream; scalar envelopes and serialization stay in 25. Chapters 22-23 consolidate gather and remove redundant wrappers/arguments, retaining their pre-widening representations and needed sentinels. Full Make suites passed on 2026-09-26: 22 (428), 23 (450), 24 (480), 25 (474), each plus its fuzzer wrapper; all include TypeTest, and 25 rebuilt `sys.o`. Logs: `build/conary-backport-*` and `chapter25/build/conary-backport-25.log`. | | #254 subtraction zero identities | Require integer operands in 4-25 and resolved integer mode in 25. Preserve floating-point signed zero and subtraction diagnostics. `Chapter12Test.testSubZeroFloat` runs in 12-25; `Chapter22Test.testSubZeroTypeError` runs in 22-25. Against the original PR, the signed-zero test fails in 25 and the diagnostic test fails in 22-25. Full Make suites passed in every chapter 4-25 on 2026-09-25, including fresh `sys.o` compilation in 25. | | Short type extrema | Memory, struct and memory-pointer BOT/TOP names from 10; function-pointer names from 18. Exact types use shortcuts in ordinary, nested and HTML prints; precise types retain their details. Full Make suites in 10-20 and type/frontend suites in 21-25 passed; canonical/nested print checks passed in 10-25. | | Integer result types | Integer-op fallbacks in 4-13 and remaining unary cases through 17 retain integer types; 5's Phis meet their data-input types. Full Make suites in 4-17 passed, plus a chapter 5 snapshot check of Phi/Add types. Integer BOT starts in 4; later arithmetic already has typed fallbacks, with unresolved bimorphic modes preserved in 25. | | B14 cyclic leaf-kind equality | 23-24 backport; 25 already correct. Cyclic equality starts in 23. `TypeTest.testCyclicLeafKinds` fails before the fix; earlier interned-child equality already checks kinds. Full affected suites passed. | | B13 / #246 null guards | Nested Not/null guard discovery from 10; short-circuit Phi availability in 23-24. Regression and full suites in 10-25 passed. Separate Chapter 25 null-dereference diagnostic is recorded above. | | B09 and #251 debug printing | Side-effect-free print/accessor paths from their first applicable chapters; `_` diagnostic naming, type interning allowed, layouts forbidden. Dead nodes use `isDead()`, not null `_inputs`, from 7. Original-printer negative checks and full affected suites passed. | | #247 emulator stores | Full eight-byte writes in ARM/RISC-V emulators, 21-25. Independent byte-pattern/overwrite regressions failed before the fix; all affected suites passed. | | B12 x86 immediate multiply | Dedicated destination-register encoder in 21; 22-25 already had it. Low/extended-register encoding regressions and full 21-25 suites passed. | | B11 optimized return checking | Remove parse-time meet in 18-24; 18 checks the optimized result type. Earlier separate Returns already accept dead mixed-type exits. Regressions start in 10/12; full 10-25 suites passed. | | Dominator caches | Shared searches from 6; char depth in 6-17, separate char depth/version from 18, checked overflow and preserved clone fields. Inlining invalidation in 18-20. Full affected suites passed; dedicated bookkeeping helpers/tests were removed at Cliff's request. | | GCM, constant cloning, isPinned removal | Shared scheduling from 11, per-function cloning of stacked constants from 18; preserve chapter-specific anti-dependencies and linked Parm ownership. Full 11-25 suites passed. Keep clone-based copyEmpty and non-final graph fields per review. | | TypeFunPtr normalization | Trailing-default normalization in 23-24; 25 already correct. Only gather cases added; lattice laws and full 23-25 suites passed. Other `7f3b8856` representations remain in 25. | | Inlining Return types and ARM execution | Live return expressions outlast a deleted inline entry (20-25); ARM register SUB, call instructions, frame byte counts/alignment, and vector growth (21-25 as applicable). Reduced negative cases and full affected suites passed. The earlier standalone ARM String/hash failure is resolved. | | Native test reliability | Full process exit status, stdout plus successful execution, and distinct concurrent HelloWorld artifact names. Crashes no longer pass merely because output matches or an exit status narrows to zero. | | Allocator progression 20-25 | Fixed cohorts, legality checks, native/emulated execution from 21, and same-compiler comparisons. Final results below. | | Build/fuzzer harness baseline | All 25 chapter targets passed after build setup and explicit seed lists. Empty default lists were not exploratory fuzz coverage. | Final allocator audit, 2026-09-20, Windows/Cygwin with assertions: | Snapshot | Full suite | Spill report | |---|---|---| | 20 | 378 tests + fuzzer | 39 compilations, 238 / 357 | | 21 | 408 tests + fuzzer | 91 compilations, 840 / 1,456 | | 22 | 422 tests + fuzzer | 115 compilations, 838 / 1,496 | | 23 | 444 tests + fuzzer | 145 compilations, 933 / 1,745 | | 24 | 473 tests + fuzzer | 212 compilations, 1,332 / 2,956 | | 25 | 468 tests across parallel groups, including fuzzer | 222 compilations, 2,597 / 5,579 | All full suites and final spill reports passed. The Chapter 25 run rebuilt `sys.o`. Negative checks against saved original classes reproduce fixed-neighbor bias, narrow-store masks, and ARM floating-memory encoding failures. The original Newton ARM allocation also failed before the fix. The Chapter 21 coalescing-off comparison passes all allocation/runtime checks but reports nine changed spill goldens, intentionally exiting nonzero. The Chapter 25 earlier-ranking ablation fails fresh library allocation, so its client-only totals are explicitly partial. Logs and saved original classes: `chapter{20..25}/build/regalloc-final/`.