|
|
@@ -36,27 +36,44 @@ which time a procedure's locals/params are no longer resolvable — so
|
|
|
|
|
|
This is exactly the "flip" the plan anticipated (§3 phase 3): the walk
|
|
|
cannot be one end-of-unit pass for *everything*; body lowering has to
|
|
|
-happen at each procedure while `SymTab` still has its scope. Options:
|
|
|
+happen at each procedure while `SymTab` still has its scope.
|
|
|
|
|
|
-1. **Grammar calls `Lower.LowerProc(node)` right before `SymTab.CloseProc`**
|
|
|
- (and the program body before `EndUnit`) — i.e. per-unit-piece lowering,
|
|
|
- still writing the `L` image.
|
|
|
-2. **Make `SymTab` keep enough per-symbol info** (kind/type/param names)
|
|
|
- accessible after the scope closes (a durable "codegen view"), so one
|
|
|
- end-of-unit walk works.
|
|
|
+## Option 1 was attempted and is *not* a small hook
|
|
|
|
|
|
-Option 1 matches the existing inline timing and is the lower-risk route;
|
|
|
-it also foreshadows the flip, where `Lower` *replaces* the inline emit at
|
|
|
-that same point.
|
|
|
+The chosen route — grammar calls `Lower.LowerProc(node)` right before
|
|
|
+`SymTab.CloseProc` — requires the `L` image to be built **concurrently**
|
|
|
+with the legacy image. `QbeGen` has a **single** session buffer and a
|
|
|
+single set of counters (`nTemp`, `nLoc`, `scopeTop`, …), so two emitters
|
|
|
+interleaved **corrupt the legacy path** — the very path that must stay
|
|
|
+byte-identical for self-hosting.
|
|
|
+
|
|
|
+A second image buffer (`altBuf` + `SetAlt`/`WriteAlt`) was added and then
|
|
|
+**reverted**: it does not solve the shared-counter problem, only the
|
|
|
+buffer. Concurrent emit needs either:
|
|
|
+
|
|
|
+- **two full independent backend states** (a second `QbeGen` instance /
|
|
|
+ duplicated counters — invasive), or
|
|
|
+- **not running both at once**: under `-lower`, suppress the legacy emit
|
|
|
+ for the unit and lower only from the AST — i.e. the **flip** (L7)
|
|
|
+ applied to the test path. Lowering then happens *during* the parse, so
|
|
|
+ every scope is open at the right time and the whole class of
|
|
|
+ "closed-scope" problems disappears.
|
|
|
+
|
|
|
+The second is the clean destination and subsumes L3. Recommendation:
|
|
|
+make the flip the next dedicated step, rather than fake option 1 with two
|
|
|
+entangled emitters.
|
|
|
|
|
|
## Verified this slice
|
|
|
|
|
|
- L0–L2 comparisons still pass (`t_exit`, `t_arith`, `t_lower1`,
|
|
|
- `t_lower2`); `t_lower3` is **gated out** of `CanLower`.
|
|
|
+ `t_lower2`); `t_lower3` is **gated out** of `CanLower`, so the tree
|
|
|
+ stays green.
|
|
|
- The call-mangling and callee-designator fixes are exercised by the
|
|
|
- parse (and would be by a future flip).
|
|
|
+ parse (and will be by the flip).
|
|
|
|
|
|
## Next
|
|
|
|
|
|
-Decide option 1 vs 2 for scope timing, then finish L3 (procedures). The
|
|
|
-rest of the subset order (L4+) is unchanged.
|
|
|
+Start the flip: a backend-level emit gate so `-lower` suppresses the
|
|
|
+legacy emit and the grammar lowers the whole unit from the AST during the
|
|
|
+parse. That completes L3 and is the basis for L4+.
|
|
|
+
|