Branch ast-stage-c. Suite 180/180 (178 + 2 Lower comparisons);
self-hosting fixpoint OK (3,090,949 bytes; slice 13 was
3,042,040). Plan of record: docs/plan-lower.md.
The mechanism that lets the AST be lowered and byte-compared against the legacy emit, plus the first subset.
compiler/src/Lower.def/.mod — the walk. CanLower(unit) gates a
unit to the supported subset; LowerUnit(unit) re-emits it:
OpenModule, SetOutName(<mod>L), SetModule(<mod>), declarations,
BeginBody, body, EndModule(<mod>).QbeGen.SetOutName (new, additive): overrides only the image file
name used by EndModule, leaving the mangled symbol name unchanged —
so a matching walk writes a byte-identical image to a different file.-lower driver flag: the grammar re-emits the program unit from the
AST inside the unit's END action, right after the legacy
EndModule and before SymTab.EndUnit (the module scope must still
be open — Lower resolves names through SymTab).SetLower/GetUnit; parser.frm's definition module
re-exports them; Lower is linked like AST.A program unit with scalar CONST/VAR and a body of
x := <integer expression> (literals/idents, + - * DIV MOD, unary -),
reproducing Design's scalar emit (LoadVar / const CopyOp) and
AssOrDecl's scalar store, including Fold2 constant folding.
./M2 -lower tests/t_exit.mod and t_arith.mod produce
gen_ssa/<Mod>L.ssa byte-identical to the legacy image (now asserted
by lower_ok in run_tests.sh).
SymTab.EndUnit closes the module scope, so Lower cannot run after
Parse (names no longer resolve). It must run at unit end, before
EndUnit — and after the legacy EndModule so re-opening the backend
does not clobber the legacy image buffer.
docs/plan-lower.md)Extend the subset one step at a time — L1 relations/booleans/short-circuit,
L2 control flow, L3 procedures, … — each gated by lower_ok + suite +
fixpoint, until a unit can be lowered from the AST instead of inline
(phase 3), then delete the inline emit and Fwd* machinery.