[Mlir-commits] [mlir] [mlir] Improve dialect conversion failure diagnostics (PR #182729)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Feb 22 00:12:31 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-core

Author: Jeongseok Son (jeongseokson)

<details>
<summary>Changes</summary>

This PR improves MLIR dialect conversion failure diagnostics when legalization fails.

Today, the error often identifies only the operation name (and, in partial conversion, whether it was explicitly marked illegal), which can make it hard to understand why a conversion failed.

This change enriches the diagnostic with:
- operand/result type context
- the conversion pattern that was rejected (when available)
- a fallback message when no conversion pattern matched

### Examples

#### Explicitly Illegal Operation
```
Before:
failed to legalize operation 'test.type_consumer' that was explicitly marked illegal

After:
failed to legalize operation 'test.type_consumer' that was explicitly marked illegal; operands ('f32'), results (); rejected by conversion pattern 'test.type_consumer -> ()'
```

#### Fallback example (no pattern matched):
```
failed to legalize operation 'test.illegal_op_f'; operands (), results ('i32'); no conversion pattern matched
```

### Tests:
- Updated `mlir/test/Transforms/test-legalizer.mlir` to check the richer diagnostic text
- Added coverage for the `no conversion pattern matched` path

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


2 Files Affected:

- (modified) mlir/lib/Transforms/Utils/DialectConversion.cpp (+52-4) 
- (modified) mlir/test/Transforms/test-legalizer.mlir (+2-2) 


``````````diff
diff --git a/mlir/lib/Transforms/Utils/DialectConversion.cpp b/mlir/lib/Transforms/Utils/DialectConversion.cpp
index c5facc32e4461..dda33918c742d 100644
--- a/mlir/lib/Transforms/Utils/DialectConversion.cpp
+++ b/mlir/lib/Transforms/Utils/DialectConversion.cpp
@@ -25,7 +25,9 @@
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
 #include <optional>
+#include <string>
 #include <utility>
 
 using namespace mlir;
@@ -57,6 +59,20 @@ static void logFailure(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args) {
   });
 }
 
+/// Format a conversion pattern as "root-op -> (generated-op, ...)".
+static std::string formatPatternForDiagnostics(const Pattern &pattern) {
+  std::string patternStr;
+  llvm::raw_string_ostream os(patternStr);
+  if (std::optional<OperationName> root = pattern.getRootKind())
+    os << root->getStringRef();
+  else
+    os << "*";
+  os << " -> (";
+  llvm::interleaveComma(pattern.getGeneratedOps(), os);
+  os << ")";
+  return patternStr;
+}
+
 /// Helper function that computes an insertion point where the given value is
 /// defined and can be used without a dominance violation.
 static OpBuilder::InsertPoint computeInsertPoint(Value value) {
@@ -2434,6 +2450,10 @@ class OperationLegalizer {
   /// was legalized, failure otherwise.
   LogicalResult legalize(Operation *op);
 
+  /// Returns the most recently attempted conversion pattern on this operation
+  /// that failed to match.
+  const Pattern *getLastFailurePattern(Operation *op) const;
+
   /// Returns the conversion target in use by the legalizer.
   const ConversionTarget &getTarget() { return target; }
 
@@ -2510,6 +2530,9 @@ class OperationLegalizer {
 
   /// The pattern applicator to use for conversions.
   PatternApplicator applicator;
+
+  /// The last conversion pattern that failed to match for a given op.
+  DenseMap<Operation *, const Pattern *> failedPatternByOp;
 };
 } // namespace
 
@@ -2530,6 +2553,13 @@ bool OperationLegalizer::isIllegal(Operation *op) const {
   return target.isIllegal(op);
 }
 
+const Pattern *OperationLegalizer::getLastFailurePattern(Operation *op) const {
+  auto it = failedPatternByOp.find(op);
+  if (it == failedPatternByOp.end())
+    return nullptr;
+  return it->second;
+}
+
 LogicalResult OperationLegalizer::legalize(Operation *op) {
 #ifndef NDEBUG
   const char *logLineComment =
@@ -2540,6 +2570,7 @@ LogicalResult OperationLegalizer::legalize(Operation *op) {
 
   // Check to see if the operation is ignored and doesn't need to be converted.
   bool isIgnored = rewriter.getImpl().isOpIgnored(op);
+  failedPatternByOp.erase(op);
 
   LLVM_DEBUG({
     logger.getOStream() << "\n";
@@ -2761,6 +2792,7 @@ LogicalResult OperationLegalizer::legalizeWithPattern(Operation *op) {
   RewriterState curState = rewriterImpl.getCurrentState();
   auto onFailure = [&](const Pattern &pattern) {
     assert(rewriterImpl.pendingRootUpdates.empty() && "dangling root updates");
+    failedPatternByOp[op] = &pattern;
     if (!rewriterImpl.config.allowPatternRollback) {
       // Erase all unresolved materializations.
       for (auto op : rewriterImpl.patternMaterializations) {
@@ -2824,6 +2856,8 @@ LogicalResult OperationLegalizer::legalizeWithPattern(Operation *op) {
     }
     if (config.listener)
       config.listener->notifyPatternEnd(pattern, result);
+    if (succeeded(result))
+      failedPatternByOp.erase(op);
     return result;
   };
 
@@ -3304,6 +3338,22 @@ struct OperationConverter {
 LogicalResult OperationConverter::convert(Operation *op,
                                           bool isRecursiveLegalization) {
   const ConversionConfig &config = rewriter.getConfig();
+  auto emitFailedToLegalizeDiag = [&](bool wasExplicitlyIllegal) {
+    InFlightDiagnostic diag =
+        op->emitError() << "failed to legalize operation '" << op->getName()
+                        << "'";
+    if (wasExplicitlyIllegal)
+      diag << " that was explicitly marked illegal";
+
+    diag << "; operands (" << op->getOperandTypes() << "), results ("
+         << op->getResultTypes() << ")";
+    if (const Pattern *pattern = opLegalizer.getLastFailurePattern(op)) {
+      diag << "; rejected by conversion pattern '"
+           << formatPatternForDiagnostics(*pattern) << "'";
+    } else {
+      diag << "; no conversion pattern matched";
+    }
+  };
 
   // Legalize the given operation.
   if (failed(opLegalizer.legalize(op))) {
@@ -3311,8 +3361,7 @@ LogicalResult OperationConverter::convert(Operation *op,
     // Full conversions expect all operations to be converted.
     if (mode == OpConversionMode::Full) {
       if (!isRecursiveLegalization)
-        op->emitError() << "failed to legalize operation '" << op->getName()
-                        << "'";
+        emitFailedToLegalizeDiag(/*wasExplicitlyIllegal=*/false);
       return failure();
     }
     // Partial conversions allow conversions to fail iff the operation was not
@@ -3321,8 +3370,7 @@ LogicalResult OperationConverter::convert(Operation *op,
     if (mode == OpConversionMode::Partial) {
       if (opLegalizer.isIllegal(op)) {
         if (!isRecursiveLegalization)
-          op->emitError() << "failed to legalize operation '" << op->getName()
-                          << "' that was explicitly marked illegal";
+          emitFailedToLegalizeDiag(/*wasExplicitlyIllegal=*/true);
         return failure();
       }
       if (config.unlegalizedOps && !isRecursiveLegalization)
diff --git a/mlir/test/Transforms/test-legalizer.mlir b/mlir/test/Transforms/test-legalizer.mlir
index 8d854aff1992f..d385dcc1dcbeb 100644
--- a/mlir/test/Transforms/test-legalizer.mlir
+++ b/mlir/test/Transforms/test-legalizer.mlir
@@ -224,7 +224,7 @@ func.func @bounded_recursion() {
 builtin.module {
 
   func.func @fail_to_convert_illegal_op() -> i32 {
-    // expected-error at +1 {{failed to legalize operation 'test.illegal_op_f'}}
+    // expected-error at +1 {{failed to legalize operation 'test.illegal_op_f'; operands (), results ('i32'); no conversion pattern matched}}
     %result = "test.illegal_op_f"() : () -> (i32)
     return %result : i32
   }
@@ -432,7 +432,7 @@ func.func @test_lookup_without_converter() {
 // expected-remark at -1 {{applyPartialConversion failed}}
 
 func.func @test_skip_1to1_pattern(%arg0: f32) {
-  // expected-error at +1 {{failed to legalize operation 'test.type_consumer'}}
+  // expected-error at +1 {{failed to legalize operation 'test.type_consumer' that was explicitly marked illegal; operands ('f32'), results (); rejected by conversion pattern 'test.type_consumer -> ()'}}
   "test.type_consumer"(%arg0) : (f32) -> ()
   return
 }

``````````

</details>


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


More information about the Mlir-commits mailing list