Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions majit/majit-backend-wasm/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2285,6 +2285,38 @@ pub fn merged_stream_has_loop_label(inputs: &ModuleBuildInputs) -> bool {
find_loop_label_index(&ops).is_some_and(|label_idx| label_idx < inputs.ops.len())
}

/// Whether the guard a region would attach to sits in the owner's peeled
/// preamble, ahead of the loop header LABEL.
///
/// `InlineGuard::branch_depth` is a depth at loop-body statement level, where
/// the per-region blocks are the innermost ones open. The preamble has not
/// entered the `loop` those blocks are opened in; its innermost blocks are the
/// LABEL resume pairs, so the same depth names a resume loader and the region
/// body stays unreachable. Such a bridge must keep the out-of-line path.
///
/// `fail_index` is the exit ordinal within the owner's own stream — the
/// numbering `collect_guards_and_vars` assigns and `InlinedBridge`
/// records as `source_fail_index`.
pub fn inline_source_guard_precedes_loop_label(
inputs: &ModuleBuildInputs,
fail_index: u32,
) -> bool {
let Some(label_idx) = find_loop_label_index(&inputs.ops) else {
return false;
};
let mut exit_ordinal = 0u32;
for (pos, op) in inputs.ops.iter().enumerate() {
if !op.opcode.is_guard() && op.opcode != OpCode::Finish {
continue;
}
if exit_ordinal == fail_index {
return pos < label_idx;
}
exit_ordinal += 1;
}
false
}

impl Clone for InlinedBridge {
fn clone(&self) -> Self {
Self {
Expand Down
22 changes: 17 additions & 5 deletions majit/majit-backend-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ use std::sync::{Arc, Mutex};
/// the region carries a CALL_ASSEMBLER the owner build emits no arm for; 50 =
/// the owner is already invalidated, so a merged region would inherit its set
/// flag instead of starting valid; 51 = the region's closing JUMP names a LABEL
/// published by another module, which no in-module `br` can reach.
pub static BRIDGE_DIAG: [AtomicU64; 52] = [const { AtomicU64::new(0) }; 52];
/// published by another module, which no in-module `br` can reach; 52 = the
/// region's source guard is in the peeled preamble, outside the `loop` its
/// block is opened in.
pub static BRIDGE_DIAG: [AtomicU64; 53] = [const { AtomicU64::new(0) }; 53];

#[repr(u8)]
#[derive(Clone, Copy)]
Expand Down Expand Up @@ -3432,9 +3434,9 @@ impl majit_backend::Backend for WasmBackend {
decline("foreign_label");
} else if !resumes_at_loop_header && !inline_nonheader_enabled() {
// Resuming at the header lets the region `br` straight to the
// `loop`. Resuming at an earlier LABEL needs the
// `loop`-wrapped dispatch, which is opt-in until its
// miscompile is root-caused (`inline_nonheader_enable`).
// `loop`. Resuming at an earlier LABEL goes through the
// `loop`-wrapped dispatch, still opt-in
// (`inline_nonheader_enable`) while its cost is measured.
diag_bump(38);
decline("not_header");
} else if let Some(mut candidate) = original_token
Expand All @@ -3453,6 +3455,16 @@ impl majit_backend::Backend for WasmBackend {
} else if !codegen::merged_stream_has_loop_label(&candidate) {
diag_bump(39);
decline("no_loop_label");
} else if codegen::inline_source_guard_precedes_loop_label(
&candidate,
source_fail_index,
) {
// The guard is in the peeled preamble, which the `loop`
// holding the region blocks has not been entered from, so
// its branch would land in a LABEL resume loader and the
// region body would be unreachable.
diag_bump(52);
decline("source_in_preamble");
} else {
self.collect_constants_from_ops(ops);
candidate.inlined_bridges.push(codegen::InlinedBridge {
Expand Down
Loading
Loading