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.
getValueType(const llvm::Record * rec)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
getOverloadableTypeIdxs(const llvm::Record & record,const char * listName)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:
LLVMIntrinsic(const llvm::Record & record)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.
getOperationName() const82 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.
getProperRecordName() const109 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.
getNumOperands() const117 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.
getNumResults() const130 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.
hasSideEffects() const142 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.
isCommutative() const153 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
getOverloadableOperandsIdxs() const162 IndicesTy getOverloadableOperandsIdxs() const {
163 return getOverloadableTypeIdxs(record, fieldOperands);
164 }
165
getOverloadableResultsIdxs() const166 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>
printBracketedRange(const Range & range,llvm::raw_ostream & os)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.
emitIntrinsic(const llvm::Record & record,llvm::raw_ostream & os)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.
emitIntrinsics(const llvm::RecordKeeper & records,llvm::raw_ostream & os)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);
244