// allocated via malloc.
Operation::~Operation() {
assert(block == nullptr && "operation destroyed but still in a block");
-
+#ifndef NDEBUG
+ if (!use_empty()) {
+ {
+ InFlightDiagnostic diag =
+ emitOpError("operation destroyed but still has uses");
+ for (Operation *user : getUsers())
+ diag.attachNote(user->getLoc()) << "- use: " << *user << "\n";
+ }
+ llvm::report_fatal_error("operation destroyed but still has uses");
+ }
+#endif
// Explicitly run the destructors for the operands.
if (hasOperandStorage)
getOperandStorage().~OperandStorage();
// In a second pass, erase all of the replaced operations in reverse. This
// allows processing nested operations before their parent region is
- // destroyed.
- for (auto &repl : llvm::reverse(replacements))
+ // destroyed. Because we process in reverse order, producers may be deleted
+ // before their users (a pattern deleting a producer and then the consumer)
+ // so we first drop all uses explicitly.
+ for (auto &repl : llvm::reverse(replacements)) {
+ repl.first->dropAllUses();
repl.first->erase();
+ }
argConverter.applyRewrites(mapping);
// legalized.
if (failed(finalize(rewriter)))
return rewriterImpl.discardRewrites(), failure();
-
// After a successful conversion, apply rewrites if this is not an analysis
// conversion.
if (mode == OpConversionMode::Analysis)
rewriterImpl.discardRewrites();
- else
+ else {
rewriterImpl.applyRewrites();
+
+ // It is possible for a later pattern to erase an op that was originally
+ // identified as illegal and added to the trackedOps, remove it now after
+ // replacements have been computed.
+ if (trackedOps)
+ for (auto &repl : rewriterImpl.replacements)
+ trackedOps->erase(repl.first);
+ }
return success();
}
// expected-remark@+1 {{op 'std.return' is not legalizable}}
return
}
+
+
+// -----
+
+
+// Check that a conversion pattern on `test.blackhole` can mark the producer
+// for deletion.
+// CHECK-LABEL: @blackhole
+func @blackhole() {
+ %input = "test.blackhole_producer"() : () -> (i32)
+ "test.blackhole"(%input) : (i32) -> ()
+ // expected-remark@+1 {{op 'std.return' is not legalizable}}
+ return
+}
let assemblyFormat = "$depth attr-dict";
}
+// Test legalization pattern: this op will be erase and will also erase the
+// producer of its operand.
+def BlackHoleOp : TEST_Op<"blackhole">,
+ Arguments<(ins AnyType)>;
+
//===----------------------------------------------------------------------===//
// Test Type Legalization
//===----------------------------------------------------------------------===//
return success();
};
};
+
+// This pattern matches `test.blackhole` and delete this op and its producer.
+struct TestReplaceEraseOp : public OpRewritePattern<BlackHoleOp> {
+ using OpRewritePattern<BlackHoleOp>::OpRewritePattern;
+
+ LogicalResult matchAndRewrite(BlackHoleOp op,
+ PatternRewriter &rewriter) const final {
+ Operation *producer = op.getOperand().getDefiningOp();
+ // Always erase the user before the producer, the framework should handle
+ // this correctly.
+ rewriter.eraseOp(op);
+ rewriter.eraseOp(producer);
+ return success();
+ };
+};
} // namespace
namespace {
TestChangeProducerTypeI32ToF32, TestChangeProducerTypeF32ToF64,
TestChangeProducerTypeF32ToInvalid, TestUpdateConsumerType,
TestNonRootReplacement, TestBoundedRecursiveRewrite,
- TestNestedOpCreationUndoRewrite>(&getContext());
+ TestNestedOpCreationUndoRewrite, TestReplaceEraseOp>(
+ &getContext());
patterns.add<TestDropOpSignatureConversion>(&getContext(), converter);
mlir::populateFuncOpTypeConversionPattern(patterns, converter);
mlir::populateCallOpTypeConversionPattern(patterns, converter);