Skip to content

Commit b21631f

Browse files
committed
cranelift/pulley: assert continuation has single predecessor (debug only)
Make the structural invariant explicit: the funcref-dispatch fusion absorbs the two `VMFuncRef` field loads from the continuation block, which is only sound when the continuation has exactly one predecessor (the brif we're replacing). Build a `ControlFlowGraph` in `pre_lower_pulley` and `debug_assert_eq!(n_preds, 1)` before sinking. Both the CFG construction and the assertion are gated on `#[cfg(debug_assertions)]` so they compile to nothing in release builds (no runtime overhead).
1 parent 24ff906 commit b21631f

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

  • cranelift/codegen/src/isa/pulley_shared

cranelift/codegen/src/isa/pulley_shared/lower.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,36 @@ where
460460
}
461461
}
462462
}
463+
464+
// Debug-only structural invariant: each continuation block we're about
465+
// to absorb loads from must have exactly one predecessor (the brif that
466+
// matched the pattern). If a second predecessor reaches the
467+
// continuation, its control-flow path would observe the absorbed
468+
// load destinations as uninitialized — exactly the bug the
469+
// trap-on-null handler fix defends against. Building
470+
// `ControlFlowGraph` is O(n) so we gate the whole thing on
471+
// `#[cfg(debug_assertions)]`; the assertion + CFG construction
472+
// compile to nothing in release builds (no runtime overhead).
473+
#[cfg(debug_assertions)]
474+
if !to_sink.is_empty() {
475+
let cfg = crate::flowgraph::ControlFlowGraph::with_function(ctx.f);
476+
for (l_code, _) in &to_sink {
477+
let cont = ctx
478+
.f
479+
.layout
480+
.inst_block(*l_code)
481+
.expect("absorbed load belongs to a block");
482+
let n_preds = cfg.pred_iter(cont).count();
483+
debug_assert_eq!(
484+
n_preds, 1,
485+
"pulley funcref-dispatch fusion absorbs continuation-block \
486+
loads, so the continuation must have exactly one predecessor \
487+
(the brif being lowered); found {} predecessors for {:?}",
488+
n_preds, cont
489+
);
490+
}
491+
}
492+
463493
for (l_code, l_vmctx) in to_sink {
464494
ctx.sink_pure_inst(l_code);
465495
ctx.sink_pure_inst(l_vmctx);

0 commit comments

Comments
 (0)