summary_short-circuit.md 1.7 KB

Short-circuit AND/OR

Tag v3-short-circuit. Main suite 111/111.

Why

V3 evaluated both sides of AND/OR; gm2 (and the compiler's own sources) rely on short-circuit — e.g. SymTab.Resolve's … AND (tform[t] = FAlias) dereferences tform[-1] when t is invalid. Roughly 25 such guard sites exist in the compiler sources, so matching gm2 is the right fix rather than rewriting them.

How

The RHS operand is already parsed (and its code emitted) by the time the binary-op action runs, so short-circuit needs deferred code:

  • QbeGen.DelayBegin redirects output into a small buffer; DelayEnd restores the previous sink; DelayFlush replays the buffer at the branch. WEmit routes to the delay buffer while active.
  • SimExpr (OR) and Term (AND) wrap the RHS Term/Fact in DelayBegin/DelayEnd and emit:
    • OR: if a then r = 1 else { <RHS>; r = b }
    • AND: if a then { <RHS>; r = b } else r = 0
  • The merge uses a 4-byte memory slot (Slot4/StoreW/LoadW) rather than a QBE phi: a phi must name the right predecessor block, but the RHS can span several blocks (e.g. a set-membership trap check), which broke the phi's predecessor list. The slot is robust to any RHS shape.

Result

Suite 111/111 (the earlier WIP failed 14 tests: DelayFlush popped the wrong buffer index, then the CopyOp-only merge collapsed the two branches to one compile-time operand).

Still open (task 2: full self-image)

The self-compiled V3 compiles a single file to byte-identical .ssa, but the whole compiler source set still aborts, now in the scanner's GetName (LexName ← GetIdent ← ProcTypeSection) — a bounds trap; next to diagnose.

Files

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