Branch ast-stage-c. Suite 182/182; fixpoint OK
(3,147,849 bytes; L2 was 3,123,487). L3 is not enabled — see the
finding; it stays out of CanLower, so the comparisons cannot false-fail.
Lower: NkCall in expressions (value) and
statements (discard), mirroring ArgList/ActParam — CallBegin,
per-actual CallArg (VAR via AddrOfVal, value via ArgClass + real
widening), CallEnd. CanCallArgs checks arity.SymTab.ParamName (new, read-only) — the i-th formal's name, for
the header.LowerDecls: Mangled/BeginFunc/FuncParam
for each formal/SetFuncRes/EndFuncHeader/EndFunc.Block now records its local NkDeclSeq (astBlkDecls), stored as
NkProcDecl child 2 — so a proc's VAR locals are in the AST.NkCall child 0 is an NkDesignator, not a
bare NkIdent; CalleeName unwraps it (this fixed calls mangling as
$_0).Lower runsThe L3 comparison does not match, and the reason is structural, not a missing emit:
SymTab.CloseProc (and EndUnit) close the scope that the inline
actions lowered inside of. Lower runs at the end (module end), by
which time a procedure's locals/params are no longer resolvable — so
LowerDesign("s") fails and the body emits nothing.
EndUnit, so L0–L2 (program body) work.This is exactly the "flip" the plan anticipated (§3 phase 3): the walk
cannot be one end-of-unit pass for everything; body lowering has to
happen at each procedure while SymTab still has its scope.
The chosen route — grammar calls Lower.LowerProc(node) right before
SymTab.CloseProc — requires the L image to be built concurrently
with the legacy image. QbeGen has a single session buffer and a
single set of counters (nTemp, nLoc, scopeTop, …), so two emitters
interleaved corrupt the legacy path — the very path that must stay
byte-identical for self-hosting.
A second image buffer (altBuf + SetAlt/WriteAlt) was added and then
reverted: it does not solve the shared-counter problem, only the
buffer. Concurrent emit needs either:
QbeGen instance /
duplicated counters — invasive), or-lower, suppress the legacy emit
for the unit and lower only from the AST — i.e. the flip (L7)
applied to the test path. Lowering then happens during the parse, so
every scope is open at the right time and the whole class of
"closed-scope" problems disappears.The second is the clean destination and subsumes L3. Recommendation: make the flip the next dedicated step, rather than fake option 1 with two entangled emitters.
t_exit, t_arith, t_lower1,
t_lower2); t_lower3 is gated out of CanLower, so the tree
stays green.Start the flip: a backend-level emit gate so -lower suppresses the
legacy emit and the grammar lowers the whole unit from the AST during the
parse. That completes L3 and is the basis for L4+.