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
17using namespace llvm;
18
19static cl::OptionCategory SDNodeInfoEmitterCat("Options for -gen-sdnode-info");
20
21static cl::opt<std::string> TargetSDNodeNamespace(
22 "sdnode-namespace", cl::cat(SDNodeInfoEmitterCat),
23 cl::desc("Specify target SDNode namespace (default=<Target>ISD)"));
24
25static 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(Val: true));
28
29namespace {
30
31class SDNodeInfoEmitter {
32 const RecordKeeper &RK;
33 const CodeGenTarget Target;
34 std::map<StringRef, SmallVector<SDNodeInfo, 2>> NodesByName;
35
36public:
37 explicit SDNodeInfoEmitter(const RecordKeeper &RK);
38
39 void run(raw_ostream &OS) const;
40
41private:
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
54static 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
75static bool haveCompatibleDescriptions(ArrayRef<SDNodeInfo> Nodes) {
76 const SDNodeInfo &N = Nodes.front();
77 return all_of(Range: drop_begin(RangeOrContainer&: Nodes), P: [&](const SDNodeInfo &Other) {
78 return haveCompatibleDescriptions(N1: Other, N2: N);
79 });
80}
81
82static void warnOnSkippedNode(const SDNodeInfo &N, const Twine &Reason) {
83 PrintWarning(WarningLoc: N.getRecord()->getLoc(), Msg: "skipped node: " + Reason);
84}
85
86SDNodeInfoEmitter::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(ClassName: "SDNode")) {
98 SDNodeInfo Node(R, HwModes);
99 auto [NS, EnumName] = Node.getEnumName().split(Separator: "::");
100
101 if (NS.empty() || EnumName.empty()) {
102 if (WarnOnSkippedNodes)
103 warnOnSkippedNode(N: Node, Reason: "invalid enum name");
104 continue;
105 }
106
107 if (NS != TargetSDNodeNamespace)
108 continue;
109
110 NodesByName[EnumName].push_back(Elt: 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(x: I);
119
120 if (haveCompatibleDescriptions(Nodes: I->second))
121 continue;
122
123 if (WarnOnSkippedNodes)
124 for (const SDNodeInfo &N : I->second)
125 warnOnSkippedNode(N, Reason: "incompatible description");
126
127 NodesByName.erase(position: I);
128 }
129}
130
131void 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(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\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
158std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
159 StringToOffsetTable NameTable;
160
161 std::vector<unsigned> NameOffsets;
162 NameOffsets.reserve(n: NodesByName.size());
163
164 for (StringRef EnumName : make_first_range(c: NodesByName)) {
165 SmallString<64> DebugName;
166 raw_svector_ostream SS(DebugName);
167 SS << TargetSDNodeNamespace << "::" << EnumName;
168 NameOffsets.push_back(x: NameTable.GetOrAddStringOffset(Str: DebugName));
169 }
170
171 NameTable.EmitStringTableDef(OS, Name: Target.getName() + "SDNodeNames");
172 OS << '\n';
173
174 return NameOffsets;
175}
176
177static 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
201static 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(Kind: C.ConstraintType);
228 StringRef VTName = VT.SimpleTy == MVT::INVALID_SIMPLE_VALUE_TYPE
229 ? "MVT::INVALID_SIMPLE_VALUE_TYPE"
230 : getEnumName(T: VT.SimpleTy);
231 OS << formatv(Fmt: "{{{}, {}, {}, {}}", Vals&: KindName, Vals&: C.OperandNo, Vals&: OtherOpNo, Vals&: VTName);
232}
233
234std::vector<std::pair<unsigned, unsigned>>
235SDNodeInfoEmitter::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(n: 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(Range: drop_begin(RangeOrContainer: Nodes), P: [&](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(Elt: 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(Seq: ConstraintsVecTy(reverse(C&: Constraints)));
268 }
269
270 ConstraintTable.layout();
271
272 OS << "static const SDTypeConstraint " << Target.getName()
273 << "SDTypeConstraints[] = {\n";
274 ConstraintTable.emit(OS, Print: 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(Range&: SkippedNodes, Element: EnumName)) {
281 ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/args: 0, /*Size=*/args: 0);
282 continue;
283 }
284
285 unsigned ConstraintsOffset =
286 ConstraintTable.get(Seq: ConstraintsVecTy(reverse(C&: Constraints)));
287 ConstraintOffsetsAndCounts.emplace_back(args&: ConstraintsOffset,
288 args: Constraints.size());
289 }
290
291 return ConstraintOffsetsAndCounts;
292}
293
294static 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(Fmt: ", {}, {}, {}, {}}, // {}\n", Vals: N.getTSFlags(), Vals&: NameOffset,
321 Vals&: ConstraintsOffset, Vals&: ConstraintCount, Vals&: EnumName);
322}
323
324void 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(t: NodesByName, u&: NameOffsets, args&: ConstraintOffsetsAndCounts))
339 emitDesc(OS, EnumName: NameAndNodes.first, Nodes: NameAndNodes.second, NameOffset,
340 ConstraintsOffset: ConstraintOffsetAndCount.first, ConstraintCount: ConstraintOffsetAndCount.second);
341
342 OS << "};\n\n";
343
344 OS << formatv(Fmt: "static const SDNodeInfo {0}GenSDNodeInfo(\n"
345 " /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"
346 " {0}SDNodeNames, {0}SDTypeConstraints);\n\n",
347 Vals&: TargetName, Vals: NodesByName.size());
348
349 OS << "} // namespace llvm\n\n";
350 OS << "#endif // GET_SDNODE_DESC\n\n";
351}
352
353void SDNodeInfoEmitter::run(raw_ostream &OS) const {
354 emitSourceFileHeader(Desc: "Target SDNode descriptions", OS, Record: RK);
355 emitEnum(OS);
356 emitDescs(OS);
357}
358
359static TableGen::Emitter::OptClass<SDNodeInfoEmitter>
360 X("gen-sd-node-info", "Generate target SDNode descriptions");
361