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

v3 step 8.2 — self-compile gaps: const folding, forward types, qualified types, capacity, compact array data (104/104)

Eric Streit 2 недель назад
Родитель
Сommit
bcf5d8a315

+ 1 - 0
compiler/run_tests.sh

@@ -61,6 +61,7 @@ expect_run t_case.mod 103
 expect_run t_bool.mod 42
 expect_run t_real.mod 31
 expect_run t_array.mod 108
+expect_run t_constfold.mod 42
 expect_run t_highlen.mod 18
 expect_run t_builtins.mod 42
 expect_run t_long.mod 42

+ 75 - 16
compiler/src/M2.atg

@@ -199,7 +199,9 @@ PRODUCTIONS
      Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
      Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
   TypeBlock<isDef: BOOLEAN>
-    = "TYPE" { TypeItem<isDef> ";" | ClassItem ";" } .
+    = "TYPE"                            (. SymTab.BeginTypeBlock; .)
+      { TypeItem<isDef> ";" | ClassItem ";" }
+                                        (. SymTab.EndTypeBlock; .) .
   TypeItem<isDef: BOOLEAN>              (. VAR n: SymTab.Name;
                                              t, op: SymTab.TypeIndex; .)
     = GetIdent<n>                       (. op := SymTab.OpaqueBase(n);
@@ -303,7 +305,7 @@ PRODUCTIONS
      offsets static and declaration-ordered. *)
   RecordType<VAR t: SymTab.TypeIndex>   (. VAR t2: SymTab.TypeIndex; .)
     = "RECORD"                          (. t := SymTab.NewRecord(); .)
-      [ RecField<t> { ";" RecField<t> } ]
+      [ RecField<t> { ";" [ RecField<t> ] } ]
       "END" .
   RecField<rec: SymTab.TypeIndex>       (. VAR n: SymTab.Name;
                                              t2: SymTab.TypeIndex; .)
@@ -315,19 +317,39 @@ PRODUCTIONS
       { "," GetIdent<n>                 (. IF NOT SymTab.FieldPending(rec,
                                              n) THEN
                                              SemError(200) END; .) } .
-  TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n: SymTab.Name;
-                                             k: INTEGER; .)
-    = GetIdent<n>                       (. IF NOT SymTab.Lookup(n) THEN
-                                             SemError(201);
-                                             t := SymTab.InvalidType
+  TypeIdent<VAR t: SymTab.TypeIndex>    (. VAR n, qid: SymTab.Name;
+                                             k: INTEGER;
+                                             dotted: BOOLEAN; .)
+    = GetIdent<n>                       (. dotted := FALSE;
+                                           IF NOT SymTab.Lookup(n) THEN
+                                             k := -1;
+                                             t := SymTab.ForwardType(n);
+                                             IF t = SymTab.InvalidType THEN
+                                               SemError(201)
+                                             END
                                            ELSE k := SymTab.SymKind(n);
                                              IF (k = SymTab.KindType)
                                                 OR (k = SymTab.KindPredef) THEN
                                                t := SymTab.SymType(n)
-                                             ELSIF k = SymTab.KindImport THEN
-                                               t := SymTab.InvalidType
-                                             ELSE SemError(221);
-                                               t := SymTab.InvalidType
+                                             ELSE t := SymTab.InvalidType
+                                             END
+                                           END; .)
+      [ "." GetIdent<qid>               (. dotted := TRUE;
+                                           IF k = SymTab.KindModule THEN
+                                             t := SymTab.QualType(n, qid);
+                                             IF t = SymTab.InvalidType THEN
+                                               SemError(201)
+                                             END
+                                           ELSE SemError(221);
+                                             t := SymTab.InvalidType
+                                           END; .) ]
+                                        (. IF NOT dotted THEN
+                                             IF (k = SymTab.KindModule)
+                                                OR ((k # SymTab.KindType)
+  AND (k # SymTab.KindPredef)
+  AND (k # SymTab.KindImport)
+  AND (k # -1)) THEN
+                                               SemError(221)
                                              END
                                            END; .) .
   Subrange<VAR t: SymTab.TypeIndex>     (. VAR tlo, thi: SymTab.TypeIndex;
@@ -486,6 +508,7 @@ PRODUCTIONS
                                              SemError(230)
                                            ELSIF NOT QbeGen.IsImm(qv) THEN
                                              SemError(230) END;
+                                           SymTab.SetSymVal(n, qv);
                                            QbeGen.DeclConst(n, qv, t); .) .
   VarBlock
     = "VAR" { VarDecl ";" } .
@@ -1226,7 +1249,7 @@ PRODUCTIONS
                                              lo, hi: INTEGER;
                                              fo: INTEGER;
                                              isOpen: BOOLEAN;
-                                             qb: QbeGen.QVal; .)
+                                             qb, cv: QbeGen.QVal; .)
     = GetIdent<n>                       (. QbeGen.CopyOp(n, qn);
                                            sfx := FALSE;
                                            IF NOT SymTab.Lookup(n) THEN
@@ -1263,6 +1286,13 @@ PRODUCTIONS
                                                         = SymTab.ClNil)) THEN
                                                    IF cls = SymTab.ClNil THEN
                                                      QbeGen.CopyOp("0", q)
+                                                   ELSIF ((cls
+                                                       = SymTab.ClInt)
+                                                     OR (cls
+                                                       = SymTab.ClChar))
+  AND SymTab.GetSymVal(n, cv)
+  AND QbeGen.IsImm(cv) THEN
+                                                     QbeGen.CopyOp(cv, q)
                                                    ELSE
                                                      QbeGen.LoadVar(n,
                                                        cls = SymTab.ClReal,
@@ -1549,8 +1579,10 @@ PRODUCTIONS
                                         (. VAR t2, res2, lt, rt:
                                                SymTab.TypeIndex;
                                              op: INTEGER;
-                                             q2, qt, wq: QbeGen.QVal;
-                                             neg, isR, isL: BOOLEAN;
+                                             q2, qt, wq, qf:
+                                               QbeGen.QVal;
+                                             neg, isR, isL, folded:
+                                               BOOLEAN;
                                              lw, rw, mw: CARDINAL; .)
     =                                   (. neg := FALSE; .)
       [ "+" | "-"                       (. neg := TRUE; .) ]
@@ -1597,6 +1629,17 @@ PRODUCTIONS
              IF t # SymTab.InvalidType THEN
                isL := SymTab.IsLongFamily(t);
                isR := SymTab.ClassOf(t) = SymTab.ClReal;
+               folded := FALSE;
+               IF (NOT isL) AND (NOT isR)
+  AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+                 IF op = SymTab.OpAdd THEN
+                   folded := QbeGen.Fold2(0, q, q2, qf)
+                 ELSE
+                   folded := QbeGen.Fold2(1, q, q2, qf)
+                 END
+               END;
+               IF folded THEN QbeGen.CopyOp(qf, q)
+               ELSE
                IF isL THEN
                  IF SymTab.IsIntFamily(lt) THEN
                    QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
@@ -1619,6 +1662,7 @@ PRODUCTIONS
                  END
                END;
                QbeGen.CopyOp(qt, q)
+               END
              ELSE QbeGen.CopyOp("0", q)
              END
            END; .) } .
@@ -1630,8 +1674,9 @@ PRODUCTIONS
                                         (. VAR t2, res2, lt, rt:
                                                SymTab.TypeIndex;
                                              op: INTEGER;
-                                             q2, qt, wq: QbeGen.QVal;
-                                             isR, isL: BOOLEAN;
+                                             q2, qt, wq, qf:
+                                               QbeGen.QVal;
+                                             isR, isL, folded: BOOLEAN;
                                              lw, rw, mw: CARDINAL; .)
     = Fact<t, q> { MulOp<op> Fact<t2, q2>
       (. IF op = SymTab.OpAnd THEN
@@ -1668,6 +1713,19 @@ PRODUCTIONS
            IF t # SymTab.InvalidType THEN
              isL := SymTab.IsLongFamily(t);
              isR := SymTab.ClassOf(t) = SymTab.ClReal;
+             folded := FALSE;
+             IF (NOT isL) AND (NOT isR)
+  AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
+               IF op = SymTab.OpTimes THEN
+                 folded := QbeGen.Fold2(2, q, q2, qf)
+               ELSIF op = SymTab.OpDiv THEN
+                 folded := QbeGen.Fold2(3, q, q2, qf)
+               ELSIF op = SymTab.OpMod THEN
+                 folded := QbeGen.Fold2(4, q, q2, qf)
+               END
+             END;
+             IF folded THEN QbeGen.CopyOp(qf, q)
+             ELSE
              IF isL THEN
                IF SymTab.IsIntFamily(lt) THEN
                  QbeGen.WidenLong(q, wq); QbeGen.CopyOp(wq, q)
@@ -1696,6 +1754,7 @@ PRODUCTIONS
                END
              END;
              QbeGen.CopyOp(qt, q)
+             END
            ELSE QbeGen.CopyOp("0", q)
            END
          END; .) } .

Разница между файлами не показана из-за своего большого размера
+ 1861 - 1802
compiler/src/M2.lst


+ 5 - 0
compiler/src/QbeGen.def

@@ -215,6 +215,11 @@ PROCEDURE NegFold (a: ARRAY OF CHAR; VAR q: QVal);
 PROCEDURE IsImm (s: ARRAY OF CHAR): BOOLEAN;
 (* TRUE for literal operands (not %temporaries). *)
 
+PROCEDURE Fold2 (kind: INTEGER; a, b: ARRAY OF CHAR; VAR r: QVal): BOOLEAN;
+(* Constant-folds integer binary ops when both operands are signed
+   decimal literals: kind 0=+ 1=- 2=* 3=DIV 4=MOD. r gets the result
+   and TRUE is returned; otherwise r is untouched and FALSE. *)
+
 PROCEDURE Remark (s: ARRAY OF CHAR);
 (* Emits a "# s" comment line. *)
 

+ 58 - 20
compiler/src/QbeGen.mod

@@ -386,6 +386,43 @@ PROCEDURE IsImm (s: ARRAY OF CHAR): BOOLEAN;
            OR (s[0] = "d")
   END IsImm;
 
+PROCEDURE ParseInt (s: ARRAY OF CHAR; VAR v: INTEGER): BOOLEAN;
+  VAR i, nd, d: CARDINAL;
+    neg: BOOLEAN;
+  BEGIN
+    v := 0; i := 0; neg := FALSE; nd := 0;
+    IF (i < HIGH(s)) AND (s[i] = "-") THEN neg := TRUE; INC(i) END;
+    IF (i >= HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN FALSE END;
+    WHILE (i < HIGH(s)) AND (s[i] # CHR(0)) DO
+      d := ORD(s[i]);
+      IF (d < ORD("0")) OR (d > ORD("9")) THEN RETURN FALSE END;
+      v := v * 10 + VAL(INTEGER, d - ORD("0"));
+      INC(i); INC(nd)
+    END;
+    IF nd = 0 THEN RETURN FALSE END;
+    IF neg THEN v := -v END;
+    RETURN TRUE
+  END ParseInt;
+
+PROCEDURE Fold2 (kind: INTEGER; a, b: ARRAY OF CHAR; VAR r: QVal): BOOLEAN;
+  VAR x, y, z: INTEGER;
+  BEGIN
+    IF NOT ParseInt(a, x) THEN RETURN FALSE END;
+    IF NOT ParseInt(b, y) THEN RETURN FALSE END;
+    IF kind = 0 THEN z := x + y
+    ELSIF kind = 1 THEN z := x - y
+    ELSIF kind = 2 THEN z := x * y
+    ELSIF kind = 3 THEN
+      IF y = 0 THEN RETURN FALSE END;
+      z := x DIV y
+    ELSE
+      IF y = 0 THEN RETURN FALSE END;
+      z := x MOD y
+    END;
+    IntStr(z, r);
+    RETURN TRUE
+  END Fold2;
+
 (* ---------------- module / data section ---------------- *)
 
 PROCEDURE OpenModule (name: ARRAY OF CHAR);
@@ -2141,13 +2178,14 @@ PROCEDURE FlushStrings;
 
 PROCEDURE ArrData (name: ARRAY OF CHAR; t: SymTab.TypeIndex);
 (* Recursive data emission; nested levels as $name_i. *)
-  VAR n, i: CARDINAL;
-    elem: SymTab.TypeIndex;
-    ecls: INTEGER;
-    cls: INTEGER;
-    bv: QVal;
-    sub: QVal;
-  BEGIN
+   VAR n, i: CARDINAL;
+     elem: SymTab.TypeIndex;
+     ecls: INTEGER;
+     cls: INTEGER;
+     esz: CARDINAL;
+     bv: QVal;
+     sub: QVal;
+   BEGIN
     IF NOT opened THEN RETURN END;
     n := SymTab.ArrayLen(t);
     elem := SymTab.ArrayElem(t);
@@ -2174,19 +2212,19 @@ PROCEDURE ArrData (name: ARRAY OF CHAR; t: SymTab.TypeIndex);
         INC(i)
       END
     ELSE
-      IF ecls = SymTab.ClChar THEN Cpy(bv, "b")
-      ELSIF ecls = SymTab.ClReal THEN Cpy(bv, "d")
-      ELSIF (ecls = SymTab.ClPtr) THEN Cpy(bv, "l")
-      ELSE Cpy(bv, "w")
-      END;
-      i := 0;
-      WHILE i < n DO
-        W(", "); W(bv); W(" 0");
-        INC(i)
-      END;
-      WL(" }")
-    END
-  END ArrData;
+       IF ecls = SymTab.ClChar THEN esz := 1
+       ELSIF (ecls = SymTab.ClReal)
+          OR (ecls = SymTab.ClPtr) THEN esz := 8
+       ELSE esz := 4
+       END;
+       IF n > 0 THEN
+         W(", z ");
+         IntStr(VAL(INTEGER, n * esz), bv);
+         W(bv)
+       END;
+       WL(" }")
+     END
+   END ArrData;
 
 PROCEDURE DeclArr (name: ARRAY OF CHAR; t: INTEGER);
   BEGIN

+ 16 - 0
compiler/src/SymTab.def

@@ -105,6 +105,12 @@ PROCEDURE SetSymType (name: ARRAY OF CHAR; t: TypeIndex);
 PROCEDURE SymKind (name: ARRAY OF CHAR): INTEGER;
 (* Kind of innermost visible entry, -1 if absent. *)
 
+PROCEDURE SetSymVal (name: ARRAY OF CHAR; v: ARRAY OF CHAR);
+(* Stores the literal value text of a CONST for constant folding. *)
+
+PROCEDURE GetSymVal (name: ARRAY OF CHAR; VAR v: ARRAY OF CHAR): BOOLEAN;
+(* v := stored literal value text of CONST name; FALSE if none. *)
+
 PROCEDURE Equal (a, b: ARRAY OF CHAR): BOOLEAN;
 
 PROCEDURE PushScope;
@@ -320,6 +326,16 @@ PROCEDURE NewClass (): TypeIndex;
 PROCEDURE SetTarget (t, base: TypeIndex);
 (* Sets an alias target (TYPE declaration completion). *)
 
+PROCEDURE BeginTypeBlock;
+PROCEDURE EndTypeBlock;
+(* Delimit a TYPE block so ForwardType is only active inside one. *)
+
+PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
+(* Inside a TYPE block: if name is not yet declared, enters it as an
+   unresolved forward alias and returns its type index (completed by
+   the later TypeItem via SetTarget). InvalidType when the name is
+   already known or forward references are not permitted. *)
+
 PROCEDURE IntType (): TypeIndex;
 PROCEDURE RealType (): TypeIndex;
 PROCEDURE CharType (): TypeIndex;

+ 66 - 17
compiler/src/SymTab.mod

@@ -2,11 +2,12 @@ IMPLEMENTATION MODULE SymTab;
 
 IMPORT FileIO;
 FROM Storage IMPORT ALLOCATE;
+FROM SYSTEM IMPORT TSIZE;
 
 CONST
-  MaxTypes  = 256;
-  MaxPend   = 64;
-  MaxMods   = 32;
+  MaxTypes  = 4096;
+  MaxPend   = 256;
+  MaxMods   = 64;
   ResDepth  = 64;
 
   (* descriptor forms *)
@@ -38,6 +39,7 @@ TYPE
                            qualified aliases) *)
     mod   : Name;       (* defining module (for QBE global names) *)
     uid   : CARDINAL;   (* KindProc: unique id for name mangling *)
+    val   : Name;       (* KindConst: literal value text, "" if none *)
   END;
   ScopeNode = RECORD
     parent : ScopePtr;  (* scope tree link *)
@@ -93,6 +95,7 @@ VAR
   modKind : ARRAY [0 .. MaxMods - 1] OF INTEGER;
   modImpl : ARRAY [0 .. MaxMods - 1] OF BOOLEAN;
   haveProg : BOOLEAN;
+  typeBlockDepth : CARDINAL;  (* > 0 inside a TYPE block *)
 
 (* ---------------- strings ---------------- *)
 
@@ -243,6 +246,7 @@ PROCEDURE RawEnter (name: ARRAY OF CHAR; kind: INTEGER): SymPtr;
     node^.scope := curScope;
     node^.left := NIL;
     node^.right := NIL;
+    node^.val[0] := CHR(0);
     IF NOT TreeInsert(curScope, node) THEN RETURN NIL END;
     RETURN node
   END RawEnter;
@@ -362,6 +366,24 @@ PROCEDURE SymKind (name: ARRAY OF CHAR): INTEGER;
     RETURN node^.kind
   END SymKind;
 
+PROCEDURE SetSymVal (name: ARRAY OF CHAR; v: ARRAY OF CHAR);
+  VAR node: SymPtr;
+  BEGIN
+    node := Find(name);
+    IF node # NIL THEN Assign(node^.val, v) END
+  END SetSymVal;
+
+PROCEDURE GetSymVal (name: ARRAY OF CHAR; VAR v: ARRAY OF CHAR): BOOLEAN;
+  VAR node: SymPtr;
+  BEGIN
+    node := Find(name);
+    IF (node = NIL) OR (node^.val[0] = CHR(0)) THEN
+      v[0] := CHR(0); RETURN FALSE
+    END;
+    Assign(v, node^.val);
+    RETURN TRUE
+  END GetSymVal;
+
 PROCEDURE PushScope;
   BEGIN
     curScope := NewScope(curScope, curScope^.level + 1)
@@ -481,6 +503,20 @@ PROCEDURE SetParent (t, p: TypeIndex);
     END
   END SetParent;
 
+PROCEDURE Resolve (t: TypeIndex): TypeIndex;
+  VAR n: CARDINAL;
+  BEGIN
+    n := 0;
+    WHILE (n < ResDepth) AND (t >= 0) AND (t < VAL(INTEGER, nTypes))
+ AND (tform[t] = FAlias) DO
+      t := tref[t]; INC(n)
+    END;
+    IF (t < 0) OR (t >= VAL(INTEGER, nTypes)) THEN
+      RETURN InvalidType
+    END;
+    RETURN t
+  END Resolve;
+
 PROCEDURE PushClassScope (t: TypeIndex);
   VAR r: TypeIndex;
   BEGIN
@@ -497,20 +533,6 @@ PROCEDURE SetTarget (t, base: TypeIndex);
     END
   END SetTarget;
 
-PROCEDURE Resolve (t: TypeIndex): TypeIndex;
-  VAR n: CARDINAL;
-  BEGIN
-    n := 0;
-    WHILE (n < ResDepth) AND (t >= 0) AND (t < VAL(INTEGER, nTypes))
- AND (tform[t] = FAlias) DO
-      t := tref[t]; INC(n)
-    END;
-    IF (t < 0) OR (t >= VAL(INTEGER, nTypes)) THEN
-      RETURN InvalidType
-    END;
-    RETURN t
-  END Resolve;
-
 PROCEDURE IntType (): TypeIndex;
   BEGIN RETURN dInt END IntType;
 
@@ -1209,6 +1231,7 @@ PROCEDURE EnterIn (s: ScopePtr; name: ARRAY OF CHAR;
     node^.link[0] := CHR(0);
     node^.fdep := 0;
     node^.uid := 0;
+    node^.val[0] := CHR(0);
     IF NOT TreeInsert(s, node) THEN RETURN FALSE END;
     RETURN TRUE
   END EnterIn;
@@ -1332,6 +1355,32 @@ PROCEDURE OpaqueBase (name: ARRAY OF CHAR): TypeIndex;
     RETURN InvalidType
   END OpaqueBase;
 
+PROCEDURE BeginTypeBlock;
+  BEGIN
+    INC(typeBlockDepth)
+  END BeginTypeBlock;
+
+PROCEDURE EndTypeBlock;
+  BEGIN
+    IF typeBlockDepth > 0 THEN DEC(typeBlockDepth) END
+  END EndTypeBlock;
+
+PROCEDURE ForwardType (name: ARRAY OF CHAR): TypeIndex;
+  VAR node: SymPtr;
+    t: TypeIndex;
+  BEGIN
+    IF typeBlockDepth = 0 THEN RETURN InvalidType END;
+    node := Find(name);
+    IF node # NIL THEN
+      IF node^.kind = KindType THEN RETURN node^.typ END;
+      RETURN InvalidType
+    END;
+    IF NOT Enter(name, KindType) THEN RETURN InvalidType END;
+    t := NewAlias();
+    SetSymType(name, t);
+    RETURN t
+  END ForwardType;
+
 PROCEDURE QualNode (mod, name: ARRAY OF CHAR): SymPtr;
   VAR i : INTEGER;
   BEGIN

+ 21 - 0
compiler/tests/t_constfold.mod

@@ -0,0 +1,21 @@
+MODULE TConstFold;
+// Named CONST folding in bounds, subranges and expressions. Exit 42.
+CONST
+  Base = 4;
+  N    = Base * 2 - 1;   // folds to 7
+  Top  = N - 2;          // folds to 5
+TYPE
+  Vec = ARRAY [0 .. N] OF INTEGER;
+  Sub = [Base .. Top];
+VAR ExitCode : INTEGER;
+VAR v : Vec;
+VAR s : Sub;
+VAR i, sum : INTEGER;
+BEGIN
+  sum := 0;
+  FOR i := 0 TO N DO v[i] := i; sum := sum + v[i] END;  // 28
+  sum := sum + v[Base] + v[Top];      // 4 + 5 = 9
+  s := Base + 1;                      // 5
+  sum := sum + s;
+  ExitCode := sum                     // 42
+END TConstFold.

+ 61 - 0
docs/summary_step8.2.md

@@ -0,0 +1,61 @@
+# V3 step 8.2 — self-compile gaps fixed for `SymTab` (104/104)
+
+Closing the measured step-8.1 gaps so V3 can compile its own sources.
+Suite 104/104 (new `t_constfold`), build warning-free.
+
+## What landed
+
+1. **Named-CONST constant folding.** `SymTab` now stores a CONST's
+   literal value text (`SetSymVal`/`GetSymVal`, new `val` field);
+   the grammar folds a CONST reference to its literal and folds
+   integer `+ - * DIV MOD` on two literals (`QbeGen.Fold2`). So
+   `ARRAY [0 .. MaxPend - 1]` and friends fold, in bounds,
+   subranges, sets, `FOR` limits and expressions. New test
+   `tests/t_constfold.mod` (42).
+
+2. **Forward type references in a `TYPE` block.** `POINTER TO X`
+   where `X` is declared later now resolves: `SymTab.ForwardType`
+   enters an unresolved alias inside a type block, completed by the
+   later `TypeItem` via the existing opaque `SetTarget` path
+   (`BeginTypeBlock`/`EndTypeBlock` gate it). `TypeIdent` uses it on
+   lookup failure instead of erroring 201.
+
+3. **Trailing `;` before a record's `END`.** `RecordType` accepts
+   `RECORD fieldlist [;] END` (grammar now `{ ";" [ RecField ] }`).
+
+4. **Qualified type names** (`FileIO.File`). `TypeIdent` accepts
+   `Module.Type` via `SymTab.QualType`; a bare module name as a type
+   still reports 221.
+
+5. **Capacity.** `MaxTypes` 256→4096, `MaxPend` 64→256,
+   `MaxMods` 32→64. The compiler's own sources exceed 256 type
+   descriptors (this alone caused ~465 spurious 230s).
+
+6. **Compact zero-filled array data.** `ArrData` emits QBE
+   `{ l n, z n*esz }` instead of one `w 0` per element. Emitting a
+   million-element `sessBuf`/`nestBufs` element-by-element was the
+   apparent "hang" once the type table stopped truncating them.
+
+7. **Source hygiene.** `Resolve` moved ahead of `PushClassScope` in
+   `SymTab.mod` (declaration-before-use; ISO-clean).
+
+## Result
+
+`SymTab.def` + `SymTab.mod` now compile **clean** under V3 (0 errors;
+verified with a stub program module). `QbeGen` is close:
+down from 205/465 errors to **85 `invalid call` + 38 undeclared + 1
+`invalid Statement`**, dominated by two remaining gaps:
+
+- **Within-module forward procedure calls** (10 distinct callees:
+  `Revive`, `LocAdd`, `ResClass`, `HApp`, `LocFindUp`, `UpAddrOf`,
+  `AllocLocal`, `FlushStrings`, `HeapSize`, `ArrData`). gm2 hoists
+  these; ISO wants declaration-before-use or `FORWARD`.
+- **Single-character string literals** (`"0"`, `"$"`, `"-"`) are
+  typed `CHAR`, so passing them to `ARRAY OF CHAR` formals
+  (`Cpy(d, "0")`) reports "invalid call". V3 needs context-sensitive
+  CHAR/string coercion.
+
+## Files
+
+`compiler/src/{M2.atg,SymTab.def,SymTab.mod,QbeGen.def,QbeGen.mod}`,
+`compiler/tests/t_constfold.mod`, `compiler/run_tests.sh`.

Некоторые файлы не были показаны из-за большого количества измененных файлов