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