1 //===----------------------------------------------------------------------===//
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 #include "Basic/SequenceToOffsetTable.h"
10 #include "Common/CodeGenDAGPatterns.h" // For SDNodeInfo.
11 #include "llvm/Support/CommandLine.h"
12 #include "llvm/Support/FormatVariadic.h"
13 #include "llvm/TableGen/Error.h"
14 #include "llvm/TableGen/StringToOffsetTable.h"
15 #include "llvm/TableGen/TableGenBackend.h"
16
17 using namespace llvm;
18
19 static cl::OptionCategory SDNodeInfoEmitterCat("Options for -gen-sdnode-info");
20
21 static cl::opt<std::string> TargetSDNodeNamespace(
22 "sdnode-namespace", cl::cat(SDNodeInfoEmitterCat),
23 cl::desc("Specify target SDNode namespace (default=<Target>ISD)"));
24
25 static cl::opt<bool> WarnOnSkippedNodes(
26 "warn-on-skipped-nodes", cl::cat(SDNodeInfoEmitterCat),
27 cl::desc("Explain why a node was skipped (default=true)"), cl::init(true));
28
29 namespace {
30
31 class SDNodeInfoEmitter {
32 const RecordKeeper &RK;
33 const CodeGenTarget Target;
34 std::map<StringRef, SmallVector<SDNodeInfo, 2>> NodesByName;
35
36 public:
37 explicit SDNodeInfoEmitter(const RecordKeeper &RK);
38
39 void run(raw_ostream &OS) const;
40
41 private:
42 void emitEnum(raw_ostream &OS) const;
43
44 std::vector<unsigned> emitNodeNames(raw_ostream &OS) const;
45
46 std::vector<std::pair<unsigned, unsigned>>
47 emitTypeConstraints(raw_ostream &OS) const;
48
49 void emitDescs(raw_ostream &OS) const;
50 };
51
52 } // namespace
53
haveCompatibleDescriptions(const SDNodeInfo & N1,const SDNodeInfo & N2)54 static bool haveCompatibleDescriptions(const SDNodeInfo &N1,
55 const SDNodeInfo &N2) {
56 // Number of results/operands must match.
57 if (N1.getNumResults() != N2.getNumResults() ||
58 N1.getNumOperands() != N2.getNumOperands())
59 return false;
60
61 // Flags must match.
62 if (N1.isStrictFP() != N2.isStrictFP() || N1.getTSFlags() != N2.getTSFlags())
63 return false;
64
65 // We're only interested in a subset of node properties. Properties like
66 // SDNPAssociative and SDNPCommutative do not impose constraints on nodes,
67 // and sometimes differ between nodes sharing the same enum name.
68 constexpr unsigned PropMask = (1 << SDNPHasChain) | (1 << SDNPOutGlue) |
69 (1 << SDNPInGlue) | (1 << SDNPOptInGlue) |
70 (1 << SDNPMemOperand) | (1 << SDNPVariadic);
71
72 return (N1.getProperties() & PropMask) == (N2.getProperties() & PropMask);
73 }
74
haveCompatibleDescriptions(ArrayRef<SDNodeInfo> Nodes)75 static bool haveCompatibleDescriptions(ArrayRef<SDNodeInfo> Nodes) {
76 const SDNodeInfo &N = Nodes.front();
77 return all_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {
78 return haveCompatibleDescriptions(Other, N);
79 });
80 }
81
warnOnSkippedNode(const SDNodeInfo & N,const Twine & Reason)82 static void warnOnSkippedNode(const SDNodeInfo &N, const Twine &Reason) {
83 PrintWarning(N.getRecord()->getLoc(), "skipped node: " + Reason);
84 }
85
SDNodeInfoEmitter(const RecordKeeper & RK)86 SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
87 : RK(RK), Target(RK) {
88 const CodeGenHwModes &HwModes = Target.getHwModes();
89
90 // Figure out target SDNode namespace.
91 if (!TargetSDNodeNamespace.getNumOccurrences())
92 TargetSDNodeNamespace = Target.getName().str() + "ISD";
93
94 // Filter nodes by the target SDNode namespace and create a mapping
95 // from an enum name to a list of nodes that have that name.
96 // The mapping is usually 1:1, but in rare cases it can be 1:N.
97 for (const Record *R : RK.getAllDerivedDefinitions("SDNode")) {
98 SDNodeInfo Node(R, HwModes);
99 auto [NS, EnumName] = Node.getEnumName().split("::");
100
101 if (NS.empty() || EnumName.empty()) {
102 if (WarnOnSkippedNodes)
103 warnOnSkippedNode(Node, "invalid enum name");
104 continue;
105 }
106
107 if (NS != TargetSDNodeNamespace)
108 continue;
109
110 NodesByName[EnumName].push_back(std::move(Node));
111 }
112
113 // Filter out nodes that have different "prototypes" and/or flags.
114 // Don't look at type constraints though, we will simply skip emitting
115 // the constraints if they differ.
116 decltype(NodesByName)::iterator Next;
117 for (auto I = NodesByName.begin(), E = NodesByName.end(); I != E; I = Next) {
118 Next = std::next(I);
119
120 if (haveCompatibleDescriptions(I->second))
121 continue;
122
123 if (WarnOnSkippedNodes)
124 for (const SDNodeInfo &N : I->second)
125 warnOnSkippedNode(N, "incompatible description");
126
127 NodesByName.erase(I);
128 }
129 }
130
emitEnum(raw_ostream & OS) const131 void SDNodeInfoEmitter::emitEnum(raw_ostream &OS) const {
132 OS << "#ifdef GET_SDNODE_ENUM\n";
133 OS << "#undef GET_SDNODE_ENUM\n\n";
134 OS << "namespace llvm::" << TargetSDNodeNamespace << " {\n\n";
135
136 if (!NodesByName.empty()) {
137 StringRef FirstName = NodesByName.begin()->first;
138 StringRef LastName = NodesByName.rbegin()->first;
139
140 OS << "enum GenNodeType : unsigned {\n";
141 OS << " " << FirstName << " = ISD::BUILTIN_OP_END,\n";
142
143 for (StringRef EnumName : make_first_range(drop_begin(NodesByName)))
144 OS << " " << EnumName << ",\n";
145
146 OS << "};\n\n";
147 OS << "static constexpr unsigned GENERATED_OPCODE_END = " << LastName
148 << " + 1;\n\n";
149 } else {
150 OS << "static constexpr unsigned GENERATED_OPCODE_END = "
151 "ISD::BUILTIN_OP_END;\n\n";
152 }
153
154 OS << "} // namespace llvm::" << TargetSDNodeNamespace << "\n\n";
155 OS << "#endif // GET_SDNODE_ENUM\n\n";
156 }
157
emitNodeNames(raw_ostream & OS) const158 std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
159 StringToOffsetTable NameTable;
160
161 std::vector<unsigned> NameOffsets;
162 NameOffsets.reserve(NodesByName.size());
163
164 for (StringRef EnumName : make_first_range(NodesByName)) {
165 SmallString<64> DebugName;
166 raw_svector_ostream SS(DebugName);
167 SS << TargetSDNodeNamespace << "::" << EnumName;
168 NameOffsets.push_back(NameTable.GetOrAddStringOffset(DebugName));
169 }
170
171 NameTable.EmitStringTableDef(OS, Target.getName() + "SDNodeNames");
172 OS << '\n';
173
174 return NameOffsets;
175 }
176
getTypeConstraintKindName(SDTypeConstraint::KindTy Kind)177 static StringRef getTypeConstraintKindName(SDTypeConstraint::KindTy Kind) {
178 #define CASE(NAME) \
179 case SDTypeConstraint::NAME: \
180 return #NAME
181
182 switch (Kind) {
183 CASE(SDTCisVT);
184 CASE(SDTCisPtrTy);
185 CASE(SDTCisInt);
186 CASE(SDTCisFP);
187 CASE(SDTCisVec);
188 CASE(SDTCisSameAs);
189 CASE(SDTCisVTSmallerThanOp);
190 CASE(SDTCisOpSmallerThanOp);
191 CASE(SDTCisEltOfVec);
192 CASE(SDTCisSubVecOfVec);
193 CASE(SDTCVecEltisVT);
194 CASE(SDTCisSameNumEltsAs);
195 CASE(SDTCisSameSizeAs);
196 }
197 llvm_unreachable("Unknown constraint kind"); // Make MSVC happy.
198 #undef CASE
199 }
200
emitTypeConstraint(raw_ostream & OS,SDTypeConstraint C)201 static void emitTypeConstraint(raw_ostream &OS, SDTypeConstraint C) {
202 unsigned OtherOpNo = 0;
203 MVT VT;
204
205 switch (C.ConstraintType) {
206 case SDTypeConstraint::SDTCisVT:
207 case SDTypeConstraint::SDTCVecEltisVT:
208 if (C.VVT.isSimple())
209 VT = C.VVT.getSimple();
210 break;
211 case SDTypeConstraint::SDTCisPtrTy:
212 case SDTypeConstraint::SDTCisInt:
213 case SDTypeConstraint::SDTCisFP:
214 case SDTypeConstraint::SDTCisVec:
215 break;
216 case SDTypeConstraint::SDTCisSameAs:
217 case SDTypeConstraint::SDTCisVTSmallerThanOp:
218 case SDTypeConstraint::SDTCisOpSmallerThanOp:
219 case SDTypeConstraint::SDTCisEltOfVec:
220 case SDTypeConstraint::SDTCisSubVecOfVec:
221 case SDTypeConstraint::SDTCisSameNumEltsAs:
222 case SDTypeConstraint::SDTCisSameSizeAs:
223 OtherOpNo = C.OtherOperandNo;
224 break;
225 }
226
227 StringRef KindName = getTypeConstraintKindName(C.ConstraintType);
228 StringRef VTName = VT.SimpleTy == MVT::INVALID_SIMPLE_VALUE_TYPE
229 ? "MVT::INVALID_SIMPLE_VALUE_TYPE"
230 : getEnumName(VT.SimpleTy);
231 OS << formatv("{{{}, {}, {}, {}}", KindName, C.OperandNo, OtherOpNo, VTName);
232 }
233
234 std::vector<std::pair<unsigned, unsigned>>
emitTypeConstraints(raw_ostream & OS) const235 SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
236 using ConstraintsVecTy = SmallVector<SDTypeConstraint, 0>;
237 SequenceToOffsetTable<ConstraintsVecTy> ConstraintTable(
238 /*Terminator=*/std::nullopt);
239
240 std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts;
241 ConstraintOffsetsAndCounts.reserve(NodesByName.size());
242
243 SmallVector<StringRef> SkippedNodes;
244 for (const auto &[EnumName, Nodes] : NodesByName) {
245 ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
246
247 bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {
248 return ArrayRef(Other.getTypeConstraints()) != Constraints;
249 });
250
251 // If nodes with the same enum name have different constraints,
252 // treat them as if they had no constraints at all.
253 if (IsAmbiguous) {
254 SkippedNodes.push_back(EnumName);
255 continue;
256 }
257
258 // Don't add empty sequences to the table. This slightly simplifies
259 // the implementation and makes the output less confusing if the table
260 // ends up empty.
261 if (Constraints.empty())
262 continue;
263
264 // SequenceToOffsetTable reuses the storage if a sequence matches another
265 // sequence's *suffix*. It is more likely that we have a matching *prefix*,
266 // so reverse the order to increase the likelihood of a match.
267 ConstraintTable.add(ConstraintsVecTy(reverse(Constraints)));
268 }
269
270 ConstraintTable.layout();
271
272 OS << "static const SDTypeConstraint " << Target.getName()
273 << "SDTypeConstraints[] = {\n";
274 ConstraintTable.emit(OS, emitTypeConstraint);
275 OS << "};\n\n";
276
277 for (const auto &[EnumName, Nodes] : NodesByName) {
278 ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
279
280 if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {
281 ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/0, /*Size=*/0);
282 continue;
283 }
284
285 unsigned ConstraintsOffset =
286 ConstraintTable.get(ConstraintsVecTy(reverse(Constraints)));
287 ConstraintOffsetsAndCounts.emplace_back(ConstraintsOffset,
288 Constraints.size());
289 }
290
291 return ConstraintOffsetsAndCounts;
292 }
293
emitDesc(raw_ostream & OS,StringRef EnumName,ArrayRef<SDNodeInfo> Nodes,unsigned NameOffset,unsigned ConstraintsOffset,unsigned ConstraintCount)294 static void emitDesc(raw_ostream &OS, StringRef EnumName,
295 ArrayRef<SDNodeInfo> Nodes, unsigned NameOffset,
296 unsigned ConstraintsOffset, unsigned ConstraintCount) {
297 assert(haveCompatibleDescriptions(Nodes));
298 const SDNodeInfo &N = Nodes.front();
299 OS << " {" << N.getNumResults() << ", " << N.getNumOperands() << ", 0";
300
301 // Emitted properties must be kept in sync with haveCompatibleDescriptions.
302 unsigned Properties = N.getProperties();
303 if (Properties & (1 << SDNPHasChain))
304 OS << "|1<<SDNPHasChain";
305 if (Properties & (1 << SDNPOutGlue))
306 OS << "|1<<SDNPOutGlue";
307 if (Properties & (1 << SDNPInGlue))
308 OS << "|1<<SDNPInGlue";
309 if (Properties & (1 << SDNPOptInGlue))
310 OS << "|1<<SDNPOptInGlue";
311 if (Properties & (1 << SDNPVariadic))
312 OS << "|1<<SDNPVariadic";
313 if (Properties & (1 << SDNPMemOperand))
314 OS << "|1<<SDNPMemOperand";
315
316 OS << ", 0";
317 if (N.isStrictFP())
318 OS << "|1<<SDNFIsStrictFP";
319
320 OS << formatv(", {}, {}, {}, {}}, // {}\n", N.getTSFlags(), NameOffset,
321 ConstraintsOffset, ConstraintCount, EnumName);
322 }
323
emitDescs(raw_ostream & OS) const324 void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {
325 StringRef TargetName = Target.getName();
326
327 OS << "#ifdef GET_SDNODE_DESC\n";
328 OS << "#undef GET_SDNODE_DESC\n\n";
329 OS << "namespace llvm {\n";
330
331 std::vector<unsigned> NameOffsets = emitNodeNames(OS);
332 std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts =
333 emitTypeConstraints(OS);
334
335 OS << "static const SDNodeDesc " << TargetName << "SDNodeDescs[] = {\n";
336
337 for (const auto &[NameAndNodes, NameOffset, ConstraintOffsetAndCount] :
338 zip_equal(NodesByName, NameOffsets, ConstraintOffsetsAndCounts))
339 emitDesc(OS, NameAndNodes.first, NameAndNodes.second, NameOffset,
340 ConstraintOffsetAndCount.first, ConstraintOffsetAndCount.second);
341
342 OS << "};\n\n";
343
344 OS << formatv("static const SDNodeInfo {0}GenSDNodeInfo(\n"
345 " /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"
346 " {0}SDNodeNames, {0}SDTypeConstraints);\n\n",
347 TargetName, NodesByName.size());
348
349 OS << "} // namespace llvm\n\n";
350 OS << "#endif // GET_SDNODE_DESC\n\n";
351 }
352
run(raw_ostream & OS) const353 void SDNodeInfoEmitter::run(raw_ostream &OS) const {
354 emitSourceFileHeader("Target SDNode descriptions", OS, RK);
355 emitEnum(OS);
356 emitDescs(OS);
357 }
358
359 static TableGen::Emitter::OptClass<SDNodeInfoEmitter>
360 X("gen-sd-node-info", "Generate target SDNode descriptions");
361