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