Skip to content

feat(selector): RQ-64-MVLOWER (#1093) — parameter-taking block types LOWER on the ARM direct selector (--relocatable), proven per shape by the #1097 oracle; every other leg stays declined with its measured reason - #1188

Merged
avrabe merged 8 commits into
mainfrom
feat/mvlower-1093
Sep 7, 2026

Conversation

@avrabe

@avrabe avrabe commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

RQ-64-MVLOWER (#1093): parameter-taking block types LOWER on the ARM direct selector (--relocatable), proven per shape by the #1097 oracle — and every other leg stays declined with its measured reason

Artifact: artifacts/release-v0.64/RQ-64-MVLOWER.yamlimplemented. Refs #1093, #1097.

The constraint, and how it was met

No shape's decline was relaxed without scripts/repro/param_block_silent_1097_differential.py passing for it. The oracle asserted refusals == 6 unconditionally, so it structurally could not pass for a lowered leg; it was strengthened first (commit 1, behaviour-identical, negative control captured), never paralleled or weakened:

Every relaxation went red-first: the leg was listed LOWERED on the previous binary (compile declines → exit 1, captured), then the lowering landed, then green.

Verdict per shape (ARM direct selector, --relocatable — the one configuration the oracle executes)

shape verdict evidence
block (param ..) LOWERED 14/14 vectors. Params are plain operand-stack entries; #509's designated-result-register landing already reconciles every forward edge (the pre-#1096 fixture matched for that reason). Zero selector lines.
if (param ..) LOWERED 16/16 vectors. The artifact's vector: pre-#1096 ipe(0) returned 0xC0DE0003 (uninitialized R3); it now returns wasmtime's 7. Checkpoint below the params; params snapshotted at If, re-pushed at Else; else-less if joined like a two-arm if (B end; else: MOV R_then, R_param), moves PLANNED first, parallel-move hazards decline loudly with nothing emitted. Includes the #1093 panicking two-param if/else repro.
loop (param ..) LOWERED 17/17 vectors. Private header register per parameter, reserved for the loop's extent like #509's result_reg, written by every back-edge (same hazard check). Retires the #509 "br to a loop header with N loop parameter(s)" refusal on this path. The RV32-pinned lpb(3) (2, want 10) is 10 on ARM.
all three, ARM self-contained image path still declined no #1097 oracle leg executes those images (the harness loads ET_REL leaf objects); the same selector code is not the same evidence. The decline names the path.
all three, RV32 still declined the RV32 selector has NO blocktype-arity plumbing; reconcile_br_edge records entry ABOVE the params so a param frame's br_if "carries nothing" (the pinned uninit-T2 vector); the loop back-edge mis-reconciles (lpb(3) 2 want 10); compile_function_with_opts is MC/DC-pinned at 10 branches. None attempted.
AArch64 still declined VCR-A64-CF-001's single-slot design; no oracle leg.
if/loop whose inputs are not on the operand stack (multi-value CALL result, spec if.wast add64_u_saturated) still declined, loudly the direct selector models a multi-value call result as one entry; multi-value call results themselves already fail the check_no_underflow pre-flight (both backends).

The guard is now a policy, not a predicate: synth_core::ParamBlockLowering + arm_param_block_lowering(relocatable) + find_unlowered_param_block_type, defined once and read by both ARM guard sites, keyed on --relocatable.

Found while proving it — a register-alias clobber, and it is on main

local.get of an R0–R3 parameter pushes the HOME register by alias, so a then-arm ending in local.get 0 makes the join's else-path mov write local 0. Adding such vectors to the oracle went RED on this lane's first if-lowering (ipa(0) 14 want 7; ipq(0) 16 want 8); canonicalize_then_results copies an aliased then-result into a private temp on the then path and both are green.

The parameter-free #313 join has the identical exposure on main — measured on the base binary (16721807), relocatable ARM:

(func (export "al") (param i32) (result i32)
  (if (result i32) (local.get 0) (then (local.get 0)) (else (i32.const 9)))
  (local.get 0) (i32.add))

al(0)18, wasmtime 9; exit 0, silent. Deliberately NOT fixed here — it moves bytes outside this lane's acceptance oracle and needs its own red-first oracle and byte accounting. Filed by the coordinator as #1189, owned by a separate lane; this PR leaves it alone.

Measured, not argued

  • Byte-identity, base (16721807) vs this tree, scripts/repro/*.wat × 3 legs (arm-reloc, arm-self, rv32-reloc), whole-object compare: 356 identical / 0 differing / 0 newly-declined / 6 newly-ACCEPTED (the six param-taking modules, arm-reloc only). Every parameter-free construct is byte-identical (lowerings keyed on params > 0).
  • Reach, spec suite if.wast on --relocatable: base skipped 16 functions (8 param-taking declines); now 13 skipped, 0 param-taking declines — 5 of the 8 lower, 2 fall through to the pre-existing Direct selector miscompiles value-returning br_table — carried value dropped (affects --relocatable/shipped path) #509 "br to a result-typed if/else join" decline, 1 to the multi-value-call decline.
  • Oracle counts: refusals 6 → 3; lowered legs 0 → 3 (51 vectors); emulation header 19 → 70; the exact emulation floor RE-DERIVED by oracle_wiring_check.py on the lane tree and moved in ci.yml + claims.yaml together.
  • Ratchets: selector_lines_code grew in three waived steps (each reason names what the lines are and that nothing could be deleted in exchange — the class had no lowering to retire); wildcard arms unchanged (55/90 — let .. else / matches!, no _ =>).
  • Tests: fmt/clippy clean; synth-core + synth-synthesis + synth-backend suites green, 11 new unit tests (policy predicate, path-keyed policy, relocatable-only acceptance per construct, reconcile/canonicalize negative controls: swap, i64 overlap, arity/width mismatch, aliased then-result copied to a private temp).

#973 ARM corpus sweep — the retired guard says so

The sweep compiles every scripts/repro/*.wat at cortex-m4f --relocatable --all-exports — the path this PR relaxed — so its ratchet arm fired on the three param_block_silent_1097_{block,if,loop}.wat EXPECTED_DECLINES entries. Their guard comment forbade removal "by lowering the shapes without passing that oracle first"; that precondition was discharged (the oracle now asserts lowered legs: 3 where it asserted refusals: 6), and the comment is rewritten to record the discharge — by whom, with what evidence, which assertion replaces the entries, and which legs stay declined on paths the sweep does not compile. Its new-decline arm did NOT fire: the three _lowered.wat fixtures compile there and Phase B executes them (block 4 exports, if 8, loop 4, ×12 vectors) — a second, independent execution oracle over the same lowerings. Measured: PHASE A fixtures=180 compiled=164 declined=16 hard-error=0; PHASE B 53 modules, 2744 vectors, 0 mismatches, 0 faults; PASS.

Earlier: rebased onto f4780608 (#1184 MACHO, #1187 CFOBLIG) — re-derived, not carried

Clean replay of all five commits. On the merged tree: oracle_wiring_check.py derives the emulation floor to exactly the pinned value (MACHO's oracle is compiles-mode and does not move it); claim_check.py 62/62 with the selector ratchets unchanged (main did not touch the selector); status_evidence_check.py 0 failures, floor-prose 0 restatements; the #1097 oracle and the #973 sweep re-run PASS on a binary built from the merged tree; three-crate tests + fmt + clippy green.

Merged with main at e96d23ff (#1190, the #1189 fix) — one mechanism, not two

Both lanes independently built the then-result alias copy at the if join. The merge keeps #1190's liveness-gated copy as the single mechanism and deletes this PR's canonicalize_then_results (and its unit test): main's inline Else block is lifted verbatim into copy_live_home_then_results (returns the emitted count so cf accounting is identical) and called at both joins — Else (every if, #1190) and the RQ-64 implicit-else join at End with end_idx = idx. What this PR still adds on top of it for parameter-taking ifs: the checkpoint landing below the params, the param snapshot/re-push, and the else-less join as an empty explicit else with the planned-move hazard check. git checkout --ours would have dropped #1190's general fix; it was resolved hunk by hunk instead.

#1189 not regressed, measured on the merged binary: join_alias_1189_differential.pyrefusals: 1; silent-wrong vectors: 14 (of 14 pinned); fixture match vectors: 60; live vectors: 149 matched, 0 diverged (of 149); PASS. The oracle was not touched.

Counters derived on the merged tree, never carried: exact emulation floor oracle_wiring_check.py325121 (ci.yml + claims.yaml in lockstep; neither side's number); selector_lines_code19740 with BOTH lanes' waivers kept and a third waiver at the merged value explaining it is the sum of two landed growths minus the deleted duplicate (19663 mine, 19355 main's: the merged value is below their deltas summed); selector_lines_total → 30565; wildcard code arms unchanged (55); claim_check 62/62; status_evidence_check 0 failures, floor-prose 0; status.json regenerated, not hand-merged. #1097 oracle on the merged binary: refusals 3, lowered 3 (51 vectors), PASS. #973 sweep on the merged corpus (now 183 fixtures incl. #1189's): 167 compiled, 16 declined, 2936 vectors, 0 mismatches, PASS. Three-crate tests + fmt + clippy green on the merged tree. Byte-identity, main e96d23ff (with #1190) vs the merged tree, scripts/repro/*.wat × 3 legs, whole-object: 364 identical / 0 differing / 0 newly-declined / 6 newly-accepted (the six param-taking modules, arm-reloc only) — every parameter-free module, #1190's own fixture modules included, carries main's bytes unchanged.

Also noticed, left as found (logged, not fixed here; neither is this PR's)

Commits (all signed): oracle mode (behaviour-identical, negative control) → block → if (+ alias finding, oracle strengthened red-first) → loop → #973 sweep guard retired with its discharge recorded.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.52286% with 103 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...esis/src/instruction_selector/select_with_stack.rs 67.30% 51 Missing ⚠️
crates/synth-synthesis/src/instruction_selector.rs 78.07% 50 Missing ⚠️
crates/synth-backend/src/arm_backend.rs 90.00% 1 Missing ⚠️
crates/synth-core/src/wasm_op.rs 99.08% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe force-pushed the feat/mvlower-1093 branch 2 times, most recently from 1d06c32 to f4ac5f6 Compare September 7, 2026 14:10
avrabe and others added 5 commits September 7, 2026 16:34
…WERED mode, set empty (red-first)

The acceptance oracle for relaxing the #1096 guard asserted `refusals == 6`
unconditionally, so it structurally could not PASS for a leg whose decline
is relaxed. This adds the third per-leg mode WITHOUT changing behaviour:

  LOWERED = {}  — a (shape, backend) leg listed here must COMPILE and every
  vector for it must equal live wasmtime on the NEW object: the fixture's
  pinned silent-wrong vectors (0xC0DE0003 etc.), the pinned match vectors,
  extra live probes (both signs + the i32 wrap boundary), and a per-construct
  `_lowered.wat` sub-shape module. A lowered leg with nothing to execute
  FAILS. The RED half (pre-#1096 fixture bytes, pinned wrong) is untouched.

Floors: refusals == 6 - len(LOWERED) and lowered legs == len(LOWERED), both
in-script and BOTH pinned exactly in ci.yml (the #1112 pattern), so a leg
moves between the two counts only in the PR that relaxes its guard.

Measured on the unchanged compiler: PASS, refusals 6, lowered 0, identical
output to the pre-change run except the header line and the new count.
Negative control: declaring block/arm lowered with NO compiler change fails
(`VACUOUS: lowered legs=0, want 1`, exit 1) — the mode is red-first by
construction for every later relaxation.

Refs #1093, #1097

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…on the ARM direct selector (--relocatable), proven by the #1097 oracle

Shape 1 of the parameter-taking block-type class, on the ONE configuration
the #1097 acceptance oracle executes. The guard becomes a per-construct,
per-path POLICY defined once in synth-core (`ParamBlockLowering`,
`arm_param_block_lowering(relocatable)`, `find_unlowered_param_block_type`)
and read by both ARM guard sites, so the backend choke point and the
selector-local guard derive from the same source and cannot drift.

WHY THIS SHAPE IS SAFE (measured, not argued): block params are plain
operand-stack entries and #509's designated-result-register landing already
reconciles every forward edge (br/br_if/br_table) into the join. The
pre-#1096 fixture matched wasmtime on this leg for that reason — its
decline was conservative — and the LOWERED run re-proves it on the NEW
object: 14/14 vectors match (fixture bpb(1)/bpb(0), extra probes bpb(2)/
bpb(-1), and the `_block_lowered.wat` sub-shapes: two params with br_if,
an unconditional br to an outer param block, br_table into nested param
blocks, a value below the params surviving the edge). The lowering adds
ZERO selector lines.

WHAT STAYS DECLINED, with the measured reason in the message:
  - the self-contained image path (optimized selector, or the direct
    selector via --no-optimize / the #509 value-carry routing): no #1097
    oracle leg executes its images, so there is no evidence to relax on;
  - `if (param ..)` and `loop (param ..)` on every path (next increments);
  - RV32 (all shapes): its frame checkpoint drops the carried param on a
    branch edge (the pinned uninit-T2 vector) and the selector has no
    blocktype-arity plumbing at all.

Oracle: block/arm listed LOWERED; refusals 6 -> 5 and lowered legs 0 -> 1
(14 vectors) pinned exactly in ci.yml; `emulations >= 19 -> 33`; the
exact emulation floor RE-DERIVED by oracle_wiring_check.py (324847 ->
324861) and moved in ci.yml + claims.yaml together. The RED half (pre-#1096
bytes pinned wrong) is untouched.

Byte-identity, base (1672180) vs this tree, scripts/repro/*.wat x 3 legs
(arm-reloc, arm-self, rv32-reloc), whole-object compare: 356 identical /
0 differing / 0 newly-declined / 2 newly-ACCEPTED (the two block-param
modules, arm-reloc only).

Ratchet: selector_lines_code 19227 -> 19233 (+6: the guard call reading the
policy — waived with reason); selector_lines_total 29873 -> 29879.
Tests: fmt/clippy clean; synth-core + synth-synthesis + synth-backend
suites green incl. 4 new tests (policy predicate, path-keyed policy,
relocatable selector lowers block + still declines if/loop, backend lowers
block on --relocatable only). Note: claims.yaml's #1096 waiver names a
test `select_with_stack_declines_param_block_types` that does not exist in
the tree (the pins live in issue_1093_param_block_decline.rs) — left as
found, flagged in the lane report.

Refs #1093, #1097

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…the ARM direct selector (--relocatable): the 0xC0DE0003 vector now returns 7

Shape 2 of the class, on the configuration the #1097 oracle executes. This
is the leg the artifact exists for: pre-#1096, `ipe(0)` returned
0xC0DE0003 — an uninitialized R3 — because the #313 frame-entry checkpoint
was taken with the block parameters still on the operand stack, so they
sat BELOW it (the same fact that made `Else`'s `split_off(checkpoint)`
PANIC on the #1093 two-param repro).

THE LOWERING (keyed on `params > 0`; parameter-free ifs are byte-identical):
  - `If`: the checkpoint lands BELOW the params and the param entries are
    snapshotted (`if_params`); every param must be register-resident.
  - `Else`: after the then-results are split off, the snapshot is re-pushed
    so the else-arm starts from the same operand stack the then-arm did
    (on that path no then-arm code ran, so the registers still hold them).
  - `End` without an else: wasm's implicit identity else is lowered as an
    explicit empty one — `B end; else:` then `MOV R_then_i, R_param_i` via
    `reconcile_implicit_else`, which PLANS the moves and LOUD-declines any
    parallel-move hazard (a swap, an i64 lo/hi overlap) with nothing
    emitted; five negative-control unit tests pin that.

FOUND WHILE PROVING IT — a register-alias clobber, and it is on main too:
`local.get` of an R0..R3 parameter pushes the HOME register by alias, so a
then-arm ending in `local.get 0` makes the join's else-path `mov` write
LOCAL 0. Strengthening the oracle with such vectors went RED on this
lane's first lowering (`ipa(0)` 14 want 7; `ipq(0)` 16 want 8, captured);
`canonicalize_then_results` now copies an aliased then-result into a
private temp on the then path (param-taking paths only) and both are
green. The PARAMETER-FREE #313 join has the identical exposure on main —
measured on the base binary: `(if (result i32) c (then (local.get 0))
(else (i32.const 9))) (local.get 0) (i32.add)` returns 18 for c=0,
wasmtime 9 — and is deliberately NOT touched here: that fix moves bytes
outside this lane's acceptance oracle and needs its own oracle-gated
increment (repro in the lane report).

ORACLE (red-first at every step): if/arm listed LOWERED; 16 vectors —
fixture ipe(0)/ipe(1) (0xC0DE0003 -> 7), extra probes, and
`_if_lowered.wat`: the #1093 panicking two-param if/else repro, a void
if (param) with else, a value below the params, an in-place then-result,
an if (param) nested in a block (param), a two-param/two-result else-less
if, and the two alias-clobber shapes — all matching wasmtime. refusals
5 -> 4 and lowered legs 1 -> 2 (34 vectors) pinned exactly in ci.yml;
`emulations >= 33 -> 53`; exact emulation floor RE-DERIVED by
oracle_wiring_check.py (324861 -> 324881), ci.yml + claims.yaml together.

STILL DECLINED, by name: `loop (param ..)` everywhere (next increment);
every shape on the self-contained image path (no oracle leg executes it)
and on RV32 (no arity plumbing in its selector; its checkpoint drops the
carried param — the pinned uninit-T2 vector). A param-taking if whose
inputs are not on the operand stack (spec if.wast `add64_u_saturated`: a
multi-value call result) declines loudly with the reason named.

REACH, measured on the spec suite's if.wast (--relocatable): base skipped
16 functions, 8 of them PARAMETER-taking declines; now 13 skipped, 0
param-taking declines — 5 of the 8 lower, 2 fall through to the
pre-existing #509 "br to a result-typed if/else join" decline, 1 to the
multi-value-call-result decline above.

Byte-identity, base (1672180) vs this tree, scripts/repro/*.wat x 3
legs, whole-object: 356 identical / 0 differing / 0 newly-declined /
4 newly-ACCEPTED (the block + if param modules, arm-reloc only).

Ratchet: selector_lines_code 19233 -> 19530 (+297: the lowering — waived
with reason), selector_lines_total 29879 -> 30307, wildcard arms
unchanged (55/90 — `let .. else`, no `_ =>`). fmt/clippy clean; the three
crates' suites green incl. 6 new unit tests and the flipped pins.

Refs #1093, #1097

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…n the ARM direct selector (--relocatable); artifact implemented with per-shape verdicts

Shape 3 of the class, on the configuration the #1097 oracle executes.
Pre-#1096 ARM never compiled this shape — the #509 "br to a loop header
with N loop parameter(s)" refusal caught it — while RV32's back-edge
mis-reconciled the header (the pinned lpb(3) = 2, want 10).

THE LOWERING (keyed on `params > 0`; parameter-free loops byte-identical):
  - `Loop`: a parameter-taking loop gets a PRIVATE header register per
    parameter (`BlockLabel::param_regs`). An entry donates its own register
    when it is a plain temp held by no other operand-stack entry; one that
    aliases a register-homed local, or is shared, is copied out first
    (the RQ-64 alias-clobber class — `lpa`: the loop param is the aliased
    r0 and local 0 is read after the loop).
  - reservation: the header registers join the per-op `live_params`
    reservation for the loop's extent — the same argument as #509's
    `result_reg`: only a back-edge writes them, only the header reads
    them, so nothing in between can observe an intervening write.
  - `edge_value_move`: a back-edge (br / br_if / br_table) PEEKS the top
    `params` entries, PLANS the moves into the header registers, LOUD-
    declines a parallel-move hazard with nothing emitted, then emits them
    before the compare/branch. This RETIRES the #509 loop-param refusal
    arm on this path — the landing replaces it.

ORACLE (red-first): loop/arm declared LOWERED on the increment-2 binary
-> compile declines -> exit 1 (captured); then this lowering -> PASS.
17/17 vectors: the RV32-pinned lpb(0)/lpb(1)/lpb(3) (a lowered leg now
executes every vector the fixture pinned for its SHAPE on ANY backend),
extra probes, and `_loop_lowered.wat`: two loop params around the
back-edge, a value below the param, an unconditional back-edge with a
forward br_if out to an enclosing block (param), and the aliased-r0 loop
param. refusals 4 -> 3, lowered legs 2 -> 3 (51 vectors) pinned in ci.yml;
`emulations >= 53 -> 70`; exact emulation floor RE-DERIVED by
oracle_wiring_check.py (324881 -> 324898), ci.yml + claims.yaml together.

ARTIFACT: RQ-64-MVLOWER -> implemented, with the per-shape verdict table
and every still-declined leg's MEASURED reason (self-contained ARM path:
no oracle leg; RV32: no arity plumbing, checkpoint above the params, MC/DC
pin; AArch64: VCR-A64-CF-001 design; multi-value-call inputs) plus the
register-alias finding on main, reported for scoping.

Byte-identity, base (1672180) vs this tree, scripts/repro/*.wat x 3
legs, whole-object: 356 identical / 0 differing / 0 newly-declined /
6 newly-ACCEPTED (the six param-taking modules, arm-reloc only).

Ratchet: selector_lines_code 19530 -> 19663 (waived with reason);
selector_lines_total 30307 -> 30440; wildcard arms UNCHANGED (55/90 — the
shared-register check is a `matches!` guard, the first draft's `match ..
_ => false` was caught by the pin and rewritten). fmt/clippy clean; the
three crates' suites green incl. the loop acceptance tests and the #509
decline pins (non-relocatable selector still declines at frame open).
`rivet validate` with the local binary: error count identical to base
(40, pre-existing); every line naming this artifact is a WARN/INFO of the
kind every sibling carries.

Refs #1093, #1097

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…s the three param-block EXPECTED_DECLINES entries: the guard's precondition was discharged, and says so

The sweep compiles every scripts/repro/*.wat at `cortex-m4f --relocatable
--all-exports` — exactly the ARM direct-selector path RQ-64-MVLOWER
relaxed — so its ratchet arm fired on all three param_block_silent_1097_
{block,if,loop}.wat entries ("now COMPILES ... but is still on
EXPECTED_DECLINES"). The entries' guard comment forbade removing them
"by lowering the shapes without passing that oracle first"; that oracle
(param_block_silent_1097_differential.py) was strengthened first and now
asserts `lowered legs: 3 (vectors: 51)` where it asserted `refusals: 6`,
red-first per leg. The comment is REWRITTEN, not deleted: it records the
discharge, by whom, with what evidence, which assertion replaces the
entries, and which legs stay declined on paths this sweep does not
compile (self-contained ARM, RV32, AArch64).

The new-decline arm did NOT fire: the three `_lowered.wat` sub-shape
fixtures COMPILE at cortex-m4f and Phase B executes them (block 4 exports,
if 8, loop 4, x 12 vectors) against wasmtime — a second, independent
execution oracle over the same lowerings.

Measured on this tree: PHASE A fixtures=179 compiled=164 declined=15
hard-error=0 (was 15+3 entries pending removal); PHASE B modules
executed=53 vectors=2744 mismatches=0 faults=0; #973 ARM CORPUS SWEEP:
PASS.

Refs #1093, #1097, #973

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
avrabe and others added 3 commits September 7, 2026 17:14
…93 — one then-result alias-copy mechanism, counters re-derived

Both lanes independently built the then-result alias copy at the if
join, with different scope: #1190 for EVERY if (liveness-gated on
`param_last_read` past the if's End, at `Else`), this branch for
parameter-taking ifs only (`canonicalize_then_results`). Resolved ON
main's mechanism, not beside it: main's inline `Else` block is lifted
verbatim into `copy_live_home_then_results` (returns the emitted count so
the control-flow accounting is identical) and called at both joins —
`Else` (every if) and the RQ-64 implicit-else join at `End` (else-less
parameter-taking if, `end_idx = idx`). This branch's helper and its unit
test are deleted. The If-arm keeps the checkpoint below the params plus
main's `if_start_idx.push`; the End arm keeps both pops.

#1189 NOT regressed, measured on the merged binary:
join_alias_1189_differential.py -> refusals: 1; silent-wrong vectors: 14
(of 14 pinned); fixture match vectors: 60; live vectors: 149 matched,
0 diverged (of 149); RESULT: PASS. The oracle was not touched.

Counters RE-DERIVED on the merged tree (never a side taken):
  exact emulation floor: oracle_wiring_check.py -> 325121 (ci.yml +
    claims.yaml in lockstep; neither 324898 nor 325070 — both comment
    blocks kept, plus a merge note).
  selector_lines_code: 19740 — BOTH lanes' waivers kept (19233/19530/
    19663 here, 19355 for #1189) plus a third at the merged value
    explaining it is two landed growths minus the deleted duplicate
    (below their deltas summed); wildcard code arms unchanged.
  selector_lines_total: 30565. claim_check 62/62; status_evidence_check
    0 failures, floor-prose 0; status.json REGENERATED, not hand-merged.

Also on the merged binary: #1097 oracle PASS (refusals 3, lowered 3 /
51 vectors); #973 ARM corpus sweep PASS (183 fixtures incl. #1189's,
167 compiled, 16 declined, 2936 vectors, 0 mismatches). Byte-identity,
main e96d23f vs this tree, scripts/repro/*.wat x 3 legs, whole-object:
364 identical / 0 differing / 0 newly-declined / 6 newly-accepted (the
six param-taking modules, arm-reloc only) — every parameter-free module,
#1190's fixtures included, carries main's bytes unchanged. Three-crate
tests + fmt + clippy green.

Refs #1093, #1097, #1189

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…kes `&mut [StackVal]` (ptr_arg), and the merge commit's gate is corrected here

The merge commit (07d2e4f) shipped with `cargo clippy -D warnings` RED:
`ptr_arg` on the extracted helper's `stack: &mut Vec<StackVal>` — both
allocator helpers it calls already take a slice. The lane's own pre-commit
gate captured CLIPPY_EXIT=101 and then did not gate on it (a `;` broke
the `&&` chain before `git commit`). Same-line signature change; no
codegen, no line-count movement.

Re-verified on the rebuilt binary, gated end to end: fmt clean; clippy
clean; join_alias_1189_differential.py refusals 1 / silent-wrong 14 of 14
/ live 149 matched 0 diverged, PASS; param_block_silent_1097_differential.py
refusals 3 / lowered 3 (51 vectors), PASS; three-crate tests green;
claim_check 62/62; status_evidence_check 0 failures.

Refs #1093, #1189

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Dependency bump only; counters re-derived below rather than carried.
@avrabe
avrabe merged commit 1aa3de9 into main Sep 7, 2026
67 checks passed
@avrabe
avrabe deleted the feat/mvlower-1093 branch September 7, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant