Add src/compiler/flow.s, the control-flow layer above the scalar emitter:
labels, absolute jumps for loop back edges, conditional branches with a fixed
five-byte short/long encoding, calls that discard frame arguments, and
prologue/epilogue helpers for the software-frame ABI. Add a counting mode to
codegen.s so one pass measures a routine without writing the buffer and the
next pass emits it; both passes must produce identical label offsets and
lengths. The x128 fixture generates a nested routine (while, if/else, for,
call, return), sizes it twice, verifies both branch encodings in memory, and
executes it.
The story is limited to the emitter: parser-to-emitter wiring, method address
assignment (sizing.s), and compiler pass 3 remain open and are recorded as
remaining risk.
src/compiler/flow.s, src/common/flow.inc (new)src/compiler/codegen.s (counting mode), src/runtime/frames.s
(frame_discard preserves A/X)tests/codegen/control-test.s (new)scripts/build-control-test.ps1, scripts/verify-control-test.ps1 (new)docs/decisions.md, docs/architecture.mdplanning/claims/E04-S04.mdbuild/if, else, while, for, calls, and return: the generated
routine runs with the expected mailbox signature A5 11 05 0A 04 5A 00 00 03
(status PASS, return 17, a 5, if/else path 10, i 4, long-branch result 5A,
wrong path 0, zero sizing mismatches, both branch forms verified).FL_MODE_COUNT (label offsets recorded, no stores) and FL_MODE_EMIT, then
compares every main-routine label offset and the total length; mismatches 0.BEQ +3 / JMP, jumps over the wrong path
(v_bad stays 0), and reaches the target (v_long is $5A).add(sum)
returns 17 through the frame ABI.frame_discard now preserves A/X; the earlier implementation left its count
in A, which destroyed a callee's result during argument cleanup. The frame
regression still passes.sizing.s, method
addresses, and parser statement emission remain the next backend story.cg_put; cg_init clears it and fl_init sets it.fl_call/fl_call_byte/fl_call_word: JSR, save the result on the
hardware stack, discard argument bytes, restore. Added fl_ret,
fl_ret_byte, and fl_ret_word, preserving results across frame_leave.cg_here is a routine, so it must
be called (jsr cg_here), not read as a variable. This was why the in-memory
branch-form checks inspected the wrong byte.scripts/verify-control-test.ps1scripts/verify-frame-test.ps1 -Port 6541scripts/verify-scalar-test.ps1 -Port 65424E 38 03 A5 00 00 00 00 11 05 0A 04 5A 00 00 03);
frame PASS; scalar PASS with its unchanged 231-byte golden stream.src/compiler/flow.s, src/common/flow.inc,
src/compiler/codegen.s, src/runtime/frames.s, tests/codegen/control-test.s,
scripts/build-control-test.ps1, scripts/verify-control-test.ps1,
docs/decisions.md, docs/architecture.md, this claim.fl_branch targets must be forward (loops use fl_jmp);
interface dispatch calls and void calls through the control layer are not
separately exercised; the long-branch expansion adds three NOPs to short
branches.Powered by TurnKey Linux.