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/CodeGenTypes/MachineValueType.h"
18#include "llvm/Support/CommandLine.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
27static llvm::cl::OptionCategory intrinsicGenCat("Intrinsics Generator Options");
28
29static 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
35static 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(Val: "LLVM_IntrOp"), llvm::cl::cat(intrinsicGenCat));
40
41static 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
47static llvm::cl::opt<std::string> aliasAnalysisRegexp(
48 "llvmir-intrinsics-alias-analysis-regexp",
49 llvm::cl::desc("Mark intrinsics that match the specified "
50 "regexp as taking alias.scopes, noalias, and tbaa metadata"),
51 llvm::cl::cat(intrinsicGenCat));
52
53// Used to represent the indices of overloadable operands/results.
54using IndicesTy = llvm::SmallBitVector;
55
56/// Return a CodeGen value type entry from a type record.
57static llvm::MVT::SimpleValueType getValueType(const llvm::Record *rec) {
58 return (llvm::MVT::SimpleValueType)rec->getValueAsDef(FieldName: "VT")->getValueAsInt(
59 FieldName: "Value");
60}
61
62/// Return the indices of the definitions in a list of definitions that
63/// represent overloadable types
64static IndicesTy getOverloadableTypeIdxs(const llvm::Record &record,
65 const char *listName) {
66 auto results = record.getValueAsListOfDefs(FieldName: listName);
67 IndicesTy overloadedOps(results.size());
68 for (const auto &r : llvm::enumerate(First&: results)) {
69 llvm::MVT::SimpleValueType vt = getValueType(rec: 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
85namespace {
86/// A wrapper for LLVM's Tablegen class `Intrinsic` that provides accessors to
87/// the fields of the record.
88class LLVMIntrinsic {
89public:
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.
95 std::string getOperationName() const {
96 llvm::StringRef name = record.getValueAsString(FieldName: fieldName);
97 if (!name.empty())
98 return name.str();
99
100 name = record.getName();
101 assert(name.starts_with("int_") &&
102 "LLVM intrinsic names are expected to start with 'int_'");
103 name = name.drop_front(N: 4);
104 llvm::SmallVector<llvm::StringRef, 8> chunks;
105 llvm::StringRef targetPrefix = record.getValueAsString(FieldName: "TargetPrefix");
106 name.split(A&: chunks, Separator: '_');
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(Begin: chunksBegin, End: chunks.end(), Separator: ".");
119 }
120
121 /// Get the name of the record without the "intrinsic" prefix.
122 llvm::StringRef getProperRecordName() const {
123 llvm::StringRef name = record.getName();
124 assert(name.starts_with("int_") &&
125 "LLVM intrinsic names are expected to start with 'int_'");
126 return name.drop_front(N: 4);
127 }
128
129 /// Get the number of operands.
130 unsigned getNumOperands() const {
131 auto operands = record.getValueAsListOfDefs(FieldName: 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.
143 unsigned getNumResults() const {
144 auto results = record.getValueAsListOfDefs(FieldName: 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.
155 bool hasSideEffects() const {
156 return llvm::none_of(
157 Range: record.getValueAsListOfDefs(FieldName: fieldTraits),
158 P: [](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.
163 bool isCommutative() const {
164 return llvm::any_of(
165 Range: record.getValueAsListOfDefs(FieldName: fieldTraits),
166 P: [](const llvm::Record *r) { return r->getName() == "Commutative"; });
167 }
168
169 IndicesTy getOverloadableOperandsIdxs() const {
170 return getOverloadableTypeIdxs(record, listName: fieldOperands);
171 }
172
173 IndicesTy getOverloadableResultsIdxs() const {
174 return getOverloadableTypeIdxs(record, listName: fieldResults);
175 }
176
177private:
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 "[]".
189template <typename Range>
190void 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.
198static 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(String: record.getName());
204
205 llvm::Regex aliasAnalysisMatcher(aliasAnalysisRegexp);
206 bool requiresAliasAnalysis = !aliasAnalysisRegexp.empty() &&
207 aliasAnalysisMatcher.match(String: 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(Elt: "Commutative");
213 if (!intr.hasSideEffects())
214 traits.push_back(Elt: "NoMemoryEffect");
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(
221 Elt: "OptionalAttr<LLVM_AccessGroupArrayAttr>:$access_groups");
222 if (requiresAliasAnalysis) {
223 operands.push_back(Elt: "OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes");
224 operands.push_back(
225 Elt: "OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes");
226 operands.push_back(Elt: "OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa");
227 }
228
229 // Emit the definition.
230 os << "def LLVM_" << intr.getProperRecordName() << " : " << opBaseClass
231 << "<\"" << intr.getOperationName() << "\", ";
232 printBracketedRange(range: intr.getOverloadableResultsIdxs().set_bits(), os);
233 os << ", ";
234 printBracketedRange(range: intr.getOverloadableOperandsIdxs().set_bits(), os);
235 os << ", ";
236 printBracketedRange(range: traits, os);
237 os << ", " << intr.getNumResults() << ", "
238 << (requiresAccessGroup ? "1" : "0") << ", "
239 << (requiresAliasAnalysis ? "1" : "0") << ">, Arguments<(ins"
240 << (operands.empty() ? "" : " ");
241 llvm::interleaveComma(c: operands, os);
242 os << ")>;\n\n";
243
244 return false;
245}
246
247/// Traverses the list of TableGen definitions derived from the "Intrinsic"
248/// class and generates MLIR ODS definitions for those intrinsics that have
249/// the name matching the filter.
250static bool emitIntrinsics(const llvm::RecordKeeper &records,
251 llvm::raw_ostream &os) {
252 llvm::emitSourceFileHeader(Desc: "Operations for LLVM intrinsics", OS&: os, Record: records);
253 os << "include \"mlir/Dialect/LLVMIR/LLVMOpBase.td\"\n";
254 os << "include \"mlir/Interfaces/SideEffectInterfaces.td\"\n\n";
255
256 auto defs = records.getAllDerivedDefinitions(ClassName: "Intrinsic");
257 for (const llvm::Record *r : defs) {
258 if (!nameFilter.empty() && !r->getName().contains(Other: nameFilter))
259 continue;
260 if (emitIntrinsic(record: *r, os))
261 return true;
262 }
263
264 return false;
265}
266
267static mlir::GenRegistration genLLVMIRIntrinsics("gen-llvmir-intrinsics",
268 "Generate LLVM IR intrinsics",
269 emitIntrinsics);
270

source code of mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp