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

docs: record address arithmetic and 4K literals

Eric Streit 15 часов назад
Родитель
Сommit
4f552072aa

+ 3 - 0
docs/INDEX.md

@@ -76,6 +76,9 @@ bisecting *why* a decision was made, not for reading end-to-end).
 
 ## Recent session summaries (newest first)
 
+- **`session-2026-10-28-address-4k.md`** — ISO `ADDADR`/`SUBADR`/
+  `DIFADR`, module/local absolute variables with 64-bit addresses, and
+  4 KiB-class string literals; tag `v3-address-arith-2026-10-28`.
 - **`session-2026-10-27-sqlite-binding.md`** — SQLite binding surface
   and tests, compiler FFI fixes (long literals, EXPORT diagnostic,
   64-bit CSIZE_T, pointer sentinels, module-level absolute variables);

+ 23 - 4
docs/STATUS.md

@@ -1,12 +1,13 @@
-# m2compiler-V3 — status (2026-10-27)
+# m2compiler-V3 — status (2026-10-28)
 
 This answers "where are we now?".
 
 ## Numbers
 
-- Suite **328/328** (`compiler/run_tests.sh`).
+- Suite **332/332** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
-  byte-identical (3,646,679 bytes).
+  byte-identical (3,671,466 bytes); per-object fixpoint **OK**
+  (11 objects).
 - Corpus **521 / 612** sources compile with 0 errors, **0 crashes**
   (`tools/v3-corpus/corpus.sh` over the gm2 testsuite; the harness now
   resolves V3's own shipped libraries); the rest are genuine language
@@ -14,7 +15,7 @@ This answers "where are we now?".
   imports of gm2-only modules.  Open-array corpus cases `unbounded`,
   `unbounded2`, `unbounded3`, `unbounded4` and `array9` now compile;
   `unbounded`/`unbounded2`/`unbounded3`/`unbounded4` also run.
-- Branch **`master`**, feature state **`v3-sqlite-binding-2026-10-27`**;
+- Branch **`master`**, feature state **`v3-address-arith-2026-10-28`**;
   tree clean.
 
 ## Architecture (current)
@@ -100,6 +101,24 @@ parameters, record fields and array elements use the sized storage;
 `ARRAY OF BYTE` is a real byte buffer.  `WORD`/`WORD32` remain 32-bit,
 and `COMPLEX*` is a declaration-compatible `REAL` placeholder.
 
+## 2026-10-28 — address arithmetic, absolute variables, 4 KiB literals
+
+- `SYSTEM.ADDADR`, `SYSTEM.SUBADR` and `SYSTEM.DIFADR` are implemented
+  as byte-addressed builtins (`t_addarith`).
+- Absolute variables now work at module and procedure scope; the
+  address is carried in Lower's own symbol table so local scopes still
+  resolve after parsing.  Addresses above `0x7FFFFFFF` materialize
+  through a new 64-bit `QbeGen.LongIntStr` path (`t_varaddress64`,
+  `t_localaddress`).
+- String literals now survive up to the 4 KiB class: `AST.MaxTxtLen`
+  and the scanner/lowered literal buffers are 4095, and QbeGen's string
+  pool dimensions match (`t_longstr4k`).  The practical limit is now
+  about 4 KiB rather than 255 bytes.
+- Pointer-difference results are narrowed to `INTEGER`; no general
+  `ADDRESS +/-` operator was added.
+- `VOLATILE` remains intentionally unimplemented; it needs a pragma and
+  backend support together.
+
 ## 2026-10-27 — SQLite binding + compiler FFI fixes
 
 - Added a broad SQLite binding under `bindings/sqlite3`: scalar/BLOB

+ 5 - 5
docs/c-binding-compiler-notes.md

@@ -9,8 +9,8 @@ SQLite binding.
 
 The previous compiler path truncated string literals during lowering
 because `LowerExprFull` and `LowerBuiltinExpr` copied the AST text into
-`SymTab.Name` (64 chars).  Fixed in `compiler/src/Lower.mod` by using a
-255-byte literal buffer.
+`SymTab.Name` (64 chars).  Fixed in `compiler/src/Lower.mod` by using an
+`AST.MaxTxtLen`-sized literal buffer.
 
 Covered by:
 
@@ -18,9 +18,9 @@ Covered by:
 compiler/tests/t_longstr.mod
 ```
 
-The practical limit is still set by the scanner/grammar buffers and
-`AST.MaxTxtLen` (255).  Keep very large SQL scripts below that limit or
-build them through a character buffer.
+The practical limit is now 4 KiB (`AST.MaxTxtLen = 4095`, matching
+scanner and QBE pools).  Very large SQL scripts should still be built
+through a character buffer.
 
 ## C scalar widths and pointer sentinels
 

+ 2 - 2
docs/comparison-PIM4-gm2-V3.md

@@ -86,8 +86,8 @@ change individual cells.
 | SYSTEM sized types (`WORD32`, `CARDINAL8`, `BITSET32`, …) | ❌ | ✅ | 🔄 `OCTET` (8-bit), `LONGCARD`, `LONGBITSET` | 🔄 `SHORTINT`/`LONGINT`/`SHORTCARD`/`LONGCARD` family | ✅ 8/16/32 names; `WORD`/`WORD32` 32-bit |
 | `BYTE` granularity | ✅ 8-bit | ✅ 8-bit | ✅ `OCTET` 8-bit | ✅ (PIM `BYTE`) | ✅ `BYTE`/`SBYTE`/`WORD8`/`CARDINAL8`/`INTEGER8` are 8-bit; `ARRAY OF BYTE` is a real buffer |
 | `SHORTREAL` (32-bit) | ❌ | ✅ | ❌ (REAL/LONGREAL) | ⚠️ dialect-dependent | ✅ true 4-byte `SHORTREAL`/`REAL32` |
-| `ADDRESS` arithmetic (`ADDADR`/`SUBADR`/`DIFADR`) | ❌ | ✅ | ❌ | ❌ | ❌ |
-| Absolute variables `VAR [addr]: T` | ❌ | ✅ (extension) | ✅ (pragma `ADDR`) | ❌ | ✅ module-level (`VAR x [0x...] : T`) |
+| `ADDRESS` arithmetic (`ADDADR`/`SUBADR`/`DIFADR`) | ❌ | ✅ | ❌ | ❌ | ✅ (`ADDADR`/`SUBADR`/`DIFADR`; no `MAKEADR`) |
+| Absolute variables `VAR [addr]: T` | ❌ | ✅ (extension) | ✅ (pragma `ADDR`) | ❌ | ✅ module/local (`VAR x [0x...] : T`; no `VOLATILE`) |
 | Set ordering (`<`, `<=`, `>`, `>=`) | ❌ (`=`/`#`/`IN` only) | ✅ (extension) | ❌ | ❌ | ❌ |
 
 ## Statements & procedures

+ 9 - 6
docs/features.md

@@ -148,9 +148,10 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 ## Declarations
 - ✅ `CONST` (integer/char/real literals, folded), `VAR`, `TYPE`,
   `PROCEDURE` headings + bodies, `FORWARD`.
-- ✅ Module-level absolute variables `VAR v [0x1000] : T`; no storage is
-  emitted and `ADR(v)` is the fixed address.  Local absolute variables
-  and >32-bit address materialization are not yet supported.
+- ✅ Absolute variables `VAR v [0x1000] : T` at module and procedure
+  scope; no storage is emitted and `ADR(v)` is the fixed address.
+  Addresses above `0x7FFFFFFF` materialize through the 64-bit path.
+  `VOLATILE` is not implemented yet.
 - ✅ `DEFINITION`/`IMPLEMENTATION` modules, `IMPORT`/`FROM … IMPORT`,
   `EXPORT` (parsed), nested local `MODULE` (including `EXPORT` lists,
   nested-nested modules, and bodies lowered as startup init functions).
@@ -185,7 +186,8 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
 - ✅ `NIL` comparisons/assignment; `ADDRESS`↔pointer equality (`ADR(p^) = p`).
 - ✅ `VAL`/`ADR`/`TSIZE`/`SIZE`/`HIGH`/`LEN` and `FLOAT`/`LFLOAT`
   work.  `VAL(ADDRESS, integer)` supports pointer sentinels such as
-  `VAL(ADDRESS, -1)`.  Array
+  `VAL(ADDRESS, -1)`.  `ADDADR`/`SUBADR`/`DIFADR` provide byte
+  address arithmetic.  Array
   `SIZE`/`TSIZE` use payload bytes (no descriptor header/term slot);
   `ADR` of a multi/open view or row is its payload base, while fixed
   1-D `ADR` keeps descriptor compatibility.  ✅ Result suffixes
@@ -227,8 +229,9 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   traps (unhandled rc 134), multi-word sets, `alloc8` heap
   (`malloc`/`free`), static-link frames, deterministic `%tN`/`@LN`,
   whole-image buffering.
-- ✅ `REAL` runtime const flush, string literals as descriptors (including
-  literals beyond the old 64-byte lowering limit).
+- ✅ `REAL` runtime const flush, string literals as descriptors (up to
+  the 4 KiB-class literal limit; the old 64-byte lowering cap and the
+  255-byte buffer cap are gone).
 - ⏸ `runtime/syslib/Trap` (traps are `$abort` for now), recursive
   `DISPOSE`.
 

+ 25 - 10
docs/future-work-2026-10-25.md

@@ -30,6 +30,17 @@ work completed since this note:
 - absolute variables (`VAR x [addr] : T`) are implemented at module level;
   see section 2.1 below.
 
+**Progress update 2026-10-28:** suite 332/332; one-shot fixpoint
+3,671,466 bytes; per-object fixpoint OK (11 objects).  Completed:
+
+- `SYSTEM.ADDADR`/`SUBADR`/`DIFADR` byte-address builtins;
+- absolute variables at module and procedure scope, including addresses
+  above `0x7FFFFFFF` via a 64-bit materialization path;
+- 4 KiB-class string literals (the practical 255-byte buffer cap is
+  gone; the scanner, AST and QBE pools all use 4095).
+- Remaining address work: `MAKEADR`, general `ADDRESS +/-` operators,
+  range-checked pointer differences and `VOLATILE`.
+
 ---
 
 ## 1. `TBITSIZE`
@@ -76,12 +87,14 @@ V3 currently supports:
 - `ADDRESS`/pointer assignment
 - `ADDRESS`/pointer equality
 - `VAL`/`CAST` for compatible scalar representations
+- byte-addressed `ADDADR`/`SUBADR` and `DIFADR` returning `INTEGER`
 
 V3 does **not** support:
 
-- `ADDADR`/`SUBADR`/`DIFADR`/`MAKEADR`
+- `MAKEADR`
 - `ADDRESS + offset`, `ADDRESS - offset`, `ADDRESS - ADDRESS`
 - C-style arithmetic on typed `POINTER TO` values
+- range-checked or 64-bit `DIFADR` results
 
 ### Why it is missing
 
@@ -104,7 +117,7 @@ escape hatch.
 
 ---
 
-### 2.1 Absolute variables (`VAR x [address] : T`) — implemented 2026-10-27
+### 2.1 Absolute variables (`VAR x [address] : T`) — implemented 2026-10-27/28
 
 GNU Modula-2 supports fixed-address variables:
 
@@ -119,17 +132,17 @@ memory-mapped I/O, firmware tables and embedded targets.
 
 Current V3 status:
 
-- syntax accepted for module-level variables: `VAR v [0x1000] : CARDINAL`;
+- syntax accepted for module-level and procedure-local variables:
+  `VAR v [0x1000] : CARDINAL`;
 - addresses are constant-folded; the V3 `0x...` form is required
   (the gm2 `1000H` suffix is not accepted);
-- `SymTab` records the absolute flag/address;
+- `SymTab` records the absolute flag/address; `Lower` copies it into its
+  own symbol table while the parser scope is still current, so local
+  absolute variables resolve after parsing;
 - `Lower` materializes the fixed address instead of `AddrOf`;
 - `QBE` emits no normal data object or initializer for the variable;
 - `ADR(v)` returns the fixed address;
-- absolute variables inside procedures are currently rejected with
-  `not supported yet`;
-- address materialization currently uses the 32-bit `IntStr` path;
-  addresses above `0x7FFFFFFF` need a long-address follow-up;
+- addresses above `0x7FFFFFFF` use `QbeGen.LongIntStr`;
 - no `VOLATILE` annotation exists yet, so memory-mapped I/O still needs
   careful review.
 
@@ -137,6 +150,8 @@ Covered by:
 
 ```text
 compiler/tests/t_varaddress.mod
+compiler/tests/t_varaddress64.mod
+compiler/tests/t_localaddress.mod
 ```
 
 The test checks `ADR(v) = VAL(ADDRESS, 0x1000)` without dereferencing an
@@ -2726,8 +2741,8 @@ classes are GObject classes.
 ## 17. Suggested order of future work
 
 1. `TBITSIZE` (small, standard-facing).
-2. `ADDADR`/`SUBADR`/`DIFADR` (small, ISO SYSTEM), then absolute
-   variables (`VAR x [const] : T`) as the next address-related gap.
+2. ~~`ADDADR`/`SUBADR`/`DIFADR` and absolute variables~~ — done
+   2026-10-28 (remaining: `MAKEADR`, `ADDRESS +/-`, `VOLATILE`).
 3. Set ordering (bounded gm2 compatibility; fixes `largeset5`).
 4. Complex types: choose ISO built-ins versus a nonstandard value-record
    library; remove the misleading `COMPLEX*` aliases.

+ 135 - 0
docs/session-2026-10-28-address-4k.md

@@ -0,0 +1,135 @@
+# Session save — 2026-10-28 (address arithmetic, absolute variables, 4 KiB literals)
+
+## Scope
+
+Three compiler slices were requested and completed:
+
+1. ISO `SYSTEM` address arithmetic
+2. absolute-variable completion
+3. 4 KiB-class string literals
+
+No compiler source file was split.
+
+## 1. Address arithmetic
+
+Implemented as `SYSTEM` builtins:
+
+```modula2
+ADDADR(addr: ADDRESS; offset: CARDINAL) : ADDRESS
+SUBADR(addr: ADDRESS; offset: CARDINAL) : ADDRESS
+DIFADR(addr1, addr2: ADDRESS) : INTEGER
+```
+
+- `SymTab` registers real procedure symbols (so imports/type checks work).
+- `Lower` intercepts them, so no C helper is linked.
+- `ADDADR`/`SUBADR` widen the offset to 64 bits and use QBE `l add/sub`.
+- `DIFADR` subtracts two 64-bit addresses and narrows to `INTEGER`.
+- No general `ADDRESS +/-` operator; `MAKEADR` is still future work.
+
+Regression:
+
+```text
+compiler/tests/t_addarith.mod
+```
+
+## 2. Absolute-variable completion
+
+Absolute variables now work at module **and procedure** scope:
+
+```modula2
+VAR
+  port [0x1000] : CARDINAL;
+
+PROCEDURE P;
+VAR scratch [0x2000] : CARDINAL;
+...
+```
+
+Implementation notes:
+
+- `Lower.NoteVar` copies the absolute flag/address into Lower's own
+  symbol table while the parser scope is still open.
+- `Lower.AbsOf` performs the same local-shadowing walk as `FindSym`, so
+  local absolute variables still resolve after parsing.
+- Absolute variables emit no data object and no stack slot.
+- `ADR(v)` is the fixed address.
+- `QbeGen.LongIntStr` materializes 64-bit addresses; `NormInt` handles
+  hex literals through the same path.
+- The parser uses `SymTab.ConstLong` for non-negative 64-bit address
+  clauses.  Negative addresses and values beyond `LONGINT` are rejected.
+
+Regressions:
+
+```text
+compiler/tests/t_varaddress.mod
+compiler/tests/t_varaddress64.mod
+compiler/tests/t_localaddress.mod
+```
+
+Limits:
+
+- `VOLATILE` is **not** implemented yet.  Memory-mapped I/O needs a
+  pragma plus backend support; do not treat current loads/stores as
+  volatile.
+- There is no runtime dereference test for arbitrary fixed addresses;
+  the tests check `ADR` only, because unmapped pages would fault.
+
+## 3. 4 KiB-class string literals
+
+Raised the practical literal limit from 255 bytes to the 4 KiB class:
+
+- `AST.MaxTxtLen = 4095` (exported from `AST.def`);
+- scanner/parser literal buffers in `M2.atg` are 4095;
+- `Lower`'s literal buffers use `AST.MaxTxtLen`;
+- `QbeGen.strTexts` inner dimension is 4095.
+
+The old 64-byte lowering truncation was already fixed in the previous
+session; this removes the remaining buffer cap.
+
+Regression:
+
+```text
+compiler/tests/t_longstr4k.mod
+```
+
+It uses a 4000-character literal and checks assignment, `LEN` and
+`HIGH`.  The nominal storage limit is 4095; leave headroom for quotes
+and terminators.
+
+Memory note: `QbeGen.strTexts` is now about 16 MiB of static storage.
+The self-hosted compiler still builds and reaches fixpoint.
+
+## Verification
+
+```text
+compiler suite:                 332/332
+one-shot fixpoint:              OK, 3,671,466 bytes
+separate-object fixpoint:       OK, 11 objects
+```
+
+Commands:
+
+```sh
+cd compiler
+./build.sh
+./run_tests.sh
+
+cd ..
+./bootstrap/fixpoint.sh
+TMP="$PWD/.scratch/fixpoint-separate-2026-10-28" \
+  ./bootstrap/fixpoint-separate.sh
+```
+
+## Remaining address/literal work
+
+- `MAKEADR`
+- general `ADDRESS +/-` operators and C-style pointer arithmetic
+- range-checked or 64-bit `DIFADR`
+- `VOLATILE` pragma and volatile backend loads/stores
+- runtime read/write tests for absolute variables using a mapped page
+
+## Tag
+
+```text
+v3-address-arith-2026-10-28
+```

+ 6 - 5
docs/triage-corpus-2026-10-24.md

@@ -63,9 +63,10 @@ No policy relaxation is recommended.
   but structurally identical array types).
 - **`SHORTREAL` vs `WORD` VAR**: `testparam3` (gm2 word-model
   extension; V3 has 64-bit reals).
-- **Absolute variables** `VAR [addr]: T`: now supported at module level;
-  the gm2 corpus uses the `1000H` suffix, so those cases still need a
-  literal-form update or scanner support.
+- **Absolute variables** `VAR [addr]: T`: now supported at module and
+  procedure scope, including 64-bit addresses; the gm2 corpus uses the
+  `1000H` suffix, so those cases still need a literal-form update or
+  scanner support.
 - **Module named `CHAR`**: `CHAR.mod` collides with the predefined
   type; gm2-specific test naming.
 
@@ -93,8 +94,8 @@ No policy relaxation is recommended.
 
 - **ADDRESS ↔ pointer comparison**: fixed for equality (`t_addreq`);
   `adraddress` remains a declaration-order corpus case.
-- **Absolute variables** and **inline ASM** remain unimplemented
-  extensions; neither is part of the current PIM/ISO target.
+- **Inline ASM** remains an unimplemented extension; absolute
+  variables are now implemented but `VOLATILE` is not.
 - **Wide sets** remain an explicit implementation limit; raising it is
   a separate design task.