[mlir][TableGen] Support intrinsics with multiple returns and overloaded operands.
[lldb.git] / mlir / tools / mlir-tblgen / LLVMIRIntrinsicGen.cpp
1 //===- LLVMIntrinsicGen.cpp - TableGen utility for converting intrinsics --===//
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 is a TableGen generator that converts TableGen definitions for LLVM
10 // intrinsics to TableGen definitions for MLIR operations.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "mlir/TableGen/GenInfo.h"
15
16 #include "llvm/ADT/SmallBitVector.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/MachineValueType.h"
19 #include "llvm/Support/PrettyStackTrace.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 static llvm::cl::OptionCategory IntrinsicGenCat("Intrinsics Generator Options");
27
28 static llvm::cl::opt<std::string>
29     nameFilter("llvmir-intrinsics-filter",
30                llvm::cl::desc("Only keep the intrinsics with the specified "
31                               "substring in their record name"),
32                llvm::cl::cat(IntrinsicGenCat));
33
34 static llvm::cl::opt<std::string>
35     opBaseClass("dialect-opclass-base",
36                 llvm::cl::desc("The base class for the ops in the dialect we "
37                                "are planning to emit"),
38                 llvm::cl::init("LLVM_IntrOp"), llvm::cl::cat(IntrinsicGenCat));
39
40 // Used to represent the indices of overloadable operands/results.
41 using IndicesTy = llvm::SmallBitVector;
42
43 /// Return a CodeGen value type entry from a type record.
44 static llvm::MVT::SimpleValueType getValueType(const llvm::Record *rec) {
45   return (llvm::MVT::SimpleValueType)rec->getValueAsDef("VT")->getValueAsInt(
46       "Value");
47 }
48
49 /// Return the indices of the definitions in a list of definitions that
50 /// represent overloadable types
51 static IndicesTy getOverloadableTypeIdxs(const llvm::Record &record,
52                                          const char *listName) {
53   auto results = record.getValueAsListOfDefs(listName);
54   IndicesTy overloadedOps(results.size());
55   for (auto r : llvm::enumerate(results)) {
56     llvm::MVT::SimpleValueType vt = getValueType(r.value());
57     switch (vt) {
58     case llvm::MVT::iAny:
59     case llvm::MVT::fAny:
60     case llvm::MVT::Any:
61     case llvm::MVT::iPTRAny:
62     case llvm::MVT::vAny:
63       overloadedOps.set(r.index());
64       break;
65     default:
66       continue;
67     }
68   }
69   return overloadedOps;
70 }
71
72 namespace {
73 /// A wrapper for LLVM's Tablegen class `Intrinsic` that provides accessors to
74 /// the fields of the record.
75 class LLVMIntrinsic {
76 public:
77   LLVMIntrinsic(const llvm::Record &record) : record(record) {}
78
79   /// Get the name of the operation to be used in MLIR.  Uses the appropriate
80   /// field if not empty, constructs a name by replacing underscores with dots
81   /// in the record name otherwise.
82   std::string getOperationName() const {
83     llvm::StringRef name = record.getValueAsString(fieldName);
84     if (!name.empty())
85       return name.str();
86
87     name = record.getName();
88     assert(name.startswith("int_") &&
89            "LLVM intrinsic names are expected to start with 'int_'");
90     name = name.drop_front(4);
91     llvm::SmallVector<llvm::StringRef, 8> chunks;
92     llvm::StringRef targetPrefix = record.getValueAsString("TargetPrefix");
93     name.split(chunks, '_');
94     auto chunksBegin = chunks.begin();
95     // Remove the target prefix from target specific intrinsics.
96     if (!targetPrefix.empty()) {
97       assert(targetPrefix == *chunksBegin &&
98              "Intrinsic has TargetPrefix, but "
99              "record name doesn't begin with it");
100       assert(chunks.size() >= 2 &&
101              "Intrinsic has TargetPrefix, but "
102              "chunks has only one element meaning the intrinsic name is empty");
103       ++chunksBegin;
104     }
105     return llvm::join(chunksBegin, chunks.end(), ".");
106   }
107
108   /// Get the name of the record without the "intrinsic" prefix.
109   llvm::StringRef getProperRecordName() const {
110     llvm::StringRef name = record.getName();
111     assert(name.startswith("int_") &&
112            "LLVM intrinsic names are expected to start with 'int_'");
113     return name.drop_front(4);
114   }
115
116   /// Get the number of operands.
117   unsigned getNumOperands() const {
118     auto operands = record.getValueAsListOfDefs(fieldOperands);
119     assert(llvm::all_of(operands,
120                         [](const llvm::Record *r) {
121                           return r->isSubClassOf("LLVMType");
122                         }) &&
123            "expected operands to be of LLVM type");
124     return operands.size();
125   }
126
127   /// Get the number of results.  Note that LLVM does not support multi-value
128   /// operations so, in fact, multiple results will be returned as a value of
129   /// structure type.
130   unsigned getNumResults() const {
131     auto results = record.getValueAsListOfDefs(fieldResults);
132     for (const llvm::Record *r : results) {
133       (void)r;
134       assert(r->isSubClassOf("LLVMType") &&
135              "expected operands to be of LLVM type");
136     }
137     return results.size();
138   }
139
140   /// Return true if the intrinsic may have side effects, i.e. does not have the
141   /// `IntrNoMem` property.
142   bool hasSideEffects() const {
143     auto props = record.getValueAsListOfDefs(fieldTraits);
144     for (const llvm::Record *r : props) {
145       if (r->getName() == "IntrNoMem")
146         return true;
147     }
148     return false;
149   }
150
151   /// Return true if the intrinsic is commutative, i.e. has the respective
152   /// property.
153   bool isCommutative() const {
154     auto props = record.getValueAsListOfDefs(fieldTraits);
155     for (const llvm::Record *r : props) {
156       if (r->getName() == "Commutative")
157         return true;
158     }
159     return false;
160   }
161
162   IndicesTy getOverloadableOperandsIdxs() const {
163     return getOverloadableTypeIdxs(record, fieldOperands);
164   }
165
166   IndicesTy getOverloadableResultsIdxs() const {
167     return getOverloadableTypeIdxs(record, fieldResults);
168   }
169
170 private:
171   /// Names of the fields in the Intrinsic LLVM Tablegen class.
172   const char *fieldName = "LLVMName";
173   const char *fieldOperands = "ParamTypes";
174   const char *fieldResults = "RetTypes";
175   const char *fieldTraits = "IntrProperties";
176
177   const llvm::Record &record;
178 };
179 } // namespace
180
181 /// Prints the elements in "range" separated by commas and surrounded by "[]".
182 template <typename Range>
183 void printBracketedRange(const Range &range, llvm::raw_ostream &os) {
184   os << '[';
185   llvm::interleaveComma(range, os);
186   os << ']';
187 }
188
189 /// Emits ODS (TableGen-based) code for `record` representing an LLVM intrinsic.
190 /// Returns true on error, false on success.
191 static bool emitIntrinsic(const llvm::Record &record, llvm::raw_ostream &os) {
192   LLVMIntrinsic intr(record);
193
194   // Prepare strings for traits, if any.
195   llvm::SmallVector<llvm::StringRef, 2> traits;
196   if (intr.isCommutative())
197     traits.push_back("Commutative");
198   if (!intr.hasSideEffects())
199     traits.push_back("NoSideEffect");
200
201   // Prepare strings for operands.
202   llvm::SmallVector<llvm::StringRef, 8> operands(intr.getNumOperands(),
203                                                  "LLVM_Type");
204
205   // Emit the definition.
206   os << "def LLVM_" << intr.getProperRecordName() << " : " << opBaseClass
207      << "<\"" << intr.getOperationName() << "\", ";
208   printBracketedRange(intr.getOverloadableResultsIdxs().set_bits(), os);
209   os << ", ";
210   printBracketedRange(intr.getOverloadableOperandsIdxs().set_bits(), os);
211   os << ", ";
212   printBracketedRange(traits, os);
213   os << ", " << intr.getNumResults() << ">, Arguments<(ins"
214      << (operands.empty() ? "" : " ");
215   llvm::interleaveComma(operands, os);
216   os << ")>;\n\n";
217
218   return false;
219 }
220
221 /// Traverses the list of TableGen definitions derived from the "Intrinsic"
222 /// class and generates MLIR ODS definitions for those intrinsics that have
223 /// the name matching the filter.
224 static bool emitIntrinsics(const llvm::RecordKeeper &records,
225                            llvm::raw_ostream &os) {
226   llvm::emitSourceFileHeader("Operations for LLVM intrinsics", os);
227   os << "include \"mlir/Dialect/LLVMIR/LLVMOpBase.td\"\n";
228   os << "include \"mlir/Interfaces/SideEffectInterfaces.td\"\n\n";
229
230   auto defs = records.getAllDerivedDefinitions("Intrinsic");
231   for (const llvm::Record *r : defs) {
232     if (!nameFilter.empty() && !r->getName().contains(nameFilter))
233       continue;
234     if (emitIntrinsic(*r, os))
235       return true;
236   }
237
238   return false;
239 }
240
241 static mlir::GenRegistration genLLVMIRIntrinsics("gen-llvmir-intrinsics",
242                                                  "Generate LLVM IR intrinsics",
243                                                  emitIntrinsics);