xref: /freebsd/contrib/llvm-project/llvm/utils/TableGen/SDNodeInfoEmitter.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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