Summary
#483 fixed block+br_if branch resolution for a single forward block (and a simple nested block + br). A multi-modal sweep of optimized-path (non---relocatable) control-flow shapes shows the bug class is broader: an if/else lowered as nested block+br_if+br, and two sequential blocks, both still emit the conditional branch as an unpatched offset-0 placeholder (bne.n encoding 0xD100) — so br_if lands mid-block, skips the guarded address-materialization, and stores to a garbage address. The --relocatable direct path is unaffected.
These were NOT covered by #483's differential (which exercised only the single-block repro and a light nested case), so the targeted fix passed while these shapes remained broken.
Repro A — if/else (nested block + br_if + br)
(func (export "ifelse") (param $c i32)
(block $end
(block $else
(br_if $else (i32.eqz (local.get $c)))
(i32.store (i32.const 4) (i32.const 100))
(br $end))
(i32.store (i32.const 4) (i32.const 200))))
synth compile … -b arm --target cortex-m4 --all-exports, then arm-none-eabi-objdump -d -M force-thumb:
d2: d100 bne.n d6 ; offset 0 = UNRESOLVED placeholder
d4: 2504 movs r5, #4 ; address const for store-100 — SKIPPED when br_if taken
d6: 2664 movs r6, #100 ; br_if lands here → stores 100 at garbage r5
ifelse(0) (br_if taken → should store 200 at addr 4) executes both stores to garbage addresses under unicorn vs wasmtime {4:200}.
Repro B — two sequential blocks
(func (export "seqblocks") (param $a i32) (param $b i32)
(block $b1 (br_if $b1 (i32.eqz (local.get $a))) (i32.store (i32.const 8) (i32.const 11)))
(block $b2 (br_if $b2 (i32.eqz (local.get $b))) (i32.store16 (i32.const 12) (i32.const 22))))
Both bne.n are 0xD100 (offset-0 placeholders). seqblocks(0,1) writes 11 (should have skipped it) to a wrong address.
Root-cause direction
The br_if target id is not present in ir_to_arm_idx at resolution time (line ~5380), so the placeholder is never patched — same mechanism as #483, but the End-label fix (End labels with the closed block's id) does not cover these shapes. Suspects: the block+br_if early-exit pattern matcher (optimizer_bridge.rs ~line 391, "Pattern 3") partially matching and re-routing the lowering; or the id pushed by Block not matching what br_if computes when a store / br sits between the br_if and the block end across multiple/nested blocks.
Scope
Belongs to the patch-accreting-codegen class the VCR-* program (#242) replaces. Next gated fix: extend the End-label/branch-resolution handling to these shapes and add them to the block_brif_483 differential corpus. (A loop-with-locals fixture in the same sweep showed a resolved backward branch but a possibly-separate locals issue — tracking only the branch-resolution class here.)
Summary
#483 fixed
block+br_ifbranch resolution for a single forward block (and a simple nested block +br). A multi-modal sweep of optimized-path (non---relocatable) control-flow shapes shows the bug class is broader: anif/elselowered as nestedblock+br_if+br, and two sequential blocks, both still emit the conditional branch as an unpatched offset-0 placeholder (bne.nencoding0xD100) — sobr_iflands mid-block, skips the guarded address-materialization, and stores to a garbage address. The--relocatabledirect path is unaffected.These were NOT covered by #483's differential (which exercised only the single-block repro and a light nested case), so the targeted fix passed while these shapes remained broken.
Repro A — if/else (nested block + br_if + br)
synth compile … -b arm --target cortex-m4 --all-exports, thenarm-none-eabi-objdump -d -M force-thumb:ifelse(0)(br_if taken → should store 200 at addr 4) executes both stores to garbage addresses under unicorn vs wasmtime{4:200}.Repro B — two sequential blocks
Both
bne.nare0xD100(offset-0 placeholders).seqblocks(0,1)writes 11 (should have skipped it) to a wrong address.Root-cause direction
The
br_iftarget id is not present inir_to_arm_idxat resolution time (line ~5380), so the placeholder is never patched — same mechanism as #483, but the End-label fix (End labels with the closed block's id) does not cover these shapes. Suspects: theblock+br_ifearly-exit pattern matcher (optimizer_bridge.rs~line 391, "Pattern 3") partially matching and re-routing the lowering; or the id pushed byBlocknot matching whatbr_ifcomputes when a store /brsits between thebr_ifand the block end across multiple/nested blocks.Scope
Belongs to the patch-accreting-codegen class the VCR-* program (#242) replaces. Next gated fix: extend the End-label/branch-resolution handling to these shapes and add them to the
block_brif_483differential corpus. (A loop-with-locals fixture in the same sweep showed a resolved backward branch but a possibly-separate locals issue — tracking only the branch-resolution class here.)