1 //===- mlir-tblgen.cpp - Top-Level TableGen implementation for MLIR -------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains the main function for MLIR's TableGen.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/TableGen/GenInfo.h"
14 #include "mlir/TableGen/GenNameParser.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/FormatVariadic.h"
18 #include "llvm/Support/InitLLVM.h"
19 #include "llvm/Support/ManagedStatic.h"
20 #include "llvm/Support/Signals.h"
21 #include "llvm/TableGen/Error.h"
22 #include "llvm/TableGen/Main.h"
23 #include "llvm/TableGen/Record.h"
24 #include "llvm/TableGen/TableGenBackend.h"
25 
26 using namespace llvm;
27 using namespace mlir;
28 
29 enum DeprecatedAction { None, Warn, Error };
30 llvm::cl::opt<DeprecatedAction> actionOnDeprecated(
31     "on-deprecated", llvm::cl::init(Warn),
32     llvm::cl::desc("Action to perform on deprecated def"),
33     llvm::cl::values(clEnumValN(DeprecatedAction::None, "none", "No action"),
34                      clEnumValN(DeprecatedAction::Warn, "warn", "Warn on use"),
35                      clEnumValN(DeprecatedAction::Error, "error",
36                                 "Error on use")));
37 
38 static llvm::ManagedStatic<std::vector<GenInfo>> generatorRegistry;
39 
GenRegistration(StringRef arg,StringRef description,const GenFunction & function)40 mlir::GenRegistration::GenRegistration(StringRef arg, StringRef description,
41                                        const GenFunction &function) {
42   generatorRegistry->emplace_back(arg, description, function);
43 }
44 
GenNameParser(llvm::cl::Option & opt)45 GenNameParser::GenNameParser(llvm::cl::Option &opt)
46     : llvm::cl::parser<const GenInfo *>(opt) {
47   for (const auto &kv : *generatorRegistry) {
48     addLiteralOption(kv.getGenArgument(), &kv, kv.getGenDescription());
49   }
50 }
51 
printOptionInfo(const llvm::cl::Option & o,size_t globalWidth) const52 void GenNameParser::printOptionInfo(const llvm::cl::Option &o,
53                                     size_t globalWidth) const {
54   GenNameParser *tp = const_cast<GenNameParser *>(this);
55   llvm::array_pod_sort(tp->Values.begin(), tp->Values.end(),
56                        [](const GenNameParser::OptionInfo *vT1,
57                           const GenNameParser::OptionInfo *vT2) {
58                          return vT1->Name.compare(vT2->Name);
59                        });
60   using llvm::cl::parser;
61   parser<const GenInfo *>::printOptionInfo(o, globalWidth);
62 }
63 
64 // Generator that prints records.
65 GenRegistration printRecords("print-records", "Print all records to stdout",
__anon70db4cfc0202(const RecordKeeper &records, raw_ostream &os) 66                              [](const RecordKeeper &records, raw_ostream &os) {
67                                os << records;
68                                return false;
69                              });
70 
71 // Generator to invoke.
72 const mlir::GenInfo *generator;
73 
74 // Returns if there is a use of `init` in `record`.
findUse(Record & record,Init * init,llvm::DenseMap<Record *,bool> & known)75 bool findUse(Record &record, Init *init,
76              llvm::DenseMap<Record *, bool> &known) {
77   auto it = known.find(&record);
78   if (it != known.end())
79     return it->second;
80 
81   auto memoize = [&](bool val) {
82     known[&record] = val;
83     return val;
84   };
85 
86   for (const RecordVal &val : record.getValues()) {
87     Init *valInit = val.getValue();
88     if (valInit == init)
89       return true;
90     if (auto *di = dyn_cast<DefInit>(valInit)) {
91       if (findUse(*di->getDef(), init, known))
92         return memoize(true);
93     } else if (auto *di = dyn_cast<DagInit>(valInit)) {
94       for (Init *arg : di->getArgs())
95         if (auto *di = dyn_cast<DefInit>(arg))
96           if (findUse(*di->getDef(), init, known))
97             return memoize(true);
98     } else if (ListInit *li = dyn_cast<ListInit>(valInit)) {
99       for (Init *jt : li->getValues())
100         if (jt == init)
101           return memoize(true);
102     }
103   }
104   return memoize(false);
105 }
106 
warnOfDeprecatedUses(RecordKeeper & records)107 void warnOfDeprecatedUses(RecordKeeper &records) {
108   // This performs a direct check for any def marked as deprecated and then
109   // finds all uses of deprecated def. Deprecated defs are not expected to be
110   // either numerous or long lived.
111   bool deprecatedDefsFounds = false;
112   for (auto &it : records.getDefs()) {
113     const RecordVal *r = it.second->getValue("odsDeprecated");
114     if (!r || !r->getValue())
115       continue;
116 
117     llvm::DenseMap<Record *, bool> hasUse;
118     if (auto *si = dyn_cast<StringInit>(r->getValue())) {
119       for (auto &jt : records.getDefs()) {
120         // Skip anonymous defs.
121         if (jt.second->isAnonymous())
122           continue;
123         // Skip all outside main file to avoid flagging redundantly.
124         unsigned buf =
125             SrcMgr.FindBufferContainingLoc(jt.second->getLoc().front());
126         if (buf != SrcMgr.getMainFileID())
127           continue;
128 
129         if (findUse(*jt.second, it.second->getDefInit(), hasUse)) {
130           PrintWarning(jt.second->getLoc(),
131                        "Using deprecated def `" + it.first + "`");
132           PrintNote(si->getAsUnquotedString());
133           deprecatedDefsFounds = true;
134         }
135       }
136     }
137   }
138   if (deprecatedDefsFounds && actionOnDeprecated == DeprecatedAction::Error)
139     PrintFatalNote("Error'ing out due to deprecated defs");
140 }
141 
142 // TableGenMain requires a function pointer so this function is passed in which
143 // simply wraps the call to the generator.
mlirTableGenMain(raw_ostream & os,RecordKeeper & records)144 static bool mlirTableGenMain(raw_ostream &os, RecordKeeper &records) {
145   if (actionOnDeprecated != DeprecatedAction::None)
146     warnOfDeprecatedUses(records);
147 
148   if (!generator) {
149     os << records;
150     return false;
151   }
152   return generator->invoke(records, os);
153 }
154 
main(int argc,char ** argv)155 int main(int argc, char **argv) {
156   llvm::InitLLVM y(argc, argv);
157   llvm::cl::opt<const mlir::GenInfo *, false, mlir::GenNameParser> generator(
158       "", llvm::cl::desc("Generator to run"));
159   cl::ParseCommandLineOptions(argc, argv);
160   ::generator = generator.getValue();
161 
162   return TableGenMain(argv[0], &mlirTableGenMain);
163 }
164