xref: /llvm-project/mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp (revision 9467645547f99ba8fa8152d514f06e76e0be8585)
1f343544bSAlex Zinenko //===- LLVMIntrinsicGen.cpp - TableGen utility for converting intrinsics --===//
2f343544bSAlex Zinenko //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f343544bSAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
5f343544bSAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f343544bSAlex Zinenko //
7f343544bSAlex Zinenko //===----------------------------------------------------------------------===//
8f343544bSAlex Zinenko //
9f343544bSAlex Zinenko // This is a TableGen generator that converts TableGen definitions for LLVM
10f343544bSAlex Zinenko // intrinsics to TableGen definitions for MLIR operations.
11f343544bSAlex Zinenko //
12f343544bSAlex Zinenko //===----------------------------------------------------------------------===//
13f343544bSAlex Zinenko 
14f343544bSAlex Zinenko #include "mlir/TableGen/GenInfo.h"
15f343544bSAlex Zinenko 
16cbf08d0fSMarcello Maggioni #include "llvm/ADT/SmallBitVector.h"
17184ca395SNico Weber #include "llvm/CodeGenTypes/MachineValueType.h"
18f343544bSAlex Zinenko #include "llvm/Support/CommandLine.h"
19f343544bSAlex Zinenko #include "llvm/Support/PrettyStackTrace.h"
204e393350SArpith C. Jacob #include "llvm/Support/Regex.h"
21f343544bSAlex Zinenko #include "llvm/Support/Signals.h"
22f343544bSAlex Zinenko #include "llvm/TableGen/Error.h"
23f343544bSAlex Zinenko #include "llvm/TableGen/Main.h"
24f343544bSAlex Zinenko #include "llvm/TableGen/Record.h"
25f343544bSAlex Zinenko #include "llvm/TableGen/TableGenBackend.h"
26f343544bSAlex Zinenko 
27bccd37f6SRahul Joshi using llvm::Record;
28bccd37f6SRahul Joshi using llvm::RecordKeeper;
29bccd37f6SRahul Joshi using llvm::Regex;
30bccd37f6SRahul Joshi using namespace mlir;
31bccd37f6SRahul Joshi 
3202b6fb21SMehdi Amini static llvm::cl::OptionCategory intrinsicGenCat("Intrinsics Generator Options");
33f343544bSAlex Zinenko 
34f343544bSAlex Zinenko static llvm::cl::opt<std::string>
35f343544bSAlex Zinenko     nameFilter("llvmir-intrinsics-filter",
36f343544bSAlex Zinenko                llvm::cl::desc("Only keep the intrinsics with the specified "
37f343544bSAlex Zinenko                               "substring in their record name"),
3802b6fb21SMehdi Amini                llvm::cl::cat(intrinsicGenCat));
39f343544bSAlex Zinenko 
40be9f09c7SMarcello Maggioni static llvm::cl::opt<std::string>
41be9f09c7SMarcello Maggioni     opBaseClass("dialect-opclass-base",
42be9f09c7SMarcello Maggioni                 llvm::cl::desc("The base class for the ops in the dialect we "
43be9f09c7SMarcello Maggioni                                "are planning to emit"),
4402b6fb21SMehdi Amini                 llvm::cl::init("LLVM_IntrOp"), llvm::cl::cat(intrinsicGenCat));
45be9f09c7SMarcello Maggioni 
464e393350SArpith C. Jacob static llvm::cl::opt<std::string> accessGroupRegexp(
474e393350SArpith C. Jacob     "llvmir-intrinsics-access-group-regexp",
484e393350SArpith C. Jacob     llvm::cl::desc("Mark intrinsics that match the specified "
494e393350SArpith C. Jacob                    "regexp as taking an access group metadata"),
5002b6fb21SMehdi Amini     llvm::cl::cat(intrinsicGenCat));
514e393350SArpith C. Jacob 
52b30422eaSTobias Gysi static llvm::cl::opt<std::string> aliasAnalysisRegexp(
53b30422eaSTobias Gysi     "llvmir-intrinsics-alias-analysis-regexp",
547105512aSTyler Augustine     llvm::cl::desc("Mark intrinsics that match the specified "
55b30422eaSTobias Gysi                    "regexp as taking alias.scopes, noalias, and tbaa metadata"),
5602b6fb21SMehdi Amini     llvm::cl::cat(intrinsicGenCat));
577105512aSTyler Augustine 
58cbf08d0fSMarcello Maggioni // Used to represent the indices of overloadable operands/results.
59cbf08d0fSMarcello Maggioni using IndicesTy = llvm::SmallBitVector;
60cbf08d0fSMarcello Maggioni 
61cbf08d0fSMarcello Maggioni /// Return a CodeGen value type entry from a type record.
62bccd37f6SRahul Joshi static llvm::MVT::SimpleValueType getValueType(const Record *rec) {
63cbf08d0fSMarcello Maggioni   return (llvm::MVT::SimpleValueType)rec->getValueAsDef("VT")->getValueAsInt(
64cbf08d0fSMarcello Maggioni       "Value");
65cbf08d0fSMarcello Maggioni }
66cbf08d0fSMarcello Maggioni 
67cbf08d0fSMarcello Maggioni /// Return the indices of the definitions in a list of definitions that
68cbf08d0fSMarcello Maggioni /// represent overloadable types
69bccd37f6SRahul Joshi static IndicesTy getOverloadableTypeIdxs(const Record &record,
70cbf08d0fSMarcello Maggioni                                          const char *listName) {
71cbf08d0fSMarcello Maggioni   auto results = record.getValueAsListOfDefs(listName);
72cbf08d0fSMarcello Maggioni   IndicesTy overloadedOps(results.size());
7389de9cc8SMehdi Amini   for (const auto &r : llvm::enumerate(results)) {
74cbf08d0fSMarcello Maggioni     llvm::MVT::SimpleValueType vt = getValueType(r.value());
75cbf08d0fSMarcello Maggioni     switch (vt) {
76cbf08d0fSMarcello Maggioni     case llvm::MVT::iAny:
77cbf08d0fSMarcello Maggioni     case llvm::MVT::fAny:
78cbf08d0fSMarcello Maggioni     case llvm::MVT::Any:
79*94676455SJessica Clarke     case llvm::MVT::pAny:
80cbf08d0fSMarcello Maggioni     case llvm::MVT::vAny:
81cbf08d0fSMarcello Maggioni       overloadedOps.set(r.index());
82cbf08d0fSMarcello Maggioni       break;
83cbf08d0fSMarcello Maggioni     default:
84cbf08d0fSMarcello Maggioni       continue;
85cbf08d0fSMarcello Maggioni     }
86cbf08d0fSMarcello Maggioni   }
87cbf08d0fSMarcello Maggioni   return overloadedOps;
88cbf08d0fSMarcello Maggioni }
89cbf08d0fSMarcello Maggioni 
90f343544bSAlex Zinenko namespace {
91f343544bSAlex Zinenko /// A wrapper for LLVM's Tablegen class `Intrinsic` that provides accessors to
92f343544bSAlex Zinenko /// the fields of the record.
93f343544bSAlex Zinenko class LLVMIntrinsic {
94f343544bSAlex Zinenko public:
95bccd37f6SRahul Joshi   LLVMIntrinsic(const Record &record) : record(record) {}
96f343544bSAlex Zinenko 
97f343544bSAlex Zinenko   /// Get the name of the operation to be used in MLIR.  Uses the appropriate
98f343544bSAlex Zinenko   /// field if not empty, constructs a name by replacing underscores with dots
99f343544bSAlex Zinenko   /// in the record name otherwise.
100f343544bSAlex Zinenko   std::string getOperationName() const {
101bccd37f6SRahul Joshi     StringRef name = record.getValueAsString(fieldName);
102f343544bSAlex Zinenko     if (!name.empty())
103f343544bSAlex Zinenko       return name.str();
104f343544bSAlex Zinenko 
105f343544bSAlex Zinenko     name = record.getName();
10688d319a2SKazu Hirata     assert(name.starts_with("int_") &&
107fc817b09SKazuaki Ishizaki            "LLVM intrinsic names are expected to start with 'int_'");
108f343544bSAlex Zinenko     name = name.drop_front(4);
109bccd37f6SRahul Joshi     SmallVector<StringRef, 8> chunks;
110bccd37f6SRahul Joshi     StringRef targetPrefix = record.getValueAsString("TargetPrefix");
111f343544bSAlex Zinenko     name.split(chunks, '_');
11202b6fb21SMehdi Amini     auto *chunksBegin = chunks.begin();
113be9f09c7SMarcello Maggioni     // Remove the target prefix from target specific intrinsics.
114be9f09c7SMarcello Maggioni     if (!targetPrefix.empty()) {
115be9f09c7SMarcello Maggioni       assert(targetPrefix == *chunksBegin &&
116be9f09c7SMarcello Maggioni              "Intrinsic has TargetPrefix, but "
117be9f09c7SMarcello Maggioni              "record name doesn't begin with it");
118be9f09c7SMarcello Maggioni       assert(chunks.size() >= 2 &&
119be9f09c7SMarcello Maggioni              "Intrinsic has TargetPrefix, but "
120be9f09c7SMarcello Maggioni              "chunks has only one element meaning the intrinsic name is empty");
121be9f09c7SMarcello Maggioni       ++chunksBegin;
122be9f09c7SMarcello Maggioni     }
123be9f09c7SMarcello Maggioni     return llvm::join(chunksBegin, chunks.end(), ".");
124f343544bSAlex Zinenko   }
125f343544bSAlex Zinenko 
126f343544bSAlex Zinenko   /// Get the name of the record without the "intrinsic" prefix.
127bccd37f6SRahul Joshi   StringRef getProperRecordName() const {
128bccd37f6SRahul Joshi     StringRef name = record.getName();
12988d319a2SKazu Hirata     assert(name.starts_with("int_") &&
130fc817b09SKazuaki Ishizaki            "LLVM intrinsic names are expected to start with 'int_'");
131f343544bSAlex Zinenko     return name.drop_front(4);
132f343544bSAlex Zinenko   }
133f343544bSAlex Zinenko 
134f343544bSAlex Zinenko   /// Get the number of operands.
135f343544bSAlex Zinenko   unsigned getNumOperands() const {
136f343544bSAlex Zinenko     auto operands = record.getValueAsListOfDefs(fieldOperands);
137bccd37f6SRahul Joshi     assert(llvm::all_of(
138bccd37f6SRahul Joshi                operands,
139bccd37f6SRahul Joshi                [](const Record *r) { return r->isSubClassOf("LLVMType"); }) &&
140f343544bSAlex Zinenko            "expected operands to be of LLVM type");
141f343544bSAlex Zinenko     return operands.size();
142f343544bSAlex Zinenko   }
143f343544bSAlex Zinenko 
144f343544bSAlex Zinenko   /// Get the number of results.  Note that LLVM does not support multi-value
145f343544bSAlex Zinenko   /// operations so, in fact, multiple results will be returned as a value of
146f343544bSAlex Zinenko   /// structure type.
147f343544bSAlex Zinenko   unsigned getNumResults() const {
148f343544bSAlex Zinenko     auto results = record.getValueAsListOfDefs(fieldResults);
149bccd37f6SRahul Joshi     for (const Record *r : results) {
150f343544bSAlex Zinenko       (void)r;
151f343544bSAlex Zinenko       assert(r->isSubClassOf("LLVMType") &&
152f343544bSAlex Zinenko              "expected operands to be of LLVM type");
153f343544bSAlex Zinenko     }
154f343544bSAlex Zinenko     return results.size();
155f343544bSAlex Zinenko   }
156f343544bSAlex Zinenko 
157f343544bSAlex Zinenko   /// Return true if the intrinsic may have side effects, i.e. does not have the
158f343544bSAlex Zinenko   /// `IntrNoMem` property.
159f343544bSAlex Zinenko   bool hasSideEffects() const {
160018645b8SAndrew Pritchard     return llvm::none_of(
161018645b8SAndrew Pritchard         record.getValueAsListOfDefs(fieldTraits),
162bccd37f6SRahul Joshi         [](const Record *r) { return r->getName() == "IntrNoMem"; });
163f343544bSAlex Zinenko   }
164f343544bSAlex Zinenko 
165f343544bSAlex Zinenko   /// Return true if the intrinsic is commutative, i.e. has the respective
166f343544bSAlex Zinenko   /// property.
167f343544bSAlex Zinenko   bool isCommutative() const {
168018645b8SAndrew Pritchard     return llvm::any_of(
169018645b8SAndrew Pritchard         record.getValueAsListOfDefs(fieldTraits),
170bccd37f6SRahul Joshi         [](const Record *r) { return r->getName() == "Commutative"; });
171f343544bSAlex Zinenko   }
172f343544bSAlex Zinenko 
173cbf08d0fSMarcello Maggioni   IndicesTy getOverloadableOperandsIdxs() const {
174cbf08d0fSMarcello Maggioni     return getOverloadableTypeIdxs(record, fieldOperands);
175cbf08d0fSMarcello Maggioni   }
176cbf08d0fSMarcello Maggioni 
177cbf08d0fSMarcello Maggioni   IndicesTy getOverloadableResultsIdxs() const {
178cbf08d0fSMarcello Maggioni     return getOverloadableTypeIdxs(record, fieldResults);
179cbf08d0fSMarcello Maggioni   }
180cbf08d0fSMarcello Maggioni 
181f343544bSAlex Zinenko private:
182fc817b09SKazuaki Ishizaki   /// Names of the fields in the Intrinsic LLVM Tablegen class.
183f343544bSAlex Zinenko   const char *fieldName = "LLVMName";
184f343544bSAlex Zinenko   const char *fieldOperands = "ParamTypes";
185f343544bSAlex Zinenko   const char *fieldResults = "RetTypes";
186f343544bSAlex Zinenko   const char *fieldTraits = "IntrProperties";
187f343544bSAlex Zinenko 
188bccd37f6SRahul Joshi   const Record &record;
189f343544bSAlex Zinenko };
190f343544bSAlex Zinenko } // namespace
191f343544bSAlex Zinenko 
1923a1b34ffSAlex Zinenko /// Prints the elements in "range" separated by commas and surrounded by "[]".
1933a1b34ffSAlex Zinenko template <typename Range>
1943a1b34ffSAlex Zinenko void printBracketedRange(const Range &range, llvm::raw_ostream &os) {
1953a1b34ffSAlex Zinenko   os << '[';
1962f21a579SRiver Riddle   llvm::interleaveComma(range, os);
1973a1b34ffSAlex Zinenko   os << ']';
198f343544bSAlex Zinenko }
199f343544bSAlex Zinenko 
200f343544bSAlex Zinenko /// Emits ODS (TableGen-based) code for `record` representing an LLVM intrinsic.
201f343544bSAlex Zinenko /// Returns true on error, false on success.
202bccd37f6SRahul Joshi static bool emitIntrinsic(const Record &record, llvm::raw_ostream &os) {
203f343544bSAlex Zinenko   LLVMIntrinsic intr(record);
204f343544bSAlex Zinenko 
205bccd37f6SRahul Joshi   Regex accessGroupMatcher(accessGroupRegexp);
2064e393350SArpith C. Jacob   bool requiresAccessGroup =
2074e393350SArpith C. Jacob       !accessGroupRegexp.empty() && accessGroupMatcher.match(record.getName());
2084e393350SArpith C. Jacob 
209bccd37f6SRahul Joshi   Regex aliasAnalysisMatcher(aliasAnalysisRegexp);
210b30422eaSTobias Gysi   bool requiresAliasAnalysis = !aliasAnalysisRegexp.empty() &&
211b30422eaSTobias Gysi                                aliasAnalysisMatcher.match(record.getName());
2127105512aSTyler Augustine 
213f343544bSAlex Zinenko   // Prepare strings for traits, if any.
214bccd37f6SRahul Joshi   SmallVector<StringRef, 2> traits;
215f343544bSAlex Zinenko   if (intr.isCommutative())
216f343544bSAlex Zinenko     traits.push_back("Commutative");
217f343544bSAlex Zinenko   if (!intr.hasSideEffects())
21886771d0bSSanjoy Das     traits.push_back("NoMemoryEffect");
219f343544bSAlex Zinenko 
220f343544bSAlex Zinenko   // Prepare strings for operands.
221bccd37f6SRahul Joshi   SmallVector<StringRef, 8> operands(intr.getNumOperands(), "LLVM_Type");
2224e393350SArpith C. Jacob   if (requiresAccessGroup)
2239170fa58SMarkus Böck     operands.push_back(
2249170fa58SMarkus Böck         "OptionalAttr<LLVM_AccessGroupArrayAttr>:$access_groups");
225b30422eaSTobias Gysi   if (requiresAliasAnalysis) {
22678d00a16SMarkus Böck     operands.push_back("OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes");
22778d00a16SMarkus Böck     operands.push_back(
22878d00a16SMarkus Böck         "OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes");
2291dda134fSMarkus Böck     operands.push_back("OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa");
2307105512aSTyler Augustine   }
231f343544bSAlex Zinenko 
232f343544bSAlex Zinenko   // Emit the definition.
233be9f09c7SMarcello Maggioni   os << "def LLVM_" << intr.getProperRecordName() << " : " << opBaseClass
2343a1b34ffSAlex Zinenko      << "<\"" << intr.getOperationName() << "\", ";
2353a1b34ffSAlex Zinenko   printBracketedRange(intr.getOverloadableResultsIdxs().set_bits(), os);
2363a1b34ffSAlex Zinenko   os << ", ";
2373a1b34ffSAlex Zinenko   printBracketedRange(intr.getOverloadableOperandsIdxs().set_bits(), os);
2383a1b34ffSAlex Zinenko   os << ", ";
2393a1b34ffSAlex Zinenko   printBracketedRange(traits, os);
2404e393350SArpith C. Jacob   os << ", " << intr.getNumResults() << ", "
2417105512aSTyler Augustine      << (requiresAccessGroup ? "1" : "0") << ", "
242b30422eaSTobias Gysi      << (requiresAliasAnalysis ? "1" : "0") << ">, Arguments<(ins"
2433a1b34ffSAlex Zinenko      << (operands.empty() ? "" : " ");
2442f21a579SRiver Riddle   llvm::interleaveComma(operands, os);
2453a1b34ffSAlex Zinenko   os << ")>;\n\n";
246f343544bSAlex Zinenko 
247f343544bSAlex Zinenko   return false;
248f343544bSAlex Zinenko }
249f343544bSAlex Zinenko 
250f343544bSAlex Zinenko /// Traverses the list of TableGen definitions derived from the "Intrinsic"
251f343544bSAlex Zinenko /// class and generates MLIR ODS definitions for those intrinsics that have
252f343544bSAlex Zinenko /// the name matching the filter.
253bccd37f6SRahul Joshi static bool emitIntrinsics(const RecordKeeper &records, llvm::raw_ostream &os) {
254b0e28eb8SShao-Ce SUN   llvm::emitSourceFileHeader("Operations for LLVM intrinsics", os, records);
255483f82b1SRiver Riddle   os << "include \"mlir/Dialect/LLVMIR/LLVMOpBase.td\"\n";
256661b234cSStephen Neuendorffer   os << "include \"mlir/Interfaces/SideEffectInterfaces.td\"\n\n";
257f343544bSAlex Zinenko 
258f343544bSAlex Zinenko   auto defs = records.getAllDerivedDefinitions("Intrinsic");
259bccd37f6SRahul Joshi   for (const Record *r : defs) {
260f343544bSAlex Zinenko     if (!nameFilter.empty() && !r->getName().contains(nameFilter))
261f343544bSAlex Zinenko       continue;
262f343544bSAlex Zinenko     if (emitIntrinsic(*r, os))
263f343544bSAlex Zinenko       return true;
264f343544bSAlex Zinenko   }
265f343544bSAlex Zinenko 
266f343544bSAlex Zinenko   return false;
267f343544bSAlex Zinenko }
268f343544bSAlex Zinenko 
269f343544bSAlex Zinenko static mlir::GenRegistration genLLVMIRIntrinsics("gen-llvmir-intrinsics",
270f343544bSAlex Zinenko                                                  "Generate LLVM IR intrinsics",
271f343544bSAlex Zinenko                                                  emitIntrinsics);
272