Skip to content

Optimized path: br_if to a forward block end still unresolved for if/else and sequential-block shapes (#483 class, broader) #500

Description

@avrabe

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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions