[Mlir-commits] [mlir] [mlir][inliner] Ask the terminator dialect before the single-block fast path (PR #237936)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Nov 2 04:06:52 PST 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-llvm

Author: VeeraBabu Jakkula  (JakkulaVeerababu)

<details>
<summary>Changes</summary>

### What was wrong

MLIR's inliner was crashing when trying to inline a `func.func` that ended with `llvm.unreachable` or `ub.unreachable`. Before taking the single-block fast path, it only asked the dialect of the function (`func`), not the dialect of the last instruction. The last instruction is then handled by its own dialect's inliner interface, which could not handle the fast path:

- `LLVMInlinerInterface::handleTerminator` casts the terminator to `llvm.return`, so `llvm.unreachable` failed the cast assertion (#<!-- -->237592).
- `UBInlinerInterface` does not implement `handleTerminator`, so `ub.unreachable` hit the default `llvm_unreachable` (#<!-- -->237590).

### What I changed

I updated the inliner to check both the function's dialect and the last instruction's dialect before taking the fast path. The LLVM dialect already disables the fast path for `llvm.unreachable`, so this alone fixes #<!-- -->237592.

I also added proper handling for `ub.unreachable` in the UB dialect: it disables the fast path for it and keeps it in place when the inlined region has more than one block, the same way the LLVM dialect handles `llvm.unreachable`.

Finally, I added two tests based on both reported reproducers. Both crashes are fixed, the new tests fail without the change, and all 122 related inliner tests are passing.

Fixes #<!-- -->237592
Fixes #<!-- -->237590

This change was made with the help of Claude Code (AI assistant). I reviewed it and can explain every part of it.

---
Full diff: https://github.com/llvm/llvm-project/pull/237936.diff


4 Files Affected:

- (modified) mlir/lib/Dialect/UB/IR/UBOps.cpp (+15) 
- (modified) mlir/lib/Transforms/Utils/InliningUtils.cpp (+13-1) 
- (modified) mlir/test/Dialect/LLVMIR/inlining.mlir (+17) 
- (modified) mlir/test/Transforms/inlining.mlir (+16) 


``````````diff
diff --git a/mlir/lib/Dialect/UB/IR/UBOps.cpp b/mlir/lib/Dialect/UB/IR/UBOps.cpp
index ee523f9522953d..64bdd4797dd633 100644
--- a/mlir/lib/Dialect/UB/IR/UBOps.cpp
+++ b/mlir/lib/Dialect/UB/IR/UBOps.cpp
@@ -29,6 +29,21 @@ struct UBInlinerInterface : public DialectInlinerInterface {
   bool isLegalToInline(Operation *, Region *, bool, IRMapping &) const final {
     return true;
   }
+
+  /// `ub.unreachable` does not return to the caller, so it stays in place when
+  /// the inlined region has more than one block.
+  void handleTerminator(Operation *op, Block *newDest) const final {
+    assert(isa<UnreachableOp>(op) && "unexpected UB terminator");
+  }
+
+  /// The single-block fast path erases the terminator and continues the
+  /// inlined block with the operations after the call, which is wrong for
+  /// `ub.unreachable`.
+  bool allowSingleBlockOptimization(
+      iterator_range<Region::iterator> inlinedBlocks) const final {
+    return inlinedBlocks.empty() ||
+           !isa<UnreachableOp>(inlinedBlocks.begin()->getTerminator());
+  }
 };
 } // namespace
 
diff --git a/mlir/lib/Transforms/Utils/InliningUtils.cpp b/mlir/lib/Transforms/Utils/InliningUtils.cpp
index ce4d6ad71b6d3a..a0c12e417e067a 100644
--- a/mlir/lib/Transforms/Utils/InliningUtils.cpp
+++ b/mlir/lib/Transforms/Utils/InliningUtils.cpp
@@ -151,7 +151,19 @@ bool InlinerInterface::allowSingleBlockOptimization(
   }
   auto *handler = getInterfaceFor(inlinedBlocks.begin()->getParentOp());
   assert(handler && "expected valid dialect handler");
-  return handler->allowSingleBlockOptimization(inlinedBlocks);
+  if (!handler->allowSingleBlockOptimization(inlinedBlocks))
+    return false;
+
+  // The terminator is handled by the interface of its own dialect, which may
+  // differ from the dialect of the parent operation, so it must also allow the
+  // fast path.
+  Block &firstBlock = *inlinedBlocks.begin();
+  if (!firstBlock.mightHaveTerminator())
+    return true;
+  auto *terminatorHandler = getInterfaceFor(firstBlock.getTerminator());
+  if (!terminatorHandler || terminatorHandler == handler)
+    return true;
+  return terminatorHandler->allowSingleBlockOptimization(inlinedBlocks);
 }
 
 Value InlinerInterface::handleArgument(OpBuilder &builder, Operation *call,
diff --git a/mlir/test/Dialect/LLVMIR/inlining.mlir b/mlir/test/Dialect/LLVMIR/inlining.mlir
index 30616eaa715359..3c5203b72637f3 100644
--- a/mlir/test/Dialect/LLVMIR/inlining.mlir
+++ b/mlir/test/Dialect/LLVMIR/inlining.mlir
@@ -794,3 +794,20 @@ llvm.func @caller_cf_br_terminator(%arg0 : i64) -> i64 {
   %0 = llvm.call @callee_cf_br_terminator(%arg0) : (i64) -> i64
   llvm.return %0 : i64
 }
+
+// -----
+
+// A func.func callee that ends in llvm.unreachable must not take the
+// single-block fast path, which only handles llvm.return.
+
+func.func @func_unreachable_callee() {
+  llvm.unreachable
+}
+
+// CHECK-LABEL: func.func @func_unreachable_caller
+// CHECK-NOT: call @func_unreachable_callee
+// CHECK: llvm.unreachable
+func.func @func_unreachable_caller() {
+  call @func_unreachable_callee() : () -> ()
+  return
+}
diff --git a/mlir/test/Transforms/inlining.mlir b/mlir/test/Transforms/inlining.mlir
index 5b3b1d8cd211f4..5c041da8ebf598 100644
--- a/mlir/test/Transforms/inlining.mlir
+++ b/mlir/test/Transforms/inlining.mlir
@@ -420,3 +420,19 @@ func.func @inline_functional_region_multiblock(%arg0: i32) -> i32 {
   %0 = call_indirect %fn(%arg0) : (i32) -> i32
   return %0 : i32
 }
+
+// A func.func callee that ends in ub.unreachable must not take the single-block
+// fast path, which would erase the terminator.
+func.func @func_with_ub_unreachable() {
+  ub.unreachable
+}
+
+// CHECK-LABEL: func @inline_ub_unreachable(
+func.func @inline_ub_unreachable() {
+  // CHECK-NOT:  call
+  // CHECK:      ub.unreachable
+  // CHECK-NEXT: ^{{.*}}:
+  // CHECK-NEXT: return
+  call @func_with_ub_unreachable() : () -> ()
+  return
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/237936


More information about the Mlir-commits mailing list