[Mlir-commits] [mlir] 765580d - [mlir] Fix crash in dialect conversion for detached root ops (#185068)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Mar 13 08:50:00 PDT 2026


Author: Mehdi Amini
Date: 2026-03-13T15:49:54Z
New Revision: 765580dee2e27309968effe13a34b878db2e6e35

URL: https://github.com/llvm/llvm-project/commit/765580dee2e27309968effe13a34b878db2e6e35
DIFF: https://github.com/llvm/llvm-project/commit/765580dee2e27309968effe13a34b878db2e6e35.diff

LOG: [mlir] Fix crash in dialect conversion for detached root ops (#185068)

When running dialect conversion with --no-implicit-module, the root op
is
parsed without a wrapping module and then detached from its temporary
parsing
block (block == nullptr). If a conversion pattern replaces this detached
root
op, ReplaceOperationRewrite::commit() would crash with a null pointer
dereference when calling op->getBlock()->getOperations().remove(op).

Fix this with two complementary changes:

1. In ReplaceOperationRewrite::commit(), add a guard that calls
   reportFatalInternalError when op->getBlock() is null. This turns the
   opaque null-pointer crash into a clear diagnostic pointing at the API
   misuse.

2. Make --convert-func-to-spirv explicitly reject detached top-level ops
at
pass startup with a clear diagnostic rather than letting the conversion
   framework abort with a fatal error.

Add a regression test in mlir/test/Conversion/ConvertToSPIRV/ that
verifies
the diagnostic is emitted instead of crashing.

Fixes #60491
Assisted-by: Claude Code

Added: 
    mlir/test/Conversion/ConvertToSPIRV/detached-root.mlir

Modified: 
    mlir/lib/Conversion/FuncToSPIRV/FuncToSPIRVPass.cpp
    mlir/lib/Transforms/Utils/DialectConversion.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Conversion/FuncToSPIRV/FuncToSPIRVPass.cpp b/mlir/lib/Conversion/FuncToSPIRV/FuncToSPIRVPass.cpp
index c0439a4033eac..ec6f4604f1a68 100644
--- a/mlir/lib/Conversion/FuncToSPIRV/FuncToSPIRVPass.cpp
+++ b/mlir/lib/Conversion/FuncToSPIRV/FuncToSPIRVPass.cpp
@@ -13,6 +13,7 @@
 #include "mlir/Conversion/FuncToSPIRV/FuncToSPIRVPass.h"
 
 #include "mlir/Conversion/FuncToSPIRV/FuncToSPIRV.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
 #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
 #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
 
@@ -36,6 +37,17 @@ void ConvertFuncToSPIRVPass::runOnOperation() {
   MLIRContext *context = &getContext();
   Operation *op = getOperation();
 
+  // This pass requires the target function to be nested inside a block so
+  // that the dialect conversion framework can properly replace or move it.
+  // Running it on a detached top-level op (e.g., via --no-implicit-module) is
+  // unsupported; wrap the input in a module op first.
+  if (!op->getBlock() && isa<func::FuncOp>(op)) {
+    op->emitError("'") << getArgument()
+                       << "' pass requires the target operation to be nested "
+                          "in a block; consider wrapping the input in a module";
+    return signalPassFailure();
+  }
+
   auto targetAttr = spirv::lookupTargetEnvOrDefault(op);
   std::unique_ptr<ConversionTarget> target =
       SPIRVConversionTarget::get(targetAttr);

diff  --git a/mlir/lib/Transforms/Utils/DialectConversion.cpp b/mlir/lib/Transforms/Utils/DialectConversion.cpp
index ad3ee5d20a534..cf59234ded233 100644
--- a/mlir/lib/Transforms/Utils/DialectConversion.cpp
+++ b/mlir/lib/Transforms/Utils/DialectConversion.cpp
@@ -1306,6 +1306,11 @@ void ReplaceOperationRewrite::commit(RewriterBase &rewriter) {
 
   // Do not erase the operation yet. It may still be referenced in `mapping`.
   // Just unlink it for now and erase it during cleanup.
+  if (!op->getBlock())
+    llvm::reportFatalInternalError(
+        "dialect conversion attempted to replace a root operation that has no "
+        "parent block; the pass must ensure its target op is nested in a "
+        "block");
   op->getBlock()->getOperations().remove(op);
 }
 
@@ -3459,6 +3464,7 @@ LogicalResult ConversionPatternRewriter::legalize(Region *r) {
 LogicalResult OperationConverter::applyConversion(ArrayRef<Operation *> ops) {
   // Convert each operation and discard rewrites on failure.
   ConversionPatternRewriterImpl &rewriterImpl = rewriter.getImpl();
+
   LogicalResult status = legalizeOperations(ops, /*onFailure=*/[&]() {
     // Dialect conversion failed.
     if (rewriterImpl.config.allowPatternRollback) {

diff  --git a/mlir/test/Conversion/ConvertToSPIRV/detached-root.mlir b/mlir/test/Conversion/ConvertToSPIRV/detached-root.mlir
new file mode 100644
index 0000000000000..87bae9b777a62
--- /dev/null
+++ b/mlir/test/Conversion/ConvertToSPIRV/detached-root.mlir
@@ -0,0 +1,10 @@
+// RUN: mlir-opt --no-implicit-module --convert-func-to-spirv -verify-diagnostics %s
+
+// Verify that applying --convert-func-to-spirv to a detached top-level op
+// (via --no-implicit-module) produces a clear diagnostic rather than crashing.
+// The pass should refuse to operate on an op that has no parent block.
+//
+// Regression test for https://github.com/llvm/llvm-project/issues/60491
+
+// expected-error at below {{'convert-func-to-spirv' pass requires the target operation to be nested in a block}}
+func.func private @foo()


        


More information about the Mlir-commits mailing list