Skip to content

[AIE] Fix findPrologueEpilogue assert on loops with multiple non-loop preds#1005

Merged
martien-de-jong merged 1 commit into
Xilinx:aie-publicfrom
erwei-xilinx:fix-findPrologueEpilogue
May 19, 2026
Merged

[AIE] Fix findPrologueEpilogue assert on loops with multiple non-loop preds#1005
martien-de-jong merged 1 commit into
Xilinx:aie-publicfrom
erwei-xilinx:fix-findPrologueEpilogue

Conversation

@erwei-xilinx
Copy link
Copy Markdown
Contributor

@erwei-xilinx erwei-xilinx commented May 18, 2026

Summary

Fix llc assertion failure on legitimate non-pipelined single-MBB loops whose loop header has multiple non-loop predecessors.

AIELoopUtils::findPrologueEpilogue (introduced in 5fbf293) asserted that a single-MBB loop has at most one non-loop predecessor / successor. That invariant only holds for pipelined loops, which was fine when the only caller was PipelineExtractor's constructor (gated by pipelining filters that guarantee uniqueness).

007425c ("[AIEX] Emit unified pipeliner optimization remarks for all loop types") added a second caller in InterBlockScheduling::emitLoopRemarks that runs on every single-MBB loop, pipelined or not. Non-pipelined loops can legitimately have multiple non-loop predecessors — e.g. an outer do-while loop reached both from a function-entry block and from an end-of-iteration cleanup block, or a halt-spin self-edge combined with normal entry paths. So the assertion now fires on legal CFGs.

Fix

Make findPrologueEpilogue return nullptr for the ambiguous side instead of asserting. The remark-emitter call site already has if (!PrologueMBB || !EpilogueMBB) continue;. The pipeliner call site keeps its own assert(PrologueMBB && EpilogueMBB && "Pipelined loop must have a unique prologue and epilogue"), so pipelined code that ever violates the invariant still fails loudly at the only site where it actually matters.

Crash signature

llc: llvm/lib/Target/AIE/Utils/AIELoopUtils.cpp:275:
  Assertion `!Prologue && "Single-MBB loop has multiple non-loop predecessors"' failed.

Running pass 'PostRA Machine Instruction Scheduler' on function '@core_0_2'
  llvm::AIELoopUtils::findPrologueEpilogue
  llvm::AIE::InterBlockScheduling::emitLoopRemarks

Impact

Currently blocking the mlir-air Ryzen AI CI on both NPU Phoenix (amd8845hs, npu1) and NPU Strix (amdhx370, npu2) runners. Reference failing run: Xilinx/mlir-air actions run 26049388494.

NPU Phoenix (npu1) — 9 failing tests:

  • xrt/02_mul_shim_1x1/run_peano.lit
  • xrt/03_mul_L1L2_1x1/run_peano.lit
  • xrt/06_add_shim_bf16/run_peano.lit
  • xrt/07_extern_linalg/run_npu1_peano.lit
  • xrt/28_gemm_loop_nest_bf16/run_peano.lit
  • xrt/29_gemm_4_level_tiling_extern_vec_4x4_bf16/run_peano.lit
  • xrt/34_cascade_vecadd/run_npu1_peano.lit
  • xrt/36_cascade_vecmat_i32/run_npu1_peano.lit
  • xrt/38_cascade_vecmat_transform_2x4_i32/run_npu1_peano.lit

NPU Strix (npu2) — 11 failing tests (superset of the npu1 list with _npu2_peano variants, plus 2 npu2-only tests: 03_mul_L1L2_1x1/run_peano_elf.lit and 30_mul_rtp_1x1/run_npu2_peano.lit).

All 9 npu1 tests verified to pass on real NPU Phoenix hardware with this patch applied to nightly 21.0.0.2026051801+55604435. The npu2 failures share the same root cause (same llc binary, same findPrologueEpilogue assertion in the same crash stack), so the fix should unblock them as well.

Bisect

nightly result
2026051501+f4933ef7 (May 15) OK — last good
2026051601+55604435 (May 16) CRASH — first bad

Test

Added llvm/test/CodeGen/AIE/aie2/schedule/postpipeliner/loop-remark-multi-pred.mir: minimal MIR with a single-MBB loop reached from two non-loop predecessors (function entry and end-of-iteration cleanup). Verified to fail on aie-public HEAD without the fix and pass with it.

… preds

Single-MBB loops with multiple non-loop predecessors are legal (e.g.
do-while style outer loops, or halt-spin self-edges combined with an
end-of-iteration backedge from a separate cleanup block). The pipeliner's
PipelineExtractor was historically the only caller of this code, and
pipelining filters guarantee a unique prologue/epilogue -- so the asserts
were safe in that context. After the helper was extracted in 5fbf293 and
reused from emitLoopRemarks in 007425c (which runs on every single-MBB
loop, pipelined or not), the assertion began firing on legitimate code.

Make findPrologueEpilogue return nullptr for the ambiguous side instead
of asserting. The pipeliner call site keeps its own assert(PrologueMBB &&
EpilogueMBB ...) so pipelined code that ever violated the invariant still
fires loudly. The remark emitter already had a nullptr guard.
@erwei-xilinx
Copy link
Copy Markdown
Contributor Author

Cc: @F-Stuckmann

erwei-xilinx added a commit to Xilinx/mlir-air that referenced this pull request May 19, 2026
…llvm-aie#1005 lands) (#1617)

Nightly llvm-aie 2026-05-16 onwards (commit 55604435 and later) crash llc
on legitimate non-pipelined single-MBB loops via an assert in
AIELoopUtils::findPrologueEpilogue, blocking 9+ Ryzen AI xrt tests on
both NPU Phoenix and NPU Strix.

Pin to the last known-good nightly (2026-05-15, f4933ef7) in both the
Ryzen AI CI workflow and the developer wheel-install script until the
upstream fix lands. See Xilinx/llvm-aie#1005 for the root cause and fix.

Revert this commit once a clean llvm-aie nightly is published.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martien-de-jong martien-de-jong merged commit cdec64b into Xilinx:aie-public May 19, 2026
8 checks passed
@martien-de-jong
Copy link
Copy Markdown
Collaborator

Thanks for fixing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants