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