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