diff --git a/mlir/docs/OpDefinitions.md b/mlir/docs/OpDefinitions.md --- a/mlir/docs/OpDefinitions.md +++ b/mlir/docs/OpDefinitions.md @@ -1526,6 +1526,19 @@ ## Appendix +### Reporting deprecation + +Classes/defs can be marked as deprecated by using the `Deprecate` helper class, +e.g., + +```td +def OpTraitA : NativeOpTrait<"OpTraitA">, Deprecated<"use `bar` instead">; +``` + +would result in marking `OpTraitA` as deprecated and mlir-tblgen can emit a +warning (default) or error (depending on `-on-deprecated` flag) to make +deprecated state known. + ### Requirements and existing mechanisms analysis The op description should be as declarative as possible to allow a wide range of diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -38,6 +38,12 @@ string result = r; } +// Helper for marking deprecated classes or defs. To mark a def as deprecated, +// mix in the `Deprecate` class with a reason. +class Deprecated { + string odsDeprecated = reason; +} + //===----------------------------------------------------------------------===// // Predicate definitions //===----------------------------------------------------------------------===// diff --git a/mlir/test/mlir-tblgen/deprecation.td b/mlir/test/mlir-tblgen/deprecation.td new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-tblgen/deprecation.td @@ -0,0 +1,15 @@ +// RUN: not mlir-tblgen -on-deprecated=error -gen-op-decls -I %S/../../include -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s + +include "mlir/IR/OpBase.td" + +def Test_Dialect : Dialect { + let name = "test_dialect"; +} + +#ifdef ERROR1 +def OpTraitA : NativeOpTrait<"OpTraitA">, Deprecated<"use `bar` instead">; + +// ERROR1: warning: Using deprecated def `OpTraitA` +// ERROR1: use `bar` instead +def OpTraitWithoutDependentTrait : Op {} +#endif diff --git a/mlir/tools/mlir-tblgen/mlir-tblgen.cpp b/mlir/tools/mlir-tblgen/mlir-tblgen.cpp --- a/mlir/tools/mlir-tblgen/mlir-tblgen.cpp +++ b/mlir/tools/mlir-tblgen/mlir-tblgen.cpp @@ -26,6 +26,15 @@ using namespace llvm; using namespace mlir; +enum DeprecatedAction { None, Warn, Error }; +llvm::cl::opt actionOnDeprecated( + "on-deprecated", llvm::cl::init(Warn), + llvm::cl::desc("Action to perform on deprecated def"), + llvm::cl::values(clEnumValN(DeprecatedAction::None, "none", "No action"), + clEnumValN(DeprecatedAction::Warn, "warn", "Warn on use"), + clEnumValN(DeprecatedAction::Error, "error", + "Error on use"))); + static llvm::ManagedStatic> generatorRegistry; mlir::GenRegistration::GenRegistration(StringRef arg, StringRef description, @@ -62,9 +71,80 @@ // Generator to invoke. const mlir::GenInfo *generator; +// Returns if there is a use of `init` in `record`. +bool findUse(Record &record, Init *init, + llvm::DenseMap &known) { + auto it = known.find(&record); + if (it != known.end()) + return it->second; + + auto memoize = [&](bool val) { + known[&record] = val; + return val; + }; + + for (const RecordVal &val : record.getValues()) { + Init *valInit = val.getValue(); + if (valInit == init) + return true; + if (auto *di = dyn_cast(valInit)) { + if (findUse(*di->getDef(), init, known)) + return memoize(true); + } else if (auto *di = dyn_cast(valInit)) { + for (Init *arg : di->getArgs()) + if (auto *di = dyn_cast(arg)) + if (findUse(*di->getDef(), init, known)) + return memoize(true); + } else if (ListInit *li = dyn_cast(valInit)) { + for (Init *jt : li->getValues()) + if (jt == init) + return memoize(true); + } + } + return memoize(false); +} + +void warnOfDeprecatedUses(RecordKeeper &records) { + // This performs a direct check for any def marked as deprecated and then + // finds all uses of deprecated def. Deprecated defs are not expected to be + // either numerous or long lived. + bool deprecatedDefsFounds = false; + for (auto &it : records.getDefs()) { + const RecordVal *r = it.second->getValue("odsDeprecated"); + if (!r || !r->getValue()) + continue; + + llvm::DenseMap hasUse; + if (auto *si = dyn_cast(r->getValue())) { + for (auto &jt : records.getDefs()) { + // Skip anonymous defs. + if (jt.second->isAnonymous()) + continue; + // Skip all outside main file to avoid flagging redundantly. + unsigned buf = + SrcMgr.FindBufferContainingLoc(jt.second->getLoc().front()); + if (buf != SrcMgr.getMainFileID()) + continue; + + if (findUse(*jt.second, it.second->getDefInit(), hasUse)) { + PrintWarning(jt.second->getLoc(), + "Using deprecated def `" + it.first + "`"); + PrintNote(si->getAsUnquotedString()); + deprecatedDefsFounds = true; + } + } + } + } + if (deprecatedDefsFounds && actionOnDeprecated == DeprecatedAction::Error) + PrintFatalNote("Error'ing out due to deprecated defs"); +} + // TableGenMain requires a function pointer so this function is passed in which // simply wraps the call to the generator. static bool mlirTableGenMain(raw_ostream &os, RecordKeeper &records) { + if (actionOnDeprecated != DeprecatedAction::None) + warnOfDeprecatedUses(records); + if (!generator) { os << records; return false;