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

language: array payload SIZE and view ADR (M4)

Eric Streit 1 день назад
Родитель
Сommit
df72d0303a

+ 3 - 0
compiler/run_tests.sh

@@ -70,6 +70,9 @@ expect_run t_multidim.mod 42
 expect_run t_multidim_local.mod 42
 expect_run t_multidim_local.mod 42
 expect_run t_open2d.mod 42
 expect_run t_open2d.mod 42
 expect_run t_open2d_high.mod 42
 expect_run t_open2d_high.mod 42
+expect_run t_open2d_size.mod 42
+expect_run t_open1d_size_adr.mod 42
+expect_run t_open2d_adr.mod 42
 expect_run t_open2d_value.mod 42
 expect_run t_open2d_value.mod 42
 expect_run t_open3d.mod 42
 expect_run t_open3d.mod 42
 expect_run t_open3d_row.mod 42
 expect_run t_open3d_row.mod 42

+ 21 - 2
compiler/src/Lower.mod

@@ -1542,7 +1542,21 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
       LowerExpr(AST.Child(n, 1), et, q); t := SymTab.IntType()
       LowerExpr(AST.Child(n, 1), et, q); t := SymTab.IntType()
     ELSIF SymTab.Equal(nm, "SIZE") OR SymTab.Equal(nm, "TSIZE") THEN
     ELSIF SymTab.Equal(nm, "SIZE") OR SymTab.Equal(nm, "TSIZE") THEN
       LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
       LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
-      QbeGen.IntStr(VAL(INTEGER, SymTab.ObjectSize(dt)), q);
+      IF (dt # SymTab.InvalidType)
+         AND (SymTab.ClassOf(dt) = SymTab.ClArray) THEN
+        IF lastView THEN
+          QbeGen.ViewSize(qd, q)
+        ELSIF SymTab.IsOpenArray(dt) THEN
+          QbeGen.OpenSize(qd, dt, q)
+        ELSIF SymTab.IsMultiArray(dt) THEN
+          QbeGen.IntStr(VAL(INTEGER, SymTab.ArrayPayloadSize(dt)), q)
+        ELSE
+          QbeGen.IntStr(VAL(INTEGER,
+            SymTab.ArrayLen(dt) * SymTab.ArrayLeafBytes(dt)), q)
+        END
+      ELSE
+        QbeGen.IntStr(VAL(INTEGER, SymTab.ObjectSize(dt)), q)
+      END;
       t := SymTab.IntType()
       t := SymTab.IntType()
     ELSIF SymTab.Equal(nm, "MIN") OR SymTab.Equal(nm, "MAX") THEN
     ELSIF SymTab.Equal(nm, "MIN") OR SymTab.Equal(nm, "MAX") THEN
       isMax := SymTab.Equal(nm, "MAX");
       isMax := SymTab.Equal(nm, "MAX");
@@ -1662,8 +1676,13 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
     ELSIF SymTab.Equal(nm, "ADR") THEN
     ELSIF SymTab.Equal(nm, "ADR") THEN
       LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
       LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
       IF k = SymTab.KindProc THEN
       IF k = SymTab.KindProc THEN
-        (* ADR of a procedure is its code address *)
         LowerProcAddr(qn, q)
         LowerProcAddr(qn, q)
+      ELSIF lastView THEN
+        QbeGen.ViewBase(qd, q)
+      ELSIF (dt # SymTab.InvalidType)
+         AND (SymTab.ClassOf(dt) = SymTab.ClArray)
+         AND SymTab.IsOpenArray(dt) AND (NOT sfx) THEN
+        QbeGen.DescData(qd, q)
       ELSIF sfx THEN QbeGen.CopyOp(qd, q)
       ELSIF sfx THEN QbeGen.CopyOp(qd, q)
       ELSIF (k = SymTab.KindVar) OR (k = SymTab.KindParam) THEN
       ELSIF (k = SymTab.KindVar) OR (k = SymTab.KindParam) THEN
         QbeGen.AddrOf(qn, q)
         QbeGen.AddrOf(qn, q)

+ 12 - 0
compiler/src/QbeGen.def

@@ -386,6 +386,18 @@ PROCEDURE ViewCopyBack (desc, base, nbytes: ARRAY OF CHAR);
 PROCEDURE ViewFree (desc: ARRAY OF CHAR);
 PROCEDURE ViewFree (desc: ARRAY OF CHAR);
 (* Release a descriptor allocated by ViewToOpen1D. *)
 (* Release a descriptor allocated by ViewToOpen1D. *)
 
 
+PROCEDURE ViewBase (view: ARRAY OF CHAR; VAR q: QVal);
+(* q := view.base (offset 24). *)
+
+PROCEDURE ViewSize (view: ARRAY OF CHAR; VAR q: QVal);
+(* q := view.count * view.elemBytes. *)
+
+PROCEDURE DescData (desc: ARRAY OF CHAR; VAR q: QVal);
+(* q := first payload byte of a 1-D legacy descriptor (desc + 8). *)
+
+PROCEDURE OpenSize (desc: ARRAY OF CHAR; ft: INTEGER; VAR q: QVal);
+(* q := desc.count * element size of the open 1-D formal ft. *)
+
 PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
 PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
 PROCEDURE ElemStore (addr, v: ARRAY OF CHAR; t: INTEGER);
 PROCEDURE ElemStore (addr, v: ARRAY OF CHAR; t: INTEGER);
 (* Element access by element-type index (CHAR→loadub … nested→loadl). *)
 (* Element access by element-type index (CHAR→loadub … nested→loadl). *)

+ 50 - 2
compiler/src/QbeGen.mod

@@ -3362,8 +3362,8 @@ PROCEDURE ViewIndex (view, idx, lo: ARRAY OF CHAR;
 
 
 PROCEDURE ViewToOpen1D (view: ARRAY OF CHAR; ft: INTEGER;
 PROCEDURE ViewToOpen1D (view: ARRAY OF CHAR; ft: INTEGER;
                         VAR q, base, nbytes: QVal);
                         VAR q, base, nbytes: QVal);
-  VAR count, elem, total, n, desc, p: QVal;
-    cls: INTEGER;
+  VAR count, elem, total, n, desc, p, p2, eoff: QVal;
+    cls, extra: INTEGER;
   BEGIN
   BEGIN
     ViewField(view, 0, count);
     ViewField(view, 0, count);
     ViewField(view, 8, elem);
     ViewField(view, 8, elem);
@@ -3373,8 +3373,16 @@ PROCEDURE ViewToOpen1D (view: ARRAY OF CHAR; ft: INTEGER;
     IF (cls = SymTab.ClByte) OR (cls = SymTab.ClSByte) THEN
     IF (cls = SymTab.ClByte) OR (cls = SymTab.ClSByte) THEN
       CopyOp(total, count)
       CopyOp(total, count)
     END;
     END;
+    extra := 0;
+    IF cls = SymTab.ClChar THEN extra := 1
+    ELSIF cls = SymTab.ClUChar THEN extra := 4
+    END;
     NewTemp(n);
     NewTemp(n);
     Op3L("add", n, total, "8");
     Op3L("add", n, total, "8");
+    IF extra > 0 THEN
+      IntStr(VAL(INTEGER, extra), eoff);
+      NewTemp(p); Op3L("add", p, n, eoff); CopyOp(p, n)
+    END;
     NewTemp(desc);
     NewTemp(desc);
     W("  "); W(desc); W(" =l call $malloc(l "); W(n); WL(")");
     W("  "); W(desc); W(" =l call $malloc(l "); W(n); WL(")");
     Revive;
     Revive;
@@ -3386,6 +3394,13 @@ PROCEDURE ViewToOpen1D (view: ARRAY OF CHAR; ft: INTEGER;
     Revive;
     Revive;
     W("  "); W(n); W(" =l call $memcpy(l ");
     W("  "); W(n); W(" =l call $memcpy(l ");
     W(p); W(", l "); W(base); W(", l "); W(total); WL(")");
     W(p); W(", l "); W(base); W(", l "); W(total); WL(")");
+    IF extra > 0 THEN
+      NewTemp(p); Op3L("add", p, desc, "8");
+      NewTemp(p2); Op3L("add", p2, p, total);
+      Revive;
+      IF extra = 1 THEN W("  storeb 0, "); WL(p2)
+      ELSE W("  storew 0, "); WL(p2) END
+    END;
     CopyOp(desc, q)
     CopyOp(desc, q)
   END ViewToOpen1D;
   END ViewToOpen1D;
 
 
@@ -3405,6 +3420,39 @@ PROCEDURE ViewFree (desc: ARRAY OF CHAR);
     W("  call $free(l "); W(desc); WL(")")
     W("  call $free(l "); W(desc); WL(")")
   END ViewFree;
   END ViewFree;
 
 
+PROCEDURE ViewBase (view: ARRAY OF CHAR; VAR q: QVal);
+  BEGIN
+    ViewField(view, 24, q)
+  END ViewBase;
+
+PROCEDURE ViewSize (view: ARRAY OF CHAR; VAR q: QVal);
+  VAR cnt, esz, r: QVal;
+  BEGIN
+    ViewField(view, 0, cnt);
+    ViewField(view, 8, esz);
+    NewTemp(r);
+    Op3L("mul", r, cnt, esz);
+    CopyOp(r, q)
+  END ViewSize;
+
+PROCEDURE DescData (desc: ARRAY OF CHAR; VAR q: QVal);
+  BEGIN
+    NewTemp(q);
+    Op3L("add", q, desc, "8")
+  END DescData;
+
+PROCEDURE OpenSize (desc: ARRAY OF CHAR; ft: INTEGER; VAR q: QVal);
+  VAR cnt, esz, r: QVal;
+  BEGIN
+    NewTemp(q);
+    Revive;
+    W("  "); W(q); W(" =l loadl "); WL(desc);
+    IntStr(VAL(INTEGER, SymTab.ArrayLeafBytes(ft)), esz);
+    NewTemp(r);
+    Op3L("mul", r, q, esz);
+    CopyOp(r, q)
+  END OpenSize;
+
 PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
 PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
 (* Loads one element of (element-)type t. Nested/pointer elements
 (* Loads one element of (element-)type t. Nested/pointer elements
    are addresses (loadl). *)
    are addresses (loadl). *)

+ 16 - 0
compiler/tests/t_open1d_size_adr.mod

@@ -0,0 +1,16 @@
+MODULE TOpen1DSizeAdr;
+(* M4: 1-D payload SIZE and open-array ADR base. *)
+FROM SYSTEM IMPORT ADDRESS, ADR;
+TYPE Vec = ARRAY [0..3] OF INTEGER;
+VAR ExitCode : INTEGER;
+    v : Vec;
+
+PROCEDURE Check (a : ARRAY OF INTEGER) : BOOLEAN;
+BEGIN
+  RETURN (SIZE(a) = 16) AND (TSIZE(a) = 16)
+         AND (ADR(a) = ADR(a[0]))
+END Check;
+
+BEGIN
+  IF Check(v) AND (SIZE(v) = 16) THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TOpen1DSizeAdr.

+ 19 - 0
compiler/tests/t_open2d_adr.mod

@@ -0,0 +1,19 @@
+MODULE TOpen2DAdr;
+(* M4: ADR of a multi-dimensional view/row is its first payload byte. *)
+FROM SYSTEM IMPORT ADDRESS, ADR;
+TYPE Mat = ARRAY [0..1] OF ARRAY [0..2] OF CHAR;
+VAR ExitCode : INTEGER;
+    m : Mat;
+
+PROCEDURE Check (VAR a : ARRAY OF ARRAY OF CHAR) : BOOLEAN;
+VAR p, q, r : ADDRESS;
+BEGIN
+  p := ADR(a);
+  q := ADR(a[0]);
+  r := ADR(a[0][0]);
+  RETURN (p = q) AND (q = r)
+END Check;
+
+BEGIN
+  IF Check(m) THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TOpen2DAdr.

+ 16 - 0
compiler/tests/t_open2d_size.mod

@@ -0,0 +1,16 @@
+MODULE TOpen2DSize;
+(* M4: payload SIZE/TSIZE on open multi-dimensional views. *)
+TYPE Cube = ARRAY [0..4] OF ARRAY [0..5] OF ARRAY [0..1] OF CHAR;
+VAR ExitCode : INTEGER;
+    c : Cube;
+
+PROCEDURE Check (a : ARRAY OF ARRAY OF ARRAY OF CHAR) : BOOLEAN;
+BEGIN
+  RETURN (SIZE(a) = 60) AND (SIZE(a[0]) = 12)
+         AND (SIZE(a[0][0]) = 2) AND (SIZE(a[0, 0]) = 2)
+         AND (TSIZE(a[0]) = 12)
+END Check;
+
+BEGIN
+  IF Check(c) THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TOpen2DSize.

+ 24 - 7
docs/design-open-arrays.md

@@ -1,9 +1,10 @@
 # Design — multi-dimensional open arrays
 # Design — multi-dimensional open arrays
 
 
-**Status: design + M1 helpers + M2 fixed storage + M3 open views
-(2026-10-23).**  Rank-2/3 fixed and open arrays now use the view ABI;
-`SIZE`/`ADR` and the remaining 1-D descriptor migration are M4.  Current
-gates: suite **292/292**, fixpoint OK **3,453,395 bytes**, corpus
+**Status: design + M1 helpers + M2 fixed storage + M3 open views + M4
+builtins (2026-10-23).**  Rank-2/3 fixed and open arrays use the view ABI;
+array `SIZE`/`TSIZE` and view `ADR` are implemented.  The 1-D open
+descriptor still uses its legacy `{count,data}` layout at ABI boundaries.
+Current gates: suite **295/295**, fixpoint OK **3,480,366 bytes**, corpus
 **518/612 compile-OK**, 0 crashes.
 **518/612 compile-OK**, 0 crashes.
 
 
 Interactive decisions already taken:
 Interactive decisions already taken:
@@ -398,14 +399,30 @@ Gates after each step: suite, fixpoint, corpus.
      allocates a descriptor and, for `VAR`, copies the payload back;
      allocates a descriptor and, for `VAR`, copies the payload back;
      this preserves write-through while the compiler still has the old
      this preserves write-through while the compiler still has the old
      1-D ABI and C shims.  It is freed after the call.  Migrating that
      1-D ABI and C shims.  It is freed after the call.  Migrating that
-     legacy 1-D path to a pointer-based view is part of M4.
+     legacy 1-D path to a pointer-based view remains a follow-up.
    - Tests: `t_open2d`, `t_open2d_value`, `t_open2d_high`,
    - Tests: `t_open2d`, `t_open2d_value`, `t_open2d_high`,
      `t_open2d_nested`, `t_open3d`, `t_open3d_row`,
      `t_open2d_nested`, `t_open3d`, `t_open3d_row`,
      `t_open_row_to_1d`, `t_open_flatten_byte`.  Corpus gains
      `t_open_row_to_1d`, `t_open_flatten_byte`.  Corpus gains
      `unbounded`, `unbounded2`, `unbounded3`, `unbounded4` and
      `unbounded`, `unbounded2`, `unbounded3`, `unbounded4` and
      `array9` (compile gate).
      `array9` (compile gate).
-4. **M4 — builtins/legacy 1-D views** (`SIZE`, `TSIZE`, `ADR`, open 1-D
-   descriptor migration, remaining `CopyArray`/assignment cases).
+4. **M4 — array builtins** — **done (2026-10-23)**
+   - `SIZE`/`TSIZE` now use payload bytes for arrays: fixed 1-D
+     `count*leaf`, open 1-D `count*element`, and runtime
+     `count*elemBytes` for every view level.  The old descriptor header
+     and 1-D CHAR terminator are excluded.
+   - `ADR` returns the first payload byte for multi views, row subviews
+     and 1-D open descriptors; fixed 1-D arrays retain their descriptor
+     address for `POINTER TO ARRAY` compatibility.
+   - The M3 row/`ARRAY OF BYTE` compatibility adapter now appends the
+     legacy CHAR/UCHAR terminator when materialising an external 1-D
+     descriptor, and is freed after `VAR` copy-back.
+   - Tests: `t_open2d_size`, `t_open2d_adr`, `t_open1d_size_adr`,
+     plus the M3 open-array tests.  `unbounded3` runs successfully with
+     `SIZE` 60/12/2/2.
+   - **Still deferred:** migrating the legacy 1-D descriptor itself to a
+     pointer-based view (row `ADR` identity through a 1-D internal call).
+     The current adapter preserves writes but is copy-based.  This does
+     not block the corpus cases that reach the compiler today.
 5. **M5 — records/constructors/corpus**
 5. **M5 — records/constructors/corpus**
    (multi array fields, records arrays, constructors; run the 8 corpus
    (multi array fields, records arrays, constructors; run the 8 corpus
    cases).
    cases).

+ 13 - 4
docs/handoff-2026-10-23-next.md

@@ -1,7 +1,7 @@
 # Next session — plan (from 2026-10-23)
 # Next session — plan (from 2026-10-23)
 
 
-**Branch:** `master`.  **Feature state:** `v3-exceptions` + `v3-ptrchain` + `v3-function-exception` + `v3-finally` + open-array M1/M2/M3.
-**Gates:** suite **292/292**, fixpoint **OK (3,453,395 bytes)**,
+**Branch:** `master`.  **Feature state:** `v3-exceptions` + `v3-ptrchain` + `v3-function-exception` + `v3-finally` + open-array M1/M2/M3/M4.
+**Gates:** suite **295/295**, fixpoint **OK (3,480,366 bytes)**,
 corpus **518/612, 0 crashes**.
 corpus **518/612, 0 crashes**.
 
 
 Read first: `docs/STATUS.md`, `docs/features.md`,
 Read first: `docs/STATUS.md`, `docs/features.md`,
@@ -106,8 +106,17 @@ value open multi calls share the view (no payload copy).  Row → 1-D and
 `ARRAY OF BYTE` conversions still use the legacy descriptor adapter, now
 `ARRAY OF BYTE` conversions still use the legacy descriptor adapter, now
 with copy-back for `VAR` and `free` after the call; migrating the 1-D
 with copy-back for `VAR` and `free` after the call; migrating the 1-D
 descriptor itself is M4.  Tests: `t_open2d*`, `t_open3d*`,
 descriptor itself is M4.  Tests: `t_open2d*`, `t_open3d*`,
-`t_open_row_to_1d`, `t_open_flatten_byte`; corpus **518/612**.  Next is
-M4: `SIZE`/`TSIZE`/`ADR`, 1-D view migration, remaining copy cases.
+`t_open_row_to_1d`, `t_open_flatten_byte`; corpus **518/612**.
+
+**M4 — array builtins — done (2026-10-23).**  `SIZE`/`TSIZE` use payload
+bytes for fixed/open/multi arrays; `ADR` returns the view base for multi
+views/rows and the payload base for 1-D open descriptors, while fixed
+1-D arrays keep their descriptor address.  External 1-D compatibility
+descriptors now include the CHAR/UCHAR terminator.  Tests:
+`t_open2d_size`, `t_open2d_adr`, `t_open1d_size_adr`; `unbounded3`
+runs with 60/12/2/2.  Suite **295/295**, fixpoint **3,480,366 bytes**,
+corpus **518/612**.  Remaining: pointer-based 1-D view migration (row
+`ADR` identity through an internal 1-D call) and M5 records/constructors.
 
 
 ## 4. Runtime range checks — done, follow-ups
 ## 4. Runtime range checks — done, follow-ups