[Mlir-commits] [mlir] [mlir][llvm] Fix crash in LLVM inliner when callee has no recognized terminator (PR #183949)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sat Feb 28 12:41:17 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Mehdi Amini (joker-eph)
<details>
<summary>Changes</summary>
When the callee of an llvm.call has a body block ending with an unregistered op (rather than a recognized LLVM terminator like llvm.return), the LLVM inliner's handleTerminator method was called with that unregistered op and crashed via a cast<LLVM::ReturnOp>() assertion or use-after-erase due to unresolved call result uses.
The root cause is that the generic MLIR verifier conservatively treats unregistered ops as potential terminators (using mightHaveTrait), so malformed IR of this shape passes verification. The inliner, however, assumes that the callee's terminator is a recognized LLVM op.
Fix by adding a guard in LLVMInlinerInterface::isLegalToInline() that refuses to inline a callee containing any block whose last operation does not have the IsTerminator trait. This prevents the crash and leaves the call site intact without any IR mutation.
Fixes #<!-- -->118766
---
Full diff: https://github.com/llvm/llvm-project/pull/183949.diff
2 Files Affected:
- (modified) mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp (+13)
- (modified) mlir/test/Dialect/LLVMIR/inlining.mlir (+17)
``````````diff
diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp
index 06c0af5445d5d..4b36cbc8bcf00 100644
--- a/mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp
@@ -725,6 +725,19 @@ struct LLVMInlinerInterface : public DialectInlinerInterface {
}))
return false;
}
+ // Refuse to inline if any block in the callee ends with an op that does
+ // not have the terminator trait. The MLIR verifier conservatively accepts
+ // unregistered ops as potential terminators, but the LLVM inliner's
+ // handleTerminator relies on the terminator being a recognized LLVM op
+ // (e.g. llvm.return). Inlining a callee with an unrecognized terminator
+ // would crash.
+ for (Block &block : funcOp.getBody()) {
+ if (!block.empty() && !block.back().hasTrait<OpTrait::IsTerminator>()) {
+ LDBG() << "Cannot inline " << funcOp.getSymName()
+ << ": block ends with non-terminator op";
+ return false;
+ }
+ }
return true;
}
diff --git a/mlir/test/Dialect/LLVMIR/inlining.mlir b/mlir/test/Dialect/LLVMIR/inlining.mlir
index 9a77c5e223110..63cc0e9fb4461 100644
--- a/mlir/test/Dialect/LLVMIR/inlining.mlir
+++ b/mlir/test/Dialect/LLVMIR/inlining.mlir
@@ -709,3 +709,20 @@ func.func @llvm_ret(%arg0 : i32) -> i32 {
// CHECK: return %[[R]]
return %res : i32
}
+
+// -----
+// Regression test for https://github.com/llvm/llvm-project/issues/118766
+// A callee whose body block ends with an unregistered (non-terminator) op used
+// to crash the inliner. The inliner should skip such callees instead.
+
+llvm.func @callee_malformed_terminator() -> i64 attributes {llvm.emit_c_interface} {
+ // Unregistered op acting as a fake terminator -- the function has no llvm.return.
+ "test.foo"() : () -> ()
+}
+
+// CHECK-LABEL: @caller_malformed_terminator
+llvm.func @caller_malformed_terminator() -> i64 attributes {llvm.emit_c_interface} {
+ // CHECK: llvm.call @callee_malformed_terminator
+ %0 = llvm.call @callee_malformed_terminator() : () -> i64
+ llvm.return %0 : i64
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/183949
More information about the Mlir-commits
mailing list