|
|
@@ -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
|
|
|
|