metal2vulkan 0.1.0

Translate Metal AIR / LLVM IR to Vulkan SPIR-V with a native Rust emitter
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//! Byte-neutral responsibility split of the former monolith; see the parent module.

use super::*;

/// Synthesized pass-through merge-block name prefix (kept distinct from any AIR label).
pub(in crate::native) const SPLIT_PREFIX: &str = "%metal2vulkan.lmerge.";

// Same-memory structurizer synthesized-name tokens. These never cross the emit->SPIR-V reparse seam,
// so they are a purely internal naming protocol; centralizing every set/match through these constants
// is what keeps a generator and its later matcher from drifting (the class Phase 1c closed for the
// switch prefixes).

/// Distinguishes the terminal-exit private-return clone inside the `SPLIT_PREFIX` family
/// (`%metal2vulkan.lmerge.texitret*`). Shared by the generator (`synth_terminal_return_clone`) and
/// the convergence matcher (`terminal_exit_convergence`) so the set/match pair cannot drift.
pub(in crate::native) const TEXITRET_TOKEN: &str = "texitret";
/// Distinguishes the terminal-loop private-return clone inside the `SPLIT_PREFIX` family
/// (`%metal2vulkan.lmerge.tloopret*`), generated by `privatize_single_loop_return_exit`.
pub(in crate::native) const TLOOPRET_TOKEN: &str = "tloopret";
/// Distinguishes the unique per-construct selection merge inside the `SPLIT_PREFIX` family
/// (`%metal2vulkan.lmerge.sel*`).
pub(in crate::native) const SEL_TOKEN: &str = "sel";

/// Synthesized do-while rotated-continue block prefix (`%metal2vulkan.cont.*`).
pub(in crate::native) const CONT_PREFIX: &str = "%metal2vulkan.cont.";
/// Synthesized multi-exit critical-edge dispatch-block prefix (`%metal2vulkan.exitedge.*`).
pub(in crate::native) const EXIT_EDGE_PREFIX: &str = "%metal2vulkan.exitedge.";
/// Synthesized multi-exit selector value prefix (`%metal2vulkan.exitsel.*`).
pub(in crate::native) const EXIT_SEL_PREFIX: &str = "%metal2vulkan.exitsel.";

/// True when `name` is a synthesized terminal-exit private-return merge block
/// (`%metal2vulkan.lmerge.texitret*`). No production DECISION calls this any more (the convergence
/// matcher and the single-exit-return synthesizer read the typed [`BlockRole::TerminalExitReturn`]
/// tag instead of the name — Workstream T3); it survives only as the tag-assignment seam
/// [`role_for_name`] uses to stamp a block whose name is the carried identity across a clone/rename.
pub(in crate::native) fn is_terminal_exit_return_name(name: &str) -> bool {
    name.strip_prefix(SPLIT_PREFIX)
        .is_some_and(|rest| rest.starts_with(TEXITRET_TOKEN))
}

/// The [`BlockRole`] a block carries given the NAME it is (re)built with. Used at the clone/rename
/// seam — where a block's name is the identity carried from its source (a cloned `texitret` block
/// keeps a `texitret`-prefixed name, a renamed-away one does not) — so the stamped role exactly
/// tracks the name. Pure synthesizers of a fresh name stamp their role directly (`Normal`, or
/// `TerminalExitReturn` at the one terminal-return clone site) rather than routing through here.
/// Byte-neutral by construction: `role == is_terminal_exit_return_name(name)` at every construction
/// site, so a consumer reading the tag decides exactly as the retired name match did.
pub(in crate::native) fn role_for_name(name: &str) -> BlockRole {
    if is_terminal_exit_return_name(name) {
        BlockRole::TerminalExitReturn
    } else if name.starts_with(SPLIT_PREFIX) {
        BlockRole::LMerge
    } else if name.starts_with(super::super::blocks::SWITCH_BYPASS_PREFIX) {
        BlockRole::SwitchBypass
    } else {
        BlockRole::Normal
    }
}

/// The full structured plan for a function: forest-augmented + structured-ordered blocks plus the
/// loop/branch/switch merge maps the emitter consumes. The R2 emission consumer (modules 1+2+order)
/// assembled; module 4's repair-FREE path consumes this. (Measured: not yet floor-safe without module
/// 3's per-block cleanup — see the journal; kept as the plan generator + tested unit.)
pub(in crate::native) struct StructuredPlan {
    pub(in crate::native) blocks: Vec<BodyBlock>,
    pub(in crate::native) loop_merges: HashMap<String, LoopMergeInfo>,
    pub(in crate::native) branch_merges: HashMap<(String, String), String>,
    pub(in crate::native) branch_merges_by_header: HashMap<String, String>,
    pub(in crate::native) switch_merges: HashMap<String, String>,
}

/// Build the structured plan for a function, or `None` if the current modules cannot FULLY structure
/// it (a loop needs node-splitting we do not do; or a conditional/switch has no synthesizable unique
/// merge — exit-reconvergence, or a phi-carrying shared merge). Combines `forest_loop_merges` (module:
/// loop merges + merge==continue split), `unique_selection_merges` (module 2: per-construct selection
/// merges), and `structured_order` (module 1: dominator-tree, merge-last ordering). When `Some`, every
/// loop and every conditional/switch header has a unique merge, so the emitter emits a complete
/// structured CFG; the kept post-hoc repair then normalizes rather than repairs.
/// Block-count ceiling for the cross-arm-EDGE machinery (self-check 3 + the 9th-attempt clone). A pure
/// PERFORMANCE bound (in the family of `MAX_REGION_BLOCKS`/`MAX_ROUNDS`): the self-check's edge walk and
/// the clone's per-round dominator recompute are super-linear in block count and are re-invoked per
/// retry tier, so on a several-thousand-block function (e.g. a runtime-descriptor-index selection giant)
/// the retry cascade grinds for minutes. Every landed cross-arm-edge win is a small function (<=126
/// emitted blocks), so gating the machinery to modest functions keeps all wins while leaving large
/// functions byte-identical to before this attempt (skipped → they admit/reject exactly as they did).
pub(in crate::native) const CROSS_ARM_EDGE_MAX_BLOCKS: usize = 300;

/// Reject-only loop-exit selection planning is intentionally bounded to modest CFGs. It walks a
/// candidate's complete in-loop region to prove that every path either reaches the candidate or exits
/// through an enclosing loop role; the bound keeps that proof from becoming a quadratic retry cost on
/// giant descriptor-index functions. A skipped graph follows the existing repair path unchanged.
pub(in crate::native) const LOOP_EXIT_SELECTION_MAX_BLOCKS: usize = 300;

/// Whether a structural retry actually changed its source CFG. `BodyBlock` deliberately does not need
/// a public `PartialEq`; the planner only needs this local name/carrier comparison to distinguish a
/// forced lowering that preserves block count (for example an empty switch) from a true no-op. The
/// comparison is carrier-first (structural `TirBlock` Debug — the carrier has no derived `Eq` because
/// its `RetEmit` carries floats), with a line fallback for the pre-`populate` window. Byte-identical to
/// the line comparison: the only blocks that differ here are lowered switch blocks, whose lines AND
/// carrier both change together (a non-switch or unchanged block is pushed as an exact clone).
pub(in crate::native) fn blocks_changed(before: &[BodyBlock], after: &[BodyBlock]) -> bool {
    before.len() != after.len()
        || before
            .iter()
            .zip(after)
            .any(|(left, right)| left.name != right.name || block_body_changed(left, right))
}

fn block_body_changed(left: &BodyBlock, right: &BodyBlock) -> bool {
    match (&left.typed, &right.typed) {
        (Some(l), Some(r)) => format!("{l:?}") != format!("{r:?}"),
        // Both un-lowered (no terminator) — an unchanged clone; a mixed None/Some is a real change.
        (None, None) => false,
        _ => true,
    }
}

pub(in crate::native) fn structured_plan(blocks: &[BodyBlock]) -> Option<StructuredPlan> {
    // A nested conditional or switch case can enter a continuation also reached by a sibling/enclosing
    // arm before either reaches its shared natural merge. The normal unique-merge synth can only
    // redirect direct predecessors of that merge, so the nested construct would otherwise escape through
    // the shared continuation. Privatize the continuation first; this is intentionally before the base
    // admission attempt because that malformed graph can look structurally admissible to the source-CFG
    // planner while still failing spirv-val. A no-op leaves every other graph byte-identical.
    let switch_private = super::clone_crossarm::privatize_switch_case_continuations(blocks);
    let deep_shared = super::clone_crossarm::privatize_deep_shared_continuations(&switch_private);
    if deep_shared.len() != blocks.len() {
        if let Some(plan) = structured_plan_ladder(&deep_shared, false) {
            return Some(plan);
        }
        if let Some(plan) = structured_plan_ladder(&deep_shared, true) {
            return Some(plan);
        }
    }

    // Two full passes of the attempt ladder. The FIRST runs with `allow_bare_exit=false` — byte-identical
    // to the pre-lever behavior, so every currently-admitting function is untouched. Only if the whole
    // ladder rejects (the function currently returns None → the primary fallback emits) do we re-run it
    // with `allow_bare_exit=true` (Keystone 2): every attempt —
    // including the cross-arm/synth clones — then may SKIP a block whose conditional arms are a bare
    // structured break/continue (one arm is an enclosing loop's merge/continue) instead of rejecting
    // `branch-no-merge`, with `bare_exit_escape_reason` rejecting any plan the skip unmasks a residual
    // unstructured exit in. Re-running the ENTIRE ladder (not a raw-blocks sweep) is required because a
    // merge-inloop function often needs a cross-arm/synth CLONE first, then the bare-exit skip on the
    // residual latch. The second pass is byte-neutral for admitting functions (never reached) and its only
    // new admissions are functions that today fall through to the fallback.
    if let Some(plan) = structured_plan_ladder(blocks, false) {
        return Some(plan);
    }
    if let Some(plan) = structured_plan_ladder(blocks, true) {
        return Some(plan);
    }
    // Reject-only shared-merge separation: when divergent return-like exits are the structural reason
    // a conditional has no natural merge, route them through one synthesized function exit and rerun
    // the unchanged ladder. A candidate is retained only when that shared exit makes the rejected CFG
    // structurable; a function that merely contains multiple returns but still rejects is untouched.
    // An already-admitted function never reaches this construction.
    if let Some(plan) = structured_plan_divergent_exit(blocks) {
        return Some(plan);
    }
    None
}

/// Final reject-only construction for divergent function exits. Return unification creates the common
/// post-dominator. If that phi-carrying exit is still shared with enclosing paths, clone only its
/// immediate shared predecessors for the nested entries; the ordinary ladder can then synthesize each
/// selection's private merge. This is the shared-merge separation C1 needs; already-admitted functions
/// never reach it.
pub(in crate::native) fn structured_plan_divergent_exit(
    blocks: &[BodyBlock],
) -> Option<StructuredPlan> {
    let single_exit = super::clone_crossarm::separate_divergent_selection_exits(blocks)?;
    let shared_exit = super::clone_crossarm::privatize_shared_phi_exit_predecessors(&single_exit);
    if !blocks_changed(&single_exit, &shared_exit) {
        return None;
    }
    for allow_bare_exit in [false, true] {
        if let Some(plan) = structured_plan_ladder(&shared_exit, allow_bare_exit) {
            return Some(plan);
        }
    }
    None
}

pub(in crate::native) fn structured_plan_ladder(
    blocks: &[BodyBlock],
    allow_bare_exit: bool,
) -> Option<StructuredPlan> {
    // Isolating the S15 trivial-cross-arm pre-pass to functions the base path cannot structure: try the
    // blocks as-is first, so every CURRENTLY-admitting function is byte-identical; only if that rejects,
    // retry on the trivial-pass-through-privatized graph (`privatize_trivial_cross_arm` — clone a shared
    // single-`br` arm so its header dominates it). A rejecting function with no such shape privatizes to
    // an identical-length graph, so we skip the redundant retry. The plan self-checks run inside
    // `structured_plan_inner4`, so this cannot over-admit the gate. See [[metal2vulkan-native-emitter]]
    // "S15 trivial cross-arm privatization". Global measurement override (`METAL2VULKAN_CONVERGE_INLOOP`):
    // force in-loop collision convergence on ALL attempts (not just the reject-triggered one below), to
    // measure the large-scale byte churn the broad form would cause. Default false → base-admitting
    // functions stay byte-identical.
    let force_converge = crate::env_vars::converge_inloop();
    if let Some(plan) =
        structured_plan_inner4(blocks, force_converge, false, false, allow_bare_exit)
    {
        return Some(plan);
    }
    let privatized = super::clone_crossarm::privatize_trivial_cross_arm(blocks);
    if privatized.len() != blocks.len() {
        if let Some(plan) =
            structured_plan_inner4(&privatized, force_converge, false, false, allow_bare_exit)
        {
            return Some(plan);
        }
    }
    // Third attempt: the merge-PRESERVING dominated-region clone for multi-successor / def-carrying
    // cross-arm shared arms (`privatize_region_cross_arm`). Unlike the trivial pass-through clone it
    // duplicates a whole dominated subtree up to its reconvergence boundary, so it rescues the dominant
    // `selection:cross-arm-shared` class. Applied AFTER the trivial pre-pass and computed on the
    // ORIGINAL blocks: this ordering, together with the existing plan self-checks in
    // `structured_plan_inner`, is floor-safe on the whole banked+frontier private capture set (G4 frontier byte-gate
    // empty, G5 banked floor 62 held). It does NOT reach the over-admitting shapes a chained
    // region-on-trivial variant hit (which needed a structured-exit completeness self-check that a
    // dominance model cannot express — see the journal). The clone also stays wired into the
    // spirv-val-gated `cfg_restructure` retry for the residual cross-arm shapes this does not admit.
    let region = super::clone_crossarm::privatize_region_cross_arm(blocks);
    if region.len() != blocks.len() {
        if let Some(plan) =
            structured_plan_inner4(&region, force_converge, false, false, allow_bare_exit)
        {
            return Some(plan);
        }
    }
    // Fourth attempt (M-B1): reject-triggered in-loop merge convergence. Only functions the base +
    // cross-arm attempts could NOT structure reach here, so this is byte-identical for every currently-
    // admitting function; it rescues the dominant `cond-phi-shared/loop-role/merge-inloop` class (07 and
    // the 107-fn bucket) by giving a loop a distinct merge when an IN-LOOP selection shares it. Floor-safe:
    // the retry cascade adopts the resulting plan only if it independently spirv-val-passes.
    if let Some(plan) = structured_plan_inner4(blocks, true, false, false, allow_bare_exit) {
        return Some(plan);
    }
    // Loop-exit convergence: a selection inside a loop may have a real in-loop convergence point even
    // though ordinary post-dominance reports the loop merge: paths that do not reach that point break
    // or continue the enclosing loop. Treat those loop-role paths as structured exits and retain the
    // proven in-loop convergence as the selection merge. This is reject-only and independently checks
    // every arm region, so base-admitting functions remain byte-identical and malformed escapes still
    // fall back. It intentionally runs before the multi-level-switch guard: the proof covers switch
    // arms too, whereas the later break-aware post-dominator attempt only handles direct conditionals.
    if blocks.len() <= LOOP_EXIT_SELECTION_MAX_BLOCKS {
        let lowered_switches = super::blocks::lower_loop_exit_switches(blocks);
        if blocks_changed(blocks, &lowered_switches) {
            // A loop-breaking switch must use the ladder form. Do not fall back to the raw `OpSwitch`
            // plan if this lowered graph declines: that raw form can look source-structured while
            // spirv-val rejects its loop-exit case construct.
            if let Some(plan) =
                structured_plan_inner5(&lowered_switches, true, false, false, allow_bare_exit, true)
            {
                return Some(plan);
            }
        } else if let Some(plan) =
            structured_plan_inner5(blocks, true, false, false, allow_bare_exit, true)
        {
            return Some(plan);
        }
    }
    // Terminal-return guards are not ordinary post-dominator selections: one arm may legitimately
    // exit through a shared function `ret`, while the other must continue through a private merge.
    // This reject-only attempt derives those continuation merges structurally, then reuses the normal
    // completeness and self-check gates. It is bounded to the same small CFGs as the other cross-arm
    // attempts, so a large descriptor kernel stays on its established fallback path.
    if blocks.len() <= TERMINAL_EXIT_SELECTION_MAX_BLOCKS {
        for &(converge, break_aware) in &[(false, false), (true, false), (true, true)] {
            if let Some(plan) = structured_plan_inner6(
                blocks,
                converge,
                break_aware,
                false,
                allow_bare_exit,
                false,
                true,
            ) {
                return Some(plan);
            }
        }
    }
    // Guard: skip the 5th/6th attempts for functions containing a SWITCH. The merge-inloop fix targets
    // conditional (2-arm) guarded breaks; these attempts only ADMIT the function, and an admitted switch
    // whose case-construct dominance the source-CFG self-check cannot verify (source-CFG dominance ≠
    // emitted structural dominance — dead-end #6) can emit invalid SPIR-V that the retry path would
    // otherwise handle validly (`Switch header does not structurally dominate its case construct`,
    // banked `02/387ebaa9` family). Leave only genuine multi-level-break switches to the repair path:
    // the M1b default (`switch_gate_excludes`) excludes just an in-loop switch with a loop-exiting arm;
    // a loop-free switch (or an in-loop switch whose arms all stay in the loop) is safe to admit.
    if switch_gate_excludes(blocks) {
        return None;
    }
    // Fifth attempt (Keystone 2): converge PLUS break-aware selection merges. The 4th attempt gives an
    // in-loop-collided loop a distinct merge and rotates its do-while latch to `{merge, continue}`, but
    // then a later in-loop GUARDED-BREAK selection (`if(!c) break; body`) still claims that loop merge as
    // its post-dominator (the break arm pollutes it), so `synth_unique_selection_merge` redirects the
    // latch's break edge and the plan rejects `branch-no-merge` on the latch (the residual
    // `cond-phi-shared/loop-role/merge-inloop` reject). Break-aware selection merges
    // (`break_aware_selection_merges`) cut the guarded break's edge to the loop merge, so the selection
    // reconverges at its non-break arm and never claims the loop merge — the latch is never redirected,
    // fixing the reject at its SOURCE (unlike LOOP_BREAK_PROTECT, which kept the break edge into the
    // claimed loop merge and over-admitted into `branches to the selection construct` invalidity;
    // dead-end #19). Reached ONLY after all four prior attempts reject, so every currently-admitting
    // function stays byte-identical; floor-safe since the retry cascade adopts the plan only if it
    // independently spirv-val-passes.
    if let Some(plan) = structured_plan_inner4(blocks, true, true, false, allow_bare_exit) {
        return Some(plan);
    }
    // Sixth attempt (Keystone 2): reject-triggered straddle-loop-merge restructure on the PRIMARY
    // path. `restructure_straddle_loop_merges` — the
    // proven M-B1/05 `cfg_restructure` retry pre-pass — gives a loop whose exit merge straddles an
    // enclosing construct its OWN pass-through merge, then the destraddled graph re-runs the inner
    // ladder (base → converge → converge+break-aware). Targets the `selection:straddle-loop-merge`
    // reject class (93 banked ok-INVALID rows, the largest CFG-reject bucket of the M-B2
    // `--primary-val-check` floor). Reached ONLY after every prior attempt rejects, so currently-
    // admitting functions stay byte-identical; the plan self-checks inside `structured_plan_inner`
    // still gate admission of the destraddled graph.
    {
        if let Some(destraddled) = restructure_straddle_loop_merges(blocks) {
            if let Some(plan) =
                structured_plan_inner4(&destraddled, force_converge, false, false, allow_bare_exit)
            {
                return Some(plan);
            }
            if let Some(plan) =
                structured_plan_inner4(&destraddled, true, false, false, allow_bare_exit)
            {
                return Some(plan);
            }
            if let Some(plan) =
                structured_plan_inner4(&destraddled, true, true, false, allow_bare_exit)
            {
                return Some(plan);
            }
        }
    }
    // Attempt 6b (Keystone 2 M3): the straddle whose loop-merge ⇄ enclosing-selection-merge collision
    // only MATERIALIZES once the converge/break-aware transforms split the blocks (`04/7da629e9`). The
    // 6th attempt's `restructure_straddle_loop_merges` derives its maps on the ORIGINAL blocks (converge
    // off, break-aware off) and returns `None` for it; running detection under converge+break-aware makes
    // the derived straddle visible, then the destraddled graph re-runs the inner ladder. Reached only
    // after every prior attempt rejects → admitting functions byte-identical.
    {
        if let Some(destraddled) = restructure_straddle_loop_merges_with(blocks, true, true) {
            if let Some(plan) =
                structured_plan_inner4(&destraddled, true, false, false, allow_bare_exit)
            {
                return Some(plan);
            }
            if let Some(plan) =
                structured_plan_inner4(&destraddled, true, true, false, allow_bare_exit)
            {
                return Some(plan);
            }
            // The straddle split can reveal a residual cross-arm-shared selection underneath (the loop
            // now has its own merge, but a post-loop selection still shares an arm). Chain the
            // merge-preserving region clone on the destraddled graph and re-run the ladder.
            let region2 = super::clone_crossarm::privatize_region_cross_arm(&destraddled);
            if region2.len() != destraddled.len() {
                if let Some(plan) =
                    structured_plan_inner4(&region2, true, false, false, allow_bare_exit)
                {
                    return Some(plan);
                }
                if let Some(plan) =
                    structured_plan_inner4(&region2, true, true, false, allow_bare_exit)
                {
                    return Some(plan);
                }
            }
        }
    }
    // Seventh attempt (Keystone 2): the ladder
    // rung the 66-case residual census found missing — the region-clone graph (attempt 3) is never
    // combined with converge/break-aware, yet the M2/O3 residual (a converged multi-exit dispatch arm
    // that is ALSO the enclosing guard's direct arm) needs BOTH the region privatization AND the
    // converge transforms. Reached only after every prior attempt rejects → admitting functions stay
    // byte-identical; the plan self-checks still gate admission of the cloned graph.
    if region.len() != blocks.len() {
        if let Some(plan) = structured_plan_inner4(&region, true, false, false, allow_bare_exit) {
            return Some(plan);
        }
        if let Some(plan) = structured_plan_inner4(&region, true, true, false, allow_bare_exit) {
            return Some(plan);
        }
    }
    // Eighth attempt (Keystone 2 M2):
    // the multi-exit shared-exit region clone. When `synth_multi_exit_merge` funnels a 2-exit loop through
    // a dispatch `M`, an exit ALSO reached from outside the loop is not `M`-dominated, so `M`'s dispatch
    // selection cannot own it (the M2 `selection:cond-phi-shared/loop-role/merge-inloop` residual —
    // `02/590c6cf2`). Enabling `multi_exit_clone` clones that exit's dominated forward region into an
    // `M`-dominated copy so both dispatch arms reconverge at a private merge. Reached ONLY after every
    // prior attempt rejects, so a function admitting earlier stays byte-identical — the clone must NOT
    // run on the base attempts (it corrupts an already-valid dispatch into a reject→repair). The
    // residual dispatch is only exposed once the converge/break-aware transforms split the in-loop
    // guard AND (for the `590c6cf2` shape) the region-cross-arm pre-pass privatizes the enclosing
    // selection, so the clone is retried across the same pre-pass combos the ladder uses. The plan
    // self-checks inside `structured_plan_inner4` still gate admission; the switch guard keeps the
    // dead-end #6 multi-level-break switches on the repair path.
    if !switch_gate_excludes(blocks) {
        for &(bl, cv, br) in &[
            (blocks, force_converge, false),
            (blocks, true, false),
            (blocks, true, true),
        ] {
            if let Some(plan) = structured_plan_inner4(bl, cv, br, true, allow_bare_exit) {
                return Some(plan);
            }
        }
        if region.len() != blocks.len() {
            for &(cv, br) in &[(force_converge, false), (true, false), (true, true)] {
                if let Some(plan) = structured_plan_inner4(&region, cv, br, true, allow_bare_exit) {
                    return Some(plan);
                }
            }
        }
    }
    // Ninth attempt (Keystone 2): the cross-arm-EDGE clone.
    // Self-check 3 rejects a DEFINITIVE cross-arm edge — a block deep in one selection arm
    // branching into a sibling arm of an ancestor selection (the 32-row ADMIT structured-exit family
    // self-check 2 misses). `privatize_cross_arm_edge` clones the sibling-arm block's dominated region
    // for the near-arm entry so the escape becomes a private, header-dominated reconvergence, then the
    // destraddled graph re-runs the inner ladder (base -> converge -> converge+break-aware). Reached
    // ONLY after every prior attempt rejects, so a currently-admitting function stays byte-identical; the
    // plan self-checks (including the cross-arm-edge self-check itself) still gate admission of the
    // cloned graph, so an unfixable residue rejects honestly and falls to repair.
    if blocks.len() <= CROSS_ARM_EDGE_MAX_BLOCKS {
        let cloned = super::clone_crossarm::privatize_cross_arm_edge(blocks);
        if cloned.len() != blocks.len() {
            for &(cv, br) in &[(force_converge, false), (true, false), (true, true)] {
                if let Some(plan) = structured_plan_inner4(&cloned, cv, br, false, allow_bare_exit)
                {
                    return Some(plan);
                }
            }
        }
    }
    // Tenth attempt (Keystone 2): the synthesis-aware
    // cross-arm-SHARED clone. `privatize_synthesized_cross_arm_shared` DERIVES the synthesized merge map
    // to LOCATE the raw `(header, arm)` of a `cross-arm-shared` violation whose merge is a synthesized
    // `%…lmerge.sel*` block, then clones `arm`'s region on the RAW blocks so the re-run synthesizes once
    // cleanly. Pairs with the multi-boundary compound (nested) handling. Reject-triggered so
    // admitting functions stay byte-identical; the plan self-checks gate the cloned result.
    if blocks.len() <= CROSS_ARM_EDGE_MAX_BLOCKS {
        for &(dcv, dbr) in &[(true, true), (force_converge, false), (true, false)] {
            let cloned = privatize_synthesized_cross_arm_shared(blocks, dcv, dbr);
            if cloned.len() != blocks.len() {
                for &(cv, br) in &[(force_converge, false), (true, false), (true, true)] {
                    if let Some(plan) =
                        structured_plan_inner4(&cloned, cv, br, false, allow_bare_exit)
                    {
                        return Some(plan);
                    }
                }
            }
        }
    }
    None
}

pub(in crate::native) fn is_switch_block(b: &BodyBlock) -> bool {
    // Read the structured terminator from the carrier (the sole substrate); a block whose lines did not
    // lower (`typed: None`) has no terminator and is not a switch.
    b.typed.as_ref().is_some_and(|carrier| {
        matches!(
            carrier.terminator,
            crate::native::tir::TirTerminator::Switch { .. }
        )
    })
}

/// Does any switch-terminating block form a MULTI-LEVEL BREAK — a switch inside a natural loop body
/// with an arm (case/default target) that EXITS that loop? That is the dead-end #6 shape (the
/// OpSwitch's arm targets the enclosing loop's merge, breaking the loop while skipping the switch's own
/// selection merge — `Switch header does not structurally dominate its case construct`), which the
/// source-CFG dominance self-check cannot reject, so the 5th/6th/7th attempts would over-admit it into
/// invalid SPIR-V. A switch NOT enclosed by any loop, or one whose every arm stays inside its enclosing
/// loop, reconverges normally and is safe to admit. This is the precise M1b narrowing (guard-off would
/// admit 35 more banked cases but break these 3: `02/387ebaa9`/`04/4b57a380`/`12/01d1972c`). See
/// [[metal2vulkan-native-emitter]] M1b.
pub(in crate::native) fn blocks_contain_multilevel_break_switch(blocks: &[BodyBlock]) -> bool {
    let switch_succs: HashMap<&str, Vec<String>> = blocks
        .iter()
        .filter(|b| is_switch_block(b))
        .map(|b| (b.name.as_str(), block_successors(b)))
        .collect();
    if switch_succs.is_empty() {
        return false;
    }
    let forest = analyze(blocks);
    for l in &forest.loops {
        let body: HashSet<&str> = l.body.iter().map(String::as_str).collect();
        for (name, succs) in &switch_succs {
            // The header's own multi-successor exit is a legal loop exit test, not a multi-level break.
            if *name == l.header.as_str() || !body.contains(name) {
                continue;
            }
            if succs.iter().any(|s| !body.contains(s.as_str())) {
                return true;
            }
        }
    }
    false
}

/// The switch guard for the 5th/6th/7th `structured_plan` attempts (M1b): exclude only the genuine
/// multi-level-break shape ([`blocks_contain_multilevel_break_switch`]) so a loop-free switch — or an
/// in-loop switch whose arms all stay inside the loop — reaches the attempts. (The prior blanket guard
/// excluded any switch-bearing function, the conservative front-stop for dead-end #6.)
pub(in crate::native) fn switch_gate_excludes(blocks: &[BodyBlock]) -> bool {
    blocks_contain_multilevel_break_switch(blocks)
}