# Session summary — 2026-10-04 (b): Lower phase L0–L2, L3 finding Branch **`ast-stage-c`**. Suite **182/182**; self-hosting fixpoint **OK** (**3,147,849 bytes**). The Lower phase (`docs/plan-lower.md`) went from a design to a working byte-compare harness plus three verified subsets — and hit a real architectural wall at L3 that is now precisely documented. ## Landed | Tag | Doc | What | | --- | --- | --- | | `v3-lower0` | `summary_two-phase-lower0.md` | mechanism: `Lower` module, `QbeGen.SetOutName`, `-lower` flag, `lower_ok` compare; scalar L0 | | `v3-lower1` | `summary_two-phase-lower1.md` | relations, booleans, short-circuit `AND`/`OR`, `NOT` | | `v3-lower2` | `summary_two-phase-lower2.md` | `IF`/`ELSIF`/`ELSE`, `WHILE`, `REPEAT`, `LOOP`/`EXIT`, `FOR` (no `BY`), `HALT` | | `v3-lower3-wip` | `summary_two-phase-lower3-finding.md` | L3 groundwork (calls, proc headers, `ParamName`, local capture, callee fix) + the blocking finding | ## How the comparison works `-lower` makes the grammar re-emit the program unit from the AST after the legacy `EndModule` and before `SymTab.EndUnit`; `LowerUnit` writes `gen_ssa/L.ssa` and `lower_ok` byte-compares it to the legacy image. Four tests pass: `t_exit`, `t_arith`, `t_lower1`, `t_lower2`. ## The L3 finding `SymTab.CloseProc`/`EndUnit` close the scope the inline actions lowered inside. `Lower` at unit end can resolve module names but **not procedure locals/params**, so L3 does not byte-compare. Fixing it at each procedure ("option 1") needs the `L` image built **concurrently** with the legacy one — but `QbeGen` has one buffer and one counter set, so interleaved emitters corrupt the legacy path. A second buffer was tried and reverted; it does not address the shared counters. **Conclusion:** the right step is the **flip** (L7) applied to the test path — under `-lower`, suppress the legacy emit and lower the whole unit from the AST *during* the parse, when every scope is open. That completes L3 and subsumes the rest. ## Bugs found & fixed (L3 groundwork) - `NkCall` child 0 is an `NkDesignator`, not a bare `NkIdent`; calls were mangling as `$_0`. Fixed with `CalleeName`. - Procedure `VAR` locals were never recorded (`Block` set `astStmt`, not the decls); now captured via `astBlkDecls` → `NkProcDecl` child 2. - `FORWARD` declarations break gm2's whole-program pass (find: L2) — the walk merges mutually-recursive bodies instead. ## State / next - Tags `v3-lower0..2`, `v3-lower3-wip`. Only `compiler/toto.mod` dirty. - **Next:** start the flip (emit gate + AST lowering during parse), completing L3 and giving the basis for L4+. ## Resume ```sh cd ~/Projets/Projets-Modula2/MyWork/m2compiler-V3 git checkout ast-stage-c cd compiler && ./build.sh && ./run_tests.sh # 182/182 cd .. && ./bootstrap/fixpoint.sh # FIXPOINT OK ```