summary_two-phase-session-2026-10-04b.md 2.8 KB

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/<Mod>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

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