summary_inline-array-fields.md 2.6 KB

Inline record array fields (self-hosting layout fix)

Tag v3-inline-array-fields. Main suite 111/111; full compiler source set still compiles under V3 and links.

Why

V3 stored a record's array fields as 8-byte pointers to static descriptors, initialised only for static records. Heap records (ALLOCATE/NEW) never initialised them, so SymTab.RawEnter's Assign(node^.name, …) dereferenced garbage → the self-compiled compiler crashed at startup.

Standard Modula-2 (and gm2) store record array fields inline, so V3 now does too.

Changes

  • SymTab layout: a fixed-array field occupies its full inline object (ArrObjSize = 8 + count*elemBytes), not 8. TypeSizeD's record branch and ComputeOffsets use FieldSize; new ArrObjSize/FieldSize; ElemBytes moved earlier.
  • QbeGen data: ArrBodyItems (inline descriptor contents: l <n>, z <bytes> or l <n>, l $sub_i, …) + ArrData (wraps it and emits nested $name_i). RecItems emits array fields inline; RecStatics only emits sub-descriptors for nested array fields.
  • Field access: Design's .field now passes the field address for array fields (was ElemLoad of the pointer).
  • CopyRecord: array fields copied via CopyArray(fieldAddr, …).
  • InitHeap: a record's inline array field is initialised in place (header = count), no separate allocation.
  • SymTab: ALLOCATE(x, TSIZE(T)) → NEW(x) (typed allocation runs InitHeap). FROM Storage IMPORT ALLOCATE kept for gm2's NEW.

Remaining blocker (the string model)

Running the self-compiled compiler still aborts, but now in a reproducible, understood place: the compiler's string helpers scan an argument for a trailing NUL (SymTab.Assign, Equal, StrLen, …), while V3 strings are count + bytes with no terminator and trap when indexed past the count. Minimal repro:

PROCEDURE Copy (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
BEGIN ... WHILE (i < HIGH(d)) AND (s[i] # CHR(0)) DO ... END ... END Copy;
Copy(p^.n, "hi");   (* s[2] out of bounds -> abort *)

gm2 strings are NUL-terminated so the sources work there. Options:

  1. NUL-terminate V3 CHAR arrays (reserve a terminator slot, allow reading index = count, emit z (n+1), literals … , b 0). Keeps the sources unchanged; broad but mechanical.
  2. Rewrite every NUL-scanning helper in the compiler sources to be length-based (must stay gm2-compatible) — larger.

Then: the rest of the bootstrap (cross-stage FileIO, fixpoint.sh).

Files

compiler/src/{M2.atg,SymTab.mod,QbeGen.mod,M2.lst}.