# Session save — 2026-09-25 (part b): V3 self-compiles; short-circuit WIP `master` @ `757f518` (`v3-selfcompiles`) — suite **111/111**, tree clean (except the user's uncommitted `compiler/toto.mod` edit). ## Headline The **self-compiled V3** (V3 compiled by V3) runs, parses, and emits code; for `tests/t_constfold.mod` its `.ssa` is **byte-identical** to the gm2-built V3's: ``` ./M2 tests/t_constfold.mod -> gen_ssa/TConstFold.ssa /tmp/M2self tests/t_constfold.mod -> gen_ssa/TConstFold.ssa (cmp: IDENTICAL) ``` That is a stage-2 self-hosting fixpoint for a test file. ## Fixed today (each with a minimal repro) 1. Nested modules (`ListHandler`) — lowered + export hoisting. 2. Inline record array fields (standard M2) — heap records' array fields now have a descriptor header. 3. NUL-terminated string model (terminator slot at index = count); stdlib `Strings` + C shim NUL-based. 4. `p^` / `a[i]^` as a `VAR` actual → pass the pointee address. 5. De-eagered NIL guards in `SymTab` and the driver's `PrintListing`. 6. Off-by-one string scans in `QbeGen` (`Len`/`Cpy`/`App`/`ParseInt` used `i < HIGH(s)`; a literal's last char was dropped → `"gen_ssa/"` became `"gen_ssa"`). ## Current blocker (full self-image) Compiling the **whole compiler source set** with `/tmp/M2self` still aborts, in `SymTab.Resolve`: ```modula2 WHILE (n < ResDepth) AND (t >= 0) AND (t < VAL(INTEGER, nTypes)) AND (tform[t] = FAlias) DO (* eager AND evaluates tform[-1] when t < 0 *) ``` V3 evaluates both sides of `AND`/`OR`; gm2 short-circuits. The compiler sources rely on short-circuit in ~25 places (`(r = InvalidType) OR (tform[r] # …)`, NIL guards, etc.). Two ways forward: - **(A) short-circuit `AND`/`OR` in V3** — the principled fix (matches gm2), done in the `wip/short-circuit` branch but **14 tests fail**; needs debugging. - **(B) de-eager every guard site** in the compiler sources (rewrite the ~25 `(idx = Invalid) OR …` patterns as sequential checks) — mechanical but fragile. ## WIP: `wip/short-circuit` (branch @ `26c01d0`) Implements short-circuit `AND`/`OR` via **deferred-output buffering**: `QbeGen.DelayBegin` buffers the RHS operand's code, `DelayEnd` restores output, `DelayFlush` replays it inside the branch; `SimExpr` (OR) and `Term` (AND) emit `if a { result = 1/0 } else { ; result = b }`. Currently **14 tests fail** (e.g. `t_bool`, `t_set`, `t_pointer`), so the branch logic/labels or the buffer replay is wrong. Do not merge as-is. ## Next steps 1. Debug `wip/short-circuit` until 111/111 (likely the branch/label or `Jnz`-with-immediate handling) — or take route (B). 2. Fix the remaining self-image abort(s); get `/tmp/M2self` to compile the whole set to a byte-identical `M2.ssa` (full fixpoint). 3. Cross-stage `FileIO` selection and `bootstrap/fixpoint.sh` (Stage1 gm2 → Stage2 V3 → Stage3 V3), with the stage-aware CR flag (`-m` for gm2, no `-m` for V3). ## Resume ```sh cd .../m2compiler-V3/compiler ./build.sh && ./run_tests.sh # 111/111 on master # WIP branch: git checkout wip/short-circuit # short-circuit experiment (14 failing) # self-image check (from master): (cd src && CRFRAMES="$(pwd)" .../CocoGm2/CR -C M2.atg >/dev/null) ./M2 # writes gen_ssa/M2.ssa qbe -o /tmp/M2self.s gen_ssa/M2.ssa; cc /tmp/M2self.s ../runtime/syslib/shim.c -o /tmp/M2self -lm /tmp/M2self tests/t_constfold.mod # byte-identical output ```