Просмотр исходного кода

lower L7: flip the switch — Lower is the only active emitter

The per-unit Lower emit is now unconditional: the driver always calls
Lower.BeginSession/BeginUnit/LowerUnitNow/EndSession, so a plain `./M2`
emits the image from the AST.  The grammar's inline emit is still
present but now always runs suppressed (SetNoEmit) and is discarded.

-lower is now test-only: it does not change what is emitted, only names
the image "<mod>L.ssa" for the byte-compare harness.  Since Lower was
verified byte-identical to the legacy inline emit on every single-file
test and every session, flipping the default did not change any image.

Drop the dead SetLower call (lowerRequested was never read).

Suite 204/204; FIXPOINT OK (3,553,701 bytes).
Eric Streit 4 дней назад
Родитель
Сommit
857c221aaa
2 измененных файлов с 64 добавлено и 57 удалено
  1. 11 10
      compiler/src/compiler.frm
  2. 53 47
      docs/STATUS.md

+ 11 - 10
compiler/src/compiler.frm

@@ -6,7 +6,7 @@ MODULE -->Grammar;
    are assumed to have been constructed with COCO/R compiler generator. *)
 
   FROM -->Scanner IMPORT lst, src, errors, Error, CharAt;
-  FROM -->Parser IMPORT Parse, Successful, SetLower, GetUnit;
+  FROM -->Parser IMPORT Parse, Successful, GetUnit;
   IMPORT
     Strings, Storage, SYSTEM, FileIO, SymTab, QbeGen, AST, Lower;
     (* and any others needed *)
@@ -275,7 +275,9 @@ MODULE -->Grammar;
     END BaseName;
 
   PROCEDURE IsLowerParam (s: ARRAY OF CHAR): BOOLEAN;
-  (* "-lower": test-only flag that re-emits the parsed unit via Lower. *)
+  (* "-lower": test-only flag.  Lower is now the only emitter, so this
+     merely names the image "<mod>L.ssa" for the byte-compare harness
+     (it does not change what is emitted). *)
   BEGIN
     RETURN (s[0] = "-") AND (s[1] = "l") AND (s[2] = "o") AND (s[3] = "w")
         AND (s[4] = "e") AND (s[5] = "r") AND (s[6] = CHR(0))
@@ -287,17 +289,17 @@ MODULE -->Grammar;
     nFiles: CARDINAL;
     f: CARDINAL;
     bad: BOOLEAN;
-    lowerAst: BOOLEAN;
+    lowerTest: BOOLEAN;
 
   BEGIN
     (* collect the session's source files (program module last) *)
     nFiles := 0;
-    lowerAst := FALSE;
+    lowerTest := FALSE;
     LOOP
       FileIO.NextParameter(sourceName);
       IF (sourceName[0] = CHR(0)) OR (nFiles > HIGH(files)) THEN EXIT END;
       IF IsLowerParam(sourceName) THEN
-        lowerAst := TRUE
+        lowerTest := TRUE
       ELSE
         Strings.Assign(sourceName, files[nFiles]);
         INC(nFiles)
@@ -309,10 +311,9 @@ MODULE -->Grammar;
     END;
 
     BaseName(files[nFiles - 1], progName);
-    IF lowerAst THEN SetLower(TRUE) END;
     SymTab.Init;
     QbeGen.OpenModule(progName);
-    IF lowerAst THEN Lower.BeginSession(progName) END;
+    Lower.BeginSession(progName);
     Error := StoreError;
 
     (* parse each unit into the shared session *)
@@ -333,9 +334,9 @@ MODULE -->Grammar;
       FileIO.WriteString(FileIO.StdOut, "Parsing");
       FileIO.WriteLn(FileIO.StdOut);
       AST.Init;
-      IF lowerAst THEN Lower.BeginUnit END;
+      Lower.BeginUnit;
       Parse;
-      IF lowerAst THEN Lower.LowerUnitNow(GetUnit(), f = nFiles - 1) END;
+      Lower.LowerUnitNow(GetUnit(), f = nFiles - 1);
       PrintListing;
       IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
       IF NOT Successful() THEN
@@ -351,7 +352,7 @@ MODULE -->Grammar;
       INC(f)
     END;
 
-    IF lowerAst THEN Lower.EndSession(lowerAst) END;
+    Lower.EndSession(lowerTest);
 
     IF bad OR NOT SymTab.HaveProgram()
       THEN FileIO.WriteString(FileIO.StdOut, "Incorrect source")

+ 53 - 47
docs/STATUS.md

@@ -1,41 +1,50 @@
-# m2compiler-V3 — status (2026-10-06)
+# m2compiler-V3 — status (2026-10-07)
 
 This answers "where are we after all the refactoring?".
 
-## MILESTONE (2026-10-07)
+## MILESTONE (2026-10-07): the switch is flipped
 
-**The L7 flip is byte-identical.**  Running the compiler on its own
-sources under `-lower` now produces exactly the same image as the
-legacy inline emit: **`cmp` reports MATCH**, both 3,510,051 bytes
-(736/736 functions).  `run_tests.sh` is **204/204** and
-`bootstrap/fixpoint.sh` is **FIXPOINT OK** (3,510,051 bytes).
+**Lower is now the only active emitter.**  A plain `./M2` run emits
+the image through `Lower` from the AST; the grammar's inline emit is
+still present but runs suppressed (`SetNoEmit`) and is discarded, so
+it no longer reaches the buffer.  Verified:
 
-The remaining work is the mechanical part of the flip: delete the
-grammar's inline emit and the `-lower` flag, then remove the dead
+- `run_tests.sh` is **204/204** (the `expect_run` tests now exercise
+  Lower end to end through qbe/cc/execution).
+- `bootstrap/fixpoint.sh` is **FIXPOINT OK** (3,553,701 bytes).
+
+The `-lower` flag is now test-only: it does not change *what* is
+emitted, only names the image `<mod>L.ssa` for the byte-compare
+harness.  The byte-identity milestone (Lower == legacy inline on every
+single-file test and every session) is what made this a no-op flip.
+
+The remaining work is purely mechanical: delete the grammar's inline
+emit (533 `QbeGen.*` call sites), retire the `-lower` flag and the
+now-degenerate byte-compare tests, then remove the dead
 `LowerSession`/`MaterializeGlobals` scaffolding.
 
 ## TL;DR
 
 - **The compiler works and self-hosts today.**  `run_tests.sh` is
   **204/204** and `bootstrap/fixpoint.sh` is **FIXPOINT OK**
-  (3,510,051 bytes, byte-identical stage2/stage3).
-- The **two-phase refactor (L0–L7) now works end to end**: `Lower`
-  reproduces the whole compiler image byte-for-byte from the AST.
-- The flip is still **opt-in** (`-lower`) and does **not** affect the
-  normal build; the final step is to make it the only emitter.
+  (3,553,701 bytes, byte-identical stage2/stage3).
+- The **two-phase refactor (L0–L7) is done**: `Lower` reproduces the
+  whole compiler image byte-for-byte from the AST, so flipping the
+  emitter was a no-op.
+- **Lower is the only active emitter.**  The grammar's inline emit is
+  retained but suppressed; deleting it (and the `-lower` test flag) is
+  the remaining cleanup.
 
 ## What the refactor actually is
 
 There are **two ways** the compiler can turn Modula-2 into QBE IR:
 
 1. **Legacy "inline" emit** — the grammar emits QBE IR *while* it
-   parses.  This is the default and is what the working compiler and
-   the self-hosting fixpoint use.
+   parses.  This used to be the default; it now only runs suppressed
+   (its output is discarded) and is being deleted.
 2. **Lower (two-phase)** — the grammar builds an **AST**, and a
    separate module `Lower` walks the AST and emits QBE IR.  This is
-   selected with the **`-lower`** flag and is the goal of the
-   refactor ("the flip" = make Lower the only emitter and delete the
-   inline emit).
+   now the **only active emitter** ("the flip").
 
 The refactor is being done in slices:
 
@@ -46,7 +55,7 @@ The refactor is being done in slices:
 | L4 | builtins, `CASE`, `WITH` | done |
 | L5 | records/arrays/pointers/sets/strings, literals | done |
 | L6 | nested modules, classes, qualified names | done |
-| **L7** | **the flip: Lower emits the whole session** | **in progress** |
+| **L7** | **the flip: Lower emits the whole session** | **done** (flip landed 2026-10-07) |
 
 Everything is committed in small steps; the many commits are
 **incremental fixes to the new `Lower` path**, not changes to the
@@ -56,19 +65,19 @@ working legacy compiler.
 
 The flip is driven per unit: the driver suppresses the grammar's
 inline emit (`SetNoEmit`), snapshots the QbeGen state before each
-unit's parse, restores it, and lets `Lower` re-emit the unit.  On the
-compiler's own sources:
+unit's parse, restores it, and lets `Lower` emit the unit from the
+AST.  This is now the *only* path — plain `./M2` runs it, and the
+self-hosting fixpoint runs it too.  `EndSession` writes the single
+session image.
 
-- `-lower` emits the **full** image: **736 functions** (same as
-  legacy), ~1.5 s.
-- It is **~74 % byte-identical** before the first divergence:
-  `first diff at byte 2,935,735, line 89,349` of `120,266`.
-- Progress of the first divergence across the session:
-  `line 9 → 327 → 1124 → 3437 → 6961 → 56294 → 70934 → 82194 → 86942 → 89349`.
+The `-lower` flag survives only for the test harness: it names the
+image `<mod>L.ssa` instead of `<mod>.ssa` so `run_tests.sh` can still
+byte-compare two runs.  Both runs now use Lower, so the comparison is
+degenerate; it will be retired together with the inline emit.
 
-The first divergence is now the generic grammar `ArgList` call, which
-has **9 arguments** but `AST.MaxChild = 8` caps a call at 7, so `Lower`
-drops the tail.
+Because `Lower` was verified byte-identical to the legacy inline emit
+on every single-file test and every session, flipping the default did
+not change any emitted image.
 
 ## What L7 fixed so far
 
@@ -89,26 +98,23 @@ drops the tail.
 
 ## What remains
 
-1. **Calls with more than 7 arguments** (the `ArgList` case):
-   chunk the call actuals and make the argument loops follow the
-   `NkBlock` chunks.  (A prototype regressed `AstCallNode`'s legacy
-   emit and was reverted.)
-2. Whatever the next first-divergence reveals.  Historically each fix
-   has exposed one more small gap; there may be a few more.
-3. Only then: delete the grammar's inline emit (the literal flip) and
-   remove the `-lower` flag.
+Purely mechanical cleanup — no codegen gaps left:
+
+1. Delete the grammar's inline emit (the 533 `QbeGen.*` call sites),
+   keeping the AST-building actions.
+2. Retire the `-lower` test flag and the now-degenerate byte-compare
+   tests in `run_tests.sh`.
+3. Remove the dead scaffolding: `LowerSession`, `MaterializeGlobals`,
+   `SaveSession`/`RestoreSession` and the `SetNoEmit` machinery (once
+   nothing emits inline), plus the dead `SetLower`/`lowerRequested`.
 
 ## Are we near completion?
 
 - **Of the working compiler:** yes — it is done, green, and
-  self-hosting.  The refactor has not broken it.
-- **Of the L7 flip:** the *hard architectural part is finished*
-  (per-unit emit, scoping, tables, the AST chunking mechanism).  The
-  remainder is a **tail of codegen gaps** that only matter because the
-  verification is exact byte-identity over a 120k-line session.
-  Realistically a handful more small fixes; not a rewrite.  It is not
-  required for the compiler to function — it is a quality/architecture
-  goal.
+  self-hosting, and it now emits through `Lower`.
+- **Of the L7 flip:** the flip is done; only the mechanical deletion of
+  the unused inline emitter remains.  It is not required for the
+  compiler to function — it is a quality/architecture goal.
 
 ## How to check for yourself