Summary
The direct instruction selector (select_with_stack) silently miscompiles a value-returning br_table: when a br (from a br_table arm) targets a block with a result type and carries a value, the carried value is dropped — the dispatched arms return 0 instead of their value. Only the default/fall-through arm is correct.
This is distinct from #507 (which is the optimized path dropping the dispatch for void br_table; fixed by declining to direct). This bug is in the direct selector itself, so it affects both --relocatable and --no-optimize — i.e. the shipped path (gust/falcon use --relocatable). Surfaced while gating the #507 fix: declining the optimized path to the direct selector exposed that the direct selector is also wrong for this shape.
Silent wrong-code, exit 0 — an #180/#185 Ok-or-Err violation on the shipped path.
Minimal repro
btval.wat:
(module
(func (export "pick") (param i32) (result i32)
(block $end (result i32)
(block $c2
(block $c1
(block $c0
(br_table $c0 $c1 $c2 (local.get 0)))
(br $end (i32.const 10)))
(br $end (i32.const 20)))
(i32.const 30))))
Oracle (wasmtime): pick(0..2) → 10, 20, 30; pick(≥2) → 30.
synth (BOTH --relocatable and default→direct), executed under unicorn:
pick(0) = 0 (expect 10) <-- carried value dropped
pick(1) = 0 (expect 20) <-- carried value dropped
pick(2) = 30 (expect 30) OK (default arm)
The br_table dispatch itself is correct (cases 0/1 don't fall to default), but br $end (value) doesn't deliver the value — r0 is 0 at return for the dispatched arms.
Scope / impact
Likely cause
The operand-stack tracking across a br that carries a block result — the value pushed before br $end isn't materialized into the result register on the branch edge. Same family as the #313 if/else result-reconciliation machinery, but for br-to-labeled-block-with-result.
Repro files on request.
Summary
The direct instruction selector (
select_with_stack) silently miscompiles a value-returningbr_table: when abr(from a br_table arm) targets a block with a result type and carries a value, the carried value is dropped — the dispatched arms return0instead of their value. Only the default/fall-through arm is correct.This is distinct from #507 (which is the optimized path dropping the dispatch for void br_table; fixed by declining to direct). This bug is in the direct selector itself, so it affects both
--relocatableand--no-optimize— i.e. the shipped path (gust/falcon use--relocatable). Surfaced while gating the #507 fix: declining the optimized path to the direct selector exposed that the direct selector is also wrong for this shape.Silent wrong-code, exit 0 — an #180/#185 Ok-or-Err violation on the shipped path.
Minimal repro
btval.wat:Oracle (wasmtime):
pick(0..2)→10, 20, 30;pick(≥2)→30.synth (BOTH
--relocatableand default→direct), executed under unicorn:The br_table dispatch itself is correct (cases 0/1 don't fall to default), but
br $end (value)doesn't deliver the value —r0is 0 at return for the dispatched arms.Scope / impact
--relocatablepath — if gust/falcon lower a value-returningbr_table(a switch-expression), it would be wrong with no diagnostic. (The one gust br_table is void-dispatch, so not hit today — needs confirming per component.)Likely cause
The operand-stack tracking across a
brthat carries a block result — the value pushed beforebr $endisn't materialized into the result register on the branch edge. Same family as the #313 if/else result-reconciliation machinery, but forbr-to-labeled-block-with-result.Repro files on request.