diff options
Diffstat (limited to 'skills/chapter25-codex-notes.md')
| -rw-r--r-- | skills/chapter25-codex-notes.md | 139 |
1 files changed, 112 insertions, 27 deletions
diff --git a/skills/chapter25-codex-notes.md b/skills/chapter25-codex-notes.md index b9b30ab5..1801ffed 100644 --- a/skills/chapter25-codex-notes.md +++ b/skills/chapter25-codex-notes.md @@ -2,33 +2,17 @@ This file is intentionally AI-facing. It records durable invariants, debugging habits, and project-owner preferences that are easy to miss when reading only -the implementation. Use the current checkpoint below rather than old session -handoffs or historical branch/status reports. -For cross-chapter work, read [the backport queue and validation record](../docs/chapter-backports.md). -It owns pending reproductions and a condensed completion record; these notes -capture reusable lessons. Detailed superseded validation history remains in Git. +the implementation. Check current code and Git state rather than treating old +session handoffs as live status. +For cross-chapter work, read [the backport queue](../docs/chapter-backports.md). +It owns pending work; these notes capture reusable lessons. Completed work and +superseded validation history remain in Git. For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.md). -## Reset checkpoint (2026-10-03) - -- Cliff committed the chapter reorganization and mutability split as `22df8918` - (`Chapter renumber, and split ch 17`). Implementation and validation are done; - there is no unfinished compiler port or hung validation process to resume. - Check current Git state before treating this checkpoint as live status. -- Cliff subsequently committed the Chapter 18 README review and backup-file - deletion as `3c13b271` (`Update README.md`). The tree was clean before this - memory refresh; only these two notes files are left modified by the refresh. - Do not commit or push without a new instruction. -- Read the mutability section and validation record in - [chapter-backports.md](../docs/chapter-backports.md) for scope and results. - Full affected chapter suites, Chapter 25 native tests, standalone 17a/17b - builds, and exporter checks passed. Logs are disposable under - `build/mutability/`; no need to repeat suites without a new change or concern. -- Next work is Cliff's review, not another inferred cleanup task. The separate - pending queue still includes float-to-int syntax and deferred BOTTOM contracts. - ## Collaboration preferences +- For densely keyed integer maps, use indexed `Ary<T>` storage rather than + `HashMap<Integer,T>`. - Build with Make alone; Maven must never be required. Cliff prefers few tests: avoid tests for every small viewer edit or rapidly changing display experiment. Use focused existing tests and disposable probes when they answer a real risk. @@ -72,6 +56,96 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m ## Mutability and chapter boundaries +- Native boundary decision (2026-10-18): no classes for native code; do not + design a native-startup policy. Ordinary supported FFI calls are separate. + Chapter 25's README owns the tutorial explanation of class initialization; + ROADMAP owns future work. Do not recreate an initialization progress document. +- GCM memory-Phi anti-dependencies constrain paths whose input differs from + the load's memory, not paths carrying that memory unchanged. Bound the idom + walk by the memory definition's depth: an alternate Phi input need not be + dominated by that definition. Also stop at `defblk.idom()`: when the memory + definition is the function entry, its null idom is the valid sentinel. + Include the boundary depth; a strict `>` skips required same-block edges + and can make a read observe a later store. Keep `testCapitalize`, + `testConditionalClassInitX86`, and + `testReadBeforeConditionalStore` passing; dropping Phi handling entirely + miscompiles the latter (140 instead of 120). + This rule applies from Chapter 13 onward. The nested-branch regression + `SchedulerTest.testMemoryPhiAcrossSiblingBranch` checks the disjoint walk; + removing the depth bound reproduces a null-stblk crash. Retain each early + chapter's evaluator scheduling rules when porting the boundary. +- CLInit lowering (2026-10-17): TypeCheck queues surviving markers; optional + Serialize saves opaque ideal IR, then Lower runs Iter before LoopTree. + Serialization includes disconnected Returns and filters cross-object links + without mutating the live graph. Parent initialization precedes the child's + flag test/call; the true flag store remains inside the initializer. Runtime + flag reads follow ordinary Load idealization, including removing control. + Handle x86 condition-flag lifetime constraints in the backend, not by pinning + ideal Loads. Generated calls remain unlinked and + conservative; do not reopen parameter flow into SCCP-optimized bodies. + Private/imported initializer bodies retain Start because opaque CLInit has no + ordinary Call edge yet; this does not change public reachability. + The selected entry gets the external arg; other clinits get zero for now. + The planned array ABI uses non-null entry args and null elsewhere; not yet + implemented. Eval2 shares singleton class objects and retains imported bodies. + Full Chapter 25 Make validation: 509 tests, including native cold/hot calls, + parent order, ARM/RISC-V encoding and nonreturning object reload. +- Chapter 25 phase structure: Opto only runs SCCP, numeric-mode + resolution and worklist seeding. IterPostOpto does peepholes/inlining; + FinalizeIR does field layout/offset cleanup and function/CU finalization. + opto() requires an explicit prior iter(). Direct calls needing the old complete + optimization sequence also name iterPostOpto().finalizeIR(); driver() handles + the sequence for later targets. Iter setup, FinalizeIR, TypeCheck, Unlink, + and InstSelect own their algorithms in separate files. CodeGen's phase wrapper + handles assertions, timing (including failures), and successful viewer phase + notifications. Its driver lists the order and skips completed steps. + Serialize decides whether ideal IR is needed: explicit Serialize or file + Export, not Encoding or in-memory linking. A staged file export must request + Serialize before Lower; ideal IR cannot be recovered from machine nodes. + Unlink detaches imports for machine selection, including requests stopping at + Select, while ideal evaluation retains them. Schedule requires Unlink or Select; + use the driver instead of skipping Lower/LoopTree in hand-written test chains. + Earlier chapters expose only phases with real work. CodeGen and its resumable + driver start in Chapter 18, with explicit LoopTree and LocalSched boundaries. + Instruction selection arrives in 19, RegAlloc in 20, Encoding/Export in 21. + FinalizeIR starts in 22 with unused-function cleanup and gains layout in 23. + Opto (SCCP) and IterPostOpto start in 24; earlier peepholes are named Iter. + Serialize, Lower, and Unlink stay in 25. Use short explicit chains such as + parse().iter(), and driver(Phase.target, ...) for longer compilation paths. +- Class-init memory checkpoint (2026-10-17) is implemented: keep CLInit + opaque through Iter/Opto, carry whole-memory effects plus the known-true + `$init` alias result, and use normal parser memory for redundant checks. + The flag store belongs inside the initializer. Parser creates the ready load + up front; ordinary Load/FldOff nodes resolve forward receivers. The fourth + CLInit input is always present in a constructed graph. If the receiver resolves + to an instance, CLInit and its speculative flag load disappear together. + Precise alias recovery can still be deferred. See `chapter25/README.md`. + Runtime lowering is implemented above; argument arrays remain deferred. Public function fields + on instances of public classes must retain Start, just like class-object + fields. publicFIDX now checks both; this fixes sys.scan.match losing Start + while building sys.o and subsequently widening after import. All Chapter 25 + Make suites pass (505 tests). Do not weaken assertions or reset imported types + to conceal incorrect public classification. + Future optimization: move final, always-constant instance fields to the class + or fold them to constants and omit their instance storage. Access semantics + and public reachability remain unchanged; do not implement layout changes yet. + Frequency estimates belong + after Opto and lowering: function entry 1, unknown branches 50/50, estimated + loop trips 10, rare init/failure edges, non-null/null 90/10; bounded, + flow-consistent estimates shared by GCM and Encoding. +- Class-init cycle validation (2026-10-14): reject an SCC only when it contains + more than one `<clinit>`. A self-edge or helper recursion with one initializer + is legal, regardless of inlining. Error reports list every internal edge of + the first rejected SCC, including helper calls; stack order is not a path. +- Class-cycle diagnostics use `SourceError.format(CompUnit, byteOffset, message)`. + Start `file:line:column: error: message` in column one for Emacs compilation + buffers; put the source and caret on separate following lines. With no source, + report the path and saved byte offset, without inventing line/column numbers. + Lexer snapshots keep their original CompUnit through inlining and Call/CLInit + serialization; recover representative sites by rescanning only on rejection. + Source text is assumed available. Imported source paths are diagnostic data, + not `_smp` rebuild/discovery state. General error-message migration is deferred. + - Constructors start in 16, independent binding/access permissions in 17a, syntax sugar and inference in 17b. Historical Chapter 17 references mean 17b. - `!Point` is writable access, `~Point` deep read-only access; `!p` allows @@ -113,8 +187,7 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m deliberately retains immutable shallow struct references resolved by name; see its README's L0/L1 notes before trying to expand recursive types. Keep Chapter 25's separate constructor-memory and incomplete-type design. - See the concrete boundary notes - in `docs/chapter-backports.md`. GCM readiness and anti-dependency checks must both + GCM readiness and anti-dependency checks must both filter by alias and ignore MemMerge as a clobber; keep the evaluator's same alias filtering too. MemMerge still needs ordinary data-dependency placement. @@ -261,8 +334,8 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m affected directories explicitly listed. Establish the destination baseline before changing it; older Makefiles may need a forced rebuild after API changes. - Keep unrelated discoveries separate in `docs/chapter-backports.md`. Completed - fixes leave the pending queue; retain a concise validation summary and move - unresolved discoveries out of the history into the actual queue. A green suite + fixes leave the pending queue; record validation in the change description. + Keep unresolved discoveries in the actual queue. A green suite after changing graph/test order does not prove an intermittent failure fixed. - Store disposable probes/logs under an appropriate ignored build directory. Preserve durable reproducers in tests or the review record, not only scratch @@ -468,6 +541,18 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m before coloring, a fixed-register definition can clobber despite `_reg==-1`. Cloning is valid only if its output mask can satisfy the use. A fixed-register clone with flexible uses needs use-side splits, not a no-op def-side path. +- Chapters 20-25 flag spills use specialized splits that exclude FP registers. + x86 can transfer FLAGS directly to a GPR or stack slot. ARM's + `SplitFlagsARM` transfers NZCV through a GPR and overrides + `isNonStackSplit()`; RegAlloc includes these transfers in loop-depth + collection and subsequent splitting. `SplitGPRARM` then admits GPRs and + stack slots, preserving the FP exclusion through cascading spills. + Keep ordinary splits excluded from those allocator paths. Test both flags + clobbers and simultaneous flags/GPR clobbers: `testFlagSpills` executes the + allocated moves for all 64 x86 status-bit and 16 ARM NZCV combinations from + Chapter 21 onward. Chapter 20 checks allocation and masks only; encoding + begins in 21. Preserve each allocator's existing ordinary-split policy: + 20-22 always skip ordinary splits here, while 23-25 skip them when min!=max. - Check function ownership and register masks immediately after allocation, before encoding rewrites tail calls or adds untyped branches. Call.regmap can itself depend on CFG.fun/idom. The test-only CheckedCodeGen override keeps |
