[mlir][ods] Add Deprecate helper

Add method to tag classes/defs as deprecated. Previously deprecations
were only verbally communicated and folks didn't have an active warning
while building about impending removal. Add mechanism to tag defs as
deprecated to allow warning users.

This doesn't change any policy, it just moves deprecation warnings from
comments to something more user visible.

Differential Revision: https://reviews.llvm.org/D122164
This commit is contained in:
Jacques Pienaar
2022-03-22 11:08:36 -07:00
parent f54931865d
commit e4fb75a354
4 changed files with 114 additions and 0 deletions

View File

@@ -1526,6 +1526,19 @@ mlir-tblgen --gen-op-interface-doc -I /path/to/mlir/include /path/to/input/td/fi
## 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

View File

@@ -38,6 +38,12 @@ class StrFunc<string r> {
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 reason> {
string odsDeprecated = reason;
}
//===----------------------------------------------------------------------===//
// Predicate definitions
//===----------------------------------------------------------------------===//

View File

@@ -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<Test_Dialect, "default_value", [OpTraitA]> {}
#endif

View File

@@ -26,6 +26,15 @@
using namespace llvm;
using namespace mlir;
enum DeprecatedAction { None, Warn, Error };
llvm::cl::opt<DeprecatedAction> 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<std::vector<GenInfo>> generatorRegistry;
mlir::GenRegistration::GenRegistration(StringRef arg, StringRef description,
@@ -62,9 +71,80 @@ GenRegistration printRecords("print-records", "Print all records to stdout",
// 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<Record *, bool> &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<DefInit>(valInit)) {
if (findUse(*di->getDef(), init, known))
return memoize(true);
} else if (auto *di = dyn_cast<DagInit>(valInit)) {
for (Init *arg : di->getArgs())
if (auto *di = dyn_cast<DefInit>(arg))
if (findUse(*di->getDef(), init, known))
return memoize(true);
} else if (ListInit *li = dyn_cast<ListInit>(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<Record *, bool> hasUse;
if (auto *si = dyn_cast<StringInit>(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;