# Test2 — SimpleMod2 with QBE backend — session summary Scalar-subset Modula-2 compiler: `SimpleQ.atg` (Coco/R) + `SymTab` (type checking, reused from Test1) + new `QbeGen` backend emitting QBE SSA to `gen_qbe/.ssa`, assembled with `qbe` and linked with `cc`. All work lives in `Test2/`, binary `Test2/SimpleQ`. ## 1. Scope (user choice: scalar subset + full SSA→run pipeline) Full SimpleMod2 syntax parses and type-checks with Test1's exact codes (200/201/202, 210–224). Only scalar data is lowered: - INTEGER / CARDINAL / SHORTINT / LONGINT / BOOLEAN / CHAR / enumerations → QBE `w`; REAL / LONGREAL → QBE `d` (widened). - Everything else parses but gets error 230 ("qbe backend: construct not supported in scalar subset"): ARRAY / RECORD / SET / POINTER variables, string variables, WITH, IN, set literals, non-literal CONST expressions and BY steps, EXIT outside LOOP, imported names used as values. Test convention (the language has no I/O): a global `VAR ExitCode : INTEGER` makes generated `main` return its value; otherwise the program returns 0. ## 2. Files - `SimpleQ.atg` (856 lines) — grammar; single-pass QBE emission. Every expression synthesizes a QBE operand (immediate like `42`, `-5`, `d_3.14` or fresh `%tN`); statements emit jumps/labels. Code emits only on type-correct subtrees, so backend 230s never cascade with type errors. - `QbeGen.def/.mod` (120/448 lines) — SSA writer: `data` globals, `export function w $main`, temporaries (`%tN`), labels (`@LN`), `ceqw/cnew/csltw/cslew/csgtw/csgew` + `ceqd/cned/cltd/cled/cgtd/cged`, `swtof` for INTEGER→REAL assignment, loop-exit stack, CASE as compare-chains, `NoQbe` guard (single 230 for WITH bodies). - `SymTab.def/.mod` — Test1 copy + 2 accessors (`PendCount`, `PendName`) so VAR-list storage is emitted before `FixPending`. - `FileIO`, `scanner.frm`, `parser.frm`, `compiler.frm` (+230 text), `compiler.gpm` — copied from Test1. - `build_qbe.sh` — regenerates (`CRFRAMES=. ../CR -m -C SimpleQ.atg`), compiles with `gm2 -fiso`, links via the two-phase module-list workaround, then runs the suite: `SimpleQ → qbe → cc → execute`, checking exit codes; negatives must be rejected with the right message. - `tests/` — 14 positive + 6 negative `.mod` sources (with `.LST` listings); `gen_qbe/` — backend output (`.ssa`, plus `.s`, binaries and empty `.qbeerr`/`.ccerr` logs from the last run). ## 3. Test results — 20/20 (verified 2026-09-14) | test | exit | covers | | ---- | ---: | ------ | | q_minimal | 0 | empty body, default `ret 0` | | q_arith | 7 | `+ - * DIV MOD`, precedence, parens | | q_const | 15 | CONST, unary minus, hex `0AH` | | q_bool | 42 | AND/OR/NOT, comparisons | | q_if | 20 | IF/ELSIF/ELSE, empty-IF fallthrough | | q_case | 103 | labels, ranges, ELSE, expr selector | | q_while | 55 | WHILE sum 1..10 | | q_repeat | 55 | REPEAT..UNTIL | | q_loop | 7 | LOOP + EXIT | | q_for | 85 | FOR, `BY -2` negative step | | q_real | 31 | REAL arith, int→real assign, real CONST (runtime init) | | q_char | 97 | CHAR literals/comparison | | q_enum | 1 | enum ordinals, CASE on enum | | q_full | 74 | combined demo (FOR/MOD/WHILE/REPEAT/LOOP/CASE) | | q_bad_array | rej/230 | ARRAY var + indexing | | q_bad_with | rej/230 | RECORD var + WITH (single 230) | | q_bad_set | rej/230 | SET var + set literal | | q_bad_exit | rej/230 | EXIT outside LOOP | | q_bad_const | rej/230 | non-literal CONST (`B = A + 1`) | | q_bad_asn | rej/210 | REAL→INTEGER (type parity with Test1) | Ad-hoc edge probe also verified: unary-minus temps (`-y`, `-r`), empty FOR (`5 TO 1` skipped), boolean CASE, `10H`, nested LOOP/EXIT (inner EXIT targets inner loop), AND-ed WHILE-style conditions. ## 4. Bugs found and fixed this session 1. `IntStr`/`NegFold` wrote the string terminator one past the end (double `Len()` evaluation) → random garbage bytes in `.ssa`. 2. `LabelList` took body/next labels as value params, so `NewLabel` results were lost → garbage labels. Made them `VAR`. 3. `LoopStat` missed the back-edge `jmp` → loop body ran once. 4. This qbe accepts only integer literals in `data` (`d 0` ok, `d 1.5` rejected) → non-zero REAL consts emit `d 0` plus a runtime `copy` flushed at function start (`BeginBody`). ## 5. Known limits - No mixed INTEGER/REAL arithmetic (Test1 rule; `r*2.0 + x` is a 211 error — convert via assignment first). - `DIV`/`MOD` truncate toward zero (C/QBE semantics, not Modula-2 floor). - Assignment targets emit a dead load before the store (valid QBE, minor inefficiency from always-loading designators). - Multi-dimensional arrays check every index but track one element type (inherited Test1 rule); unsupported anyway (230). ## 6. Rebuild / run - Full suite: `./build_qbe.sh` (from `Test2/`). - Single file: `./SimpleQ tests/.mod` (listing next to the source, SSA in `gen_qbe/`), then `qbe -o gen_qbe/.s gen_qbe/.ssa && cc gen_qbe/.s -o gen_qbe/ && gen_qbe/; echo $?`. - Requires: `CR` binary (repo root, `./build.sh`), `gm2 -fiso`, `qbe`, `cc`.