summaryrefslogtreecommitdiff
path: root/chapter25/initialization.md
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-10 12:07:28 -0700
committerCliff Click <cliffc@acm.org>2026-10-10 12:07:28 -0700
commit1213e8e8f359c8a1abb05ee8a220d9c56bc36051 (patch)
treecc186a55129881fd32c1293d27bda0df78fe4bb1 /chapter25/initialization.md
parent7096ba70c05eabfe96fd7e69d276b16ac1bd0bb9 (diff)
downloadSimple-1213e8e8f359c8a1abb05ee8a220d9c56bc36051.tar.gz
Simple-1213e8e8f359c8a1abb05ee8a220d9c56bc36051.zip
First cut clinit tracking for cycles
Diffstat (limited to 'chapter25/initialization.md')
-rw-r--r--chapter25/initialization.md375
1 files changed, 290 insertions, 85 deletions
diff --git a/chapter25/initialization.md b/chapter25/initialization.md
index d03c2eb7..1e5bb509 100644
--- a/chapter25/initialization.md
+++ b/chapter25/initialization.md
@@ -1,9 +1,10 @@
# Lazy class initialization design
-Design proposal, 2026-10-08. This specifies the next implementation; the current
-compiler does not yet enforce this protocol. Declared constant final fields
-already have static-data lowering, but general lazy run-once initialization,
-dependency validation, and initialization guards remain to be implemented.
+Design and implementation plan, revised 2026-10-10. Checkpoint 1 implements
+post-Opto initialization dependencies and cycle checking for review. Declared
+constant final fields already have static-data lowering. General lazy run-once
+initialization, serialized dependency validation, and initialization guards
+remain to be implemented; the compiler does not yet enforce the full protocol.
Initialization metadata belongs with class identity and compilation-unit
ownership, not solely on the `<clinit>` FunNode. Represent active touches in
@@ -27,13 +28,15 @@ Use one runtime bit in the first, single-threaded implementation.
a called function to observe unfinished dynamic state in an ancestor or any
other class.
- Initialization requirements form a DAG across the complete linked program,
- including module boundaries. Reject cycles, including potential cycles
- behind runtime conditionals. Ordinary recursive functions are still legal.
+ including module boundaries. Count only requirements surviving Opto and its
+ call-graph analysis. Reject cycles behind conditionals that remain unresolved;
+ eliminated branches and call targets contribute no edges. Ordinary recursive
+ functions are still legal.
- Initialization order follows actual execution and parent requirements, not
parser discovery order, worklist seed, or filesystem enumeration order.
For example, A conditionally reading `B.foo`, with B initializing `foo` from
-`A.bar`, is rejected. Neither an initial zero nor an early evaluation of
+`A.bar`, is rejected if that read survives Opto. Neither an initial zero nor an early evaluation of
`A.bar` resolves that cycle. If B is A's child, the parent requirement already
provides the reverse dependency.
@@ -49,36 +52,49 @@ Keep `CompUnit._deps` for source/object discovery and rebuild dependencies.
Do not reuse it as the initialization graph: passive references belong there,
and its elements are files rather than necessarily individual classes.
-Add a small `ClassInit` record owned by a CompUnit. The file class is available
-as `CompUnit._init`; a canonical map in CodeGen resolves records by full class
-name. Classes nested within a file do not each have a CompUnit today. They can
-have records owned by that same unit, including records with no dynamic body.
-This avoids treating an instance `<init>` as a class `<clinit>`.
+Use CompUnit directly for the current runtime initialization graph. The parser
+creates a file `<clinit>` in `parseStruct(true, ...)`; nested struct declarations
+use `parseStruct(false, ...)` for instance initialization. Do not introduce a
+separate ClassInit object or invent a dynamic class initializer for those
+instance constructors.
-Suggested information, with names provisional:
+Reuse `_cname` for stable identity, `_par` for the filesystem parent, `_clz` for
+the canonical class object and hidden field, and existing function identities
+for the body. Add only initialization-specific information:
| Member | Meaning |
|---|---|
-| `_name` | Stable, fully qualified class identity |
-| `_owner` | Defining CompUnit; owns native data and initializer definitions |
-| `_parent` | Enclosing class record, if any |
-| `_body` | Optional current `<clinit>` FunNode; absent for unloaded/static-only bodies |
-| `_deps` | Lazily allocated, deduplicated direct initialization dependencies |
-| `_complete` | All declarations, call effects, and dependency targets resolved |
-| `_static` | Proven that this class's own initialization requires no execution |
-| `_flagAlias` | Alias of the hidden `$init` field in this class object |
-
-An absent body is not proof of `_static`: it may simply not have been loaded.
-Metadata flags describe compiler knowledge, not additional runtime states.
-An explicitly defined namespace-only parent can have an empty initialization
-record; a missing parent definition must not silently become such a record.
-FunNodes can refer back to their initialization record; the record survives
-inlining, dead-function elimination, and conversion of initialization to data.
-
-`addInitDep(target, reason)` adds a direct edge only once and invalidates the
-cached graph validation/order. Retain a source location and, for indirect
-dependencies, a call-site witness for diagnostics. Do not turn every dependency
-addition into a scan of all initializer bodies.
+| `_classInitDeps` | Lazily allocated `Ary<CompUnit>` of direct initialization requirements |
+| `_initComplete` | All initializer touches and relevant call targets have resolved |
+| `_initStatic` | Proven that this unit's own initializer needs no execution |
+
+Keep diagnostic witnesses in the checker's temporary function summaries: source
+locations and indirect call witnesses where applicable. CompUnit retains only
+the edges and analysis flags after validation. Derive the flag alias from `_clz`'s
+`$init` field rather than maintaining a duplicate mapping. Locate the body by
+its stable function identity; a transient FunNode cache may be useful but does
+not define class identity. An unloaded or eliminated FunNode is not proof of
+static initialization.
+
+Static class objects for nested struct declarations keep their existing named
+TypeStruct identity and defining CompUnit. Their materialized data need no new
+runtime graph vertex; an active use that requires the enclosing file initialized
+maps to its CompUnit. Preserve initialization-independent static accesses. If a
+future language feature adds an independent dynamic initializer inside one file,
+then introduce the additional per-class graph identity that feature needs.
+
+`CompUnit.addClassInitDep(code, target)` allocates the collection lazily, deduplicates
+edges, and invalidates a compilation-wide validation stamp. Keep self-edges so
+they can be diagnosed. Explicit namespace-only parents have no dynamic body;
+a missing parent definition must not silently become an empty initializer.
+
+There are two different dataflows. Runtime `$init` values use ordinary memory
+aliases and support flag-load/CLInitNode optimization. Dependency discovery
+records which initializers may be requested, independently of incoming flag
+values and normal return. A dedicated analysis-only memory slice could encode
+that second forward dataflow, but the first implementation uses explicit
+CompUnit edges plus small function summaries. It does not add a second family
+of memory aliases, and it does not infer cycles from runtime `$init` types.
## Discovering and validating dependencies
@@ -87,17 +103,30 @@ Topological output therefore places prerequisites before dependents. A dynamic
child has a structural dependency on its parent. An active touch of another
class during A's initializer contributes an edge; a passive reference does not.
-Record semantic touches before inlining and guard elimination. Collect direct
-touches in ordinary function bodies too, and propagate their initialization
-effects through the call graph with a worklist. Recursive function SCCs reach
-a fixed point of finite dependency sets; they are not themselves initialization
-cycles. Indirect calls contribute the union of all possible targets' effects.
-
-The initial policy is conservative: a runtime conditional contributes both
-branches' possible requirements. Do not let inlining choices or random peephole
-order decide whether a cycle is legal. Dependencies may be added as forward
-references and call targets resolve. Before declaring a graph complete, every
-reachable initialization-time call must have a complete effect summary.
+Insert active touches as CLInitNodes on executable control. After Opto finishes
+SCCP, inlining, and cleanup, collect surviving touches and calls. Use final
+function-pointer target sets and propagate initialization effects through the
+resulting call graph with a worklist. Recursive function SCCs reach a fixed
+point of finite dependency sets; they are not themselves initialization cycles.
+Indirect calls contribute the union of targets still possible after optimization.
+
+Do not accumulate historical targets or source touches eliminated by Opto.
+There is no side table watching Node replacements or type changes. An inlined
+helper's surviving CLInitNodes belong to its caller; an uninlined helper's
+summary propagates through its surviving calls. Walk executable control
+forwards so touches before nonreturning calls and infinite loops are included.
+Preserve initializer boundaries until validation; expanding/inlining CLInit
+operations early must not erase the class identity needed for checking.
+
+A field value becoming constant does not prove its initializer unnecessary.
+CLInitNode survives independently of the load. Dead control may remove both;
+a static-initialization proof may remove the obligation. Such proofs must not
+assume the very acyclicity being checked.
+
+Acceptance now depends on what Opto proves. Worklist determinism remains an
+implementation goal, but preserving every intermediate target set is not the
+language rule. Before declaring a graph complete, every surviving required
+initialization-time call must have a complete effect summary.
An unknown function-pointer target or native callback is not an empty summary.
Reject such a call during initialization unless an explicit trusted contract
@@ -131,7 +160,7 @@ initialize every possible dependency, or a prefix of the topological list.
## Ideal IR and runtime protocol
-Introduce an effectful `CLInitNode` with a stable ClassInit identity:
+Introduce an effectful `CLInitNode` with a stable target CompUnit identity:
```text
CLInit(C, ctrl, memory) -> (ctrl, memory)
@@ -146,8 +175,9 @@ It can execute arbitrary initializer effects, so it is not a pure flag test.
At dynamic class-field reads and writes, required instance creation, and calls
requiring initialized class state, consult parser flow facts first and insert
CLInitNode only if the required initialization is not already established.
-Record the semantic requirement even when no check needs inserting. Passive
-Simple class references remain subject to checks at their later field accesses.
+A check omitted by a valid dominating initialization proof needs no separate
+historical dependency record: its prerequisite is represented on the covering
+path. Passive Simple class references remain subject to checks at later field accesses.
Native escape of class references is a separate boundary described below.
Forward references must preserve the pending check until the target is known.
@@ -225,7 +255,7 @@ proved; tracking only the flag would lose the initializer's field and I/O effect
There are two different facts:
-1. **Static body completion:** `_static` proves that C's own initial contents
+1. **Static body completion:** `_initStatic` proves that C's own initial contents
are supplied by data and there are no remaining initializer effects. Its
`$init` field starts true, and its redundant body/store can disappear.
2. **Completion at a program point:** normal return from CLInitNode establishes
@@ -253,11 +283,13 @@ control-flow facts become available. A separate late must-analysis is optional,
not a prerequisite for removing straightforward redundant checks.
Carry known-initialized class identities with parser control flow, for example
-as a BitSet in ScopeNode keyed by compilation-local class IDs. These are path
-facts, not mutable global properties of ClassInit or its declared TypeStruct:
+as a BitSet in ScopeNode keyed by the existing remapped `$init` alias IDs. These
+are path facts, not mutable global properties of CompUnit or its declared
+TypeStruct:
-- On an active touch, record the dependency, then consult the facts. If C and
- its required ancestors are known ready, omit the CLInitNode.
+- On an active touch, consult the facts. If C and its required ancestors are
+ known ready, omit the CLInitNode. Otherwise insert the obligation in the IR;
+ post-Opto analysis collects surviving requirements.
- Normal return from CLInit(C) adds C and its required ancestors. Do not add
every possible dependency: a conditional initializer may never touch one.
- Copy facts on a branch and intersect them at a reachable merge. An abrupt
@@ -339,7 +371,8 @@ without materializing every function body. Serialize:
Do not serialize transient node references, a cached global topo index, or the
runtime value of a dynamic flag as an unconditional type fact. A definition
-and its imported declarations must resolve to the same ClassInit record.
+and its imported declarations must resolve to the same owning CompUnit and
+canonical class/flag identity.
On load, register identities first, resolve parent/dependency edges second,
then resolve function summaries and IR. Check conflicting owners, missing
@@ -354,7 +387,7 @@ importing compiler inlines and removes its local checks. Ordinary function
symbols and function-pointer constants are not replaced by checked wrappers.
Do not serialize a parser's transient BitSet as unconditional entry knowledge.
-Its local class IDs can change on reload; retained checks, control flow, and
+Its local alias IDs can change on reload; retained checks, control flow, and
explicit entry contracts must support any omitted checks. Recompute flow facts
for loaded graphs and verify hidden-field ownership/layout and that the flag
store belongs to `<clinit>`, not to each checking caller.
@@ -438,37 +471,209 @@ export/startup mechanism is deferred; it must not require the native caller to
invoke `<clinit>`. Existing native-linking tests are not evidence that this
future initialization guarantee is already implemented.
-## Implementation sequence and validation
-
-1. Add ClassInit identities and a distinct lazy dependency collection; preserve
- passive class references. Add direct/function effect summaries and cycle
- diagnostics, including unresolved external-call handling.
-2. Serialize/load the directory and summaries. Validate combined graphs before
- imported-body unlinking and at final composition. Preserve ordinary function
- entries; establish startup handling and diagnostics for unsupported native
- handoffs before claiming initialization coverage at the FFI boundary.
-3. Add serializable CLInitNode IR, the hidden field in each class object, and
- the flag Store in `<clinit>`. Use the same node for ordinary checks and
- startup. Add parser flow tracking and the basic constant/dominating-check
- peepholes as part of this implementation, rather than relying on a later
- cleanup pass to remove every repeated check.
-4. Refine flow proofs and fully-static classification. Extend constant-memory
- lowering only with the required observation/identity proofs.
-
-Validation should cover the actual boundaries, with a small number of focused
-fixtures: parent-before-child order; an untouched branch performing no I/O;
-two callers executing one initializer once; direct, indirect, sibling and
-cross-module cycle rejection; passive/static reference cycles; a shared class
-and flag reached through two separately compiled importers; a join/loop where
-the first check cannot be removed; and rejection of unsupported native class
-handoffs. Check function-pointer acquisition at the class field, with no new
-ordinary function wrapper. Check parser omission and ideal elimination separately: a dominating
-check, a true-test branch, an unchecked merge arm, and a zero-trip loop. Verify
-one body-owned flag store before inlining, and that folding its early true
-value cannot hide a cycle or bypass field initialization checks. Preserve entry
-arguments/results and verify after serialization reload.
-Run existing Chapter 25 suites and ARM/RISC/native execution checks for each
-implementation frontier. No earlier-chapter backport is implied by this design.
+## Implementation sequence
+
+Implement in Chapter 25 only, with the following reviewable checkpoints. Keep
+existing source/native entry conventions and pointer widths. Each checkpoint
+must build; do not claim the runtime guarantee until the executable protocol,
+serialization, and final-composition validation are all present.
+
+### 1. CompUnit dependencies and cycle checking
+
+Implemented for review (2026-10-10). Files: `CompUnit.java`, `ParseAll.java`,
+`Parser.java`, `ClassInitDependencies.java`, `CLInitNode.java`, `CallEndNode.java`,
+`Opto.java`, and `CodeGen.java`. Generic `Node` needs no changes.
+
+- CompUnit holds direct `_classInitDeps`, `_initComplete`,
+ and reserved `_initStatic`. Existing file-discovery `_deps` is unchanged.
+ Diagnostic witnesses remain local to the dependency check.
+- Parser inserts a control-flow CLInitNode for an active class-field touch or
+ a receiver whose class is not yet resolved. Ordinary instance receivers
+ fold the obligation away when resolved. Passive references and direct
+ own-initializer field accesses do not insert a check.
+- This checkpoint's ideal node has control at input 0 and receiver at input 1;
+ it neither consumes nor produces memory yet. The full runtime form described
+ above will add memory at slot 1 and move the receiver to slot 2. No hidden
+ flag or runtime initialization call is generated in this checkpoint.
+- After Opto, the collector walks live control forwards in each surviving
+ function, reads final call-target sets, and propagates sparse may-touch
+ summaries. Ordinary helper inlining naturally relocates its checks into the
+ caller. Class initializers are not inlined before validation.
+- Reachable nonreturning calls retain a conservative CallEnd during dataflow.
+ Calls left out-of-line retain their continuations, so otherwise unreachable
+ touches after them can contribute initialization dependencies. This extra
+ ordering is accepted; users can remove such unreachable code.
+ Inlining discovers bodies forwards from their entry, including non-exiting
+ paths, and explicitly retains the Return node even when disconnected from
+ executable control. Nonreturning functions can therefore inline normally.
+- TypeCheck builds fresh CompUnit requirements from those summaries and parent
+ edges. It rejects cycles with source/call witnesses and computes a stable
+ prerequisite-first order. No source-history or Node watcher table is kept.
+- Following validation, the temporary CLInit obligations are erased for the
+ existing backend. Executable lowering and serialized obligations remain
+ later checkpoints. The full lazy-initialization runtime guarantee is not
+ implemented by this batch.
+
+Imported bodies, native calls, and unresolved targets leave `_initComplete`
+false; the returned order covers known edges and is not a completeness
+certificate. `_initStatic` remains false until static classification is added.
+
+Validation covers direct, helper-mediated, forward, conditional, indirect,
+sibling, and parent/child cycles; legal ordinary recursion and passive
+references; unknown native/imported effects; dependencies eliminated by SCCP;
+unused helpers; discarded field values; and effects before versus after a
+nonreturning call. Cycle and optimized-target cases run with three worklist
+seeds. A bounded Eval2 run verifies that a retained nonreturning call executes
+rather than falling through; its x86, ARM, and RISC-V encoding checks also pass.
+All 492 tests in the parallel Chapter 25 Make suite pass. The encoding checks
+were added afterward and pass in the focused suite. Two existing RISC-V spill
+expectations change from 9 to 7; execution and allocation checks pass.
+Generated fixtures are under
+`chapter25/build/init-deps/`; full-suite logs are under `build/init-deps-opto*.log`.
+
+### 2. Serializable flags and dependency summaries
+
+Files: `Parser.java`, `Serialize.java`, `ElfReader.java`, `GlobalBits.java`,
+`ParseAll.java`, and `StaticData.java`.
+
+- Add hidden `$init` fields with the normal field/alias/layout mechanism before
+ `freezePublicInterface()`. Declared flag type is boolean; initial image value
+ is false unless static initialization has been proved. Do not make a dynamic
+ flag's declared type the constant false.
+- Serialize per-unit initialization metadata and function may-touch summaries
+ in `.simple`, early enough for graph validation without loading every body.
+ Preserve stable owner/alias/function identities and diagnostic witnesses.
+- Register imported units first, resolve/remap edges and hidden fields second,
+ then validate the combined graph. Neither a module-local topological order
+ nor a summary from an older object file is a final-link proof.
+- Keep metadata for fully static or body-eliminated units. Rebuild objects
+ under the existing unversioned `C0DE` convention; add no compatibility layer.
+
+Checkpoint: a two-module cycle rejected from source is also rejected after
+serialization; an acyclic diamond resolves to one shared class and flag.
+Two local topo orders can be merged even when their modules must interleave.
+
+### 3. CLInitNode and the executable protocol
+
+Files: new `node/CLInitNode.java`; `Node.java`, `Parser.java`, `CodeGen.java`,
+`Serialize.java`, `Eval2.java`, and the printer adapters as needed.
+
+- Add an effectful, serializable CLInitNode targeting the resolved CompUnit,
+ with control/memory inputs and outputs in slots 0/1. Pending targets remain
+ explicit until resolved. Its type computation is conservative about the
+ initializer's whole memory effects; final normal output guarantees completion.
+- Integrate its possible call with existing Call/CallEnd linking, argument and
+ return propagation, and body liveness. A reference to CompUnit metadata alone
+ must not let SCCP discard the initializer as uncalled. Reuse the call machinery
+ or a generated guard helper, rather than implementing a second independent
+ calling convention in the node. Test an out-of-line imported initializer as
+ well as an inlined one.
+- Insert checks at class-field accesses, including function-pointer loads;
+ use the same operation for required instance creation and startup. Do not
+ instrument ordinary function headers or rewrite their function-pointer data.
+- Insert `$init = true` once in each dynamic `<clinit>` body, naturally at entry.
+ The checking node performs the test/conditional call and contains no store.
+ A false startup test becomes a direct call preserving `arg` and the result.
+- Give Eval2 the same shared-object/flag behavior. Lower checks to existing
+ control, memory, and call IR for machine compilation; use an out-of-line
+ helper if necessary rather than adding target-specific machine operations.
+- Reject the presently unsupported native class/raw-initializer handoffs. Keep
+ ordinary checked-field function pointers unchanged. Do not add an explicit
+ initialization obligation to C drivers or other native callers. Direct native
+ entry into a dynamic class remains outside the new guarantee until a
+ compiler-managed startup/export mechanism exists.
+
+Checkpoint: sequential touches run the body once; parent effects precede child
+initialization; an untaken branch runs no child code. Verify result/argument
+preservation and flag sharing in Eval2, ARM/RISC emulation, and native execution
+through Simple startup. Existing direct native tests must not be mistaken for
+coverage of the deferred dynamic-export case.
+
+### 4. Parser flow and ideal simplification
+
+Files: `ScopeNode.java`, `Parser.java`, `CLInitNode.java`, and existing
+control/memory lookup helpers.
+
+- Add path-local known-initialized facts to ScopeNode. Copy in `dup`, intersect
+ reachable predecessors in merges, and respect entry/backedge distinctions in
+ loop completion, break, continue, and return handling. The established
+ control/memory path must justify each check omitted by the parser.
+- Implement constant-true, constant-false, preceding dominating CLInit, and
+ dominating true-test peepholes. Use dependencies to revisit proofs after SCCP
+ or inlining sharpens values. Keep remaining parent requirements when only a
+ child's own body is static.
+- Preserve initialization effects when folding field loads into function
+ constants. Never hoist a check onto a path that previously did not touch the
+ class. Do not confuse an early true flag inside `<clinit>` with permission
+ to read that initializer's unfinished fields.
+- Add fully-static classification using the initializer body and its effects,
+ excluding the generated flag store from the test. Emit a true initial flag
+ only after proof; remove the now-redundant body/store/checks. Extend mutable
+ constant-field lowering only where observations during initialization remain
+ correct. Do not use classification to erase evidence of a genuine cycle.
+
+Checkpoint: distinguish a dominating check from a one-arm-only check, a
+zero-trip loop from a definitely executed touch, and a static child from an
+unfinished dynamic parent. Check that early flag folding cannot hide a cycle.
+
+### 5. Phase ordering and final composition
+
+Files: `CodeGen.java`, `ParseAll.java`, `Serialize.java`, `ElfReader.java`,
+`Main.java`, and the native linking path in `TestC.java` as appropriate.
+
+The current driver runs parse, Iter, Opto, TypeCheck, LoopTree,
+`unlinkImports`, serialization, then selection and scheduling. Add explicit
+initialization-analysis boundaries without relying on a particular peephole
+worklist order:
+
+1. During parse/load, register identities and represent active initialization
+ obligations in ideal IR. Ordinary Opto resolves targets and removes dead
+ control; do not collect a historical union of temporary types or calls.
+2. After Opto finishes, collect surviving CLInitNodes and calls, complete
+ function summaries, classify provably static work without circular
+ assumptions, and validate the graph. Any remaining unknown required
+ initialization effects prevent certification.
+3. Before `unlinkImports`, retain imported summaries needed for validation and
+ serialization. Serialize ideal CLInitNodes and their proof-bearing control
+ and memory edges, plus the independent requirements.
+4. Lower remaining CLInitNodes before final loop-tree construction and machine
+ selection. Because lowering introduces branches/calls, rebuild affected
+ control information and run ordinary ideal/type validation on that graph.
+ Move the final LoopTree step if needed rather than using a stale tree from
+ before expansion. Keep the serialized representation ideal and reloadable.
+ Ensure the generated call participates in call linking before the final
+ unlink step; imported bodies may remain represented by symbolic targets.
+5. Before native executable composition, load metadata for the exact full
+ Simple object set being linked, merge requirements, and validate again.
+ A plain GCC/ELF link is not an initialization-graph checker. Do the same
+ validation for a complete in-memory image.
+
+If optimization or loading discovers another target, mark summaries dirty and
+rerun validation before emission. No runtime `initializing` state is introduced;
+compile-time DFS bookkeeping is independent of the single runtime flag.
+
+Checkpoint: source, imported ideal IR, and final linked metadata agree on
+acceptance/rejection for equivalent optimized graphs. Exercise different
+worklist seeds and object-discovery orders to check determinism; eliminated
+branches and targets must not leave stale dependencies. Missing required
+metadata fails diagnostically.
+
+### Validation and scope
+
+Use a small set of focused fixtures covering several boundaries each, plus the
+existing full Chapter 25 Make suites. Include effects before a nonreturning
+call so dependency checking cannot accidentally depend on returned memory.
+Exercise duplicate touches, conditional cycles, native-handoff diagnostics,
+serialization alias remapping, class/flag ownership, and normal startup results.
+Run the fixed allocation cohorts only after runtime semantics stabilize; graph
+and spill changes are expected, and any new golden counts must follow execution
+and register-correctness checks. Keep Jig tests ignored.
+
+Do not backport this feature, change pointer widths, introduce concurrent or
+recoverable initialization, add dynamic module loading, or solve the general
+native-export policy in these checkpoints. The extra analysis-only alias slice
+remains an alternative representation if explicit summaries prove awkward;
+implementing both would add work without improving the initial guarantee.
## Module goals retained from the old notes