1 //===- LLVMIntrinsicGen.cpp - TableGen utility for converting intrinsics --===//
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
7 //===----------------------------------------------------------------------===//
9 // This is a TableGen generator that converts TableGen definitions for LLVM
10 // intrinsics to TableGen definitions for MLIR operations.
12 //===----------------------------------------------------------------------===//
14 #include "mlir/TableGen/GenInfo.h"
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"
27 static llvm::cl::OptionCategory
intrinsicGenCat("Intrinsics Generator Options");
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
));
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
));
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
));
47 static 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
));
53 // Used to represent the indices of overloadable operands/results.
54 using IndicesTy
= llvm::SmallBitVector
;
56 /// Return a CodeGen value type entry from a type record.
57 static llvm::MVT::SimpleValueType
getValueType(const llvm::Record
*rec
) {
58 return (llvm::MVT::SimpleValueType
)rec
->getValueAsDef("VT")->getValueAsInt(
62 /// Return the indices of the definitions in a list of definitions that
63 /// represent overloadable types
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());
74 case llvm::MVT::iPTRAny
:
76 overloadedOps
.set(r
.index());
86 /// A wrapper for LLVM's Tablegen class `Intrinsic` that provides accessors to
87 /// the fields of the record.
90 LLVMIntrinsic(const llvm::Record
&record
) : record(record
) {}
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
);
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(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");
118 return llvm::join(chunksBegin
, chunks
.end(), ".");
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(4);
129 /// Get the number of operands.
130 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");
136 "expected operands to be of LLVM type");
137 return operands
.size();
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
143 unsigned getNumResults() const {
144 auto results
= record
.getValueAsListOfDefs(fieldResults
);
145 for (const llvm::Record
*r
: results
) {
147 assert(r
->isSubClassOf("LLVMType") &&
148 "expected operands to be of LLVM type");
150 return results
.size();
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 record
.getValueAsListOfDefs(fieldTraits
),
158 [](const llvm::Record
*r
) { return r
->getName() == "IntrNoMem"; });
161 /// Return true if the intrinsic is commutative, i.e. has the respective
163 bool isCommutative() const {
165 record
.getValueAsListOfDefs(fieldTraits
),
166 [](const llvm::Record
*r
) { return r
->getName() == "Commutative"; });
169 IndicesTy
getOverloadableOperandsIdxs() const {
170 return getOverloadableTypeIdxs(record
, fieldOperands
);
173 IndicesTy
getOverloadableResultsIdxs() const {
174 return getOverloadableTypeIdxs(record
, fieldResults
);
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";
184 const llvm::Record
&record
;
188 /// Prints the elements in "range" separated by commas and surrounded by "[]".
189 template <typename Range
>
190 void printBracketedRange(const Range
&range
, llvm::raw_ostream
&os
) {
192 llvm::interleaveComma(range
, os
);
196 /// Emits ODS (TableGen-based) code for `record` representing an LLVM intrinsic.
197 /// Returns true on error, false on success.
198 static bool emitIntrinsic(const llvm::Record
&record
, llvm::raw_ostream
&os
) {
199 LLVMIntrinsic
intr(record
);
201 llvm::Regex
accessGroupMatcher(accessGroupRegexp
);
202 bool requiresAccessGroup
=
203 !accessGroupRegexp
.empty() && accessGroupMatcher
.match(record
.getName());
205 llvm::Regex
aliasAnalysisMatcher(aliasAnalysisRegexp
);
206 bool requiresAliasAnalysis
= !aliasAnalysisRegexp
.empty() &&
207 aliasAnalysisMatcher
.match(record
.getName());
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("NoMemoryEffect");
216 // Prepare strings for operands.
217 llvm::SmallVector
<llvm::StringRef
, 8> operands(intr
.getNumOperands(),
219 if (requiresAccessGroup
)
221 "OptionalAttr<LLVM_AccessGroupArrayAttr>:$access_groups");
222 if (requiresAliasAnalysis
) {
223 operands
.push_back("OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes");
225 "OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes");
226 operands
.push_back("OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa");
229 // Emit the definition.
230 os
<< "def LLVM_" << intr
.getProperRecordName() << " : " << opBaseClass
231 << "<\"" << intr
.getOperationName() << "\", ";
232 printBracketedRange(intr
.getOverloadableResultsIdxs().set_bits(), os
);
234 printBracketedRange(intr
.getOverloadableOperandsIdxs().set_bits(), os
);
236 printBracketedRange(traits
, os
);
237 os
<< ", " << intr
.getNumResults() << ", "
238 << (requiresAccessGroup
? "1" : "0") << ", "
239 << (requiresAliasAnalysis
? "1" : "0") << ">, Arguments<(ins"
240 << (operands
.empty() ? "" : " ");
241 llvm::interleaveComma(operands
, os
);
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.
250 static bool emitIntrinsics(const llvm::RecordKeeper
&records
,
251 llvm::raw_ostream
&os
) {
252 llvm::emitSourceFileHeader("Operations for LLVM intrinsics", os
, records
);
253 os
<< "include \"mlir/Dialect/LLVMIR/LLVMOpBase.td\"\n";
254 os
<< "include \"mlir/Interfaces/SideEffectInterfaces.td\"\n\n";
256 auto defs
= records
.getAllDerivedDefinitions("Intrinsic");
257 for (const llvm::Record
*r
: defs
) {
258 if (!nameFilter
.empty() && !r
->getName().contains(nameFilter
))
260 if (emitIntrinsic(*r
, os
))
267 static mlir::GenRegistration
genLLVMIRIntrinsics("gen-llvmir-intrinsics",
268 "Generate LLVM IR intrinsics",