Index: mlir/lib/IR/AsmPrinter.cpp =================================================================== --- mlir/lib/IR/AsmPrinter.cpp +++ mlir/lib/IR/AsmPrinter.cpp @@ -25,6 +25,7 @@ #include "mlir/IR/OpImplementation.h" #include "mlir/IR/Operation.h" #include "mlir/IR/SubElementInterfaces.h" +#include "mlir/IR/Verifier.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" @@ -40,6 +41,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/Regex.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/Threading.h" #include @@ -1245,9 +1247,37 @@ } // namespace detail } // namespace mlir +/// Verifies the operation and switches to generic op printing if verification +/// fails. We need to do this because custom print functions may fail for +/// invalid ops. +static OpPrintingFlags verifyOpAndAdjustFlags(Operation *op, + OpPrintingFlags printerFlags) { + // Protection from infinite recursion: operation verification may want to + // print the operation itself, causing this function to be called recursively. + thread_local bool alwaysPrintGenericForm = false; + if (alwaysPrintGenericForm) + return printerFlags.printGenericOpForm(); + + if (!printerFlags.shouldPrintGenericOpForm()) { + // Ignore errors emitted by the verifier. We check the thread id to avoid + // consuming other threads' errors. + auto parentThreadId = llvm::get_threadid(); + ScopedDiagnosticHandler diagHandler(op->getContext(), [&](Diagnostic &) { + return success(parentThreadId == llvm::get_threadid()); + }); + alwaysPrintGenericForm = true; + if (failed(verify(op))) + printerFlags.printGenericOpForm(); + alwaysPrintGenericForm = false; + } + + return printerFlags; +} + AsmState::AsmState(Operation *op, const OpPrintingFlags &printerFlags, LocationMap *locationMap) - : impl(std::make_unique(op, printerFlags, locationMap)) {} + : impl(std::make_unique( + op, verifyOpAndAdjustFlags(op, printerFlags), locationMap)) {} AsmState::~AsmState() = default; const OpPrintingFlags &AsmState::getPrinterFlags() const { Index: mlir/lib/IR/Operation.cpp =================================================================== --- mlir/lib/IR/Operation.cpp +++ mlir/lib/IR/Operation.cpp @@ -1097,6 +1097,8 @@ // Check that any value that is used by an operation is defined in the // same region as either an operation result. auto *operandRegion = operand.getParentRegion(); + if (!operandRegion) + return op.emitError("operation's operand is unlinked"); if (!region.isAncestor(operandRegion)) { return op.emitOpError("using value defined outside the region") .attachNote(isolatedOp->getLoc()) Index: mlir/test/Conversion/AffineToStandard/lower-affine.mlir =================================================================== --- mlir/test/Conversion/AffineToStandard/lower-affine.mlir +++ mlir/test/Conversion/AffineToStandard/lower-affine.mlir @@ -1,4 +1,5 @@ -// RUN: mlir-opt -lower-affine %s | FileCheck %s +// `-debug` is used here to check that we don't crash when it's specified. +// RUN: mlir-opt -lower-affine -debug %s | FileCheck %s // CHECK-LABEL: func @empty() { func @empty() { Index: mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir =================================================================== --- mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir +++ mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir @@ -1,4 +1,5 @@ -// RUN: mlir-opt -allow-unregistered-dialect -convert-scf-to-cf %s | FileCheck %s +// `-debug` is used here to check that we don't crash when it's specified. +// RUN: mlir-opt -allow-unregistered-dialect -convert-scf-to-cf -debug %s | FileCheck %s // CHECK-LABEL: func @simple_std_for_loop(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index) { // CHECK-NEXT: cf.br ^bb1(%{{.*}} : index)