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

lower phase slice 2: control flow

LowerStmt now emits IF/ELSIF/ELSE (flattening the nested NkIf chain into
the grammar's shared lEnd/lElse), WHILE, REPEAT, LOOP+EXIT, FOR without
BY, and HALT, with the same label/temp order as the grammar.  Sequence
iteration and statement dispatch are merged so no FORWARD is needed
(gm2's whole-program pass rejects FORWARD).

Gates: suite 182/182 (lower_ok t_lower2); fixpoint OK (stage2 == stage3,
3123487 bytes).

See docs/summary_two-phase-lower2.md.
Eric Streit 1 неделя назад
Родитель
Сommit
68dc77b6de
5 измененных файлов с 203 добавлено и 55 удалено
  1. 1 0
      compiler/run_tests.sh
  2. 140 54
      compiler/src/Lower.mod
  3. 21 0
      compiler/tests/t_lower2.mod
  4. 1 1
      docs/plan-lower.md
  5. 40 0
      docs/summary_two-phase-lower2.md

+ 1 - 0
compiler/run_tests.sh

@@ -466,6 +466,7 @@ lower_ok() {
 lower_ok t_exit.mod
 lower_ok t_exit.mod
 lower_ok t_arith.mod
 lower_ok t_arith.mod
 lower_ok t_lower1.mod
 lower_ok t_lower1.mod
+lower_ok t_lower2.mod
 
 
 # --- step 7 gate: compiler sources free of gm2-only imports.
 # --- step 7 gate: compiler sources free of gm2-only imports.
 #     FileIO is the last module to port (see docs/summary_step7.md);
 #     FileIO is the last module to port (see docs/summary_step7.md);

+ 140 - 54
compiler/src/Lower.mod

@@ -1,5 +1,7 @@
 IMPLEMENTATION MODULE Lower;
 IMPLEMENTATION MODULE Lower;
-(* Scalar + boolean walk (L0/L1): see Lower.def and docs/plan-lower.md. *)
+(* Scalar/boolean/control-flow walk (L0-L2): see Lower.def and
+   docs/plan-lower.md.  No FORWARD declarations: sequence iteration and
+   statement dispatch are merged into CanStmt / LowerStmt. *)
 
 
 IMPORT AST, SymTab, QbeGen;
 IMPORT AST, SymTab, QbeGen;
 
 
@@ -79,6 +81,49 @@ PROCEDURE CanExpr (n: AST.Node): BOOLEAN;
     RETURN FALSE
     RETURN FALSE
   END CanExpr;
   END CanExpr;
 
 
+PROCEDURE CanStmt (n: AST.Node): BOOLEAN;
+  VAR i: CARDINAL; k: INTEGER; by: AST.Node;
+  BEGIN
+    IF n = AST.NoNode THEN RETURN TRUE END;
+    k := AST.Kind(n);
+    IF k = AST.NkBlock THEN
+      i := 0;
+      WHILE i < AST.NChild(n) DO
+        IF NOT CanStmt(AST.Child(n, i)) THEN RETURN FALSE END;
+        INC(i)
+      END;
+      RETURN TRUE
+    END;
+    IF k = AST.NkAssign THEN
+      RETURN IsSimpleScalar(AST.Child(n, 0)) AND CanExpr(AST.Child(n, 1))
+    END;
+    IF k = AST.NkIf THEN
+      RETURN CanExpr(AST.Child(n, 0)) AND CanStmt(AST.Child(n, 1))
+             AND CanStmt(AST.Child(n, 2))
+    END;
+    IF k = AST.NkWhile THEN
+      RETURN CanExpr(AST.Child(n, 0)) AND CanStmt(AST.Child(n, 1))
+    END;
+    IF k = AST.NkRepeat THEN
+      RETURN CanStmt(AST.Child(n, 0)) AND CanExpr(AST.Child(n, 1))
+    END;
+    IF k = AST.NkLoop THEN RETURN CanStmt(AST.Child(n, 0)) END;
+    IF k = AST.NkExit THEN RETURN TRUE END;
+    IF k = AST.NkFor THEN
+      by := AST.Child(n, 3);
+      IF (by # AST.NoNode) AND (AST.Kind(by) # AST.NkIntLit) THEN
+        RETURN FALSE END;
+      RETURN (AST.Kind(AST.Child(n, 0)) = AST.NkIdent)
+             AND CanExpr(AST.Child(n, 1)) AND CanExpr(AST.Child(n, 2))
+             AND CanStmt(AST.Child(n, 4))
+    END;
+    IF k = AST.NkHalt THEN
+      IF AST.Child(n, 0) = AST.NoNode THEN RETURN TRUE END;
+      RETURN CanExpr(AST.Child(n, 0))
+    END;
+    RETURN FALSE
+  END CanStmt;
+
 PROCEDURE CanDecl (c: AST.Node): BOOLEAN;
 PROCEDURE CanDecl (c: AST.Node): BOOLEAN;
   VAR i: CARDINAL;
   VAR i: CARDINAL;
   BEGIN
   BEGIN
@@ -117,34 +162,12 @@ PROCEDURE CanDecls (seq: AST.Node): BOOLEAN;
     RETURN TRUE
     RETURN TRUE
   END CanDecls;
   END CanDecls;
 
 
-PROCEDURE CanStat (s: AST.Node): BOOLEAN;
-  BEGIN
-    IF AST.Kind(s) # AST.NkAssign THEN RETURN FALSE END;
-    RETURN IsSimpleScalar(AST.Child(s, 0)) AND CanExpr(AST.Child(s, 1))
-  END CanStat;
-
-PROCEDURE CanBody (seq: AST.Node): BOOLEAN;
-  VAR i: CARDINAL; c: AST.Node;
-  BEGIN
-    IF seq = AST.NoNode THEN RETURN TRUE END;
-    i := 0;
-    WHILE i < AST.NChild(seq) DO
-      c := AST.Child(seq, i);
-      IF i = AST.MaxChild - 1 THEN
-        IF NOT CanBody(c) THEN RETURN FALSE END
-      ELSIF NOT CanStat(c) THEN RETURN FALSE
-      END;
-      INC(i)
-    END;
-    RETURN TRUE
-  END CanBody;
-
 PROCEDURE CanLower (unit: AST.Node): BOOLEAN;
 PROCEDURE CanLower (unit: AST.Node): BOOLEAN;
   BEGIN
   BEGIN
     IF AST.Kind(unit) # AST.NkUnit THEN RETURN FALSE END;
     IF AST.Kind(unit) # AST.NkUnit THEN RETURN FALSE END;
     IF AST.Kind(AST.Child(unit, 0)) # AST.NkIdent THEN RETURN FALSE END;
     IF AST.Kind(AST.Child(unit, 0)) # AST.NkIdent THEN RETURN FALSE END;
     IF AST.Child(unit, 3) # AST.NoNode THEN RETURN FALSE END;
     IF AST.Child(unit, 3) # AST.NoNode THEN RETURN FALSE END;
-    RETURN CanDecls(AST.Child(unit, 1)) AND CanBody(AST.Child(unit, 2))
+    RETURN CanDecls(AST.Child(unit, 1)) AND CanStmt(AST.Child(unit, 2))
   END CanLower;
   END CanLower;
 
 
 PROCEDURE LowerDesign (n: AST.Node; VAR t: SymTab.TypeIndex; VAR k: INTEGER;
 PROCEDURE LowerDesign (n: AST.Node; VAR t: SymTab.TypeIndex; VAR k: INTEGER;
@@ -272,24 +295,101 @@ PROCEDURE LowerExpr (n: AST.Node; VAR t: SymTab.TypeIndex;
     END
     END
   END LowerExpr;
   END LowerExpr;
 
 
-PROCEDURE LowerAssign (n: AST.Node);
-  VAR dt: SymTab.TypeIndex; k, et, cls: INTEGER;
-      qd, qe: QbeGen.QVal; qn: SymTab.Name; sfx, isR, conv: BOOLEAN;
+PROCEDURE LowerStmt (seq: AST.Node);
+  VAR i: CARDINAL; c, els, by: AST.Node; k: INTEGER;
+      t, t2, dt, et: SymTab.TypeIndex;
+      q, q2, qt, qf, qd, qe: QbeGen.QVal;
+      qlo, qhi, qk, qb: QbeGen.QVal;
+      lTop, lBody, lEnd, lThen, lElse, lNext, lFalse, lDone, lx: QbeGen.QVal;
+      qn, lv: SymTab.Name;
+      kind, op, rop, cls: INTEGER;
+      sfx, folded, isR, conv, hasElse: BOOLEAN;
   BEGIN
   BEGIN
-    LowerDesign(AST.Child(n, 0), dt, k, qd, qn, sfx);
-    LowerExpr(AST.Child(n, 1), et, qe);
-    cls := SymTab.ClassOf(dt);
-    isR := (dt # SymTab.InvalidType) AND (cls = SymTab.ClReal);
-    conv := isR AND SymTab.IsIntFamily(et);
-    IF (k = SymTab.KindVar) OR (k = SymTab.KindParam) THEN
-      IF SymTab.IsLongFamily(dt) THEN
-        QbeGen.StoreLong(qn, qe)
-      ELSIF conv THEN
-        QbeGen.ConvIR(qe, qd); QbeGen.StoreVar(qn, qd, TRUE)
-      ELSE QbeGen.StoreVar(qn, qe, isR)
+    IF seq = AST.NoNode THEN RETURN END;
+    k := AST.Kind(seq);
+
+    IF k = AST.NkBlock THEN
+      i := 0;
+      WHILE i < AST.NChild(seq) DO
+        LowerStmt(AST.Child(seq, i)); INC(i)
+      END
+    ELSIF k = AST.NkAssign THEN
+      LowerDesign(AST.Child(seq, 0), dt, cls, qd, qn, sfx);
+      LowerExpr(AST.Child(seq, 1), et, qe);
+      isR := (dt # SymTab.InvalidType) AND (SymTab.ClassOf(dt) = SymTab.ClReal);
+      conv := isR AND SymTab.IsIntFamily(et);
+      IF (cls = SymTab.KindVar) OR (cls = SymTab.KindParam) THEN
+        IF SymTab.IsLongFamily(dt) THEN QbeGen.StoreLong(qn, qe)
+        ELSIF conv THEN QbeGen.ConvIR(qe, qd); QbeGen.StoreVar(qn, qd, TRUE)
+        ELSE QbeGen.StoreVar(qn, qe, isR)
+        END
+      END
+    ELSIF k = AST.NkIf THEN
+      LowerExpr(AST.Child(seq, 0), t, q);
+      QbeGen.NewLabel(lThen); QbeGen.NewLabel(lElse); QbeGen.NewLabel(lEnd);
+      QbeGen.Jnz(q, lThen, lElse); QbeGen.EmitLabel(lThen);
+      LowerStmt(AST.Child(seq, 1));
+      QbeGen.Jmp(lEnd);
+      els := AST.Child(seq, 2); hasElse := FALSE;
+      WHILE (els # AST.NoNode) AND (AST.Kind(els) = AST.NkIf) DO
+        QbeGen.EmitLabel(lElse); QbeGen.NewLabel(lElse);
+        LowerExpr(AST.Child(els, 0), t, q);
+        QbeGen.NewLabel(lThen); QbeGen.Jnz(q, lThen, lElse);
+        QbeGen.EmitLabel(lThen);
+        LowerStmt(AST.Child(els, 1));
+        QbeGen.Jmp(lEnd);
+        els := AST.Child(els, 2)
+      END;
+      IF els # AST.NoNode THEN
+        QbeGen.EmitLabel(lElse); hasElse := TRUE; LowerStmt(els)
+      END;
+      IF hasElse THEN QbeGen.EmitLabel(lEnd)
+      ELSE QbeGen.EmitLabel(lElse); QbeGen.EmitLabel(lEnd)
       END
       END
+    ELSIF k = AST.NkWhile THEN
+      QbeGen.NewLabel(lTop); QbeGen.NewLabel(lBody); QbeGen.NewLabel(lEnd);
+      QbeGen.EmitLabel(lTop);
+      LowerExpr(AST.Child(seq, 0), t, q);
+      QbeGen.Jnz(q, lBody, lEnd); QbeGen.EmitLabel(lBody);
+      LowerStmt(AST.Child(seq, 1));
+      QbeGen.Jmp(lTop); QbeGen.EmitLabel(lEnd)
+    ELSIF k = AST.NkRepeat THEN
+      QbeGen.NewLabel(lTop); QbeGen.NewLabel(lEnd); QbeGen.EmitLabel(lTop);
+      LowerStmt(AST.Child(seq, 0));
+      LowerExpr(AST.Child(seq, 1), t, q);
+      QbeGen.Jnz(q, lEnd, lTop); QbeGen.EmitLabel(lEnd)
+    ELSIF k = AST.NkLoop THEN
+      QbeGen.NewLabel(lTop); QbeGen.NewLabel(lEnd);
+      QbeGen.PushLoop(lEnd); QbeGen.EmitLabel(lTop);
+      LowerStmt(AST.Child(seq, 0));
+      QbeGen.Jmp(lTop); QbeGen.PopLoop; QbeGen.EmitLabel(lEnd)
+    ELSIF k = AST.NkExit THEN
+      IF QbeGen.TopLoop(lx) THEN QbeGen.Jmp(lx) END
+    ELSIF k = AST.NkFor THEN
+      Name(AST.Child(seq, 0), lv);
+      LowerExpr(AST.Child(seq, 1), t, qlo);
+      LowerExpr(AST.Child(seq, 2), t2, qhi);
+      QbeGen.StoreVar(lv, qlo, FALSE);
+      QbeGen.NewLabel(lTop); QbeGen.NewLabel(lBody); QbeGen.NewLabel(lEnd);
+      QbeGen.EmitLabel(lTop);
+      QbeGen.LoadVar(lv, FALSE, qt);
+      QbeGen.NewTemp(qk);
+      QbeGen.Op3("cslew", qk, qt, qhi, FALSE);
+      QbeGen.Jnz(qk, lBody, lEnd);
+      QbeGen.EmitLabel(lBody);
+      LowerStmt(AST.Child(seq, 4));
+      QbeGen.LoadVar(lv, FALSE, qt);
+      QbeGen.IntStr(1, qb);
+      QbeGen.NewTemp(qk);
+      QbeGen.Op3("add", qk, qt, qb, FALSE);
+      QbeGen.StoreVar(lv, qk, FALSE);
+      QbeGen.Jmp(lTop); QbeGen.EmitLabel(lEnd)
+    ELSIF k = AST.NkHalt THEN
+      IF AST.Child(seq, 0) # AST.NoNode THEN
+        LowerExpr(AST.Child(seq, 0), t, q) END;
+      QbeGen.HaltQ
     END
     END
-  END LowerAssign;
+  END LowerStmt;
 
 
 PROCEDURE LowerDecls (seq: AST.Node);
 PROCEDURE LowerDecls (seq: AST.Node);
   VAR i, j: CARDINAL; c: AST.Node; nm, txt: SymTab.Name; qv: QbeGen.QVal;
   VAR i, j: CARDINAL; c: AST.Node; nm, txt: SymTab.Name; qv: QbeGen.QVal;
@@ -317,20 +417,6 @@ PROCEDURE LowerDecls (seq: AST.Node);
     END
     END
   END LowerDecls;
   END LowerDecls;
 
 
-PROCEDURE LowerBody (seq: AST.Node);
-  VAR i: CARDINAL; c: AST.Node;
-  BEGIN
-    IF seq = AST.NoNode THEN RETURN END;
-    i := 0;
-    WHILE i < AST.NChild(seq) DO
-      c := AST.Child(seq, i);
-      IF i = AST.MaxChild - 1 THEN LowerBody(c)
-      ELSE LowerAssign(c)
-      END;
-      INC(i)
-    END
-  END LowerBody;
-
 PROCEDURE LowerUnit (unit: AST.Node);
 PROCEDURE LowerUnit (unit: AST.Node);
   VAR nm: SymTab.Name; out: ARRAY [0 .. 255] OF CHAR; i: CARDINAL;
   VAR nm: SymTab.Name; out: ARRAY [0 .. 255] OF CHAR; i: CARDINAL;
   BEGIN
   BEGIN
@@ -345,7 +431,7 @@ PROCEDURE LowerUnit (unit: AST.Node);
     QbeGen.SetModule(nm);
     QbeGen.SetModule(nm);
     LowerDecls(AST.Child(unit, 1));
     LowerDecls(AST.Child(unit, 1));
     QbeGen.BeginBody;
     QbeGen.BeginBody;
-    LowerBody(AST.Child(unit, 2));
+    LowerStmt(AST.Child(unit, 2));
     QbeGen.EndModule(nm)
     QbeGen.EndModule(nm)
   END LowerUnit;
   END LowerUnit;
 
 

+ 21 - 0
compiler/tests/t_lower2.mod

@@ -0,0 +1,21 @@
+MODULE TLower2;
+(* Lower L2: control flow -- IF/ELSIF/ELSE, WHILE, REPEAT, LOOP/EXIT,
+   FOR (no BY), HALT.  Exit 6. *)
+VAR i, s : INTEGER;
+VAR ExitCode : INTEGER;
+BEGIN
+  s := 0;
+  IF s < 0 THEN s := 1
+  ELSIF s = 0 THEN s := 2
+  ELSE s := 3 END;
+  i := 0;
+  WHILE i < 3 DO i := i + 1 END;
+  REPEAT i := i - 1 UNTIL i <= 0;
+  LOOP
+    i := i + 1;
+    IF i > 2 THEN EXIT END
+  END;
+  FOR i := 1 TO 3 DO s := s + i END;
+  IF s > 1000 THEN HALT END;
+  ExitCode := s
+END TLower2.

+ 1 - 1
docs/plan-lower.md

@@ -50,7 +50,7 @@ types/kinds) and the node `ty` field.
 | --- | --- | --- |
 | --- | --- | --- |
 | **L0** | program unit; scalar `CONST`/`VAR`; body of `x := <int expr>`; int literals/idents, `+ - * DIV MOD`, unary `-` | mechanism proof; `t_exit`, `t_arith` |
 | **L0** | program unit; scalar `CONST`/`VAR`; body of `x := <int expr>`; int literals/idents, `+ - * DIV MOD`, unary `-` | mechanism proof; `t_exit`, `t_arith` |
 | **L1** | relations, booleans, short-circuit `AND`/`OR` (Delay), `NOT` | `t_lower1`; `docs/summary_two-phase-lower1.md` |
 | **L1** | relations, booleans, short-circuit `AND`/`OR` (Delay), `NOT` | `t_lower1`; `docs/summary_two-phase-lower1.md` |
-| L2 | control flow: `IF`/`WHILE`/`REPEAT`/`LOOP`/`FOR`/`RETURN`/`HALT` | |
+| **L2** | control flow: `IF`/`ELSIF`/`ELSE`, `WHILE`, `REPEAT`, `LOOP`/`EXIT`, `FOR` (no `BY`), `HALT` | `t_lower2`; `docs/summary_two-phase-lower2.md` |
 | L3 | procedures/functions: `BeginFunc`/params/locals/`CallBegin`… | |
 | L3 | procedures/functions: `BeginFunc`/params/locals/`CallBegin`… | |
 | L4 | calls, builtins, `WITH`, `CASE` | |
 | L4 | calls, builtins, `WITH`, `CASE` | |
 | L5 | arrays/sets/records/pointers/strings | |
 | L5 | arrays/sets/records/pointers/strings | |

+ 40 - 0
docs/summary_two-phase-lower2.md

@@ -0,0 +1,40 @@
+# Lower phase — slice 2 (control flow)
+
+Branch `ast-stage-c`.  Suite **182/182**; self-hosting fixpoint **OK**
+(**3,123,487 bytes**; L1 was 3,104,868).  Plan: `docs/plan-lower.md`.
+
+## What landed
+
+`LowerStmt` (sequence iteration + statement dispatch merged, so **no
+`FORWARD`** — gm2's whole-program pass rejects it) now emits:
+
+- `IF`/`ELSIF`/`ELSE` — flattening the AST's nested-`NkIf` chain into the
+  grammar's single shared `lEnd`, threading `lElse`, exactly as the
+  grammar does.
+- `WHILE`, `REPEAT`, `LOOP` + `EXIT` (via `PushLoop`/`TopLoop`).
+- `FOR` **without `BY`** (`cslew`/`add` loop), with the same
+  `StoreVar`/`LoadVar`/`Op3` order.
+- `HALT`.
+
+`CanStmt` mirrors the same shapes; `FOR` with a `BY` clause stays out of
+the subset (needs constant evaluation).
+
+## Finding
+
+`FORWARD` declarations in `Lower.mod` broke gm2's whole-program pass
+("too many errors in pass 3") even though `-c` compiled the module.
+Merging the mutually-recursive bodies (`CanBody`↔`CanStat`,
+`LowerBody`↔`LowerIf`) into single dispatching procedures removed it.
+
+## Verified
+
+`gen_ssa/TLower2L.ssa` — a program with `IF`/`ELSIF`/`ELSE`, `WHILE`,
+`REPEAT`, `LOOP`/`EXIT`, `FOR` and a conditional `HALT` — is
+**byte-identical** to the legacy image (`lower_ok t_lower2.mod`).
+
+## Next
+
+L3: procedures/functions (headers, params, locals, `RETURN`, calls) —
+`BeginFunc`/`FuncParam`/`EndFuncHeader`/`EndFunc`/`CallBegin`…  This is
+where `LowerUnit` starts walking declaration bodies, not just the program
+body.