# Lower phase — L7 (the flip): status and plan (2026-10-05) Branch **`ast-stage-c`**. L7 = "flip: lower from the AST instead of inline; delete the inline emit + `Fwd*` machinery". ## Why L7 is not incremental The flip makes `Lower` the sole emitter. The obstacle is that `QbeGen` holds **one** session state, used by the inline emit *during* parse; a per-unit `Lower` emit would be clobbered by the next unit's inline emit. So the flip is a **big-bang**: either remove/suppress the grammar's 533 `QbeGen.*` call sites, or give `QbeGen` two states — neither is a byte-verifiable step. The pragmatic path is therefore a **session re-emit**: collect every unit's AST and re-emit the whole session with `Lower` after parsing. That is byte-comparable against the legacy session image and becomes the flip once the inline emit is removed. ## Started (`d30abfd`, tag `v3-lower7-scaffold`) - `Lower.LowerSession/LowerOneUnit/MaterializeGlobals`. - Driver (`compiler.frm`) collects `GetUnit()` per file and calls `Lower.LowerSession` under `-lower`. - `SymTab.Materialize` makes a unit's own VAR/CONST visible in `globScope`, so `GlobalRef` resolves `"_"` at session end (the unit scopes are closed by then). - Removed `Lower.Reset` from the `M2` root so the owned tables persist across a session. Still scaffolding (inline emit untouched); suite **200/200**, FIXPOINT OK (3,401,129 bytes). ## Progress (`ce2fc67`, tag `v3-lower7-session`) - **`DefUnit` now builds an AST** (proc headings recorded as `NkProcDecl`); `LowerDecls` replays a bodyless proc as a heading. - **Session re-emit works** for small sessions: `lower_ok_files` added, and `DBasicProg`, `DQualProg`, `TQual` (def-CONST), `TQualP` (def-PROC) byte-match. Fixes: image named after the program module; `MaterializeGlobals` also materializes TYPE/CLASS names; `SymTab.ReenterModGlobal` keeps nested-module globals' `"_"` symbols; `AST.Init` once per session under `-lower`. Suite **204/204**; FIXPOINT OK (3,405,813 bytes). ## Remaining gaps 1. **AST-arena size (fundamental).** `LowerSession` needs every unit's AST alive at once, but `AST.MaxNodes = 65536` is sized for one unit; the compiler's session overflows it (`MakeNode` → `NoNode`, later units dump `(nil)`). Growing the arena past the 4 MiB `sessBuf` is not viable. So the session re-emit **cannot** handle the compiler — the flip must instead emit **per unit** at each unit's END (AST valid, scope open), which is the two-state / inline-emit-removal problem. 2. **External procs.** `storage_prog` differs: Lower emits `$malloc_6(l link, ...)` where the legacy emits `$malloc(...)` — the `ext` flag in `Lower.NoteProc` is hardcoded `FALSE` and `Mangled`'s `ProcLink` fails after scopes close. 3. Definition-module proc replay (done), nested globals (done). ## The "hang" was a Lower bug, not a miscompile `LowerDecls` emitted a procedure's parameter loop even when `ProcInfo` failed, using an uninitialised `np` — so the loop ran ~forever inside `FindProc`. (`MaxProc = 64` is far too small for a whole session, so `ProcInfo` failed for most of the compiler's procs.) **Fixed** (`a9a8ecd`): the proc is skipped unless `ProcInfo` succeeds, and each `ParamInfo` is guarded. Also grew the AST arena/pool (`caeeeb5`) — the arena/pool data lines are compact `z` runs, so this is nearly free. ## What remains for the session re-emit - **Correctness:** `MaxProc`/`MaxSym` must be ≥ the session's proc/var counts (the compiler has hundreds of procs; 64 is too small), so procs beyond the cap are currently skipped. - **Performance:** `FindSym`/`FindProc` are linear scans, and `Lower` looks up per node — O(nodes × table size). A whole-session re-emit is very slow. A hash index (or reusing `SymTab` for module-level names) would fix it. - **Size-dependent behaviour:** with `MaxProc = 1024` the same run **segfaults** (in `CopySpan` via `ParamInfo`) — a size/layout-sensitive latent bug, the same family as the earlier `ASTDUMP` fragility. So the session re-emit is capacity-fixable but needs (a) a fast lookup and (b) the size-dependent bug isolated. The grammar rewrite remains the alternative flip path.