[llvm] [TableGen] Add a backend generating SDNode descriptions (PR #123002)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 17 05:16:01 PST 2025
================
@@ -0,0 +1,369 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Basic/SequenceToOffsetTable.h"
+#include "Common/CodeGenDAGPatterns.h" // For SDNodeInfo.
+#include "llvm/Support/CommandLine.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+using namespace llvm;
+
+static cl::OptionCategory SDNodeInfoEmitterCat("Options for -gen-sdnode-info");
+
+static cl::opt<std::string> TargetSDNodeNamespace(
+ "sdnode-namespace", cl::cat(SDNodeInfoEmitterCat),
+ cl::desc("Specify target SDNode namespace (default=<Target>ISD)"));
+
+static cl::opt<bool> WarnOnSkippedNodes(
+ "warn-on-skipped-nodes", cl::cat(SDNodeInfoEmitterCat),
+ cl::desc("Explain why a node was skipped (default=true)"), cl::init(true));
+
+namespace {
+
+class SDNodeInfoEmitter {
+ const RecordKeeper &RK;
+ const CodeGenTarget Target;
+ std::vector<SDNodeInfo> AllNodes;
+ std::map<StringRef, SmallVector<const SDNodeInfo *, 2>> TargetNodesByName;
+
+public:
+ explicit SDNodeInfoEmitter(const RecordKeeper &RK);
+
+ void run(raw_ostream &OS) const;
+
+private:
+ void emitEnum(raw_ostream &OS) const;
+
+ std::vector<unsigned> emitNodeNames(raw_ostream &OS) const;
+
+ std::vector<std::pair<unsigned, unsigned>>
+ emitTypeConstraints(raw_ostream &OS) const;
+
+ void emitDescs(raw_ostream &OS) const;
+};
+
+} // namespace
+
+static bool haveCompatibleDescriptions(const SDNodeInfo *N1,
+ const SDNodeInfo *N2) {
+ // Number of results/operands must match.
+ if (N1->getNumResults() != N2->getNumResults() ||
+ N1->getNumOperands() != N2->getNumOperands())
+ return false;
+
+ // Flags must match.
+ if (N1->isStrictFP() != N2->isStrictFP() ||
+ N1->getTSFlags() != N2->getTSFlags())
+ return false;
+
+ // We're only interested in a subset of node properties. Properties like
+ // SDNPAssociative and SDNPCommutative do not impose constraints on nodes,
+ // and sometimes differ between nodes sharing the same enum name.
+ constexpr unsigned PropMask = (1 << SDNPHasChain) | (1 << SDNPOutGlue) |
+ (1 << SDNPInGlue) | (1 << SDNPOptInGlue) |
+ (1 << SDNPMemOperand) | (1 << SDNPVariadic);
+
+ return (N1->getProperties() & PropMask) == (N2->getProperties() & PropMask);
+}
+
+static bool haveCompatibleDescriptions(ArrayRef<const SDNodeInfo *> Nodes) {
+ const SDNodeInfo *N = Nodes.front();
+ return all_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
+ return haveCompatibleDescriptions(Other, N);
+ });
+}
+
+static void warnOnSkippedNode(const SDNodeInfo *N, const Twine &Reason) {
+ PrintWarning(N->getRecord()->getLoc(), "skipped node: " + Reason);
+}
+
+SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
+ : RK(RK), Target(RK) {
+ const CodeGenHwModes &HwModes = Target.getHwModes();
+
+ // Figure out target SDNode namespace.
+ if (!TargetSDNodeNamespace.getNumOccurrences())
+ TargetSDNodeNamespace = Target.getName().str() + "ISD";
+
+ // Parse all SDNode records.
+ for (const Record *R : RK.getAllDerivedDefinitions("SDNode"))
+ AllNodes.emplace_back(R, HwModes);
+
+ // Filter nodes by the target SDNode namespace and create a mapping
+ // from an enum name to a list of nodes that have that name.
+ // The mapping is usually 1:1, but in rare cases it can be 1:N.
+ for (const SDNodeInfo &Node : AllNodes) {
+ StringRef QualifiedEnumName = Node.getEnumName();
+ auto [NS, EnumName] = QualifiedEnumName.split("::");
+
+ if (NS.empty() || EnumName.empty()) {
+ if (WarnOnSkippedNodes)
+ warnOnSkippedNode(&Node, "invalid enum name");
+ continue;
+ }
+
+ if (NS != TargetSDNodeNamespace)
+ continue;
+
+ TargetNodesByName[EnumName].push_back(&Node);
+ }
+
+ // Filter out nodes that have different "prototypes" and/or flags.
+ // Don't look at type constraints though, we will simply skip emitting
+ // the constraints if they differ.
+ decltype(TargetNodesByName)::iterator Next;
+ for (auto I = TargetNodesByName.begin(), E = TargetNodesByName.end(); I != E;
+ I = Next) {
+ Next = std::next(I);
+
+ if (haveCompatibleDescriptions(I->second))
+ continue;
+
+ if (WarnOnSkippedNodes)
+ for (const SDNodeInfo *N : I->second)
+ warnOnSkippedNode(N, "incompatible description");
+
+ TargetNodesByName.erase(I);
+ }
+}
+
+void SDNodeInfoEmitter::emitEnum(raw_ostream &OS) const {
+ OS << "#ifdef GET_SDNODE_ENUM\n";
+ OS << "#undef GET_SDNODE_ENUM\n\n";
+ OS << "namespace llvm::" << TargetSDNodeNamespace << " {\n\n";
+
+ if (!TargetNodesByName.empty()) {
+ StringRef FirstName = TargetNodesByName.begin()->first;
+ StringRef LastName = TargetNodesByName.rbegin()->first;
+
+ OS << "enum GenNodeType : unsigned {\n";
+ OS << " " << FirstName << " = ISD::BUILTIN_OP_END,\n";
+
+ for (StringRef EnumName : make_first_range(drop_begin(TargetNodesByName)))
+ OS << " " << EnumName << ",\n";
+
+ OS << "};\n\n";
+ OS << "static constexpr unsigned GENERATED_OPCODE_END = " << LastName
+ << " + 1;\n\n";
+ } else {
+ OS << "static constexpr unsigned GENERATED_OPCODE_END = "
+ "ISD::BUILTIN_OP_END;\n\n";
+ }
+
+ OS << "} // namespace llvm::" << TargetSDNodeNamespace << "\n\n";
+ OS << "#endif // GET_SDNODE_ENUM\n\n";
+}
+
+std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
+ StringToOffsetTable NameTable;
+
+ std::vector<unsigned> NameOffsets;
+ NameOffsets.reserve(TargetNodesByName.size());
+
+ for (StringRef EnumName : make_first_range(TargetNodesByName)) {
+ SmallString<64> DebugName;
+ (TargetSDNodeNamespace + "::" + EnumName).toVector(DebugName);
----------------
arsenm wrote:
Use raw_svector_ostream?
https://github.com/llvm/llvm-project/pull/123002
More information about the llvm-commits
mailing list