Jelajahi Sumber

wip(symtab): GetAttr new-reuse, EnterIdent single-depth arms; version-skew finding

Original tails discard arm101/102 results (jump_stack) - reuse new,
drop sub+writebacks. Arm3 IF/ELSE becomes two single-depth AND-IFs.
M2.COM retail is Version 1.00 (writes SYM v3); shipped originals are
SYM v2 (Borland-internal toolchain) - explains laxer original spellings.
Big modules need the vm_linux instrumented-heap path; plan recorded.
Eric Streit 5 hari lalu
induk
melakukan
7a637624a7
2 mengubah file dengan 38 tambahan dan 16 penghapusan
  1. 22 0
      SESSION.md
  2. 16 16
      src/compiler/SYMTAB.MOD

+ 22 - 0
SESSION.md

@@ -109,3 +109,25 @@ Next: GENZ80 media hunt; stage-2 pty-driven MCD diff per module.
    then switch to vm_linux instrumentation (heap high-water log) for ground
    truth instead of blind bisection. Do NOT rename everything terse.
 5. Refresh SUMMARY.md + retag at each verify tag.
+
+## SYMTAB session 2026-10-07 (EnterIdent heap wall)
+- GetAttr arms 101/102: dropped writebacks + `sub` local, reuse `new`
+  (original tail-calls with `jump_stack`: result discarded, memoization is
+  the effect). EnterIdent arm3: nested IF/ELSE -> two single-depth AND-IFs.
+  Neither fixed the OOM (kept: both are the honest minimal shapes).
+- Heap-model verdict after full falsification battery (names, chars,
+  comments, slots, forwards, locals-count, prefix-size, ASCII, $[-retention,
+  nesting): budget is per-proc NODES; EnterIdent needs ~10 nodes over budget
+  with every retail-legal spelling exhausted (all 6 temp-assigns proven
+  mandatory: transfer-index/deref bans, VAR-formal bans).
+- VERSION SKEW (decisive context): retail M2.COM = "Version 1.00", writes
+  SYM v3; shipped originals carry SYM v2 (Borland-internal/older toolchain).
+  Only v1.00 retail on hand (other M2.COM is a different vendor product).
+  Explains laxer-original spellings (direct ADR pushes, `ADR->VAR`,
+  Buf->Identifier) that retail rejects or OOM-masks.
+- Conclusion: big modules (SYMTAB/EXPRESS/STATEMEN/PASS1) cannot compile
+  under v1.00-retail heap with faithful sources. Next: vm_linux path —
+  build the portable M-code VM, run retail m2.ovr under instrumented
+  mcode.c (heap high-water log) for ground truth; heap size does not affect
+  emitted code, so verification stays sound. Fallback: partial (prefix)
+  verification, honestly labeled.

+ 16 - 16
src/compiler/SYMTAB.MOD

@@ -297,7 +297,7 @@ END PutAttr;
 PROCEDURE GetAttr(VAR node: T1; idx: CARDINAL);
 (* proc22 @0281 — load a scope entry back into a heap node (inverse of PutAttr).
    Entry word1 = static node pointer or memoized node (returned as-is). *)
-VAR e: Words; new: T1; nw: Words; sub: T1;
+VAR e: Words; new: T1; nw: Words;
 BEGIN
   node := NIL; (*{/*0283*/}*)
   IF idx = 0 THEN RETURN END; (*{/*0286*/}*)
@@ -318,10 +318,10 @@ BEGIN
   | 99, 100:
     UnlinkScope(ADR(nw^[5]), e^[5]); (*{/*02c4*/}*)
   | 101:
-    sub := T1(nw^[5]); GetAttr(sub, e^[5]); nw^[5] := CARDINAL(sub); (* in-place reload; VAR needs a real variable (v1.00 OOMs on CARDINAL actual) *)
+    new := T1(nw^[5]); GetAttr(new, e^[5]); (* tail: result discarded, memoization is the effect (original jump_stack) *)
   | 102:
-    sub := T1(nw^[5]); GetAttr(sub, e^[5]); nw^[5] := CARDINAL(sub);
-    sub := T1(nw^[6]); GetAttr(sub, e^[6]); nw^[6] := CARDINAL(sub);
+    new := T1(nw^[5]); GetAttr(new, e^[5]);
+    new := T1(nw^[6]); GetAttr(new, e^[6]);
     nw^[7] := e^[7];
   ELSE (* 92..98: nothing beyond words 1..4 *)
   END;
@@ -414,21 +414,21 @@ BEGIN
     nw^[5] := CARDINAL(nm2);
     RETURN;
   END;
-  IF sentry^[4] >= 5 THEN (*{/*037b*/}*)
-    IF rp = NIL THEN (*{/*0381*/}*)
-      Scanner.GetStack(mark); (*{/*0385*/}*)
-      nw^[6] := 0; (*{/*0389*/}*)
-      nm2 := ADDRESS(nw) + 12; (* address of nw^[6] without ADR(indexed) (expression-heap spike on v1.00) *)
-      UnlinkScope(nm2, sentry^[6]); (*{/*038c*/}*)
-      IF (sentry^[4] = 5) AND (stgFlag <> 0) THEN (*{/*0393*/} global25 *)
-        Compiler.allocHook(ADDRESS(new), mark); (* hook ADDRESS set by PASS1 (store dword27); was allocateSignature (a type symbol) — wrong target {/*039d*/} *)
-      END;
-    ELSE
-      vw := Words(rp);
-      nw^[6] := vw^[6]; (*{/*03a5*/}*)
+  IF (sentry^[4] >= 5) AND (rp = NIL) THEN (*{/*037b*/}{/*0381*/} single-depth: nested IF OOMs on v1.00 *)
+    Scanner.GetStack(mark); (*{/*0385*/}*)
+    nw^[6] := 0; (*{/*0389*/}*)
+    nm2 := ADDRESS(nw) + 12; (* address of nw^[6] without ADR(indexed) (expression-heap spike on v1.00) *)
+    UnlinkScope(nm2, sentry^[6]); (*{/*038c*/}*)
+    IF (sentry^[4] = 5) AND (stgFlag <> 0) THEN (*{/*0393*/} global25 *)
+      Compiler.allocHook(ADDRESS(new), mark); (* hook ADDRESS set by PASS1 (store dword27); was allocateSignature (a type symbol) — wrong target {/*039d*/} *)
     END;
     RETURN;
   END;
+  IF (sentry^[4] >= 5) AND (rp # NIL) THEN
+    vw := Words(rp);
+    nw^[6] := vw^[6]; (*{/*03a5*/}*)
+    RETURN;
+  END;
   IF (sentry^[4] = 1) AND (CARDINAL(new^.link2) = CARDINAL(Compiler.LongrealType)) THEN
     (* COMPILER.word11; 8-byte real value {/*03ab*/} *)
     nm2 := ADR(nw^[5]); (* temp: VAR formal rejects ADR() results (proven "Variable expected"); original pushes computed address directly *)