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