|
@@ -0,0 +1,314 @@
|
|
|
|
|
+# Future-work analysis — 2026-10-25
|
|
|
|
|
+
|
|
|
|
|
+This note records the design discussions from 2026-10-25 so the
|
|
|
|
|
+questions do not have to be re-derived later. It is not a commitment
|
|
|
|
|
+to implement everything.
|
|
|
|
|
+
|
|
|
|
|
+**State at the time of writing:** suite 310/310, one-shot fixpoint OK
|
|
|
|
|
+(3,845,439 bytes), per-object fixpoint OK (11 objects), corpus
|
|
|
|
|
+521/612 compile-OK, 0 crashes, 64 `LANG` and 27 `LIB`.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 1. `TBITSIZE`
|
|
|
|
|
+
|
|
|
|
|
+`TBITSIZE` is a GNU Modula-2 `SYSTEM` procedure function:
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+PROCEDURE TBITSIZE (<type>) : CARDINAL;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+It returns the **minimum number of bits needed to represent a type**,
|
|
|
|
|
+not its normal storage size in bytes. It is mainly useful for packed
|
|
|
|
|
+records/bitfields. A `BOOLEAN` may be one bit logically but occupy a
|
|
|
|
|
+word in an unpacked record.
|
|
|
|
|
+
|
|
|
|
|
+- GNU Modula-2 supports it (and uses it in `M2WIDESET`).
|
|
|
|
|
+- V3 does not support it.
|
|
|
|
|
+- V3's existing `SIZE`/`TSIZE` report storage size in bytes.
|
|
|
|
|
+- Adding it would need:
|
|
|
|
|
+ - a type/variable argument form in the grammar;
|
|
|
|
|
+ - a descriptor query for minimum bit width (integer/real/char/enum/
|
|
|
|
|
+ subrange/record/packed-record cases);
|
|
|
|
|
+ - constant folding so `TBITSIZE(BYTE) = 8` works in `CONST`;
|
|
|
|
|
+ - interaction with packed record layout.
|
|
|
|
|
+
|
|
|
|
|
+It is a small-to-medium language gap, not a runtime feature.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 2. `ADDRESS` arithmetic
|
|
|
|
|
+
|
|
|
|
|
+ISO `SYSTEM` defines:
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+PROCEDURE ADDADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
|
|
|
|
|
+PROCEDURE SUBADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
|
|
|
|
|
+PROCEDURE DIFADR (addr1, addr2: ADDRESS): INTEGER;
|
|
|
|
|
+PROCEDURE MAKEADR (high: <type>; ...): ADDRESS;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+V3 currently supports:
|
|
|
|
|
+
|
|
|
|
|
+- `ADR`
|
|
|
|
|
+- `ADDRESS`/pointer assignment
|
|
|
|
|
+- `ADDRESS`/pointer equality
|
|
|
|
|
+- `VAL`/`CAST` for compatible scalar representations
|
|
|
|
|
+
|
|
|
|
|
+V3 does **not** support:
|
|
|
|
|
+
|
|
|
|
|
+- `ADDADR`/`SUBADR`/`DIFADR`/`MAKEADR`
|
|
|
|
|
+- `ADDRESS + offset`, `ADDRESS - offset`, `ADDRESS - ADDRESS`
|
|
|
|
|
+- C-style arithmetic on typed `POINTER TO` values
|
|
|
|
|
+
|
|
|
|
|
+### Why it is missing
|
|
|
|
|
+
|
|
|
|
|
+`ADDRESS` is an opaque pointer-like value in V3's type system. Raw
|
|
|
|
|
+pointer arithmetic is not part of the PIM/ISO base language and would
|
|
|
|
|
+weaken V3's separation between typed pointers and addresses.
|
|
|
|
|
+
|
|
|
|
|
+### Recommended implementation
|
|
|
|
|
+
|
|
|
|
|
+Add the ISO SYSTEM functions first, not general pointer arithmetic:
|
|
|
|
|
+
|
|
|
|
|
+- byte-addressed `ADDADR`/`SUBADR`;
|
|
|
|
|
+- `DIFADR` returning the byte difference (range-checked for `INTEGER`);
|
|
|
|
|
+- optional `MAKEADR`;
|
|
|
|
|
+- tests for offsets, null addresses, overflow and pointer/address
|
|
|
|
|
+ equality.
|
|
|
|
|
+
|
|
|
|
|
+For external C/assembly interaction, a C helper remains the pragmatic
|
|
|
|
|
+escape hatch.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 3. Set ordering
|
|
|
|
|
+
|
|
|
|
|
+PIM/ISO set operations are:
|
|
|
|
|
+
|
|
|
|
|
+- equality `=`, inequality `#`, membership `IN`;
|
|
|
|
|
+- union `+`, difference `-`, intersection `*`, symmetric difference `/`.
|
|
|
|
|
+
|
|
|
|
|
+There is **no standard `<`, `<=`, `>`, `>=` ordering for sets**. GNU
|
|
|
|
|
+Modula-2 adds inclusion ordering:
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+a <= b (* a is a subset of b *)
|
|
|
|
|
+a < b (* a is a proper subset of b *)
|
|
|
|
|
+a >= b (* a is a superset of b *)
|
|
|
|
|
+a > b (* a is a proper superset of b *)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+V3 currently rejects these; corpus case `largeset5` remains a `LANG`
|
|
|
|
|
+failure.
|
|
|
|
|
+
|
|
|
|
|
+### If implemented as a gm2-compatibility extension
|
|
|
|
|
+
|
|
|
|
|
+Fixed/inline sets:
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+a <= b iff (a AND NOT b) == 0 for every word
|
|
|
|
|
+a < b iff a <= b AND a # b
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Dynamic sorted-range sets:
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+a <= b iff every range of a is covered by a range of b
|
|
|
|
|
+a < b iff a <= b AND a # b
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Compiler work:
|
|
|
|
|
+
|
|
|
|
|
+- `SymTab.RelCheck`/`OrdCheck`: accept set ordering;
|
|
|
|
|
+- `Lower`: branch fixed/dynamic set ordering;
|
|
|
|
|
+- `QbeGen`: subset/superset comparisons;
|
|
|
|
|
+- tests: empty sets, equal sets, subset, proper subset, superset,
|
|
|
|
|
+ disjoint sets, mixed widths;
|
|
|
|
|
+- `largeset5` would likely move from `LANG` to compile-OK.
|
|
|
|
|
+
|
|
|
|
|
+This is a safe gm2 compatibility extension, not standard PIM/ISO.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 4. PIM versus ISO coroutines
|
|
|
|
|
+
|
|
|
|
|
+### PIM
|
|
|
|
|
+
|
|
|
|
|
+Low-level SYSTEM primitives:
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+TYPE PROCESS;
|
|
|
|
|
+PROCEDURE NEWPROCESS (body: PROC; workspace: ADDRESS;
|
|
|
|
|
+ size: CARDINAL; VAR p: PROCESS);
|
|
|
|
|
+PROCEDURE TRANSFER (VAR from: PROCESS; to: PROCESS);
|
|
|
|
|
+PROCEDURE IOTRANSFER (VAR from, to: PROCESS;
|
|
|
|
|
+ interruptNo: CARDINAL);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- caller supplies workspace;
|
|
|
|
|
+- caller explicitly transfers;
|
|
|
|
|
+- no scheduler;
|
|
|
|
|
+- no coroutine identity query;
|
|
|
|
|
+- no attach/detach/protection model;
|
|
|
|
|
+- `IOTRANSFER` uses an explicit interrupt number.
|
|
|
|
|
+
|
|
|
|
|
+V3 implements this with `ucontext`; `IOTRANSFER` currently degrades to a
|
|
|
|
|
+normal transfer because there is no interrupt subsystem.
|
|
|
|
|
+
|
|
|
|
|
+### ISO 10514-1 `COROUTINES`
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+DEFINITION MODULE COROUTINES;
|
|
|
|
|
+
|
|
|
|
|
+TYPE
|
|
|
|
|
+ COROUTINE;
|
|
|
|
|
+ INTERRUPTSOURCE = CARDINAL;
|
|
|
|
|
+ PROTECTION = [UnassignedPriority .. 7];
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE NEWCOROUTINE (procBody: PROC; workspace: ADDRESS;
|
|
|
|
|
+ size: CARDINAL; VAR cr: COROUTINE;
|
|
|
|
|
+ [initProtection: PROTECTION]);
|
|
|
|
|
+PROCEDURE TRANSFER (VAR from: COROUTINE; to: COROUTINE);
|
|
|
|
|
+PROCEDURE IOTRANSFER (VAR from: COROUTINE; to: COROUTINE);
|
|
|
|
|
+PROCEDURE ATTACH (source: INTERRUPTSOURCE);
|
|
|
|
|
+PROCEDURE DETACH (source: INTERRUPTSOURCE);
|
|
|
|
|
+PROCEDURE IsATTACHED (source: INTERRUPTSOURCE): BOOLEAN;
|
|
|
|
|
+PROCEDURE HANDLER (source: INTERRUPTSOURCE): COROUTINE;
|
|
|
|
|
+PROCEDURE CURRENT (): COROUTINE;
|
|
|
|
|
+PROCEDURE LISTEN (p: PROTECTION);
|
|
|
|
|
+PROCEDURE PROT (): PROTECTION;
|
|
|
|
|
+PROCEDURE TurnInterrupts (to: PROTECTION): PROTECTION;
|
|
|
|
|
+PROCEDURE ListenLoop;
|
|
|
|
|
+END COROUTINES.
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Main differences:
|
|
|
|
|
+
|
|
|
|
|
+| Area | PIM | ISO |
|
|
|
|
|
+| --- | --- | --- |
|
|
|
|
|
+| Context type | `PROCESS` | `COROUTINE` |
|
|
|
|
|
+| Creation | `NEWPROCESS` | `NEWCOROUTINE` + optional protection |
|
|
|
|
|
+| Switch | `TRANSFER` | `TRANSFER` |
|
|
|
|
|
+| Interrupt transfer | `IOTRANSFER(..., interruptNo)` | `IOTRANSFER(from,to)` with attached source |
|
|
|
|
|
+| Current context | none | `CURRENT` |
|
|
|
|
|
+| Interrupt registration | none | `ATTACH`/`DETACH`/`IsATTACHED`/`HANDLER` |
|
|
|
|
|
+| Protection/priority | none | `PROTECTION`, `LISTEN`, `PROT`, `TurnInterrupts` |
|
|
|
|
|
+| Event loop | user-written | `ListenLoop` |
|
|
|
|
|
+
|
|
|
|
|
+V3 has PIM coroutines but no ISO `COROUTINES` module. A pure
|
|
|
|
|
+coroutine subset (`NEWCOROUTINE`, `TRANSFER`, `CURRENT`, workspace
|
|
|
|
|
+management) could map onto the existing ucontext layer. The real
|
|
|
|
|
+interrupt/protection parts need an OS signal/event layer.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 5. External assembly instead of inline `ASM`
|
|
|
|
|
+
|
|
|
|
|
+External assembly works today through `EXTERNAL` and the normal linker;
|
|
|
|
|
+no compiler support for inline `ASM` is required.
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+DEFINITION MODULE AsmOps;
|
|
|
|
|
+PROCEDURE Add (a, b : CARDINAL) : CARDINAL;
|
|
|
|
|
+END AsmOps.
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```modula2
|
|
|
|
|
+IMPLEMENTATION MODULE AsmOps;
|
|
|
|
|
+PROCEDURE Add (a, b : CARDINAL) : CARDINAL;
|
|
|
|
|
+ EXTERNAL;
|
|
|
|
|
+END AsmOps.
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```asm
|
|
|
|
|
+ .text
|
|
|
|
|
+ .globl Add
|
|
|
|
|
+ .type Add,@function
|
|
|
|
|
+Add:
|
|
|
|
|
+ movl %edi, %eax
|
|
|
|
|
+ addl %esi, %eax
|
|
|
|
|
+ ret
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```sh
|
|
|
|
|
+cc -c asm_add.s -o asm_add.o
|
|
|
|
|
+M2 -c AsmOps.def AsmOps.mod
|
|
|
|
|
+qbe -o AsmOps.s AsmOps.ssa
|
|
|
|
|
+cc -c AsmOps.s -o AsmOps.o
|
|
|
|
|
+cc Prog.o AsmOps.o asm_add.o runtime/syslib/shim.c -o Prog -lm
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+For `M2make --separate`, add the assembly object with `--extra
|
|
|
|
|
+asm_add.o` or link manually.
|
|
|
|
|
+
|
|
|
|
|
+### ABI notes
|
|
|
|
|
+
|
|
|
|
|
+- QBE target is `amd64_sysv`.
|
|
|
|
|
+- `INTEGER`/`CARDINAL` are 32-bit; `LONGINT` is 64-bit.
|
|
|
|
|
+- pointers are 64-bit `l` values.
|
|
|
|
|
+- `VAR` parameters receive addresses.
|
|
|
|
|
+- external procedures get no V3 static-link argument.
|
|
|
|
|
+- open arrays, especially `ARRAY OF CHAR`, are passed as descriptor
|
|
|
|
|
+ addresses, not raw C arrays.
|
|
|
|
|
+- record/array value parameters are passed by address.
|
|
|
|
|
+- composite results use a hidden result pointer.
|
|
|
|
|
+
|
|
|
|
|
+For anything beyond scalar arguments, use a small C wrapper around the
|
|
|
|
|
+assembly routine so the V3-facing signature is C-compatible.
|
|
|
|
|
+
|
|
|
|
|
+This is the recommended route for low-level code; inline `ASM` is
|
|
|
|
|
+unnecessary.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 6. Dynamic `SET OF CARDINAL` / `INTEGER` — chosen design
|
|
|
|
|
+
|
|
|
|
|
+V3 now uses a sorted linked list of disjoint inclusive ranges:
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+set = pointer to
|
|
|
|
|
+ { lo : CARDINAL; hi : CARDINAL; next : pointer }
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+This was chosen over a full 2^32-bit bit-vector (512 MiB per set value)
|
|
|
|
|
+and over a linked list of individual bits (too much per-value overhead).
|
|
|
|
|
+The range-list representation is:
|
|
|
|
|
+
|
|
|
|
|
+- O(number of contiguous runs), not O(cardinality);
|
|
|
|
|
+- compact for dense ranges;
|
|
|
|
|
+- supports the full 32-bit base range;
|
|
|
|
|
+- `INTEGER` values map with `VAL(CARDINAL, x) XOR 0x80000000`.
|
|
|
|
|
+
|
|
|
|
|
+Implemented operations include empty/range literals, `INCL`/`EXCL`/`IN`,
|
|
|
|
|
+assignment, equality, union/intersection/difference and symmetric
|
|
|
|
|
+difference. Bounded sets such as `SET OF [0..100000]` keep the inline
|
|
|
|
|
+bit-vector form.
|
|
|
|
|
+
|
|
|
|
|
+### Current limitations
|
|
|
|
|
+
|
|
|
|
|
+- dynamic `SET CONST` is rejected (loud `230`), because no static
|
|
|
|
|
+ range-list layout exists yet;
|
|
|
|
|
+- record/array assignment containing dynamic sets currently copies the
|
|
|
|
|
+ pointer rather than deeply copying the range list;
|
|
|
|
|
+- nodes use `malloc`; assignment frees old lists but there is no GC or
|
|
|
|
|
+ program-exit cleanup;
|
|
|
|
|
+- `PACKEDSET` remains capped at 32 bits.
|
|
|
|
|
+
|
|
|
|
|
+Future work: runtime-initialised dynamic constants, deep composite
|
|
|
|
|
+copy, ownership/lifetime rules, and optionally a hash/paged bitset if
|
|
|
|
|
+range lists become too fragmented.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 7. Suggested order of future work
|
|
|
|
|
+
|
|
|
|
|
+1. `TBITSIZE` (small, standard-facing).
|
|
|
|
|
+2. `ADDADR`/`SUBADR`/`DIFADR` (small, ISO SYSTEM).
|
|
|
|
|
+3. Set ordering (bounded gm2 compatibility; fixes `largeset5`).
|
|
|
|
|
+4. ISO `COROUTINES` subset, then interrupt/protection layer.
|
|
|
|
|
+5. Dynamic `SET CONST` and deep composite copies.
|
|
|
|
|
+6. Parser error recovery and diagnostics.
|
|
|
|
|
+
|
|
|
|
|
+External assembly needs no compiler work; document it as the supported
|
|
|
|
|
+low-level escape hatch.
|